标签:独立 结束 tst tac 形式 实例化 遍历 rgs 并且
mybatis 是一个用java编写的持久层框架, 它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等繁杂过程,它使用了ORM思想实现了结果 集的封装
ORM Object Relational Mapping 对象关系映射,把数据库表和实体类及实体类的属性对应起来,让我们可以操作实体类就实现操作数据库表
创建数据库,创建User表
创建maven工程并导入坐标
<project ...>
...
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
</dependencies>
</project>
编写实体类
public class User{
private Integer id;
private String username;
private String password;
getter()...
setter()...
toString()...
}
创建UserDao接口
public interface UserDao{
List<User> findAll(); //查询所有用户
}
创建主配置文件SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- mybatis的主配置文件 -->
<configuration>
<!-- 配置环境 -->
<environments default="mysql">
<!-- 配置mysql的环境 -->
<environment id="mysql">
<!-- 配置事务的类型 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<!-- 配置连接数据库的四个基本信息 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
<mappers>
<mapper resource="com/whteway/dao/UserDao.xml"/>
</mappers>
</configuration>
创建映射配置文件UserDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.whteway.dao.UserDao">
<!-- 配置查询所有,id为对应方法名,resultType指定结果封装位置 -->
<select id="findAll" resultType="com.whteway.domain.User">
select * from user
</select>
</mapper>
使用
public void test(){
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工厂生产SqlSession对象
SqlSession session = factory.openSession();
//4.使用SqlSession创建Dao接口的代理对象
UserDao userDao = session.getMapper(UserDao.class);
//5.使用代理对象执行方法
List<User> users = userDao.findAll();
for(User user: users)
System.out.println(user);
//6.释放资源
session.close();
in.close();
}
注解方式
主配置文件SqlMapConfig.xml
...
<mappers>
<!-- <mapper resource="com/whteway/dao/UserDao.xml"/> -->
<mapper class="com.whteway/dao/UserDao"/>
</mappers>
...
UserDao.java
public interface UserDao{
@Select("select * from uesr")
List<User> findAll();
}
删除UserDao.xml
连接池
mybatis连接池提供三种方式的配置
配置位置:主配置文件中dataSource标签的type属性
type取值:
POOLED
采用传统的javax.sql.DataSource规范中的连接池
UNPOOLED
采用传统的获取连接的方式,实现了DataSource接口,没有用连接池思想
JNDI
采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器能拿到的DataSource不一样,如果不是web或者maven的war工程,就不能使用
事务管理
自定义dao实现类(不常用, 用来研究原理)
public class UserDaoImpl implements UserDao{
private SqlSessionFactory factory;
public UserDaoImpl(SqlSessionFactory factory){
this.factory = factory;
}
public List<User> findAll(){
//使用工厂创建SqlSession对象
SqlSession session = factory.openSession();
//使用session对象执行sql语句
List<User> users = session.selectList("映射配置文件的namespace.findAll");
session.close();
//返回查询结果
return users;
}
}
//-----------------使用----------------
public void test(){
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
UserDao userdao = new UserDaoImpl(factory);
List<User> users = userDao.findAll();
in.close();
}
使用过程
读配置文件
读取配置文件时用绝对路径和相对路径(web工程部署后没有src路径)都有一定问题,实际开发中一般有两种方法
创建SqlSessionFactory工厂
建造者模式(Builder Pattern)
使用工厂生产SqlSession对象
工厂模式(Factory Pattern)
使用SqlSession创建Dao接口的代理对象
代理模式(Proxy Pattern)
使用代理对象执行方法
释放资源
自定义dao中selectList()方法的执行流程,也是代理对象增强的逻辑
创建代理对象流程
根据dao接口的字节码创建dao的代理对象
public <T> T getMapper(Class<T> daoInterfaceClass){
/*
类加载器,使用和被代理对象相同的类加载器
接口数组,和被代理对象实现相同的接口
增强逻辑,自己提供,此处是一个InvocationHandler接口,需要写一个该接口的实现类,在其中调用selectList()方法
*/
Proxy.newProxyInstance(类加载器, 接口数组,增强逻辑);
}
分析用到的类
public void test(){
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
SqlSession session = factory.openSession();
UserDao userDao = session.getMapper(UserDao.class);
List<User> users = userDao.findAll();
for(User user: users) System.out.println(user);
session.close();
in.close();
}
解决读取配置信息的问题
读取xml文件具体实现可以使用dom4j和xpath技术,坐标名dom4j和jaxen
//模拟Mybatis的配置类
public class Configuration{
private String driver;
private String url;
private String username;
private String password;
/*
mappers中的数据结构形如
-------------------------------------------------------------------------
key | value
-----------------------------------|-------------------------------------
"com.whteway.dao.UserDao.findAll()"| queryString:"select * from user"
| resultType:"com.whteway.domain.User"
-----------------------------------|-------------------------------------
"com.whteway.dao.StuDao.findAll()" | queryString:"select * from stu"
| resultType:"com.whteway.domain.Stu"
-----------------------------------|-------------------------------------
"package.daoInterface.method()" | queryString: SQL语句
| resultType: 实体类的全限定类名
*/
private Map<String Mapper> mappers;
//to generate getters and setters exclude setMappers;
public void setMappers(Map<String, Mapper> mappers){
this.mappers.putAll(mappers); //追加而不是覆盖
}
}
//用于封装映射配置文件的信息,SQL语句和封装实体类的全限定类名
//SqlMapConfig.xml的mappers标签中的每一个mapper标签对应一个Mapper对象,
//也就是说第一个映射配置文件对应一个Mapper对象
public class Mapper{
private String queryString;
private String resultType;
//to generate getters and setters;
}
//读取xml文件的工具类
public class XMLConfigBuilder{
//读取SqlMapConfig.xml文件并调用方法读取映射配置文件或加注解的类,将配置信息存入Configuration中并返回
public static Configuration loadConfiguration(InputStream is){
Configuration config = new Configuration();
//1.读取SqlMapConfig文件,将数据库连接信息存入config
/*2.根据SqlMapConfig文件中的mappers,属性循环读取每一个映射配置文件
如果用的是resource属性
mappers = loadMapperResource(resource.value);
如果用的是class属性
mappers = loadMapperAnnotation(class.value);
使用追加的方式将mappers存入config.mappers中
config.setMappers(mappers);
*/
return config;
}
//读取映射配置文件
public static Map<String, Mapper> laodMapperResource(String daoClassPath) throws Exception{
Map<String, Mapper> mappers = new HashMap<String, Mapper>();
/*
1 循环读取每一个映射配置文件,如UserDao.xml
2 读取mappers标签的namespace
3 循环mappers标签中的每一个子标签(对应一个方法)
4 读取id,组成namespace.id形式的字符串,作为key
5 读取sql语句存入Mapper中的queryString
6 读取resultType存入Mapper中的resultType
7 将Mapper作为value,与key组成键值对,存入mappers中
*/
return mappers;
}
//读取加注解的类
public static Map<String, Mapper> laodMapperAnnotation(String daoClassPath) throws Exception{
Map<String, Mapper> mappers = new HashMap<String, Mapper>();
/*
1.得到dao接口的字节码对象
2.循环每一个方法(对应一个Mapper)
3. 判断是否有select注解,如果有
4. 获取各种数据封装到一个Mapper中,包名,类名,@Select的value值,返回类型的泛型的实际类型,方法名
5. 把Mapper存入mappers中
*/
return mappers;
}
}
Resources
//将文件名转换为输入流
public class Resources{
//根据传入的参数,返回一个输入流
//1.Resources.class 获取当前类的字节码
//2.getClassLoader() 获取这个字节码的类加载器
//3.getResourceAsStream(filePath) 根据类加载器读取文件配置
public static InputStream getResourceAsStream(String filePath){
return Resources.class.getClassLoader().getResourceAsStream(filePath);
}
}
SqlSessionFactoryBuilder 读取配置文件将配置信息存入配置类中的代码在此类中调用
public class SqlSessionFactoryBuilder{
//根据参数的字节输入流来构建一个SqlSessionFactory工厂
public SqlSessionFactory build(InputStream config){
Configuration cfg = XmlConfigBuilder.loadConfiguration(config);
return new DefaultSqlSessionFactory(config);
}
}
SqlSessionFactory
//接口
public interface SqlSessionFactory{
public SqlSession openSession();
}
//实现类
public class DefaultSqlSessionFactory implements SqlSessionFactory{
private Configuration cfg;
//实例化此类必须传入配置信息
public DefaultSqlSessionFactory(Configuration cfg){
this.cfg = cfg;
}
//创建一个新的操作数据库的对象
@Override
public SqlSession openSession(){
return new DeaultSqlSession(cfg);
}
}
SqlSession jdbc代码调用全在此类和此类的代理对象中
//接口,和数据库交互的核心类
public interface SqlSession{
//创建代理对象,参数是dao接口的字节码
public <T> T getMapper(Class<T> daoInterfaceClass);
//释放资源
public void close();
}
//实现类
public class DefaultSqlSession implements SqlSession{
private Configuration cfg;
private Connection conn;
//实例化此类必须传入配置信息
public DefaultSqlSession(Configuration cfg){
this.cfg = cfg;
conn = DataSourceUtil.getConnection(cfg);
//使用传统JDBC方式获取连接,创建数据源,建议封装到一个工具类中,源码中叫executor
try{
Class.forName(cfg.getDriver());
conn = DriverManager.getConnection(cfg.getUrl(), cfg.getUserName(), cfg.getPassword())
}catch(Exception e){
throw new RuntimeException(e);
}
}
@Override
public <T> T getMapper(Class<T> daoInterfaceClass){
return Proxy.newProxyInstance(daoInterfaceClass.getClassLoader()), new Class[]{daoInterfaceClass}, new MapperProxy(cfg.getMappers()));
}
//关闭Connection
public void close(){
if(conn != null){
try{
conn.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
}
//作为创建代理对象时的第三个参数,用于对方法进行增强,此处的增强只需要调用selectList()方法
public class MapperProxy implements InvocationHandler{
//接收参数
private Map<String, Mappers> mappers;
private Connection conn;
public MapperProxy(Map<String, Mapper> mappers, Connection conn){
this.mappers = mappers;
this.conn = conn;
}
//定义增强逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
//获取方法名
String methodName = method().getName();
//获取方法所在的类的名称
String className = method.getDeclaringClass().getName();
//组合key
String key = className + "." + methodName();
//获取mappers中的mapper对象
Mapper mapper = mappers.get(key);
//判断参数是否合法
if(mapper == null)
throw new IllegalArgumentException("传入参数有误");
//使用传统JDBC方式执行SQL语句并封装到实体类列表中返回,建议作为selectList()方法封装到工具类中
selectList();
return null;
}
}
DML操作后要调用SqlSession.commit()方法进行事务提交
需要指定参数类型(实体类的全限定类名,基本类型可以用int,Integer,java.lang.Integer)
SQL语句传参时需要用#{实体类的属性值},此处的属性值是setter方法的set后的字符串并首字母小写
保存 UserDao.saveUser(User user);
xml
<insert id="saveUser" parameterType="com.whteway.domain.User">
insert into user(username, address, sex, birthday) values(#{id},#{username},#{password})
</insert>
更新 UserDao.updateUser(User user);
xml
<update id="updateUser" parameterType="com.whteway.domain.User">
update user set username=#{username}, password#{password}) where id=#{id}
</insert>
注解
删除 UserDao.deleteUser(Integer userId);
xml
<delete id="deleteUser" parameterType="Integer">
update from user where id=#{uid}
</delete>
查询单个 UserDao.findById(Integer id);
xml
<select id="findById" parameterType="INT" resultType="com.whteway.domain.User">
select * from user where id=#{id}
</select>
查询多个 UserDao.findByName(String name);
xml,传参的时候需要加%%(推荐,底层用的是PerparedStatement执行语句),不加%%可以使用固定写法 ‘%${value}%‘(底层用的是Statement执行语句)
<select id="findByName" parameterType="string" resultType="com.whteway.domain.User">
select * from user where username like #{name}
/* select * from user where username like '%${value}' */
</select>
聚合函数 UserDao.findTotal();
xml
<select id="findTotal" resultType="int">
select count(id) from user;
</select>
OGNL表达式
Object Graphic Navigation Language 对象 图 导航 语言
通过对象的取值方法来获取数据,省略get
如:user.getUsername(); --> user.username
mybatis中可以直接写username而不写user.,因为在parameterType中已经指定了类
根据自定义的条件类查询 UserDao.findByName(QueryVo vo); 设QueryVo中有属性User
xml
<select id="findByName" parameterType="com.whteway.domain.QueryVo" resultType="com.whteway.domain.User">
select * from user where username like #{user.username}
</select>
获取保存数据的id
sql中有一条语句"select last_insert_id();"可以返回上一条保存的记录的id,使用下面的配置后mybatis会将查出来的id值存入作为参数的user对象中
<insert ...>
<selectKey keyProperty="id" keyColum="id" resultType="int" order="AFTER">
select last_insertid()
</selectKey>
insert ...
</insert>
返回值与列名的对应
返回值可以是基本类型,也可以是pojo(Plain Ordinary Java Object,普通java对象,即javaBeans)
当实体类属性和查询结果列名不一致时会导致数据无法封装进resultType中
可以在sql语句中通过起别名的方式解决
在映射配置文件中配置查询结果的列名和实体类属性名的对应关系,设User中有uid,name,pwd属性
<!-- 定义 -->
<resultMap id="userMap" type="com.whteway.domain.User">
<id property="uid" column="id"></id>
<result property="name" column="username"></result>
<result property="pwd" column="password"></result>
</resultMap>
<!-- 使用,替换resultType -->
<select id="findAll" resultMap="userMap">
select * from user
</select>
自定义dao实现类完成CRUD
用session的不同方法执行sql语句,传入映射配置文件的namespace.SQL语句的id和sql需要的参数
selectOne,selectList,insert,update
public class UserDaoImpl implements UserDao{
//只有第二步需要变化
@Override
public void saveUser(User user){
//1.使用工厂创建SqlSession对象
SqlSession session = factory.openSession();
//2.使用session对象执行sql语句
session.insert("com.whteway.dao.UserDao.saveUser", user);
//3.释放资源
session.close();
//4.返回查询结果
return;
}
}
session.selectList()源码分析
//UserDaoImpl.java
public List<User> findAll(){
session.selectList("com.whteway.dao.UserDao.findAll");
}
//DefaultSqlSession.java
public <E> List<E> selectList(String statement){
this.selectList(statement, null);
}
public <E> List<E> selectList(String statement, Object parameter){
this.selectList(statement, parameter, RowBounds.DEFAULT);
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds){
try{
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapColeection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e){
} finally {
ErrorContext.instance().reset();
}
}
//CachingExecutor.java
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException{
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException{
...
return delegate.<E> query(ms, parameterObect, rowBounds, resultHandler, key, boundSql);
}
//BaseExecutor.java...
//经过一系列的调用,会走到PreparedStatementHandler类中的一个query方法
//PreparedStatementHandler.java
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException{
//出现JDBC代码
PreparedStatement ps = (PreparedStatement)statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
//DefaultResultSetHandler.java
public List<Object> handleResultSets(Statement stmt) throws SQLException{
//封装结果集的代码
}
创建dao代理对象源码分析
properties标签
配置properties,可以用标签配,也可以引用外部配置文件信息
resource属性,用于指定配置文件的位置,按照类路径的写法来写,并且必须存在于类路径下(放在和SqlMapConfig.xml相同路径下)
url属性,按照url的写法来写地址:协议://主机:端口 URI
<configuration>
<properties resource="jdbcConfig.properties">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
<!-- 在实际配置中用"${driver}"取值 -->
</configuration>
typeAliases标签
配置别名,只能配置实体类的别名,当指定了别名就不再区分大小写
mybatis为基本类型和String配置了别名
<typeAliases>
<typeAlias type="com.whteway.domain.User" alias="user"></typeAlias>
</typeAliases>
package标签
在typeAliases中使用:为整个包中的类配置别名,当指定之后,该包下的实体类都会以类名注册别名
在mappers中使用:指定dao接口所在的包,指定后,不需要再写resource或class了
<typeAliases>
<package name="com.whteway.domain"></package>
</typeAliases>
<mappers>
<package name="com.whteway.dao"></package>
</mappers>
if标签:满足条件则将标签内的语句拼接到sql语句后
<!-- 根据条件查询,传入的条件可能是id,也可能是username -->
<select id="findUserByCondition" resultMap="userMap" parameterType="user">
select * from where 1=1
<if test="username != null">and username=#{username}</if>
<if test="id != null">and id=#{id}</if>
</select>
where标签:sql后加 where 再将标签内的语句拼接到sql后
<!-- if标签代码改进 -->
<select id="findUserByCondition" resultMap="userMap" parameterType="user">
select * from
<where>
<if test="username != null">and username=#{username}</if>
<if test="id != null">and id=#{id}</if>
</where>
</select>
foreach标签
循环将标签内部的语句和属性中的片段拼接到sql后
open属性:开始片段;close属性:结束片段;separator属性:每个元素的分割符
<!-- 根据queryvo中提供的id集合,查询用户信息 -->
<select id="findByIds" resultMap="userMap" parameterType="queryvo">
select * from user
<where>
<if test="ids != null and ids.size() > 0">
<foreach collection="ids" open="and id in (" close=")" item="id" separator=",">
#{id}
</foreach>
</if>
</where>
</select>
sql标签
抽取重复的sql语句
<!-- 定义,末尾不要加分号 -->
<sql id="defaultUser">select * from user</sql>
<!-- 使用 -->
<select id="findUserByCondition" resultMap="userMap" parameterType="user">
<include refid="defaultUser"></include>
where id=#{id}
</select>
标签:独立 结束 tst tac 形式 实例化 遍历 rgs 并且
原文地址:https://www.cnblogs.com/whteway/p/12051992.html