七. 映射关系
7.1 一对一
- 通过配置XxxMapper.xml实现一对一
实体类
public class Person {
private Integer id;
private String name;
private IdenCard card;
}
public class IdenCard {
private Integer id;
private String card_sn;
}
接口方法
public interface IdenCardMapper {
//根据id获取身份证
public IdenCard getIdenCardById(Integer id);
}
public interface PersonMapper {
//通过person的id获取到person关联的idencard对象
public Person getPersonById(Integer id);
}
mapper.xml文件
<mapper namespace="com.zzti.mybatis.mapper.IdenCardMapper">
<select id="getIdenCardById" parameterType="Integer" resultType="IdenCard">
select * from idencard where id =#{id}
</select>
</mapper>
<mapper namespace="com.zzti.mybatis.mapper.PersonMapper">
<resultMap id="personResultMap" type="Person">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!--一个复杂类型的关联-->
<association property="card" javaType="com.zzti.mybatis.entity.IdenCard">
<!--将关联的card对象那些属性放入到这个resultMap-->
<result property="id" column="id"/>
<result property="card_sn" column="card_sn"/>
</association>
</resultMap>
<select id="getPersonById" parameterType="Integer" resultMap="personResultMap">
select * from person,idencard where person.id =#{id}
and person.card_id = idencard.id
</select>
</mapper>
测试一下
@Test
public void getPersonById(){
Person personById = personMapper.getPersonById(1);
System.out.println("getPersonById结果= " + personById);
if (sqlSession != null) {
sqlSession.close();
}
}
===============================================
==> Preparing: select * from person,idencard where person.id =? and person.card_id = idencard.id
==> Parameters: 1(Integer)
<== Columns: id, name, card_id, id, card_sn
<== Row: 1, 张三, 1, 1, 1111111111111
<== Total: 1
getPersonById结果= Person{id=1, name='张三', card=IdenCard{id=1, card_sn='1111111111111'}}
通过mapper.xml方式2
<!--方式2-->
<resultMap id="personResultMap2" type="person">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!--当前这个person的card属性,这个数据从getIdenCardById获取;-->
<!-- card_id: 是从select * from person where id =#{id}查询结果给它的-->
<!--相当于, 把多表联查分解成单表操作-->
<association property="card" column="card_id"
select="com.zzti.mybatis.mapper.IdenCardMapper.getIdenCardById"/>
</resultMap>
<select id="getPersonById2" parameterType="Integer" resultMap="personResultMap2">
select * from person where id =#{id}
</select>
@Test
public void getPersonById2(){
Person personById = personMapper.getPersonById2(1);
System.out.println("getPersonById2结果= " + personById);
if (sqlSession != null) {
sqlSession.close();
}
}
=================================================
==> Preparing: select * from person where id =?
==> Parameters: 1(Integer)
<== Columns: id, name, card_id
<== Row: 1, 张三, 1
====> Preparing: select * from idencard where id =?
====> Parameters: 1(Integer)
<==== Columns: id, card_sn
<==== Row: 1, 1111111111111
<==== Total: 1
<== Total: 1
getPersonById2结果= Person{id=1, name='张三', card=IdenCard{id=1, card_sn='1111111111111'}}
- 通过注解的方式实现
相关接口
public interface IdenCardMapperAnnotation {
//根据id获取身份证
@Select("select * from idencard where id =#{id}")
public IdenCard getIdenCardById(Integer id);
}
public interface PersonMapperAnnotation {
//通过person的id获取到person关联的idencard对象
@Select("select * from person where id =#{id}")
@Results({
@Result(id = true, property = "id", column = "id"),
@Result(property = "name", column = "name"),
@Result(property = "card", column = "card_id",
one = @One(select = "com.zzti.mybatis.mapper.IdenCardMapperAnnotation.getIdenCardById"))
})
public Person getPersonById(Integer id);
}
测试一把
@Test
public void getPersonById3(){
PersonMapperAnnotation personMapperAnnotation =
sqlSession.getMapper(PersonMapperAnnotation.class);
Person personById = personMapperAnnotation.getPersonById(1);
System.out.println("getPersonById3= " + personById);
if (sqlSession != null) {
sqlSession.close();
}
}
===============================================
==> Preparing: select * from person where id =?
==> Parameters: 1(Integer)
<== Columns: id, name, card_id
<== Row: 1, 张三, 1
====> Preparing: select * from idencard where id =?
====> Parameters: 1(Integer)
<==== Columns: id, card_sn
<==== Row: 1, 1111111111111
<==== Total: 1
<== Total: 1
getPersonById3= Person{id=1, name='张三', card=IdenCard{id=1, card_sn='1111111111111'}}
注意细节:
1. 表是否设置外键,对MyBatis进行对象/级联映射没有影响
7.2 多对一
User — Pet: 一个用户可以养多只宠物
Dep — Emp: 一个部门有多个员工
学习双向的多对一的关系,比如通过user可以查询到对应的pet,反过来通过pet也可以级联查询到对应的user信息。
- 通过mapper.xml方式
实体类
public class Pet {
private Integer id;
private String nickname;
private Userr userr;
}
public class Userr {
private Integer id;
private String name;
private List<Pet> pets;
}
接口方法
public interface PetMapper {
//通过Userr的id来获取pet对象
public List<Pet> getPetByUserrId(Integer userrId);
//通过pet的id获取pet对象
public Pet getPetById(Integer id);
}
public interface UserrMapper {
//通过id获取Userr对象
public Userr getUserrById(Integer id);
}
mapper.xml文件
PetMapper
<mapper namespace="com.zzti.mybatis.mapper.PetMapper">
<resultMap id="PetResultMap" type="Pet">
<id property="id" column="id"/>
<result property="nickname" column="nickname"/>
<association property="userr" column="userr_id"
select="com.zzti.mybatis.mapper.UserrMapper.getUserrById"/>
</resultMap>
<!--userr_id =#{xx} xx就是调用 getPetByUserrId()传入的用户id,可以随意指定-->
<select id="getPetByUserrId" parameterType="Integer" resultMap="PetResultMap">
select * from pet where userr_id =#{userrId}
</select>
<select id="getPetById" parameterType="Integer" resultMap="PetResultMap">
select * from pet where id =#{id}
</select>
</mapper>
UserrMapper
<mapper namespace="com.zzti.mybatis.mapper.UserrMapper">
<resultMap id="UserrResultMap" type="Userr">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!--collection:一对多关联查询,表示一个用户可能对应多个pet对象,-->
<!--ofType: 集合中元素对象的类型 -->
<!--id: 当前Userr表的主键,通过这个id去查询对应的pet有哪些-->
<collection property="pets" ofType="Pet" column="id"
select="com.zzti.mybatis.mapper.PetMapper.getPetByUserrId"/>
</resultMap>
<select id="getUserrById" parameterType="Integer" resultMap="UserrResultMap">
select * from userr where id =#{id}
</select>
</mapper>
- 通过注解方式
PetMapper接口
@Select("select * from pet where userr_id =#{userrId}")
@Results({
@Result(id = true,property = "id",column = "id"),
@Result(property = "nickname",column = "nickname"),
})
public List<Pet> getPetByUserrId2(Integer userrId);
@Select("select id as tnId,nickname as tnNickname," +
"userr_id as tnUserr_id from pet where id =#{id}")
@Results({
@Result(id = true,property = "id",column = "tnId"),
@Result(property = "nickname",column = "tnNickname"),
@Result(property = "userr",column = "tnUserr_id",
one = @One(select = "com.zzti.mybatis.mapper.UserrMapper.getUserrById2"))
})
public Pet getPetById2(Integer id);
UserrMapper接口
@Select("select * from userr where id =#{id}")
@Results({
@Result(id = true,property = "id",column = "id"),
@Result(property = "name", column = "name"),
@Result(property = "pets",column = "id",
many = @Many(select = "com.zzti.mybatis.mapper.PetMapper.getPetByUserrId2"))
})
public Userr getUserrById2(Integer id);
八. 缓存
一级缓存
1. 默认情况下,mybatis是启用一级缓存的/本地缓存/localCache,它是SqlSession级别的
2. 同一个SqlSession接口对象调用了相同的select语句,会直接从缓存里面获取,而不是再去查询数据库
3. 当执行的SQL查询中间发生了增删改的操作,mybatis会把SqlSession的缓存清空。
4. 一级缓存失效分析:
a) 关闭sqlSession会话后,再次查询,回到数据库查询;
b) 当执行sqlSession.clearCache()会使一级缓存失效,再次查询,会去数据库查询;
c) 当对同一个对象修改,该对象在一级缓存会失效,再次查询,会去数据库查询。
二级缓存
- 二级缓存和一级缓存都是为了提高检索效率的技术;
- 最大的区别就是作用域的范围不一样,
一级缓存的作用域是sqlSession会话级别,在一次会话有效,而二级缓存作用域是全局范围(同一个nameSpace下的mapper映射文件内容,多个SqlSession共享。Mybatis需要手动设置启动二级缓存。),针对不同的会话都有效
。一个会话,查询一条数据,这个数据会被放在当前会话的一级缓存中;如果会话被关闭了,若有二级缓存,一级缓存汇总的数据会被保存到二级缓存。新的会话查询信息就会参照二级缓存。 - 二级缓存的使用原则:
3.1 只能在一个命名空间下使用二级缓存。由于二级缓存中的数据是基于nameSpace的,即不同nameSpace中的数据互不干扰。在多个nameSpace中若均存在对同一个表的操作,那么这多个nameSpace中的数据可能就会出现不一致现象。
3.2 在单表上使用二级缓存。如果一个表与其他表有关联关系,那么就非常有可能存在多个nameSpace对同一数据的操作。而不同nameSpace中的数据互不干扰,所以就有可能出现多个nameSpace中的数据不一致现象。
3.3 查询多于修改时使用二级缓存。在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓存,对二级缓存的频繁刷新将降低系统性能。
快速入门
1.开启二级缓存
<settings>
<!--配置mybatis自带的日志输出,查看原生的SQL-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--开启二级缓存,默认是true-->
<setting name="cacheEnabled" value="true"/>
</settings>
- 使用二级缓存
entity类实现系列化接口(serializable)
,因为二级缓存可能使用到序列化技术 - 对应的XxxMapper.xml文件设置二级缓存的策略
<!--eviction="FIFO"==>缓存策略,先进先出-->
<!--flushInterval="30000"==>每30000毫秒 刷新一次-->
<!--size="360" ===> 二级缓存设置最大保存360个对象,超过了,就启用FIFO策略处理,默认是1024-->
<!--readOnly="true" ===> 只读,为了提高效率-->
<cache eviction="FIFO" flushInterval="30000" size="360" readOnly="true"/>
-
四大策略
LRU-最近最少使用的: 移除最长时间不被使用的对象,它是默认
FIFO-先进先出: 按对象进入缓存的顺序来移除它们
SOFT-软引用: 移除基于垃圾回收期状态和软引用规则的对象
WEAK-弱引用: 更积极地移除基于垃圾收集器状态和弱引用规则的对象 -
如何禁用二级缓存
a) 在mybatis-config.xml文件中二级缓存设置为false
b) 在XxxMapper.xml文件中关闭cache
c) 在mapper.xml中的某个配置方法上指定useCache=“false”,useCache默认是true,一般不需要修改,使用默认的就行。
<select id="findMonsterByNameOrId" parameterType="Monster" resultType="Monster" useCache="false">
select * from monster where id=#{id} or name=#{name}
</select>
d) mybatis刷新二级缓存的设置,flushCache=“true”
insert、update、delete操作数据后需要刷新缓存,若不执行刷新缓存会出现脏读,默认是true,一般不需要修改
<update id="updateMonster" parameterType="Monster" flushCache="true">
update monster set age=#{age},birthday=#{birthday},email=#{email},
gender=#{gender},name=#{name},salary=#{salary}
where id=#{id}
</update>
- MyBatis的一级缓存和二级缓存执行顺序:
二级缓存 ==> 一级缓存 ==> 数据库
不会出现一级缓存和二级缓存中都有同一个数据,因为二级缓存是在一级缓存关闭之后才有的