1. select 标签
select 标签是用于定义插入语句的, select 标签是Mybatis 中最复杂的标签了.
1.1 常用属性
select 的属性和子标签比较多, 常用属性有:
id: sql 片段在命名空间内的唯一标识. 和mapper 中方法名保持一致
useGeneratedKeys: 是否回填自动生成的主键
keyProperty: 主键回填到哪个属性
keyColumn: 主键回填的字段名, 可省略
parameterType: 参数类型, 通常都可以省略.
flushCache: 是否刷新(清空)一级缓存和二级缓存, 默认为false.
useCache: 是否将查询结果存入二级缓存, 默认为true. 对一级缓存无效.
timeout: sql 执行超时时间, 默认未设置, 由数据库驱动决定.
resultType: 指定结果集中每一行的封装类型, 支持自动封装为java中定义的类对象. 默认使用类的全限定名称, 配置别名后, 支持类的简单名称.
resultMap: 指定结果集中每一行的封装类型, 是使用resultMap 标签定义的类型.
statementType: 执行sql时使用的statement类型, 默认为PREPARED. 可选值为:STATEMENT,PREPARED 或 CALLABLE 的一个
<insert
id="insertAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
keyProperty=""
keyColumn=""
useGeneratedKeys=""
timeout="20">
</insert>
1.2 返回值
Mybatis 的查询结果集封装着实很强大, 同一个sql片段, 会根据接口方法定义时返回值的不同, 而做自动封装. 笔者介绍一下常见的几种返回类型.
返回值类型 返回描述
String/Integer/Float… 返回单个的基本类型, Mybatis为基本类型内置了别名, resultType 可直接写别名.
List/Set<String/Integer…> 返回基本类型的集合
VO 单个的java 类对象, 不局限于PO, VO, DTO 等均可
List/Set java 对象集合
Map<String, Object> 单个对象的字段名, 与值构成的Map
Map<Object, VO> 单个对象的某个属性和对象构成key=value 键值对的map, 如 Map<PK, PO>; 需要注意两点: 第一, 需要借助于@Mapper 注解指定key的字段; 第二, 如果PK类型指定为Long, 那么当id较小时, 便会返回Integer, 而报错.
2. Select 标签返回举例
2.1 Mapper 方法定义
public interface DepartmentMapper {
// 返回单个基本类型
String queryDepartmentName(Long id);
// 返回基本类型集合
List<Long> queryAllIds();
// 返回单一对象
DepartmentPO findById(Long id);
// 返回对象集合
List<DepartmentPO> queryAll();
// 返回单个对象的所有属性
Map<String , Object> queryProperties(Long id);
// 返回id和对象组成的map
// -需要通过@MapKey 注解指定map中key从哪个字段获取
// -当key类型设置为Long时, 一般会返回Integer, 会报错
@MapKey("id")
Map<Object, DepartmentPO> queryMap();
}
2.2 sql 片段
<!-- 命名空间使用对应的Mapper类全限定名称 -->
<mapper namespace="org.zongf.learn.mybatis3.l01.mapper.DepartmentMapper">
<!-- 返回单个基本类型 -->
<select id="queryDepartmentName" resultType="string">
select name from t_department where id = #{id};
</select>
<!-- 返回基本类型集合 -->
<select id="queryAllIds" resultType="long">
select id from t_department;
</select>
<!-- 返回单一对象 -->
<select id="findById" resultType="DepartmentPO">
select * from t_department where id = #{id};
</select>
<!-- 返回对象集合 -->
<select id="queryAll" resultType="DepartmentPO">
select * from t_department;
</select>
<!-- 返回单个对象的所有属性 -->
<select id="queryProperties" resultType="map">
select * from t_department where id = #{id};
</select>
<!-- 返回id和对象组成的map -->
<select id="queryMap" resultType="DepartmentPO">
select * from t_department;
</select>
</mapper>
2.3 测试类
public class Test_DepartmentMapper {
DepartmentMapper departmentMapper;
@Before
public void setUp(){
departmentMapper = SqlSessionUtil.getMapper(DepartmentMapper.class, false);
}
// 返回单个基本类型
@Test
public void queryDepartmentName(){
String departmentName = this.departmentMapper.queryDepartmentName(1L);
System.out.println(departmentName);
}
// 返回基本类型集合
@Test
public void queryAllIds(){
List<Long> ids = this.departmentMapper.queryAllIds();
ids.forEach(System.out::println);
}
// 返回单一对象
@Test
public void findById(){
DepartmentPO dep = this.departmentMapper.findById(1L);
System.out.println(dep);
}
// 返回对象集合
@Test
public void queryAll(){
List<DepartmentPO> departmentPOS = this.departmentMapper.queryAll();
departmentPOS.forEach(System.out::println);
}
// 返回单个对象的所有属性
@Test
public void queryProperties(){
Map<String, Object> properties = this.departmentMapper.queryProperties(1L);
System.out.println(properties);
properties.forEach((key,val)->{
System.out.println(key + "=" + val);
});
}
// 返回id和对象组成的map
@Test
public void queryMap(){
Map<Object, DepartmentPO> map = this.departmentMapper.queryMap();
map.forEach((key, val)->{
System.out.println(key + "=" + val);
});
}
}
3. 缓存测试
缓存测试需要主要通过观察日志, 看相同条件的第二次查询是否发送sql.
3.1 测试一级缓存
影响一级缓存的属性只有flushCache
一级缓存是同一个sqlSession中到缓存, 默认查询结果会存放在一级缓存中.
测试时, 通过设置flushCache=true 来清空缓存
测试结果:
默认条件下: 一级缓存生效, 第二次查询不会发出sql查询日志
修改flushCache=true: 一级缓存被清空, 第二次查询重新发送sql查询日志
@Test
public void testCache() {
// 第一次查询
DepartmentPO dep1 = this.departmentMapper.findById(1L);
System.out.println("开始第二次查询");
DepartmentPO dep2 = this.departmentMapper.findById(1L);
}
3.2 测试二级缓存属性
影响二级缓存的属性有: flushCache 和 useCache
二级缓存默认不开启, 且缓存对象需要实现序列化接口:
DepartmentMapper.xml 开启二级缓存, 即添加标签
DepartmentPO 实现序列化接口: Serializable
测试结果:
默认情况下, 第二次查询不发送sql, 而是从二级缓存中获取
当设置flushCache=true, 或useCache=false时, 第二次查询均会发送sql
@Test
public void testCache2(){
SqlSession sqlSession1 = SqlSessionUtil.openSession(false);
SqlSession sqlSession2 = SqlSessionUtil.openSession(false);
DepartmentMapper depMapper1 = sqlSession1.getMapper(DepartmentMapper.class);
DepartmentMapper depMapper2 = sqlSession2.getMapper(DepartmentMapper.class);
DepartmentPO dep1 = depMapper1.findById(1L);
// sqlSession 关闭时, 会将一级缓存存放到二级缓存中
sqlSession1.close();
System.out.println("开始第二次查询");
DepartmentPO dep2 = depMapper2.findById(1L);
}