业务背景
根据客户id查询客户基本信息,以及客户存在的订单信息
两张数据表
- 客户表
- 订单表
实体类
- 客户实体类:Customer
private Integer id; private String name; private Integer age; //封装存在的订单信息 List<Order> orders = new ArrayList<>();
- 订单实体类:Order
private Integer id; private String orderNumber; private Double orderPrice;
CustomerMapper.java接口
//根据客户id查询客户基本信息,以及客户存在的订单信息 Customer getCustomerById(Integer id);
CustomerMapper.xml映射文件
<!-- //根据客户id查询客户基本信息,以及客户存在的订单信息 Customer getCustomerById(Integer id); Customer实体类: private Integer id; private String name; private Integer age; List<Order> orders = new ArrayList<>(); Order实体类: private Integer id; private String orderNumber; private Double orderPrice; --> <!-- 查询结果的映射规则--> <resultMap id="customerMap" type="customer"> <!-- 主键映射 --> <id property="id" column="cid"/> <!-- 非主键映射 --> <result property="name" column="name"/> <result property="age" column="age"/> <!-- 定义数据容器的映射规则 --> <collection property="orders" ofType="order"> <!-- 容器中元素的映射规则 --> <id property="id" column="oid"/> <result property="orderNumber" column="orderNumber"/> <result property="orderPrice" column="orderPrice" /> </collection> </resultMap> <!-- 核心标签 --> <select id="getCustomerById" parameterType="int" resultMap="customerMap"> select c.id cid, name, age, o.id oid, orderNumber, orderPrice, customer_id from customers c left join orders o on c.id=o.customer_id where c.id=#{id} </select>
测试
//SqlSession对象 SqlSession sqlSession; //获取CustomerMapper的mybatis动态代理对象 CustomerMapper customerMapper; //获取SqlSession @Before public void getSqlSession() throws IOException { //读取核心配置文件 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); //创建SqlSessionFactory对象 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); //获取SqlSession sqlSession = factory.openSession(); //获取各Mapper接口的mybatis动态代理对象 customerMapper = sqlSession.getMapper(CustomerMapper.class); } //归还SqlSession @After public void closeSession(){ sqlSession.close(); } //测试查询标签 @Test public void testGetCustomerById(){ Customer customer = customerMapper.getCustomerById(1); System.out.println(customer); }
结果
==> Preparing: select c.id cid, name, age, o.id oid, orderNumber, orderPrice, customer_id from customers c left join orders o on c.id=o.customer_id where c.id=? ==> Parameters: 1(Integer) <== Columns: cid, name, age, oid, orderNumber, orderPrice, customer_id <== Row: 1, 荷包蛋, 22, 11, 20, 22.22, 1 <== Row: 1, 荷包蛋, 22, 12, 60, 16.66, 1 <== Total: 2 Customer{id=1, name='荷包蛋', age=22, orders=[ Order{id=11, orderNumber='20', orderPrice=22.22}, Order{id=12, orderNumber='60', orderPrice=16.66} ] }
结果分析
- sql语句的查询结果
<== Columns: cid, name, age, oid, orderNumber, orderPrice, customer_id <== Row: 1, 荷包蛋, 22, 11, 20, 22.22, 1 <== Row: 1, 荷包蛋, 22, 12, 60, 16.66, 1 <== Total: 2
- 实际注入到实体类中的数据
Customer{id=1, name='荷包蛋', age=22, orders=[ Order{id=11, orderNumber='20', orderPrice=22.22}, Order{id=12, orderNumber='60', orderPrice=16.66} ] }
- mybatis框架对查询结果会自动去重,按照查询结果的映射规则,完成数据向实体类的注入操作
- 将"1, 荷包蛋, 22 "分别注入到实体类Customer的前三个简单属性中,只注入一组
- 将关联查询到的两条订单数据分别注入到Order实体类中的对应属性中
- 并将Order对象封装到集合中
- 最后将Customer的三个属性值和orders集合封装成一个Customer对象返回
- 由于在数据映射标签中没有指明对customer_id的映射规则,所以在查询时会显示该字段数据,但是并没有被注入到实体类中
注意
在一对多关联查询时,注意根据实际业务需求选择合适的连接查询语句,在本例中选择:左外连接
如果选择内连接,当用户未下订单时,查询不到用户信息
- 外连接查询结果:无订单信息,且用户信息可以正常显示
==> Preparing: select c.id cid, name, age, o.id oid, orderNumber, orderPrice, customer_id from customers c left join orders o on c.id=o.customer_id where c.id=? ==> Parameters: 3(Integer) <== Columns: cid, name, age, oid, orderNumber, orderPrice, customer_id <== Row: 3, 小张, 24, null, null, null, null <== Total: 1 Customer{id=3, name='小张', age=24, orders=[]}
- 内连接查询结果:无订单信息,则用户信息也无法正常显示
==> Preparing: select c.id cid, name, age, o.id oid, orderNumber, orderPrice, customer_id from customers c join orders o on c.id=o.customer_id where c.id=? ==> Parameters: 3(Integer) <== Total: 0 null