芸妈翻唱的四季大人的同人曲,爱了!
Mybatis的缓存机制
用于提高查询的效率
mybatis支持缓存,并且有两个缓存机制
一级缓存
线程级别的缓存,sqlsession对象缓存
特点:
- id必须一致
- 基于一个sqlsession对象的
二级缓存
进程级别的,sqlsessionfactory对象缓存
默认是不开启的,在mapper XML 中使用
标签开启
可以不基于同一个sqlsession对象
小插曲:
如果我们的实体类没有实现可序列化Serializable接口的话,我们的查询在映射时候会报错
如果不想实现Serializable接口可以在标签中设置readOnly属性位true
resultMap标签
解决列名和属性名不一致
也就是auto_mapper(自动映射)失败
<resultMap id="user" type="uamp">
<id column="name" property="name1"/>
<result column="password" property="password1"/>
</resultMap>
id标签:用于映射主键字段
result标签:用于映射其他的字段
属性 | 作用 |
---|---|
column | 表示表里面的字段名称 |
property | 表示实体类的属性名称 |
使用:
在操作标签上不在使用resultType属性而是使用resultMap属性写入标识的id即可。
多表关联查询
表结构
学生表(t_student),字段有id,name,age,gender,cid
班级表(t_class),字段有id,name,room
实体类(pojo)结构
由于我们是多表链接查询所以说,我们在学生表中也要要求有班级表里面的内容
解决方案:使用类的链接,在学生实体类内添加一个属性为班级实体类的引用,就可以解决问题了
实现方式一:业务装配(多对一)
将多表查询分层多个查询执行
使用service层查询多次,将实体类封装完毕
在持久层(mapper),中声明两个查询方法,一个用来查寻所有的学生信息,另一个是用来通过学生id查询所属的班级
然后在service调用这些持久层中的查询完成数据的装配,返回一个list用于输出给contrcler层
public List<Student> selAll() {
SqlSession session = MyBatisUtil.getSession();
// 学生mapper
StudentMapper stuMapper = session.getMapper(StudentMapper.class);
List<Student> list = stuMapper.selAll();
// 班级mapper
ClassMapper clsMapper = session.getMapper(ClassMapper.class);
// 为每一个student组装班级信息
for (Student s : list) {
Clazz clazz = clsMapper.selById(s.getCid());
s.setClazz(clazz);
}
session.close();
return list;
}
优点:sql语句简单,不用书写复杂的sql语句
缺点:业务层的逻辑复杂,这个例子只是一个简单的两个表的链接查询,逻辑还算简单,要是有很多个表的链接查询再附带很多条件,业务层的逻辑就会非常复杂,不容易进行debug,修改和阅读。
查询的次数多效率低
实现方式二:N+1查询(使用resultMap标签)
实质还是之前的业务装配,不过不同的是业务装配的过程由mabatis的resultMap标签实现,自己不用写逻辑很复杂的代码
多对一
resultMap的association标签:用于关联一个查询
association的属性
属性 | 作用 |
---|---|
property | 表示实体类的属性名称 |
select | 查询的命名空间加id |
column | 需要讲那个列的数据作为参数传入 |
studentmapper.xml文件配置
<resultMap id="sMap" type="student">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="gender" column="gender"/>
<result property="cid" column="cid"/>
<!--用于关联一个对象-->
<association property="clazz" select="top.byfree.mapper.ClassMapper.selById" column="cid"/>
</resultMap>
<select id="selAll" resultMap="sMap">
select * from t_student
</select>
classmapper.xml
<select id="selById" resultType="clazz" parameterType="int">
select * from t_class where id=#{0}
</select>
然后再定义一个studentmapper的java接口类关联xml文件定义抽象方法调用即可
注意:在n+1类型的查询中的resultMap标签中可以忽略property和column相同的标签不写
但是mybatis有个特性是,忽略不写的标签代表的属性只可以使用一次,说以因为下面的assciation标签使用cid作为参数使用过,cid不可以忽略
简写之后的xml文件
<resultMap id="sMap" type="student">
<result property="cid" column="cid"/>
<association property="clazz" select="top.byfree.mapper.ClassMapper.selById" column="cid"/>
</resultMap>
一对多
具体思想和多对一差不多,就是反过来而已
实体类设计:在clazz类中声明存储student类的链接的list集合用于存储查到的多个学生数据
resultMap的collection标签:用于关联一个查询,并将查询的结果封装成一个集合lsit
collection的属性和之前的association基本一致
属性 | 作用 |
---|---|
property | 表示实体类的属性名称 |
select | 查询的命名空间加id |
column | 需要讲那个列的数据作为参数传入 |
<resultMap id="cMap" type="clazz">
<id property="id" column="id"/>
<collection property="list" select="top.byfree.mapper.StudentMapper.selByCid" column="id"/>
</resultMap>
<select id="selAll" resultMap="cMap">
select * from t_class
</select>
其他代码,因差异不在赘述
实现方式三:resultMap关联方式实现多表查询
使用resultMap关联的方式来进行,这种方式只需要进行一次查询,但是查询的语句需要是多表连接查询语句,一次查出需要的数据
多对一
association标签下可一继续写子标签,用于规定sql语句中多表查询在关联的那个实体类里面的数据写入问题
子标签 | 作用 |
---|---|
id | 主键 |
result | 其他字段 |
新的属性使用
属性 | 作用 |
---|---|
javaType | 用于规定生成的数据的类型->可以理解位实体类是哪个 |
<resultMap id="selAll" type="student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="age" column="age"/>
<result property="gender" column="gender"/>
<result property="cid" column="cid"/>
<association property="clazz" javaType="clazz">
<id property="id" column="cid"/>
<result property="name" column="cname"/>
<result property="room" column="room"/>
</association>
</resultMap>
<select id="selAll" resultMap="selAll">
select s.id sid, s.name sname, s.age, s.gender, c.id cid, c.name cname, c.room
from t_student s
left join t_class c
on s.cid = c.id;
</select>
注意:关联的方式实现时就算是表的字段名和实体类的属性名相同也不能省略result或者id标签不写,不然会报错
一对多
具体还是和上述的多对一的方式差不多,只是association标签要换成collection标签使用就好
collection标签下可一继续写子标签,用于规定sql语句中多表查询在关联的那个实体类里面的数据写入封装成集合的问题
子标签 | 作用 |
---|---|
id | 主键 |
result | 其他字段 |
新的属性使用
属性 | 作用 |
---|---|
javaType | 用于规定生成的数据类型这里是集合 |
ofType | 规定集合内的属性的类型(简易理解为泛型) |
<resultMap id="cMap" type="clazz">
<id property="id" column="id"/>
<result property="name" column="cname"/>
<result property="room" column="room"/>
<collection property="list" javaType="list" ofType="student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="age" column="age"/>
<result property="gender" column="gender"/>
<result property="cid" column="cid"/>
</collection>
</resultMap>
<select id="selAll" resultMap="cMap">
select c.id, c.name cname, c.room, s.id sid, s.name name, s.age, s.gender
from t_student s
right join t_class c
on s.cid = c.id;
</select>
实现方式四:通过Auto-Mapper实现
最简单的方式,不需要写很复杂的逻辑代码,也不需要配置很多的xml标签,使用mybatis自动映射完成,我们只需要在查询语句上配置相关的别名就好。个人超级喜欢
实现方式很简单就是在sql语句上起别名即可
别名的格式为,想吧那个字段的数据封装到对象内,就[实体类.属性名]即可,注意这里的实体类和属性名必须完全对应,不然就会出错
小问题:在sql语句中起的别名是不能有特殊符号的'.'也算在内
处理方法:mysql数据库:使用``符号包含别名即可
oracle数据库:使用''符号包含别名即可
<select id="selAll" resultType="student">
select s.id, s.name, s.age, s.gender, s.cid, c.id `clazz.id`, c.name `clazz.name`, c.room `clazz.room`
from t_student s
left join t_class c
on s.cid = c.id;
</select>
这样就可以不配置任何的resultMap标签和写service逻辑代码就能实现多表连接查询了,特别方便。
Q.E.D.