MyBatis
1. 简介
1.1. 基于ORM半自动化轻量级持久层框架
1.1.1. 底层是对jdbc代码进行封装、对jdbc中频繁创建释放数据库连接、硬编码、手动封装参数和结果集等问题进行了规避和解决(2010年6月由apache迁移大google ibatis3.X更名为mybatis)
1.1.2. ORM对象-关系映射
- 基于XML定义POJO与数据库对应关系
- 需要手动编写SQL语句
- 启动时需要资源少
- SQL编写在XML配置文件中解决了硬编码问题
- SQL和Java代码分开功能边界清晰
- 手动编写SQL语句、灵活性较高可对SQL语句进行优化提高执行效率
- selectOne(statementId,args)
- selectList(statementId,args)
- insert(statementId,args)
- update(statementId,args)
- delete(statementId,args)
1.1.3. 半自动化
1.1.4. 轻量
1.1.5. 优点
1.1.6. Hibernate 只需操作POJO持久化对象就可完成对数据库的操作,无需编写SQL语句,功能强大,失去了对SQL语句优化的可能性
2. 基本开发步骤
2.1. 导入MyBatis相关jar包
2.2. 创建sqlMapConfig.xml文件
2.3. 创建POJO实体类
2.4. 创建实体类对应的mapper.xml映射文件
2.5. 基本CRUD操作
2.5.1. 构建sqlSessionFactory对象获取sqlSession对象访问数据库 【调用sqlSession相关方法】
3. 配置文件
3.1. 映射配置文件mapper.xml
3.1.1. dtd约束头
3.1.2. 根标签 mapper
属性namespace定义命名空间
• select标签【定义查询操作】
属性 id 定义 唯一标识 和 namespace共同组成
属性parameterType 定义参数类型
属性resultType 定义返回值类型
属性resultMap自定义返回值 返回结果不是POJO基本表类型
• resultMap标签(自定义返回结果集、复杂映射开发、多表操作)
属性 id=“xx” 定义唯一标识
type=“xx” 主表全限定类名
• OneToOne
• result标签定义主表POJO实体与表【查询结果集】字段对应关系
属性 property=“” 定义POJO字段名
属性column=“” 定义POJO对应的查询结果集字段名
• association标签 定义关联表POJO实体与表【查询结果集】字段对应关系
属性property=“” 对应主表POJO实体里面定义的关联表的POJO实体对象名称
属性javaType=“” 关联表的POJO实体的全限定类名
• result标签定义关联表POJO实体与关联表字段对应关系
属性 property=“” 定义关联表POJO字段名
属性column=“” 定义关联表POJO对应的字段名
• OneToMany
• result标签定义主表POJO实体与表【查询结果集】字段对应关系
属性 property=“” 定义POJO字段名
属性column=“” 定义POJO对应的查询结果集字段名
• collection标签 定义子表POJO实体与表【查询结果集】字段对应关系
属性property=“” 对应主表POJO实体里面定义的子表的POJO实体对象名称
属性ofType=“” 子表的POJO实体的全限定类名
• result标签定义子表POJO实体与子表字段对应关系
属性 property=“” 定义子表POJO字段名
属性column=“” 定义子表POJO对应的字段名
• ManyToMany
【实际上是双向的一对多查询】
• insert标签【定义添加操作】
相同属性同select
属性useGeneratedKeys=true/false制定是否使用数据库主键自增生成策略
属性keyProperty 指定 POJO对应主键字段名
属性keyColumn 指定数据库表对应字段名
• selectKey标签【指定序列类型的数据库主键生成】
属性keyProperty 指定 POJO对应主键字段名
属性keyColumn 指定数据库表对应字段名
属性order =before/after 定义执行前后顺序
属性resultType 定义返回值类型
标签内容【select sequence.nextval from dual】
• update标签【定义修改操作】属性同上
• delete标签【定义删除操作】属性同上
• 标签中的SQL
【select * from user where id =#{id}】
【 #{} 中的字段名与 POJO字段名对应 】
• 动态SQL: <where>标签
包裹if标签 自动为SQL语句
加上where关键字并去除SQL语句中第一个and
• 动态SQL: if标签
属性:test=“” 判断POJO属性从而动态拼接SQL
• 动态SQL: foreach标签 【用于条件多值查询】
属性collection=“array” 定义 参数类型
属性open = “id in (”
定义 SQL 开始内容
属性close = ")" 定义SQL结束内容
属性item= “id” 定义 接收变量
属性 separator = “,”定义SQL多值分隔符
• 动态SQL:sql标签【SQL片段抽取】
属性id=“xx” 定义SQL片段唯一标识
在需要调用的地方使用
include标签 【引用SQL片段】
属性refid=“xxx” 值为引用sql片段的id唯一标识
3.2. 核心配置文件sqlMapConfig.xml
3.2.1. dtd约束头
3.2.2. 根标签configuration
根标签下的子标签有固定的从上到下的编写顺序
- properties标签【加载外部的数据配置文件】
属性 resource = “配置文件路径”
<properties resource="jdbc.properties">
</properties>
• 创建properties配置文件【定义数据库驱动、用户名、密码等相关信息】
• EL表达式注入运行环境中的数据源
- settings【mybatis全局参数设置】
• setting具体参数设置
属性name="cacheEnabled" 配置是否开启二级缓存 值为true/false
属性name="logImpl" 配置是否开启二级缓存 值为LOG4J/SLF4J/.. 制定日志文件输出
....... 各种全局配置
- typeAliases标签 【给POJO实体类的全限定类名起别名】
• typeAlias标签【给单独的实体起别名】
属性 type = “全限定类名”
属性 alias = “别名”
• package 标签 【批量起别名,类名为该包下所有类的类名,不区分大小写】
属性 name = "全限定包名"
• MyBatis默认定义的常用数据类型别名
string 【String】、long【Long】
int 【Integer】、double【Double】
boolean【Boolean】
- plugins标签【用于引入自定义插件或者MyBatis第三方插件】
• plugin标签【定义具体要引入的插件】
属性interceptor=“xx”要引入插件的全限定类名
• property标签【用于定义插件的一些初始化参数】
属性name='xx' 定义参数名称
属性 value=“xx” 定义参数值
- environments标签【定义运行环境支持多环境配置】
属性default=development 定义默认运行环境
可使用子标签定义多种环境
【开发环境】【测试环境】【生成环境】
• environment标签
属性id=“development” 定义唯一标识
• transactionManager标签【事务管理器】定义事务管理方式 【2种】
属性 type=JDBC 定义事务使用JDBC进行提交/回滚,依赖于数据源获取的连接管理事务
属性 type=MANAGED 对事物什么都不处理,交由容器【Tomcat/jboss】来管理事务
一般不用这种模式
• dataSource标签【定义mybatis数据源】
属性 type【3种】
type=UNPOOLED 【每次请求数据时获取连接用完关闭】
type=POOLED 【使用连接池管理JDBC连接】
type=JNDI 【使用web容器管理】
• property标签定义数据库驱动、用户名等连接信息
- mappers标签【定义加载映射配置文件】
• mapper标签【引入映射配置文件3种属性方式引入】
属性 resource=“XML文件项目相对路径路径”
属性 url=“盘符文件路径”
属性 class = “接口/实现类的全限定类名”
• package标签【基于package目录自动扫描引入】
属性 name="全限定包名"
要求接口和对应的的映射配置文件同包同名
4. 相关API
4.1.
Resource工具类【配置文件的加载,把配置文件加载成字节输入流】
InputStream resourceAsStream =
Resources.getResourceAsStream("sqlMapConfig.xml");
4.2.
解析配置文件并创建SQLSessionFactory工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
4.3.
生成SQLSession
sqlSession = sqlSessionFactory.openSession();
【默认开启一个事务但不会自动提交】
【增、删、改、时要手动提交事务】
.openSession(true); 自动提交事务
4.4. SQLSession调用方法
4.4.1. selectOne(statementId,args)
4.4.2. selectList(statementId,args)
4.4.3. insert(statementId,args)
4.4.4. update(statementId,args)
4.4.5. delete(statementId,args)
4.4.6. getMapper(接口.class)返回接口的代理类对象
4.5. sqlSession.commit();
5. DAO层传统开发方式
5.1.
手动编写接口及其实现类
在实现类中重写接口方法调用相关API
创建实现类对象调用实现类中方法完成数据访问
6. DAO层代理开发方式
6.1.
只需编写mapper接口【相当于Dao接口】
由MyBatis框架通过JDK动态代理自动生成Mapper接口代理对象【相当于手动编写的Dao接口实现类】
使用相关API中的getMapper(接口.class)调用
编写mapper接口中的方法完成数据访问
6.1.1. 遵循以下规则
mapper.xml中的namespace与mapper接口的全限定名相同
mapper接口中的方法名和mapper.xml文件中statementId相同
mapper接口方法的输入参数和mapper.xml文件中parameterType的类型相同
mapper接口方法的返回值类型和mapper.xml文件中resultType的类型相同
7. 注解开发
不用再编写映射配置文件
7.1. @Insert【实现新增】
7.1.1. @Insert("insert into user values(#{id},#{username})")
7.2. @Update【实现修改】
7.2.1. @Update("update user set username = #{username} where id = #{id}")
7.3. @Delete【实现删除】
7.3.1. @Delete("delete from user where id =
#{id}")
7.4. @select【实现查询】
7.4.1. @Select("select * from user")
7.5. @Result【实现结果集封装】
7.6. @Results【与@Result一起使用】封装多个结果集
7.7. @One【实现一对一结果集封装】
7.7.1. @Select("select * from
orders")
@Results({
@Result(property =
"id",column = "id"),
@Result(property =
"orderTime",column = "orderTime"),
@Result(property =
"total",column = "total"),
@Result(property =
"user",column = "uid",javaType = User.class,
one=@One(select =
"com.lagou.mapper.IUserMapper.findUserById"))
})
7.8. @Many【实现一对多结果集封装】
7.8.1. @Select("select * from user")
@Results({
@Result(property =
"id",column = "id"),
@Result(property =
"username",column = "username"),
@Result(property =
"orderList",column = "id",javaType = List.class,
many=@Many(select =
"com.lagou.mapper.IOrderMapper.findOrderByUid"))
})
8. MyBatis缓存
【缓存就是内存中的数据,是对数据中查询结果的保存,减少与数据库的交互提高查询效率】
8.1. 一级缓存【SqlSession级别】默认开启
8.1.1. 底层数据结构是一个hashMap集合,不同的SqlSession之间互补影响
map中的CacheKey组成【statementId、parameters、boundSql、rowBounds】
map中的value为查询结果实体对象
8.1.2. 添加/删除/修改/手动提交事务 都会自动清空一级缓存
手动调用clearCache()方法也可清空一级缓存
8.2.
二级缓存【mapper/namespace 级别】不同sqlSession共享同一个namespace二级缓存
默认关闭
需要在sqlMapConfig.xml配置文件中使用settings标签手动开启
单独开启二级缓存 可在映射配置文件中使用cache标签或者使用注解方式在接口类上使用注解@CacheNamespace
@CacheNamespace 可手动制定具体的缓存实现类 方式为
@CacheNamespace(implementation = RedisCache.class)
8.2.1. 默认使用PerpetualCache类实现二级缓存
同样使用haspMap作为底层数据结构缓存数据
8.2.2. 二级缓存缓存介质是多样的不一定缓存在内存中所以POJO类都必须实现序列化接口
8.2.3. 可以在mappe.xml文件 select标签中使用
属性useCache=true/false 单独开启或禁用二级缓存
属性flushCache=true/false 单独设置是否刷新二级缓存
8.3. 使用redis实现二级缓存
8.3.1. 导入mybatis-redis.jar
8.3.2. @CacheNamespace(implementation = RedisCache.class)手动指定二级缓存为redis二级缓存
8.3.3. 创建redis.properties定义redis连接信息
8.3.4. 底层使用hash结构存储
9. MyBatis插件
本质是对MyBatis功能进行拓展
增强核心组件的功能,其本质就是使用了拦截器对核心组件进行拦截,基于JDK动态代理增强核心组件
9.1. Executor执行器
9.2. StatementHandler 【SQL语句构建器】
9.2.1. ParameterHandler参数处理器
9.2.2. ResultHandler结果集处理器
10. 自定义MyBatis插件
10.1. 创建一个类ExamplePlugin
实现MyBatisplugin包下的Interceptor接口
10.2. 在ExamplePlugin类上使用注解@Intercepts
将该类加入MyBatis拦截器链中
@Signature 定义要拦截的哪个核心组件类中的哪个方法
10.2.1.@Intercepts({
@Signature(type=
StatementHandler.class,
method =
"prepare",
args =
{Connection.class,Integer.class})
})
- setProperties(Properties properties)方法用于获取配置文件中初始化参数
- plugin(Object target) 方法{
Object wrap = Plugin.wrap(target, this);
}
把当前的拦截器生成代理存到拦截器链中 - intercept(Invocation invocation)方法
对方法进行增强并继续执行原方法
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("对方法进行了增强....");
return invocation.proceed(); //原方法执行
} - 基于XML配置
- 基于注解
- Executor
MyBatis执行器负责动态语句生成、
查询缓存的维护
10.3. 使用plugins标签将该插件定义
在sqlMapConfig.xml这样mybatis在启动时才可以加载该插件
10.3.1. <plugins>
<plugin interceptor="com.lagou.plugin.MyPlugin">
</plugin>
</plugins>
11. MyBatis第三方分页插件pageHelper
11.1. 导入pageHelper相关jar包
11.2. 将pageHandler插件加入sqlMapConfig.xml
不同数据库的分页实现方式不一样
所以定义属性描述对应数据库的方言
11.2.1.
<plugin interceptor="com.github.pagehelper.PageHelper">
<property
name="dialect" value="mysql"/>
</plugin>
11.3. 编写相关调用方法
11.3.1.PageHelper.startPage(1,1);
List<User> users = userMapper.selectUser();
PageInfo<User> pageInfo = new PageInfo<>(users);
pageInfo.getTotal()、pageInfo.getPages()、pageInfo.getPageNum()、pageInfo.getPageSize()
12. MyBatis第三方插件通用mapper
主要用于实现单表CRUD
不需要再编写SQL语句
不需要在mapper接口编写具体方法
12.1. 导入通用mapper相关jar包
12.2. 将通用mappe插件加入sqlMapConfig.xml
12.2.1.
<plugin
interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor">
<!--指定当前通用mapper接口使用的是哪一个-->
<property
name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
</plugin>
12.3. 使用注解@Table在POJO实体类上描述与表的对应关系
12.3.1.@Table(name = "user")
12.4. 使用注解@Id和@GeneratedValue
在POJO实体类上主键字段描述与表的的对应关系及主键生成策略
12.4.1. @Id
//对应的是注解id
@GeneratedValue(strategy =
GenerationType.IDENTITY) //设置主键的生成策略
private Integer id;
12.5. 创建具体mapper接口类
继承通用Mapper<User>类即可实现功能
12.6. 编写相关调用方法
12.6.1.UserMapper mapper =
sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(1);
User user1 =
mapper.selectOne(user);
System.out.println(user1);
12.7. Example方法实现CRUD
12.7.1.Example example = new Example(User.class);
example.createCriteria().andEqualTo("id",1);
List<User> users =
mapper.selectByExample(example);
for (User user2 : users) {
System.out.println(user2);
}
13. MyBatis源码分析
13.1. 接口层
提供对外访问的API接口
13.1.1.基于statementId
13.1.2. 基于mappe接口动态代理
13.2. 数据处理层
13.2.1.ParameterHandler
参数映射
13.2.2.SqlSource
SQL解析
13.2.3.Executor
SQL执行
13.2.4.ResultSetHandler
结果处理和映射
13.3. 框架支撑层
13.3.1.SQL语句配置方式
13.3.2.事务管理
13.3.3.连接池管理
13.3.4.缓存机制
13.4. 层次结构和调用流程
13.4.1.sqlSession
顶层API接口作为会话访问完成CRUD
• StatementHandler
负责处理jdbcstatement的交互、设置参数返回结果集转换
• ParameterHandler
将传递参数对 [ JDBC ] statement进行设置
• TypeHandler
jdbc 类型 与java类型的专类
• ResultSetHandler
将 【jdbc】resultSet结果集装换为list返回
• TypeHandler
jdbc 类型 与java类型的专类
13.4.2.configuration
- MappedStatement
• SqlSouce
• ResultMap