首页 > 其他分享 >Mybatis多表关联映射

Mybatis多表关联映射

时间:2023-11-08 18:07:00浏览次数:41  
标签:多表 映射 sales public user Mybatis NULL id


Mybatis多表关联映射

查询结果集ResultMap

resultMap 元素是 MyBatis 中最重要最强大的元素。它就是让你远离 90%的需要从结果 集中取出数据的 JDBC 代码的那个东西,而且在一些情形下允许你做一些 JDBC 不支持的事 情。 事实上, 编写相似于对复杂语句联合映射这些等同的代码,也许可以跨过上千行的代码。

有朋友会问,之前的示例中我们没有用到结果集,不是也可以正确地将数据表中的数据映射到Java对象的属性中吗?是的。这正是resultMap元素设计的初衷,就是简单语句不需要明确的结果映射,而很多复杂语句确实需要描述它们的关系。我们对Mybatis的设置进行了详细的讲解,不知道朋友们是否还记得有这样一个设置变量autoMappingBehavior,其默认值为PARTIAL,这就意味着Mybatis会自动映射没有定义在resultMap中的字段。前提是Java中的属性名称与数据表中的字段名称完全一样(大小写敏感),又或者是Java中使用了驼峰命名规则,但数据表中使用是下划线连词规则,且我们把Mybatis中的设置变量mapUnderscoreToCamelCase设置为true。

resultMap元素中,允许有以下直接子元素:

constructor - 类在实例化时,用来注入结果到构造方法中(本文中暂不讲解)

id - 作用与result相同,同时可以标识出用这个字段值可以区分其他对象实例。可以理解为数据表中的主键,可以定位数据表中唯一一笔记录

result - 将数据表中的字段注入到Java对象属性中

association - 关联,简单的讲,就是“有一个”关系,如“用户”有一个“帐号”

collection - 集合,顾名思议,就是“有很多”关系,如“客户”有很多“订单”

discriminator - 使用结果集决定使用哪个个结果映射(暂不涉及)

 

每个元素的用法及属性我会在下面结合使用进行讲解。下面就正式进入今天的主题。

 

案例背景

在第三讲《Mybatis之简单示例》的基础上,我们在数据库中额外创建三张数据表,分别表示销售人员、客户,以及销售和客户多对多的对应关系。每个销售、客户都有一个登录帐号。

