18.Mybatis多表查询
· resultType 实现一对一映射
情景模拟:(兼职信息表对发布信息的用户表为1对1)
mysql语句(连接查询):
SELECT p.*,u.username,u.age,u.sex,u.phone FROM t_user u,t_parttimeinfo p WHERE p.pi_userId=u.id;
步骤:
· 创建扩展类(包含查询信息)用于对应resultType
public class ParttimeInfoCustom extends ParttimeInfo{
/*
添加查询出来的所需要的用户属性
*/
private String username;
private String sex;
private Integer age;
private String phone;
// setter getter
}
· resutlMap实现一对一映射
情景模拟:订单表对用户表为1对1,订单表对订单明细1对多
mysql语句:
SELECT t_orders.*,t_user.username,t_user.sex,t_user.address
FROM t_orders,t_user
首先在 order类中加入 User user 属性;
其次,在 Mapper.xml中配置resultMap
<resultMap type="po.Orders" id="OrdersUserResultMap">
<id column="id' property="id"/>
<result column="表的列明" property="order类属性名"/>
...
<!--配置映射的关联用户信息-->
<association property="user(order关联对象属性名)" javaType="po.User">
<id column="唯一标识用户的列名" property="id"/>
<result column="列名" property="user类属性名"/>
</association>
</resultMap>
· 综上,可看出 resultType更简单,resultMap需要单独定义,如果对查询结果又特殊要求,使用resultMap可以将关联查询映射到pojo的属性中。(resultMap可实现延迟加载)
· 举一反三,就知道一对多的resultMap映射如何实现了
假设现在要求查询订单包含订单明细,订单明细表每一个元组记录了商品及数量。订单与订单明细属于一对多。
首先在order类中加入 List<OrderDetail> orderDetails属性
接着:
<resultMap type=po.Orders" id="xxxxMap" extends="resultMapId">
继承上面的resultMap就可以不用写 user在order中的配置啦。
下面写order和orderDetials的一对多配置
<collection property="将关联查询映射到order的那个属性" ofType="集合元素类型:就是OrderDetail">
<id column="" property=""/>
<result column="" property=""/>
...
</resultMap>
· 举一反三!集合中的元素也能关联或作集合
<resultMap id="manytomany" type="op.User">
<id/>
<result/>
<collection property="orderList" ofType >
<id/>
<result/>
<collection property="orderDetails" ofType>
<id/>
<result/>
<association property="items" javaType="po.Items">
</association>
</collection>
</collection>
</resultMap>
综上:究竟使用resultMap还是resultType呢?
根据需求而决定。
对于多对多查询,如果结果需要list集合去重(查询10名用户购买所有商品),则用resultMap,如果不需要(查询购买商品记录),则用resultType更简单。
举例:
需求一,查询一个用户账号,及他所购买商品名称,商品价格。列表。
你要是用resultMap关联一大串,慢且复杂。
使用resultType,构造好sql语句直接将结果映射到扩展po类,简便快捷。
需求二,查询一批用户账号,订单,商品明细(鼠标移动上去才显示)。用户账号列表。
用rusultType极不方便集合映射,所以选择resultMap,且可以延迟加载。
19. 延迟加载
什么是延迟加载?
做关联查询时,先从单表查询,需要时再从关联表去关联查询,减轻数据库压力,提高数据库性能。
举例:
select * FROM t_order,t_user WHERE t_order.userId=t_user.id;
延迟加载:
select t_order.* FROM t_order;
当有需要时:
select t_order.*,(SELECT username FROM t_user WHERE t_order.userId = t_user.id)username FROM t_order;
<association property="" javaType="" select="statementId" column=""/>
MyBatis默认未开启延迟加载,需要在sqlMapConfig.xml中setting配置。
<settings>
<!--打开延迟加载的开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--将积极延迟加载改为按需延迟加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
20. mybatis 中的一级缓存,二级缓存?二级缓存应用场景?
一级缓存在sqlSession中,一个sqlSession拥有一个一级缓存(默认存在,框架自带)
第一次查询 -> 写入sqlSession一级缓存区->第二次查询(查询语句相同)则直接从缓存中读取->提交事务(commit)清空一级缓存(Why?保证数据库更新,避免脏/幻读)
正式开发,是将mybatis和spring进行整合开发,事务控制在service中。一个service方法中包括很多mapper方法调用。
如果执行两次service调用查询相同的用户信息,不走一级缓存,因为sqlSession早关闭过了。
二级缓存(一个namespace一个二级缓存区域,要配置)
使用二级缓存:
· 开启mybatis二级缓存(配置文件)
<setting name="cacheEnabled" value="true"/>
· 开启本namespace下的二级缓存
mapper.xml(二级缓存按照mapper的namespace划分)
<cache 参数配置/>
所有po对象必须序列化,因为二级缓存不一定存在内存,也可能在硬盘,所以需要反序列化。
禁用二级缓存: 在statement中配置 userCache="false"
事务操作刷新缓存: statement中, flushCache="false"
清空缓存时间间隔: flushInterval
应用场景: 对查询结果实时性要求不高的,二级缓存可以降低数据库访问量,提高访问速度;或者耗时较高的统计分析sql等
其实 用缓存肯定是 对实时性要求不高的。
但是二级缓存应用度极地!!!why?
二级缓存的局限性:
缓存数据有一万条,只要有一条被 事务操作(insert,update...)就会清空缓存内容(一万条缓存全被清空),二级缓存对细粒度的数据级别的缓存实现不好(缓存命中率低),所以应用场景很低。解决此类问题需要在业务层根据需求对数据针对性缓存(三级缓存/细粒度缓存)。
新名词: 缓存命中率,细粒度数据级别 三级缓存
· resultType 实现一对一映射
情景模拟:(兼职信息表对发布信息的用户表为1对1)
mysql语句(连接查询):
SELECT p.*,u.username,u.age,u.sex,u.phone FROM t_user u,t_parttimeinfo p WHERE p.pi_userId=u.id;
步骤:
· 创建扩展类(包含查询信息)用于对应resultType
public class ParttimeInfoCustom extends ParttimeInfo{
/*
添加查询出来的所需要的用户属性
*/
private String username;
private String sex;
private Integer age;
private String phone;
// setter getter
}
· resutlMap实现一对一映射
情景模拟:订单表对用户表为1对1,订单表对订单明细1对多
mysql语句:
SELECT t_orders.*,t_user.username,t_user.sex,t_user.address
FROM t_orders,t_user
首先在 order类中加入 User user 属性;
其次,在 Mapper.xml中配置resultMap
<resultMap type="po.Orders" id="OrdersUserResultMap">
<id column="id' property="id"/>
<result column="表的列明" property="order类属性名"/>
...
<!--配置映射的关联用户信息-->
<association property="user(order关联对象属性名)" javaType="po.User">
<id column="唯一标识用户的列名" property="id"/>
<result column="列名" property="user类属性名"/>
</association>
</resultMap>
· 综上,可看出 resultType更简单,resultMap需要单独定义,如果对查询结果又特殊要求,使用resultMap可以将关联查询映射到pojo的属性中。(resultMap可实现延迟加载)
· 举一反三,就知道一对多的resultMap映射如何实现了
假设现在要求查询订单包含订单明细,订单明细表每一个元组记录了商品及数量。订单与订单明细属于一对多。
首先在order类中加入 List<OrderDetail> orderDetails属性
接着:
<resultMap type=po.Orders" id="xxxxMap" extends="resultMapId">
继承上面的resultMap就可以不用写 user在order中的配置啦。
下面写order和orderDetials的一对多配置
<collection property="将关联查询映射到order的那个属性" ofType="集合元素类型:就是OrderDetail">
<id column="" property=""/>
<result column="" property=""/>
...
</resultMap>
· 举一反三!集合中的元素也能关联或作集合
<resultMap id="manytomany" type="op.User">
<id/>
<result/>
<collection property="orderList" ofType >
<id/>
<result/>
<collection property="orderDetails" ofType>
<id/>
<result/>
<association property="items" javaType="po.Items">
</association>
</collection>
</collection>
</resultMap>
综上:究竟使用resultMap还是resultType呢?
根据需求而决定。
对于多对多查询,如果结果需要list集合去重(查询10名用户购买所有商品),则用resultMap,如果不需要(查询购买商品记录),则用resultType更简单。
举例:
需求一,查询一个用户账号,及他所购买商品名称,商品价格。列表。
你要是用resultMap关联一大串,慢且复杂。
使用resultType,构造好sql语句直接将结果映射到扩展po类,简便快捷。
需求二,查询一批用户账号,订单,商品明细(鼠标移动上去才显示)。用户账号列表。
用rusultType极不方便集合映射,所以选择resultMap,且可以延迟加载。
19. 延迟加载
什么是延迟加载?
做关联查询时,先从单表查询,需要时再从关联表去关联查询,减轻数据库压力,提高数据库性能。
举例:
select * FROM t_order,t_user WHERE t_order.userId=t_user.id;
延迟加载:
select t_order.* FROM t_order;
当有需要时:
select t_order.*,(SELECT username FROM t_user WHERE t_order.userId = t_user.id)username FROM t_order;
<association property="" javaType="" select="statementId" column=""/>
MyBatis默认未开启延迟加载,需要在sqlMapConfig.xml中setting配置。
<settings>
<!--打开延迟加载的开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--将积极延迟加载改为按需延迟加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
20. mybatis 中的一级缓存,二级缓存?二级缓存应用场景?
一级缓存在sqlSession中,一个sqlSession拥有一个一级缓存(默认存在,框架自带)
第一次查询 -> 写入sqlSession一级缓存区->第二次查询(查询语句相同)则直接从缓存中读取->提交事务(commit)清空一级缓存(Why?保证数据库更新,避免脏/幻读)
正式开发,是将mybatis和spring进行整合开发,事务控制在service中。一个service方法中包括很多mapper方法调用。
如果执行两次service调用查询相同的用户信息,不走一级缓存,因为sqlSession早关闭过了。
二级缓存(一个namespace一个二级缓存区域,要配置)
使用二级缓存:
· 开启mybatis二级缓存(配置文件)
<setting name="cacheEnabled" value="true"/>
· 开启本namespace下的二级缓存
mapper.xml(二级缓存按照mapper的namespace划分)
<cache 参数配置/>
所有po对象必须序列化,因为二级缓存不一定存在内存,也可能在硬盘,所以需要反序列化。
禁用二级缓存: 在statement中配置 userCache="false"
事务操作刷新缓存: statement中, flushCache="false"
清空缓存时间间隔: flushInterval
应用场景: 对查询结果实时性要求不高的,二级缓存可以降低数据库访问量,提高访问速度;或者耗时较高的统计分析sql等
其实 用缓存肯定是 对实时性要求不高的。
但是二级缓存应用度极地!!!why?
二级缓存的局限性:
缓存数据有一万条,只要有一条被 事务操作(insert,update...)就会清空缓存内容(一万条缓存全被清空),二级缓存对细粒度的数据级别的缓存实现不好(缓存命中率低),所以应用场景很低。解决此类问题需要在业务层根据需求对数据针对性缓存(三级缓存/细粒度缓存)。
新名词: 缓存命中率,细粒度数据级别 三级缓存