首页 > 其他分享 >Mybatis集合映射

Mybatis集合映射

时间:2023-11-08 18:07:51浏览次数:26  
标签:customer 映射 集合 user time Mybatis id cu


Mybatis系列之集合映射

上篇文章我们讲了关联映射,实现了销售与登录用户之间的关联。本文我们接着来讲一讲集合映射,实现销售与客户的多对多关系。

实现销售与客户多对多关系

Mybatis系列之关联映射》中的映射接口和测试用例,这里仅对增加和修改的内容进行讲解。

第一步,在动手编写映射文件之前,我们需要对Sales类增加一个List属性,用以保存销售员对应的客户列表。


/**
 * 
 */  
private List<Customer> customers;  
  
public Sales() {  
super();  
this.setCustomers(new ArrayList<Customer>());  
}  
  
public List<Customer> getCustomers() {  
return customers;  
}  
  
protected void setCustomers(List<Customer> customers) {  
this.customers = customers;  
}


package com.emerson.learning.pojo;  
  
import java.sql.Timestamp;  
  
public class Customer {  
  
/**
     * 
     */  
private int customerId;  
  
/**
     * 
     */  
private String customerName;  
  
/**
     * 
     */  
private int isValid;  
  
/**
     * 
     */  
private Timestamp createdTime;  
  
/**
     * 
     */  
private Timestamp updateTime;  
  
/**
     * 
     */  
private User userInfo;  
  
@Override  
public String toString() {  
return "Customer [customerId=" + customerId + ", customerName=" + customerName + ", isValid=" + isValid  
", createdTime=" + createdTime + ", updateTime=" + updateTime + ", userInfo=" + userInfo + "]";  
    }  
  
public int getCustomerId() {  
return customerId;  
    }  
  
public void setCustomerId(int customerId) {  
this.customerId = customerId;  
    }  
  
public String getCustomerName() {  
return customerName;  
    }  
  
public void setCustomerName(String customerName) {  
this.customerName = customerName;  
    }  
  
public int getIsValid() {  
return isValid;  
    }  
  
public void setIsValid(int isValid) {  
this.isValid = isValid;  
    }  
  
public Timestamp getCreatedTime() {  
return createdTime;  
    }  
  
public void setCreatedTime(Timestamp createdTime) {  
this.createdTime = createdTime;  
    }  
  
public Timestamp getUpdateTime() {  
return updateTime;  
    }  
  
public void setUpdateTime(Timestamp updateTime) {  
this.updateTime = updateTime;  
    }  
  
public User getUserInfo() {  
return userInfo;  
    }  
  
public void setUserInfo(User userInfo) {  
this.userInfo = userInfo;  
    }  
}



 

 第二步,修改映射文件。我们先使用嵌套查询方式来实现为销售加载客户列表。首先在resultMap中增加客户集合映射的定义。

嵌套查询 


<!-- 定义一对多集合信息(每个销售人员对应多个客户) -->  
<collection property="customers" javaType="ArrayList" column="sales_id" ofType="Customer" select="getCustomerForSales" />



集合映射的定义与关联映射定义很相似,除了关键字不同外,还多了两个属性JavaType和ofType。

property用于指定在Java实体类是保存集合关系的属性名称

JavaType用于指定在Java实体类中使用什么类型来保存集合数据,多数情况下这个属性可以省略的。

column用于指定数据表中的外键字段名称。

ofType用于指定集合中包含的类型。

select用于指定查询语句。

 

然后再定义查询客户的查询语句。




<select id="getCustomerForSales" resultType="com.emerson.learning.pojo.Customer">  
    SELECT c.customer_id, c.customer_name, c.user_id, c.is_valid,  
    c.created_time, c.update_time  
    FROM customer c INNER JOIN customer_sales s USING(customer_id)  
s.sales_id = #{id}  
</select>



需要注意的是,无论是关联还是集合,在嵌套查询的时候,查询语句的定义都不需要使用parameterType属性定义传入的参数类型,因为通常作为外键的,都是简单数据类型,查询语句会自动使用定义在association或是collection元素上column属性作为传入参数的。

 


运行测试用例,看到如下结果就说明我们的映射文件是正确的了。

