ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

MyBatis:一对多表关系详解(从案例中解析)

2020-08-13 09:02:20  阅读:223  来源: 互联网

标签:Customer customer 多表 name 配置文件 Order 详解 MyBatis id


摘自: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中有两条数据,且正是我们设置到数据库的数据。

标签:Customer,customer,多表,name,配置文件,Order,详解,MyBatis,id
来源: https://www.cnblogs.com/xichji/p/13494376.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有