CREATE TABLE `customer` (  
int(10) NOT NULL AUTO_INCREMENT,  
varchar(200) NOT NULL,  
int(10) DEFAULT NULL,  
NOT NULL DEFAULT '1',  
NOT NULL,  
timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,  
PRIMARY KEY (`customer_id`),  
KEY `customer_name` (`customer_name`) USING BTREE  
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;  
  
CREATE TABLE `salesman` (  
int(10) NOT NULL AUTO_INCREMENT,  
varchar(64) NOT NULL,  
varchar(32) DEFAULT NULL,  
varchar(32) DEFAULT NULL,  
varchar(100) DEFAULT NULL,  
int(10) DEFAULT NULL,  
int(10) DEFAULT '0',  
NOT NULL DEFAULT '1',  
DEFAULT NULL,  
timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,  
PRIMARY KEY (`sales_id`),  
KEY `sales_name` (`sales_name`)  
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;  
  
CREATE TABLE `customer_sales` (  
int(10) NOT NULL AUTO_INCREMENT,  
int(10) NOT NULL,  
int(10) NOT NULL,  
NOT NULL,  
timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,  
PRIMARY KEY (`id`),  
UNIQUE KEY `customer_id` (`customer_id`,`sales_id`) USING BTREE,  
KEY `sales_id` (`sales_id`)  
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;


 

实现销售与登录用户一对一关系

为了巩固上一篇文章《Mybatis系列之接口式编程》中的知识,这里采用Mybatis的接口式编程。无论是对单表进行映射,还是对多表映射,步骤都是相同的,唯一的不同就在映射文件的编写上,所以,这里我们只把重点放在映射文件上,其他部分就一笔提过。

 

首先,我们需要销售创建一个Java类,其中的userInfo属性对应销售的登录用户信息的。

 

package com.emerson.learning.pojo;  
  
import java.sql.Timestamp;  
  
public class Sales {  
/**
     * 
     */  
private int salesId;  
      
/**
     * 
     */  
private String salesName;  
      
/**
     * 
     */  
private String phone;  
      
/**
     * 
     */  
private String fax;  
      
/**
     * 
     */  
private String email;  
      
/**
     * 
     */  
private int isValid;  
  
/**
     * 
     */  
private Timestamp createdTime;  
  
/**
     * 
     */  
private Timestamp updateTime;  
      
/**
     * 
     */  
private User userInfo;  
  
@Override  
public String toString() {  
return "Sales [salesId=" + salesId + ", salesName=" + salesName + ", phone=" + phone + ", fax=" + fax  
", email=" + email + ", isValid=" + isValid + ", createdTime=" + createdTime + ", updateTime="  
", userInfo=" + userInfo.toString() + "]";  
    }  
  
public int getSalesId() {  
return salesId;  
    }  
  
public void setSalesId(int salesId) {  
this.salesId = salesId;  
    }  
  
public String getSalesName() {  
return salesName;  
    }  
  
public void setSalesName(String salesName) {  
this.salesName = salesName;  
    }  
  
public String getPhone() {  
return phone;  
    }  
  
public void setPhone(String phone) {  
this.phone = phone;  
    }  
  
public String getFax() {  
return fax;  
    }  
  
public void setFax(String fax) {  
this.fax = fax;  
    }  
  
public String getEmail() {  
return email;  
    }  
  
public void setEmail(String eamil) {  
this.email = eamil;  
    }  
  
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;  
    }  
}


<?xml version="1.0" encoding="UTF-8"?>  
  
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"   
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
  
<mapper namespace="com.emerson.learning.dao.ISalesDao">  
<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" />  
  
<!-- 定义多对一关联信息(每个销售人员对应一个登录帐号) -->  
<association property="userInfo" column="user_id" javaType="User" select="selectUser">  
<id property="userId" column="userId" />  
<result property="userName" column="user_name" />  
<result property="userPassword" column="user_password" />  
<result property="nickName" column="nick_name" />  
<result property="email" column="email" />  
<result property="isValid" column="is_valid" />  
<result property="createdTime" column="created_time" />  
<result property="updateTime" column="update_time" />  
</association>  
</resultMap>  
      
<select id="selectUser" resultType="User">  
        SELECT user_id, user_name, user_password, nick_name, email, is_valid, created_time  
user_id = #{id}  
</select>  
      
<select id="getById" parameterType="int" resultMap="salesResultMap" >  
        SELECT sales_id, sales_name, sales_phone, sales_fax, sales_email, user_id, is_valid, created_time, update_time  
sales_id=#{id}  
</select>  
</mapper>


第三步,将映射文件注册到Mybatis中。这一步很简单,简单到很多朋友会忘掉。却也是最重要的一环。

 


<mappers>  
<mapper resource="com/emerson/learning/mapping/User.xml" />  
<mapper resource="com/emerson/learning/mapping/Sales.xml" />  
</mappers>



 

第四步,编写接口。

 


package com.emerson.learning.dao;  
  
import com.emerson.learning.pojo.Sales;  
  
public interface ISalesDao {  
      
public Sales getById(int id);  
  
}



 

第四步,编写测试用例。

 



package com.emerson.learning.dao;  
  
import static org.junit.Assert.*;  
  
import java.io.IOException;  
import java.io.Reader;  
  
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.After;  
import org.junit.Before;  
import org.junit.Test;  
  
import com.emerson.learning.dao.ISalesDao;  
import com.emerson.learning.pojo.Sales;  
  
public class SalesDaoTest {  
  
private Reader reader;  
private SqlSessionFactory sqlSessionFactory;  
  
@Before  
public void setUp() throws Exception {  
try {  
"mybatis.xml");  
catch (IOException e) {  
            e.printStackTrace();  
        }  
new SqlSessionFactoryBuilder().build(reader);  
    }  
  
@After  
public void tearDown() throws Exception {  
    }  
  
@Test  
public void getById() {  
        SqlSession session = sqlSessionFactory.openSession();  
try {  
class);  
2);  
            assertNotNull(sales);  
            System.out.println(sales);  
finally {  
            session.close();  
        }  
    }  
  
}

 下面我们就针对第二步,映射文件中的resultMap编写进行详细讲解。


<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="createdTime" />  
<result property="updateTime" column="update_time" />  
  
<!-- 定义多对一关联信息(每个销售人员对应一个登录帐号) -->  
<association property="userInfo" column="user_id" javaType="User" select="selectUser">  
<id property="userId" column="userId" />  
<result property="userName" column="user_name" />  
<result property="userPassword" column="user_password" />  
<result property="nickName" column="nick_name" />  
<result property="email" column="email" />  
<result property="isValid" column="is_valid" />  
<result property="createdTime" column="created_time" />  
<result property="updateTime" column="update_time" />  
</association>  
</resultMap>



<resultMap id="salesResultMap" type="com.emerson.learning.pojo.Sales">


使用id和result元素指定数据表中字段与Java类中属性的映射关系,除了我phone、fax和email三行映射代码,其余的全部可以省去不写。为什么?这个就像前面示例中使用到的User类一样,Mybatis会自动帮助我们完成映射工作,不需要我们额外编写代码。那么为什么phone、fax和email这三个字段的映射关系不能省略呢?这是因为我在编写Sales类的时候埋下了伏笔,我故意不按照按驼峰规则对这三个属性进行命名,同时也不与数据表中的字段名相同,为了确保可以正确的将字段映射到属性上,我们必须手工编写映射在代码,明确地告诉Mybatis我们的映射规则。


<resultMap id="salesResultMap" type="com.emerson.learning.pojo.Sales">  
<result property="phone" column="sales_phone" />  
<result property="fax" column="sales_fax" />  
<result property="email" column="sales_email" />  
</resultMap>

 

下面重点来了,association元素来帮助我们完成销售与登录用户对应关系的映射。她实现了“有一个”的关系映射,我们需要做的只是告诉Mybatis,这个关系是通过哪一个字段来建立关联的,被关联的对象类型是什么,以及将关联对象映射到哪个属性上面。如果被关联对象的数据结构比较简单,就如本文中的登录用户表这样,那么可以有更简单的写法。


<association property="userInfo" column="user_id" javaType="User" select="selectUser" />



我们还需要告诉Mybatis,加载关联的方式。MyBatis 在这方面会有两种不同的方式:

嵌套查询:通过执行另外一个 SQL 映射语句来返回预期的复杂类型。

嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集。


嵌套查询


我们在这里先使用嵌套查询来实现。使用属性select指定了关联数据的查询语句。



<select id="selectUser" resultType="User">  
    SELECT user_id, user_name, user_password, nick_name, email, is_valid, created_time  
user_id = #{id}  
</select>


你执行了一个单独的 SQL 语句来获取结果列表(就是“+1”)。

对返回的每条记录,你执行了一个查询语句来为每个加载细节(就是“N”)。

这个问题会导致成百上千的 SQL 语句被执行。这通常不是期望的。

MyBatis 能延迟加载这样的查询就是一个好处,因此你可以分散这些语句同时运行的消耗。然而,如果你加载一个列表,之后迅速迭代来访问嵌套的数据,你会调用所有的延迟加载,这样的行为可能是很糟糕的。

嵌套结果

下面我们就来讲一下另一种实式方式:嵌套结果。使用这种方式,就可以有效地避免了N+1问题。



<?xml version="1.0" encoding="UTF-8"?>  
  
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"   
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
  
<mapper namespace="com.emerson.learning.dao.ISalesDao">  
<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" jdbcType="TIMESTAMP" />  
  
<!-- 定义多对一关联信息(嵌套结果方式) -->  
<association property="userInfo" resultMap="userResult" />  
</resultMap>  
  
<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>  
  
<select id="getById" parameterType="int" resultMap="salesResultMap">  
        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)  