Opening JDBC Connection  
Created connection 632249781.  
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@25af5db5]  
==>  Preparing: SELECT sales_id, sales_name, sales_phone, sales_fax, sales_email, salesman.is_valid, salesman.created_time, salesman.update_time, sys_user.user_id as user_id, user_name, user_password, nick_name, email as user_email, sys_user.is_valid as user_is_valid, sys_user.created_time as user_created_time, sys_user.update_time as user_update_time FROM salesman left outer join sys_user using(user_id) WHERE sales_id=?   
==> Parameters: 2(Integer)  
<==    Columns: sales_id, sales_name, sales_phone, sales_fax, sales_email, is_valid, created_time, update_time, user_id, user_name, user_password, nick_name, user_email, user_is_valid, user_created_time, user_update_time  
<==        Row: 2, Bing Gu, 021-3418 1999, null, [email protected], 1, 2015-10-01 20:21:26.0, 2015-10-01 21:56:40.0, 25, binggu, 5f4dcc3b5aa765d61d8327deb882cf99, Bing Gu, null, 1, 2015-09-30 22:04:34.0, 2015-09-30 22:04:34.0  
====>  Preparing: SELECT c.customer_id, c.customer_name, c.user_id, c.is_valid, c.created_time, c.update_time FROM customer c INNER JOIN customer_sales s USING(customer_id) WHERE s.sales_id = ?   
====> Parameters: 2(Integer)  
<====    Columns: customer_id, customer_name, user_id, is_valid, created_time, update_time  
<====        Row: 161, 客户1, null, 1, 2015-10-01 20:24:05.0, 2015-10-01 20:24:05.0  
<====        Row: 163, 客户2, null, 1, 2015-10-01 20:24:05.0, 2015-10-01 20:24:05.0  
<====        Row: 164, 客户3, null, 1, 2015-10-01 20:24:05.0, 2015-10-01 20:24:05.0  
<====      Total: 3  
<==      Total: 1


我们看到这里与数据库进行了两查询交互,说明这里仍然存在着“N+1”的问题。下面,我们改用嵌套结果的方式来实现销售与客户的映射关系。

嵌套结果


<resultMap id="salesResultMap" type="com.emerson.learning.pojo.Sales">  
<id property="salesId" column="sales_id" />  
<result property="salesName" column="sales_name" />  
<result property="phone" column="sales_phone" />  
<result property="fax" column="sales_fax" />  
<result property="email" column="sales_email" />  
<result property="isValid" column="is_valid" />  
<result property="createdTime" column="created_time" />  
<result property="updateTime" column="update_time" />  
  
<!-- 定义多对一关联信息(嵌套结果方式) -->  
<association property="userInfo" resultMap="userResult" />  
  
<!-- 定义一对多集合信息(每个销售人员对应多个客户) -->  
<!-- <collection property="customers" column="sales_id" select="getCustomerForSales" /> -->  
  
<collection property="customers" ofType="com.emerson.learning.pojo.Customer">  
<id property="customerId" column="customer_id" />  
<result property="customerName" column="customer_name" />  
<result property="isValid" column="is_valid" />  
<result property="createdTime" column="created_time" />  
<result property="updateTime" column="update_time" />  
<!-- 映射客户与登录用户的关联关系,请注意columnPrefix属性 -->  
<association property="userInfo" resultMap="userResult" columnPrefix="cu_" />  
</collection>  
</resultMap>



这里将客户的映射关系直接写在了销售的resultMap中。上述代码与关联映射十分相似,只是有一点需要朋友们留心,那就是在对客户数据进行映射的时候,我们使用了association元素的一个新的属性columnPrefix。这个属性是做什么用的呢?从名字上理解,就是给每个栏位之前加上前缀。Bingo!答对了,那么什么情况下会使用到这个属性呢?后面我们会结合着修改后的查询语句来说明这个属性的使用场景。请耐心的往下看。:)

映射结果修改好了,紧接着我们就要修改查询语句了。


<select id="getById" parameterType="int" resultMap="salesResultMap">  
    SELECT  
        s.sales_id, s.sales_name, s.sales_phone, s.sales_fax, s.sales_email,  
        s.is_valid, s.created_time, s.update_time,  
        su.user_id as user_id, su.user_name, su.user_password, su.nick_name,  
        su.email as user_email,  
        su.is_valid as user_is_valid,  
        su.created_time as user_created_time,  
        su.update_time as user_update_time,  
        c.customer_id, c.customer_name, c.is_valid as customer_is_valid,  
        c.created_time as customer_created_time,  
        c.update_time as customer_update_time,  
        cu.user_id as cu_user_id, cu.user_name as cu_user_name, cu.user_password as cu_user_password,   
        cu.nick_name as cu_nick_name, cu.email as cu_user_email, cu.is_valid as cu_user_is_valid,  
        cu.created_time as cu_user_created_time, cu.update_time as cu_user_update_time  
    FROM  
