MyBatis实战教程
SqlSession 代表和数据库的一次会话,用完必须关闭
SqlSession 和 connection 一样都是非线程安全的。每次使用都应该去获取新的对象
mapper接口没有实现类,但是mybatis会为这个接口生成一个代理对象
两个重要配置文件:
全局配置文件中配置databaseIdProvider属性
sql映射文件中,为sql语句标签添加databaseId属性
mysql支持自增主键,自增主键值的获取,mybatis也是利用statement.getGeanreatedKeys() 获取主键值策略
sql映射文件中,为sql语句标签添加useGeneratedKeys="true"属性
keyProperty:指定对应的主键属性,也就是mybatis获取到主键值以后,将这个值封装给javaBean的哪个属性
Oracle不支持自增,使用序列来模拟自增
<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
select EMPLOYEES_SQL.nextval from dual
selectKey>
单个参数:mybatis不会做特殊处理
#{参数名},取出参数值(其实参数名可以随便写)
多个参数:mybatis会做特殊处理,多个参数会被封装成一个map
默认
key:param1 … paramN,或者参数的索引(0…n)
value:参数值
直接填写参数名会报异常:org.apache.ibatis.binding.BindingException
可以在方法参数前添加@Param("") 注解
key:@Param指定的值
value:参数值
POJO
如果多个参数正好是业务逻辑的数据模型,我们就可以直接传入pojo
#{属性名}:取出传入的pojo的属性值
Map
如果多个参数不是业务模型中的数据,没有对应的pojo,为了方便,我们也可以传入map
#{key}:取出map中对应的值
TO
如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(Transfer Object)数据传输对象
Page {
int index;
int size;
}
特别注意:如果是Collection(List、Set)类型或者是数组,也会特殊处理。也是把传入的list或者数组封装在map中
key:Collection(collection),如果是List还可以使用key(list),数组(array)
public Employee getEmpById(List<Integer> ids);
取值:取出第一个id的值:#{list[0]}
源码分析
names:{0=id, 1=lastName}:构造器的时候就确定了
确定流程
区别:
#{}:是以预编译的形式,将参数设置到sql语句中,PreparedStatement;防止sql注入
${}:取出的值直接拼装在sql语句中,会有安全问题
select * from tbl_employee where id = ${id} and last_name = #{lastName}
Preparing:select * from tbl_employee where id = 2 and last_name = ?
大多情况中,我们取参数的值都应该去使用#{};
原生jdbc不支持占位符的地方我们就可以使用${}进行取值
比如分表:按照年份分表拆分
select * from ${
year}_salary where xxx;
按字段排序
select * from tbl_employee order by ${f_name} ${
order};
#{} 更丰富的用法:
规定参数的一些规则:
javaType,jdbcType,mode(存储过程),numericScale,resultMap,typeHandler,jdbcTypeName,expression
jdbcType通常需要在某些特定的条件下被设置:在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错):JdbcType OTHER:无效的类型:因为mybatis对所有的null都映射的是原生jdbc的OTHER类型
由于全局配置中:jdbcTypeForNull=OTHER;oracle不支持
返回list集合,resultType填写元素的类型
返回一条记录的map:key:列名,value:对应的值。resultType填写map
多条记录封装一个map:Map@MapKey("id")
联合查询:级联属性封装结果集
<result column="dept_name" property="dept.departmentName" />
association指定联合javaBean对象(一对一),property指定那个属性是联合的对象,javaType指定属性的类型
<association property="dept" javaType="...">
<id column="id" property="id" />
...
association>
使用association进行分步查询:select:表明当前属性是调用select指定的方法查出的结果,column:指定将哪一列的值传给这个方法,并封装给 property
<association property="dept" select="com.antherd.mybatis.dao.DepartmentMapper.getDeptById" column="d_id">
<id column="id" property="id" />
...
association>
可以使用延迟加载:每次查询Employee对象的时候,都将一起查询起来。部门信息在我们使用的时候再去查询。分段查询的基础之上加上两个全局配置:lazyLoadingEnable = true,aggressiveLazyLoading = false
collection定义关联集合类型的属性封装规则(一对多)
<collection property="emps" ofType="...">
<id cloumn="eid" property="id" />
...
collection>
collection:定义关联集合类型的属性的封装规则
ofType:指定集合里面元素的类型
使用collection进行分步查询:
<collection
property="emps"
select="com.antherd.mybatis.dao.EmployeeMapperPlus.getEmpsByDid"
column="id">
collection>
拓展:多列值传递给关联查询
将多列的值封装map传递
column="{key1=columnName1, key2=columnName2}"
fetchType属性:表示使用延迟加载
discriminator鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为
封装Employee:
如果查出的是女生,就把部门信息查询出来,否则不查询
如果是男生,把last_name这一列的值赋值给email
<discriminator javaType="string" column="gender">
<case value="0" resultType="com.antherd.mybatis.bean.Employee">
<association ...>
case>
<case value="1" resultType="com.antherd.mybatis.bean.Employee">
<id ...>
<result ...>
case>
discriminator>
if:判断
拼装问题解决:
choose:选择
trim:去除
foreach:循环,open:遍历所有结果拼接一个开始的字符,close:遍历所有结果拼接一个结束的字符,sqparator:元素之间的分隔符,index:遍历list的时候是索引,遍历map的时候表示map的key,item表示map的值
set:去除多余”,“
数据库连接属性中添加 allowMultiQueries=true允许一条语句中,使用”;“来分割多条查询
Oracle数据库批量保存:
Oracle不支持values (), (), ()
Oracle支持批量方法
insert into employees(id, name, email)
select id, name email from (
select "1" id, "name1" name, "email1" email from dual
union
select "2" id, "name2" name, "email2" email from dual
...
)
mybatis默认还有两个内置参数:
_parameter:代表整个参数
单个参数:_parameter就是这个参数
多个参数:参数会被封装为一个map:_parameter就是代表这个map
_databaseId:如果配置了databaseIdProvider标签
_databaseId就是代表当前数据库的别名oracle
<select id="getEmpsTestInnerParamter" resultType="com.antherd.mybatis.dao.Employee">
<if test="_databaseId=='mysql'">
select * from tbl_employee
<if test="_parameter !=null">
where last_name = #{_parameter.lastName}
if>
if>
<if test="_databaseId=='oracle'">
select * from tbl_employee
if>
select>
bind:可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值
<bind name="_lastName" value="'%' + lastName + '%'" />
抽取可重用sql片段,方便后面引用
<sql id="insertColumn">
id, name, email
sql>
<include refid="insertColumn">
<property name="testColumn" value="1234" />
include>
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率
MyBatis系统中默认定义了两级缓存
一级缓存 和 二级缓存(全局缓存)
一级缓存失效情况(没有使用到当前一级缓存的情况,效果就是,还需要再向数据库发出查询)
二级缓存使用:
缓存相关的设置/属性:
mybatis 提供了与第三方整合的工具包
包含redis-cache、spring、ehcache-cache等
ehcache-core
mapper中开启cache
<cache type="org.mybatis.caches.ehcache.EhcacheCache">cache>
ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="D:\ehcache" />
<defaultCache
maxElementInMemory="10000"
maxElementsOnDisk="100000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
tiemToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
defaultCache>
ehcache>
引用缓存:namespace 指定和哪个名称空间缓存一致
<cache-ref namespace="" />
第三方缓存整合:
1. 导入第三方缓存包即可
2. 导入与第三方缓存整合的适配包:官方有
3. mapper.xml 中使用自定义缓存
总结:
注意:
四大对象每个创建的时候都有一个interceptorChain.pluginAll流程
获取到所有的Interceptor(拦截器),返回target包装后的对象
插件机制,我们可以使用插件为目标对象创建一个代理对象;AOP(面向切面)
我们的插件可以为四大对象创建出代理对象
代理对象就可以拦截到四大对象的每一个执行