sales_id=#{id}  
</select>  
</mapper>



和嵌套查询相比,使用嵌套结果方式,在映射文件上主要有以下三处修改:

一、修改association元素,无需指定column,另外将resultType改为使用resultMap。为什么?这是因为后面我们会把select语句改为多表关联查询,这样就会有些字段名是冲突的,我们不得不使用别名。这一点对于Mybatis而言,就相当于字段名发生了变化,那么就需要我们手工来维护映射关系。另外,我们也无需指定javaType属性了,因为在resultMap中,已经指定了对应的Java实体类,这里就可以省略了。




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


二、为关联结果集编写映射关系,大家可以看到,好多字段名称已经发生了变化,如is_valid这个字段由于salesman和sys_user表中都存在这个字段,所以我们不得不为其起了一个别名user_is_valid。



<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>



三、修改查询语句,由单表查询改表多表关联查询


<select id="getById" parameterType="int" resultMap="salesResultMap">  
SELECT sales_id, sales_name, sales_phone, sales_fax, sales_email,  
           salesman.is_valid, salesman.created_time, salesman.update_time,  
as user_id, user_name, user_password, nick_name,  
as user_email,  
as user_is_valid, sys_user.created_time as  
           user_created_time,  
as user_update_time  
FROM salesman left outer join sys_user using(user_id)  
WHERE sales_id=#{id}  
</select>