s.user_id = su.user_id  
        INNER JOIN customer_sales cs USING(sales_id)  
        LEFT OUTER JOIN customer c USING(customer_id)  
c.user_id = cu.user_id  
sales_id=#{id}  
</select>



这个语句乍看起来有些复杂,其实很容易理解。这里用到了四张数据表,销售、客户、客房销售关系表和登录用户表。具体的字段我就不说了,主要说一下这个登录用户表。这张数据表在查询语句中出现了两次,为什么呢?因为销售与登录用户有关联关系,同样地,客户也与登录用户表有关联关系,所以我们需要对用户表进行两次Join操作。

那么问题来了,销售要用户有关联,客户也要与用户有关联,这种映射语句应该如何写呢?难道要对用户表写两次映射?聪明的朋友一定会说,我们可以复用之前写过的用户映射结果集呀!答案是肯定的。我们不妨在这里再次贴出这段代码,一起回忆一下。

<resultMap id="userResult" type="User">  
<id property="userId" column="user_id" />  
<result property="userName" column="user_name" />  
<result property="userPassword" column="user_password" />  
<result property="nickName" column="nick_name" />  
<result property="email" column="user_email" />  
<result property="isValid" column="user_is_valid" />  
<result property="createdTime" column="user_created_time" />  
<result property="updateTime" column="user_update_time" />  
</resultMap>


数据表中的字段与Java实体类中的属性的映射关系是一一对应的,Mybatis会根据我们定义的映射关系,将数据表中字段的映射到Java实体类属性上。

可是我们的查询语句中对用户表进行了两次Join操作,第一次是销售与用户的Join,第二次是客户与用户的Join。而SQL语句是不允许在同一条查询语句中出现相同字段名的(虽然我们有时候会这样写,但是数据库会自动帮我们为重名的字段名起个别名的,比如在字段名后添加数字)。如果我们为第二次Join进来的用户表中的字段使用别名方式,那么就会导致映射的到客户类中的用户信息缺失,因为字段名与我们在映射文件中的定义不一致。如何解决这个问题呢?这时候该columnPrefix属性出场了。

Mybatis也考虑到这种情况的出现,她允许我们在重复出现的字段名前加上一个统一的字符前缀,这样就可以有效的避免字段重名,又可以复用之前定义的映射结果集。

在上述的查询语句中,我们为第二次Join进来的用户表中的字段都加上了“cu_”做为区分重名字段的前缀,同时使用columnPrefix属性告诉Mybatis在第二次对用户表映射的时候,将字段名是以“cu_”打头的字段值映射到Java实体类属性当中。这样就可以正确的把客户与用户的关联信息映射到Customer对象当中了。


<association property="userInfo" resultMap="userResult" columnPrefix="cu_" />


上述的表达可能有些臃肿,不知道小伙朋友们明白了没有。理工男的写作水平,你们懂的。

 

彩蛋奉上(共享不同映射文件中的结果集)

我们之前在User.xml文件中定义过用户表的映射结果集,现在在Sales.xml中也需要使用到同样的结果集,是否可以直接跨文件引用呢?答案是肯定的了,不然对于同一个映射结果集,我们要多处编写,多处维护,这样不仅工作量大,对日后的维护也带来了一定的麻烦。我们只需要在引用处使用结果集的全限定名就可以了。


<resultMap id="salesResultMap" type="com.emerson.learning.pojo.Sales">  
<id property="salesId" column="sales_id" />  
<result property="salesName" column="sales_name" />  
<result property="phone" column="sales_phone" />  
<result property="fax" column="sales_fax" />  
<result property="email" column="sales_email" />  
<result property="isValid" column="is_valid" />  
<result property="createdTime" column="created_time" />  
<result property="updateTime" column="update_time" />  
  
<!-- 定义多对一关联信息(嵌套查询方式) -->  
<association property="userInfo" column="user_id" javaType="User"   
select="selectUser" fetchType="lazy"> </association> -->  
  
<!-- 定义多对一关联信息(嵌套结果方式) -->  
<association property="userInfo" resultMap="com.emerson.learning.xml.user.userResult" />  
  
