zoukankan      html  css  js  c++  java
  • MyBatis:一对多表关系详解(从案例中解析)

    摘自:https://blog.csdn.net/xzm_rainbow/article/details/15336933

    1,案例一:产生问题

    客户(Customer表)和订单(Order表)之间的关系是一对多的关系,即一个用户可以有多个订单。

    (1)建立表,并建立一对多关联。

    主表

     

    1.  
      create table Customer(
    2.  
      id int primary key,
    3.  
      name varchar(32)
    4.  
      );

    从表,customer_id是指向Customer表的主键id的外键

    1.  
      create table `order`(
    2.  
      id int primary key,
    3.  
      name varchar(32),
    4.  
      customer_id int
    5.  
      );

    建立主外键关系

    alter table `order` add constraint foreign key(customer_id) references customer(id);
    

    向两个表中插入数据:

    Customer

    Order

    (2)建立Javabean,Customer和Order

    Customer

    1.  
      public class Customer {
    2.  
      private Integer id;
    3.  
      private String name;
    4.  
      private List<Order> orderList = new ArrayList<Order>();
    5.  
      ………………getter and setter ……………………
    6.  
      }

    Order

    1.  
      public class Order {
    2.  
      private Integer id;
    3.  
      private String name;
    4.  
      private Customer customer;
    5.  
      ……………………getter and setter ………………………
    6.  
      }

    (3)建立Customer的Mapper.xml配置文件CustomerMapper.xml,编写配置文件:

    1.  
      <?xml version="1.0" encoding="UTF-8"?>
    2.  
      <!DOCTYPE mapper
    3.  
      PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4.  
      "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5.  
       
    6.  
      <mapper namespace="cn.itcast.test1.domain">
    7.  
      <!-- 建立一对多关系,多表查询解析 -->
    8.  
      <!-- 配置结果映射,解析结果集,id为定义的名称,指向SQL的返回结果类型 -->
    9.  
      <resultMap type="cn.itcast.test1.domain.Customer" id="customerTest">
    10.  
      <id property="id" column="id" />
    11.  
      <result property="name" column="name" />
    12.  
      <!-- 一对多配置,建立集合,集合中存放多表对应的对象 -->
    13.  
      <!-- property是实体类中的集合的名字,ofType是集合中存放的对象的全限定名称 -->
    14.  
      <collection property="orderList" ofType="cn.itcast.test1.domain.Order">
    15.  
      <!-- id用于定义主表的唯一标识,是从表识别主表主键的唯一标识,column是结果集对应的字段名 -->
    16.  
      <id property="id" column="id" />
    17.  
      <!-- result是普通属性,其他解释同上 -->
    18.  
      <result property="name" column="name" />
    19.  
      </collection>
    20.  
      </resultMap>
    21.  
       
    22.  
      <!-- 配置resultType,则每返回一条结果类型,就建立一个对象 -->
    23.  
      <!-- 配置resultMap,则会将返回的结果集进行解析,对应上面的resultMap解析器,根据定义的字段产生实体对象 -->
    24.  
      <select id="getinfo" parameterType="int" resultMap="customerTest">
    25.  
      select * from customer c,`order` o where c.id = o.customer_id and c.id=#{id}
    26.  
      </select>
    27.  
      </mapper>

    (4)在mybatis-config.xml中配置mapper的映射

    1.  
      <mappers>
    2.  
      <mapper resource="cn/itcast/domain/UserMapper.xml" />
    3.  
      <mapper resource="cn/itcast/test1/domain/CustomerMapper.xml"/>
    4.  
      </mappers>

    (5)建立测试类,TestCustomer.java,测试查询

    1.  
      public class TestCustomer {
    2.  
      //MyBatis的Session工厂,底层是jdbc实现,
    3.  
      private SqlSessionFactory factory;
    4.  
      @Before
    5.  
      //在初始化方法中初始化配置文件,并构建工厂
    6.  
      public void init() throws IOException {
    7.  
      //读取核心配置文件,这个配置文件配置了数据库的相关信息以及映射的mapper配置文件的读取配置
    8.  
      String resource = "mybatis-config.xml";
    9.  
      //通过inputstream将配置文件读取到内存中,用于构建session工厂,
    10.  
      //Resources类封装的是类加载器,通过类加载器的getResourceAsStream获取输入流。
    11.  
      InputStream inputStream = Resources.getResourceAsStream(resource);
    12.  
      //通过build方法构建工厂对象
    13.  
      factory = new SqlSessionFactoryBuilder().build(inputStream);
    14.  
      }
    15.  
       
    16.  
      @Test
    17.  
      public void findCustomer() {
    18.  
      //获取session对象,session对象底层通过jdbcAPI实现,通过反射的方式,获取配置文件配置的类信息以及方法
    19.  
      SqlSession session = factory.openSession();
    20.  
      //CURD其实是使用jdbc的ResultSet结果集实现,
    21.  
      Customer customer = session.selectOne("cn.itcast.test1.domain.getinfo", 1);
    22.  
      System.out.println(customer);
    23.  
      //记得释放资源
    24.  
      session.close();
    25.  
      }
    26.  
      }

    (6)通过junit运行findCustomer,

    结果如下:

    使用我们的sql语句

    select * from customer c,`order` owhere c.id = o.customer_id and c.id=1;

    在数据库查询,结果是:

    案例一的问题:

    存入集合的对象应该是order对象,但是里面的数据却是customer的数据,为什么呢?

    这涉及到ResultMap的实现机制。ResultMap是把实体类也就是我们的javabean的属性与返回结果集的字段进行匹配,如果匹配,则建立对象,并通过设个对象的这个属性的set方法将值设置进去。

    详解:

    ResultMap接收的结果集信息,当前的返回的结果集信息也就是上面通过sql语句在数据库查询出来的图上所示的信息。ResultMap会从返回的结果集逐条读取记录(每一行数据),当前获取第一行数据:



     

    每次获取一行后,又会逐列读取信息。读取到id字段,值为1。根据配置文件

    <resultMaptype="cn.itcast.test1.domain.Customer"id="customerTest">,发现这是一个Customer对象,就new一个Customer对象,然后再将结果集中的字段值id与column属性值逐一匹配,<id property="id"column="id" />匹配,就通过Customer的set方法将id的值1设置进id属性;然后对结果集这一列的匹配并没有结束,ResultMap会继续拿着这个id字段向下匹配,下面一个配置是<resultproperty="name" column="name" />,不匹配,则不做任何动作;继续向下匹配,发现<collection property="orderList"ofType="cn.itcast.test1.domain.Order">建立了一对多关系,实体是Order,于是就建立了一个Order对象,然后继续拿着id字段向下匹配,接着发现在Order中也有id,<id property="id"column="id" />匹配了,再用Order的serId方法将id字段的值1设置进去,所以现在List集合中的一个Order对象的id值是1。继续拿着id向下匹配,<resultproperty="name" column="name" />,不匹配,且配置结束,则结束这个id字段的配置。

    进入下一个name字段的配置,与id一样,进去匹配,当前记录Customer的name还没有通过set方法赋值,就set进去。后面的Order也是一样。

    至此,结果集中建立的对象包括:一个id=1,name=小强的Customer对象,一个id=1,name=小强的Order对象,并且Order对象存储在Customer对象的List集合中。

    进行下一个结果集字段的匹配,下一个也是id,同样进入配置文件逐一匹配,需要注意的是,如果之前有一个与当前字段id相同的字段,在同一条记录中对同一个对象做了set操作,那么当前这个id字段将不会再对当前对象做set操作。

    也就是说,当前的字段是id,这个id在配置文件中匹配的时候,遍历到column为id的列,就要通过创建的当前对象Customer的setId()方法设值,但是,在同一条记录中,之前有一个字段相同(同为id)的字段已经通过这个Customer对象的serId()方法设值,那么当前这个id字段就不会再set赋值,所以,这个id匹配过后,Customer的id值不变,Order的值也不变,后面的name字段同理,所以,集合中的Order的id是1,name是小强。

     

    另外,执行完毕后,集合里面只有一条记录,而查询出来的是两条记录。因为查询的是Customer,而且是通过selectOne查询的,MyBatis根据返回的记录数判断,通过selectOne查询,返回必须是没有或者只有一条,如果超过一条,就会报错。但是通过selectList查询,就不会出现这个问题了。

    如果查询的是Order,因为order表中有两条记录,所以查询结果是两条,如果通过selectOne查询,会报异常,必须通过selectList查询。

     

    2,案例二:解决了案例一中的问题

    为了解决案例一中的问题,需要修改一下我们的sql语句。通过上面的解析可以知道,如果字段名重复,则后面的字段不会再通过set方法设值。所以解决办法就是如果出现重复字段,就换个名称,让所有的字段不一样即可。

    具体方案:

    查询语句中,为字段建立别名,通过别名返回结果集(为重复的字段建立别名)。只需要修改CustomerMapper.xml文件即可。

    具体如下:

     

    1.  
      <mapper namespace="cn.itcast.test1.domain">
    2.  
      <resultMap type="cn.itcast.test1.domain.Customer" id="customerTest">
    3.  
      <id property="id" column="id" />
    4.  
      <result property="name" column="name" />
    5.  
      <collection property="orderList" ofType="cn.itcast.test1.domain.Order">
    6.  
      <id property="id" column="o_id" />
    7.  
      <result property="name" column="o_name" />
    8.  
      </collection>
    9.  
      </resultMap>
    10.  
      <select id="getinfo" parameterType="int" resultMap="customerTest">
    11.  
      select c.*,o.id as o_id,o.name as o_name from customer c,`order` o where c.id = o.customer_id and c.id=#{id}
    12.  
      </select>
    13.  
      </mapper>

    这样所有的字段就都不一样了,可以获取正确结果,通过junit执行,可以获取如下控制台信息:

     

    可以看到,orderList中有两条数据,且正是我们设置到数据库的数据。

  • 相关阅读:
    Serialize and Deserialize Binary Tree
    sliding window substring problem汇总贴
    10. Regular Expression Matching
    《深入理解计算机系统》(CSAPP)读书笔记 —— 第七章 链接
    程序员如何写一份合格的简历?(附简历模版)
    9个提高代码运行效率的小技巧你知道几个?
    《深入理解计算机系统》(CSAPP)读书笔记 —— 第六章 存储器层次结构
    24张图7000字详解计算机中的高速缓存
    《深入理解计算机系统》(CSAPP)实验四 —— Attack Lab
    《深入理解计算机系统》(CSAPP)读书笔记 —— 第五章 优化程序性能
  • 原文地址:https://www.cnblogs.com/xichji/p/13494376.html
Copyright © 2011-2022 走看看