至此,关联映射已讲解完了。还有集合映射没有讲,哇咔咔,内空实在是太多了〜〜〜〜今晚通宵也未必能写得完了。暂时先写到这儿吧,下回再继续讲解如何实现多对多的集合映射。



标签:多表,映射,sales,public,user,Mybatis,NULL,id
From: https://blog.51cto.com/u_809530/8257100

相关文章

  • 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">......
  • 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......
  • docker 创建容器 设置端口映射 目录映射
    dockerrun-d-p8042:8080-v$PWD:/usr/local/tomcat/webapps--namemyProjectNametomcat:8-jdk8-d后台运行-p端口映射将容器的8080端口映射到主机的8042端口-v目录映射$PWD:指定宿主机当前目录挂载到容器的webapps--name指定容器名称为myProjectName tomcat:8-jdk8......
  • Windows编程之文件映射
    一、文件映射概述内存映射文件:文件映射可以用于将磁盘上的文件映射到内存中。这样,文件的内容可以直接从内存中读取或写入,而不必通过磁盘I/O进行数据交换。这对于处理大型文件或需要频繁读写的文件非常有用,可以提高性能。进程间通信:文件映射也可用于进程间通信(IPC),允许不同进......
  • 解决mybatis嵌套查询使用PageHelper分页不准确
    版权声明:本文为CSDN博主「那只小猿」的原创文章,遵循CC4.0BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/weixin_41869361/article/details/116427385springboot项目中嵌套查询使用PageHelper发现使用结果不正确,上PageHelper官网看了一下果然是......
  • 内核态内存映射
    内核态的内存映射机制,主要包含以下几个部分:内核态内存映射函数vmalloc、kmap_atomic是如何工作的;内核态页表是放在哪里的,如何工作的?swapper_pg_dir是怎么回事;出现了内核态缺页异常应该怎么办?内核页表的顶级目录init_top_pgt,定义在__INITDATA里面。它们都有代码段,还有一些初始......
  • springboot mybatis-plus 登录接口
    下面是使用SpringBoot和MyBatis-Plus实现登录接口的示例代码:添加依赖在pom.xml文件中添加以下依赖:<dependencies><!--SpringBoot--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</a......