<!-- 定义一对多集合信息(每个销售人员对应多个客户) -->  
<collection property="customers" column="sales_id" select="getCustomerForSales"   
/> -->  
  
<collection property="customers" ofType="com.emerson.learning.pojo.Customer">  
<id property="customerId" column="customer_id" />  
<result property="customerName" column="customer_name" />  
<result property="isValid" column="is_valid" />  
<result property="createdTime" column="created_time" />  
<result property="updateTime" column="update_time" />  
<association property="userInfo" resultMap="com.emerson.learning.xml.user.userResult" columnPrefix="cu_" />  
</collection>  
</resultMap>


标签:customer,映射,集合,user,time,Mybatis,id,cu
From: https://blog.51cto.com/u_809530/8257068

相关文章

  • MyBatis的返回参数类型
    MyBatis的返回参数类型分两种1.对应的分类为:1.1.resultMap:1.2.resultType:2.对应返回值类型:2.1.resultMap:结果集2.2.resultType:int,string,long,class3.注意点:在MyBatis进行查询映射时,其实查询出来的每一个属性都是放在一个对应的Map里面的,其中键是属性名,值则是其......
  • Mybatis多表关联映射
    Mybatis多表关联映射查询结果集ResultMapresultMap元素是MyBatis中最重要最强大的元素。它就是让你远离90%的需要从结果集中取出数据的JDBC代码的那个东西,而且在一些情形下允许你做一些JDBC不支持的事情。事实上,编写相似于对复杂语句联合映射这些等同的代码,也许可以......
  • mybatis insert 返回主键
    需求:使用MyBatis往MySQL数据库中插入一条记录后,需要返回该条记录的自增主键值。方法1(推介使用):在mapper中指定keyProperty属性,示例如下:<insertid="insert"parameterType="com.cl.media.model.Advert" useGeneratedKeys="true"keyProperty=advertId"> insertintor_adv......
  • mybatis在xml文件中处理大于号小于号的方法
    第一种方法:用了转义字符把>和<替换掉,然后就没有问题了。SELECT*FROMtestWHERE1=1ANDstart_date <=CURRENT_DATEANDend_date>=CURRENT_DATE附:XML转义字符           <                     ......
  • Mybatis if test 判断数字时需要注意
    mybatis做if判断注意mybatis做if判断注意:下面这种写法只适用于id类型为字符串.<iftest="id!=nullandid!=''">id=#{id}</if>如果id类型为int当id=0时这个判断不会传该值的。test="id!=nullandid!=''orid==0">......
  • 以下哪些选项可以将集合A转化为数组?
    AArray.from(A)B[].slice.apply(A)C[…A]D[].map.call(A,o=>o)正确答案:ABCDArray.from(arr,mapfn,thisArg)方法,用于将两类可以把对象转换为真正的数组:类似数组的对象和可遍历的对象(部署了Iterator接口的,String,ES6新增的Map和Set)。可以传3个参数,其中第一个是数组,必传......
  • 七、Java集合
    一、集合概述集合的特点如下:动态大小:集合可以根据需要动态调整大小,不像数组需要提前指定大小。灵活性:集合提供了各种不同类型的数据结构和容器,例如列表、集、映射、队列等,以满足不同的存储和操作需求。高效性:Java集合框架中的实现类经过优化,提供高效的插入、删除和查找......
  • mybatis 多个条件in查询
    1.UI如下,共有四个查询条件,每个查询可以多选,所以考虑用in查询 2.查询DTO类封装如下:@Data@ApiModel(value="整机下市明细信息查询对象")publicclassMachineOffMarketQueryDto{@ApiModelProperty(value="型号编码集合",required=true)@NotEmpty(message="型......
  • mybatisplus轻松完成一次模糊+分页查询
    之前一直用mybatis+pageinfo完成模糊+分页查询,还需要手写sql语句,之前一直没做尝试,今天试了试mybatisplus一个人完成模糊+分页,挺简单的有一个小插曲是,我的前端接受的data中,data.list变成了data.record,一开始没有查到数据,让我差点怀疑自己哪里写错了,在使用sout的检查中我发现servic......
  • C# 更新集合中某一个信息也是集合的数据
    比如一个集合中,还有一个多选项,此多选项是作为一个集合,传输在总的集合上,更新这个多选项集合的代码1.先更新总集合的基本数据2.针对这个多选项集合的值,更新多选项1//从DB删除已经删除的问题2if(exam.ID>0)3{4......