1.嵌套结果
这里先展示一对多的例子,一对一的例子暂时不展示了,区别一对多的关联查询使用collection进行映射,一对一的关联查询使用了association来进行映射,区别不大
展示的例子如下:一个用户有多个订单,查询出所有的用户的,并把用户的关联的订单也打印处理
- 用户User的实体类(省略get和set方法)
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//用户创建的订单列表
private List<Orders> orderList;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
", orderList=" + orderList +
'}';
}
}
- 订单类Orders的实体类(省略get和set方法)
public class Orders {
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
}
- 对应的UserMapper的方法
public interface UserMapper {
//通过user来关联订单号
List<User> findOrdersByUser();
}
- 对应的UserMapper.xml的接口的内容
<!-- 查询用户所在的订单号,一个用户对应多个订单号-->
<select id="findOrdersByUser" parameterType="com.mybatis.project.po.User" resultMap="findOrdersByUserMap">
select users.*,
orders.id orders_id,
orders.createtime,
orders.note,
orders.number
from orders , user users
where orders.user_id = users.id ;
</select>
<resultMap id="findOrdersByUserMap" type="com.mybatis.project.po.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="sex" column="sex"/>
<result property="birthday" column="birthday"/>
<result property="address" column="address"/>
<collection property="orderList" ofType="com.mybatis.project.po.Orders">
<id property="id" column="orders_id"/>
<result property="userId" column="id"/>
<result property="number" column="number"/>
<result property="createtime" column="createtime"/>
<result property="note" column="note"/>
</collection>
</resultMap>
- 具体测试类
//嵌套结果 一对多
@Test
public void nestedResultTest(){
SqlSession sqlSession =sqlSessionFactory.openSession();
//创建userMapper对象,mybatis自动生成mapper代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> list = userMapper.findOrdersByUser();
System.out.println(list);
}
- 输出结果 :可以发现用户id为1的有两个订单,用户id为2的有一个订单,如果是直接查询原有的sql语句,发现了有三条记录,mybatis对user的信息相同的记录进行了合并
[User{id=1, username='sunkang', birthday=Sun Oct 07 00:00:00 CST 2018, sex='1', address='杭州:update:update:update:update:update', orderList=[com.mybatis.project.po.Orders@6cc7b4de, com.mybatis.project.po.Orders@32cf48b7]},
User{id=2, username='陈王五', birthday=Sun Oct 07 00:00:00 CST 2018, sex='1', address='北京:update:update:update:update', orderList=[com.mybatis.project.po.Orders@679b62af]}]
- 更加完整的例子
参考github的项目mybatis下的mybatis-generator工程下的com.mybatis.bestPractice.nested
地址为: https://github.com/sunkang123/mybatis
2.嵌套查询
嵌套查询可以实现延迟加载,有效的避免了n+1的问题,只是根据需要的时候才把关联的数据查询出来
- 开启延迟加载的配置
如果要实现延迟加载需要在mybatis的配置文件上配置:
<settings>
<!--lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。-->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- aggressiveLazyLoading当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载(参考lazyLoadTriggerMethods).-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
·
- 编写对应的mapper接口
//嵌套查询,可以实现延迟加载,避免了n+1问题
List<User> findOrdersByUserLazy();
- 编写mapper的文件
<!--通过延迟加载 只需要查询所需要的数据-->
<select id = "findOrdersByUserLazy" parameterType="com.mybatis.project.po.User" resultMap="findOrdersByUserLazyMap" >
select * from user
</select>
<resultMap id="findOrdersByUserLazyMap" type="com.mybatis.project.po.User" >
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="sex" column="sex"/>
<result property="birthday" column="birthday"/>
<result property="address" column="address"/>
<!--select 表示查询的sql语句,column表示关联查询的参数-->
<collection property="orderList" ofType="com.mybatis.project.po.Orders" select="findOrdersById" column="id"/>
</resultMap>
<select id="findOrdersById" resultType="com.mybatis.project.po.Orders" parameterType="int" >
select * from orders where user_id =#{id}
</select>
- 编写测试类
//嵌套结果查询 ,可以设置延迟加载,来达到延迟加载的效果
@Test
public void nestedQuery() throws InterruptedException {
SqlSession sqlSession =sqlSessionFactory.openSession();
//创建userMapper对象,mybatis自动生成mapper代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//这里是1次
List<User> list = userMapper.findOrdersByUserLazy();
System.out.println(list.get(0).getBirthday());
Thread.sleep(5000);
System.out.println("----------");
//当要使用的时候他们再去拉取数据 这里就是N次
System.out.println(list.get(0).getOrderList().get(0).getId());
System.out.println(list.get(1).getOrderList().get(0).getId());
//1次 + N次问题
}
- 输出结果如下:
可以发现先输出select * from user 这个语句,当加载具体的orders的内容时才输出select * from orders where user_id =?
,说明已经达到了延迟的加载的目的
DEBUG [main] - Created connection 1370651081.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@51b279c9]
DEBUG [main] - ==> Preparing: select * from user
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 5
Sun Oct 07 00:00:00 CST 2018
----------
DEBUG [main] - ==> Preparing: select * from orders where user_id =?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 2
1
DEBUG [main] - ==> Preparing: select * from orders where user_id =?
DEBUG [main] - ==> Parameters: 2(Integer)
DEBUG [main] - <== Total: 1
3
总结:
嵌套结果和嵌套查询的区别在于嵌套结果需要一次性把所有的记录查询出来,一般需要使用join语句,需要表进行关联,但是嵌套查询可以实现了按需查询,避免了n+1的问题,一般要先查单表的数据,这是一次查询,然后根据单表的id,跟多表的一方进行关联查询,需要n次关联的数据,就会有n次查询。如果需要全部展示关联表的数据,使用该方式就会造成n+1的问题,推荐使用嵌套结果的方式。
网友评论