MyBatis 由浅入深(实践篇)-3

MyBatis 高级知识

  1. 开发环境

[Java环境] :jdk1.8.0_91
[开发工具] : IDEA 2016.2.5
[数据库] : MySQL 5.7.13
[项目管理工具]:Maven 3.1.1

  1. 数据库
--Table structure for tb_items
DROP TABLE IF EXISTS `tb_items`;
CREATE TABLE `tb_items` (
  `id` int(11) NOT NULL,
  `name` varchar(255) DEFAULT NULL COMMENT '商品名',
  `price` float(10,1) DEFAULT NULL COMMENT '价格',
  `detail` varchar(255) DEFAULT NULL COMMENT '商品介绍',
  `stock` int(255) DEFAULT NULL COMMENT '库存',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


-- Table structure for tb_orderdetail
DROP TABLE IF EXISTS `tb_orderdetail`;
CREATE TABLE `tb_orderdetail` (
  `id` int(11) NOT NULL,
  `order_id` int(11) NOT NULL COMMENT '订单id',
  `item_id` int(11) NOT NULL COMMENT '商品id',
  `count` int(11) DEFAULT NULL COMMENT '订单量',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- Table structure for tb_orders
DROP TABLE IF EXISTS `tb_orders`;
CREATE TABLE `tb_orders` (
  `id` int(11) NOT NULL,
  `userId` int(11) NOT NULL,
  `number` varchar(255) DEFAULT NULL COMMENT '订单号',
  `createtime` date DEFAULT NULL,
  `note` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- Table structure for tb_user
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `sex` varchar(10) DEFAULT NULL,
  `birthday` date DEFAULT NULL,
  `address` varchar(500) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

用户表 tb_user:记录用户信息
订单表 tb_orders:记录用户所创建的订单(购买商品的订单)
订单明细表 tb_orderdetail:记录了订单的详细信息即购买商品的信息
商品表 tb_items:记录了商品信息

表之间的关联关系

实际业务中,实体与实体之间存在一定的业务关系(一对一,一对多,多对多),而关系性数据库通常是根据业务实体建表,所以上述几个关系也可以用来形容表之间的关联关系。

那么根据上述所建的 订单类业务表分析:

  1. 用户(tb_user) 和 订单 (tb_orders)
    一个用户可以有多个订单(一对多)
    一个订单只能由一个用户创建(一对一)
  2. 订单(tb_orders) 和 订单详情(tb_orderdetail)
    一个订单可以有多个订单明细(一对多)
    一个订单明细只能属于一个订单(一对一)
  3. 订单详情(tb_orderdetail) 和 商品(tb_items)
    一个订单明细只能对应一个商品(一对一)
    一个商品可以包含在多个订单明细中(一对多)
  4. 订单(tb_orders) 和 商品(tb_items)
    一个订单可以包含多个商品,同理一个商品可以被多个订单包含(多对多)

订单(tb_orders) 和 商品(tb_items)之间的这种多对多关系在很多实际业务中不利于DTO数据建模及开发维护,此时,使用订单详情(tb_orderdetail) 可以将 一个多对多关系拆分成两个一对多关系。
多对多关系拆分成一对多关系,是简化数据库实体建模常用的一种方式

复杂数据模型

在实际开发过程中,经常从多个有关联关系的表中提取出符合业务需求的数据,此时需要将返回的数据封装成一个对象,此时这个对象就需要针对表关联关系(一对一,一对多,多对多)进行设计。
如何使用 MyBatis 将这个包含关联关系的复杂对象与 sql 返回数据进行绑定,使程序运行时自动映射成该对象实例呢?

一对一查询

业务需求:查询订单列表用户及下订单的用户信息

sql 实现业务:

SELECT orders.*, USER.username, USER.sex, USER.address FROM tb_orders orders, tb_user user WHERE orders.userId = user.id

使用 resultType

1.创建实体对象(pojo)

Order.java

import cn.guan.mybatis.example2.User;

import java.util.Date;
import java.util.List;

/**
 *  订单实体类
 **/
public class Order {
    private Integer id;
    private Integer userId;
    private String number;
    private Date createtime;
    private String note;
    //用户信息
    private User user;
    //订单明细
    private List orderdetails;

    //setter and getter and toString
    ···
}

Orderdetail.java

**
 * 订单详情实体类
 **/
public class Orderdetail {
    private Integer id;
    private Integer order_id;
    private Integer itemsId;
    private Integer count;
    //明细对应的商品信息
    private Item items;

    //setter and getter and toString
    ···
}

Item.java

/**
 * 商品实体类
 **/
public class Item {
    private Integer id;
    private String name;
    private Float price;
    private String detail;
    private Integer stock;
    
    //setter and getter and toString
    ···
}

OrderCustom.java

/**
 * @Description : 通过此类映射订单和用户查询的结果,让此类继承包括 字段较多的pojo类
 */
public class OrderCustom extends Order{

    //用户属性
    private String username;
    private String sex;
    private String address;

    //setter and getter and toString
    ···
}

2.创建 Mapper 接口
OrderCustomMapper.java

/**
 *@Description : mapper 接口
 */
public interface OrderCustomMapper {
    OrderCustom findOrdersUserByResultType() throws Exception;
}

3.创建 Mapper 接口对应 *-mapper.xml 并配置 路径
ordercustom-mapper.xml







    


mybatis-config.xml 中配置加载路径

  
    
        
    

4.测试

MyBatisMappingTest.java

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;

import java.io.InputStream;

/**
 * @Description : MyBatis 多种映射关系测试
 */
public class MyBatisMappingTest {

    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void init() throws Exception
    {
        //创建sqlSessionFactory
        //Mybatis 配置文件
        String resource = "mybatis-config.xml";
        //得到配置文件流
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建会话工厂,传入Mybatis的配置文件信息
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void testOne2One(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //创建usermapper对象,mybatis自动生成代理对象
        OrderCustomMapper orderCustomMapper = sqlSession.getMapper(OrderCustomMapper.class);
        //调用UserMapper的方法
        OrderCustom orderCustom = null;
        try {
            orderCustom = orderCustomMapper.findOrdersUserByResultType();
            System.out.println(orderCustom);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

使用 resultMap

使用 resultMap 将查询结果中的订单信息映射到 Orders 对象中,在 orders 类中添加 User 属性,将关联查询出来的用户信息映射到 orders 对象中的 user 属性中。

1.修改 ordercustom-mapper.xml
定义 resultMap








    
    
        
        
        
        
        
        
        
        
        
            
            
            
            
            
            
        
    
    
    


2.新增一个接口
OrderCustomMapper.java

/**
 *@Description : mapper 接口
 */
public interface OrderCustomMapper {
    OrderCustom findOrdersUserByResultType() throws Exception;
    List findOrdersUserByResultMap() throws Exception;
}

3.测试

    
    ···

    @Test
    public void testOne2OneByResultMap(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //创建usermapper对象,mybatis自动生成代理对象
        OrderCustomMapper orderCustomMapper = sqlSession.getMapper(OrderCustomMapper.class);
        //调用UserMapper的方法
        List orderList = null;
        try {
            orderList = orderCustomMapper.findOrdersUserByResultMap();
            System.out.println(orderList);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

一对多查询

需求:查询订单及订单明细

使用sql 实现业务:

SELECT c.username,c.sex,c.address,a.*,b.id as orderdetailId,b.item_id as itemId,b.count 
FROM tb_orders a 
LEFT JOIN tb_orderdetail b on a.id = b.order_id
LEFT JOIN tb_user c on a.userId = c.id;

使用 resultType

分析:
如果使用 resultType 将查询结果映射到 pojo 中,订单信息就会重复。

使用 resultMap

预期:
对 order 的映射不能出现重复记录。
在 Order 中添加 一个 Orderdetail 的 List 属性,将查询结果中的订单信息映射到 Order 中,订单明细信息映射到 List 中,这样订单信息就不会重复。

1.修改 ordercustom-mapper.xml
定义 resultMap








    
    
        
        
        
        
        
        
        
        
        
            
            
            
            
            
            
        
    

    
    
        
        
        
        
        
            
            
            
            
            
        
    
    
    


2.新增一个接口

OrderCustomMapper.java

/**
 *@Description : mapper 接口
 */
public interface OrderCustomMapper {
    OrderCustom findOrdersUserByResultType() throws Exception;
    List findOrdersUserByResultMap() throws Exception;
    List findAllOrdersAndOrderdetails() throws Exception;
}

3.测试

    
    ···

    @Test
    public void testOne2MoreByResultMap(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //创建usermapper对象,mybatis自动生成代理对象
        OrderCustomMapper orderCustomMapper = sqlSession.getMapper(OrderCustomMapper.class);
        //调用UserMapper的方法
        List orderList = null;
        try {
            orderList = orderCustomMapper.findOrdersUserByResultMap();
            System.out.println(orderList);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

多对多查询

需求:查询用户及用户购买的商品信息

sql 实现业务:

SELECT c.username,c.sex,c.address,a.*,b.id as orderdetailId,b.order_id as orderId ,b.item_id as itemId,b.count as count,d.`name` as itemName,d.price as itemPrice,d.detail as itemDetail
FROM tb_orders a 
LEFT JOIN tb_orderdetail b on a.id = b.order_id
LEFT JOIN tb_user c on a.userId = c.id
LEFT JOIN tb_items d on b.item_id = d.id;

映射思路:
用户信息映射到 User 中,
在 User 中添加 List 属性,用于映射用户下的订单
Order 中的 List 用于映射 Order 对应的订单明细
Orderdetail 中添加 Item 属性用于映射明细对应商品的信息

1.pojo 类

User.java


public class User {
    //user 基本属性
    ···
    private List orders;
    
    //setter and getter and toString
    ···
}

Order.java

public class Order {
    //Order 基本属性
    ···
    //订单明细
        private List orderdetails;
    
        //setter and getter and toString
        ···
}

Orderdetail.java

public class Orderdetail {
    //Orderdetail 基本属性
    ···
    //明细对应的商品信息
        private Item items;
    
        //setter and getter and toString
        ···
}

2.映射文件

userorders-mapper.xml







    
    
        
        
        
        
        
        

        
        
            
            
            
            
            
            
            
                
                
                
                

                
                
                    
                    
                    
                    
                
            

        


    
    
    



加载映射文件:
mybatis-config.xml




    
        
            
            
            
            
                
                
                
                
            
        
    
    
    
        ···
        
    

2.接口定义
UserOrdersMapper.java

import java.util.List;

/用户及所下订单信息查询接口

public interface UserOrdersMapper {
    List findUserAndOrdersByUserId(int userid) throws  Exception;
}

3.测试


    ···

    @Test
    public void testMore2MoreByResultMap(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //创建usermapper对象,mybatis自动生成代理对象
        UserOrdersMapper userOrdersMapper = sqlSession.getMapper(UserOrdersMapper.class);
        //调用UserMapper的方法
        List users = null;
        try {
            users = userOrdersMapper.findUserAndOrdersByUserId(1);
            System.out.println(users);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

结果集映射总结

  1. resultType
    按照sql 查询结果的字段名(column)与 resultType 定义的 pojo 类的属性名一对一映射,完成映射。
    通常用于简单的不包含表连接关系的结果映射。

  2. resultMap
    需要完成一对一,一对多,多对多这些特殊的包含表连接关系的复杂结果映射。

  3. association
    将一对一的关联查询信息映射到 pojo 中,使用 resultType 无法完成成员属性为对象的自动映射。
    例:订单 pojo 中包含一个 User 对象,使用 resultType 无法将 sql 的查询结果中的 User 信息自动映射到 User 成员变量中。

  4. collection
    将一对多的关联查询信息映射到一个 List 集合中

使用 resultMap 能对复杂的查询结果集进行面向对象设计,而且可以通过成员变量的形式,大大减少 pojo 类的创建,同时也可以减少冗余信息的存储,方便对结果集进行遍历查询。


参考链接:
http://blog.csdn.net/column/details/13905.html

你可能感兴趣的:(MyBatis 由浅入深(实践篇)-3)