Mybatis多表查询之一对一查询的多种实现-XML配置

Mybatis 中对于多表查询提供了非常强大的实现方式,主要是通过resultMap的结果映射对于多表查询后的返回值进行封装,让我们来看一下官网上对于resultMap的解释:resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的长达数千行的代码。ResultMap 的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。通过描述对象之间的关系将查询后的结果映射到我们定义的实体类中。

  首先介绍一下本例中的实体类以及其映射关系,Demo中存在User类以及Account类,其关系为一个用户对应零个、一个或者多个账户,账户中为了简单单单保存用户的账户余额以及所属用户的ID。我们实现的查询的目标为:每次查询一个账户的时候同时将其所属的用户信息也展示出来。为了更好的帮助理解,我们将展示一种非mybatis方式以及两种mybatis方式的实现来实现。User类以及Accoun类t的内容如下:

 1 import java.io.Serializable;
 2
 3 public class Account implements Serializable{
 4     private Integer id;
 5     private Integer uid;
 6     private Double money;
 7     private User user;
 8
 9     public User getUser() {
10         return user;
11     }
12
13     public void setUser(User user) {
14         this.user = user;
15     }
16
17     public Integer getId() {
18         return id;
19     }
20
21     public void setId(Integer id) {
22         this.id = id;
23     }
24
25     public Integer getUid() {
26         return uid;
27     }
28
29     public void setUid(Integer uid) {
30         this.uid = uid;
31     }
32
33     public Double getMoney() {
34         return money;
35     }
36
37     public void setMoney(Double money) {
38         this.money = money;
39     }
40
41     @Override
42     public String toString() {
43         return "Account{" +
44                 "id=" + id +
45                 ", uid=" + uid +
46                 ", money=" + money +
47                 ‘}‘;
48     }
49 }

 1 import java.io.Serializable;
 2 import java.util.Date;
 3
 4 public class User implements Serializable{
 5     private Integer id;
 6     private String username;
 7     private Date birthday;
 8     private String sex;
 9     private String address;
10
11     public Integer getId() {
12         return id;
13     }
14
15     public void setId(Integer id) {
16         this.id = id;
17     }
18
19     public String getUsername() {
20         return username;
21     }
22
23     public void setUsername(String username) {
24         this.username = username;
25     }
26
27     public Date getBirthday() {
28         return birthday;
29     }
30
31     public void setBirthday(Date birthday) {
32         this.birthday = birthday;
33     }
34
35     public String getSex() {
36         return sex;
37     }
38
39     public void setSex(String sex) {
40         this.sex = sex;
41     }
42
43     public String getAddress() {
44         return address;
45     }
46
47     public void setAddress(String address) {
48         this.address = address;
49     }
50
51     @Override
52     public String toString() {
53         return "User{" +
54                 "id=" + id +
55                 ", username=‘" + username + ‘\‘‘ +
56                 ", birthday=" + birthday +
57                 ", sex=‘" + sex + ‘\‘‘ +
58                 ", address=‘" + address + ‘\‘‘ +
59                 ‘}‘;
60     }
61 }

数据库的建表语句如下:

 1 DROP TABLE IF EXISTS user;
 2
 3 CREATE TABLE user (
 4     id INT(11) NOT NULL auto_increment,
 5     username VARCHAR(32) NOT NULL COMMENT ‘用户名称‘,
 6     birthday datetime default NULL COMMENT ‘生日‘,
 7     sex char(1) default NULL COMMENT ‘性别‘,
 8     address varchar(256) default NULL COMMENT ‘地址‘,
 9     PRIMARY KEY (id)
10 )ENGINE=InnoDB default CHARSET=utf8
11 INSERT INTO `user` VALUES (‘41‘, ‘老王‘, ‘2018-02-27 17:47:08‘, ‘男‘, ‘石家庄‘);
12 INSERT INTO `user` VALUES (‘45‘, ‘老李‘, ‘2018-02-27 17:47:08‘, ‘男‘, ‘石家庄‘);
13 INSERT INTO `user` VALUES (‘46‘, ‘老郭‘, ‘2018-02-27 17:47:08‘, ‘男‘, ‘石家庄‘);
14
15 DROP TABLE IF EXISTS account;
16 CREATE TABLE account(
17 ID int(11) NOT NULL COMMENT ‘编号‘,
18 UID INT(11) DEFAULT NULL COMMENT ‘用户编号‘,
19 MONEY DOUBLE DEFAULT NULL COMMENT  ‘金额‘,
20 PRIMARY KEY (ID),
21 KEY FK_Reference_8 (UID),
22 CONSTRAINT FK_Reference_8 FOREIGN KEY (UID) REFERENCES user (id)
23 )ENGINE=INNODB DEFAULT CHARSET=utf8
24
25 INSERT INTO accountc (ID,UID,MONEY) VALUES (1,46,1000),(2,45,1000),(3,46,2000);

搭建项目的过程就不展示了,主要的核心实体类和对应的数据库表如上,接下来我们展示我们所要展示的三种方式实现一对一的联表查询。

1.非mybatis的高级结果映射方式实现联表查询。

  这种方式的原理为通过创建一个新的类AccountUser类继承Account类并在AccountUser类中添加我们想要查询的User的信息,并且在账户查询的Dao.xml文件中配置相应的sql语句即可实现。假如我们查询Account的信息的时候同时想要查询用户的名称以及地址,那就在AccountUser的类中声明用户的名称以及地址。这种实现方式只是作为一种拓展的实现方式,在实际使用过程中并不推荐使用。

  (1)声明AccountUser类 

 1 public class AccountUser extends Account {
 2     private String username;
 3     private String address;
 4
 5     public String getUsername() {
 6         return username;
 7     }
 8
 9     public void setUsername(String username) {
10         this.username = username;
11     }
12
13     public String getAddress() {
14         return address;
15     }
16
17     public void setAddress(String address) {
18         this.address = address;
19     }
20
21     @Override
22     public String toString() {
23         return super.toString() + "       "+"AccountUser{" +
24                 "username=‘" + username + ‘\‘‘ +
25                 ", address=‘" + address + ‘\‘‘ +
26                 ‘}‘;
27     }
28 }

需要注意的是该类继承了Account类,声明了我们需要的User类中的用户名称以及地址,对AccountUser类的toString()方法进行了改造,添加了super.toString(),方便我们打印的时候可以打印出从父类中继承的属性的属性值。

  (2)在AccountDao类中声明查询账户信息的方法

/**
     * 查找所有账户同时包含用户的姓名和地址
     * @return
     */
    List<AccountUser> findAllAccountUser();

  (3)在AccountDao.xml中配置findAllAccountUser方法的实现

 <select id="findAllAccountUser" resultType="com.example.domain.AccountUser">
        SELECT a.*,u.username,u.address FROM USER u,account a WHERE a.UID= u.id;
    </select>

  (4)测试该方法

@Test
    public void findAllAccounUsertTest(){
        List<AccountUser> accountList = accountDao.findAllAccountUser();
        for (AccountUser account:accountList){
            System.out.println(account);
        }
    }

  测试结果:

  2.通过Mybatis中的高级结果映射的resultMap的关联属性(association)来实现多表的一对一查询。

  关联属性主要用来处理“有一个”类型的关系,关联的关键之处是我们需要告诉 MyBatis 如何加载关联。在MyBatis 中有两种不同的方式加载关联:一是嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。二是嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。通过这两种不同的方式衍生出两种不同的方式去实现多表的一对一查询。

    1.关联的嵌套SELECT查询

    (1)因为我们要实现的是在查询账户的时候期望可以得到账户所属用户的某些信息,所以我们需要在Account类中声明User对象,用来将查询到的结果进行封装。如下

private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    (2)AccountDao类中添加查询的方法的声明。

 /**
     * 查找所有账户同时包含用户的所有信息
     * @return
     */
    List<Account> findAll();

    (3)在AccountDao.xml中配置findAll方法的的结果映射。首先声明结果映射关系resultMap,resultMap的id为该结果映射的唯一标识,type为结果类的完全限定名,resultMap中的属性说明:id 和 result 元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。这两者之间的唯一不同是,id 元素表示的结果将是对象的标识属性,这会在比较对象实例时用到。 这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。id和result中的属性说明:property

映射到列结果的字段或属性,其实就是实体类中属性的名称。column是指数据库中的列名,对应实体类的属性。在下面的<id property="id" column="aid"/>中的column属性的值aid没有完全匹配上数据中的id,是因为在查询语句中对account中的id字段设置了别名aid。association的属性的 property为user对应实体类中声明的user对象,其类型使用javaType属性指定为User类,column为数据表的列名,并作为参数传递给此 select 语句。select属性用于加载复杂类型属性的映射语句的 ID,它会从 column 属性中指定的列检索数据。

 <resultMap id="accountUserMap" type="com.example.domain.Account">
        <id property="id" column="aid"/>
        <result property="uid" column="uid"/>
        <result property="money" column="money"/>
        <!--关联的嵌套的select查询-->
        <association property="user" javaType="com.example.domain.User" column="uid" select="selectUser"/>
    </resultMap>

    (4)在AccountDao.xml中配置结果映射中的<association property="user" javaType="com.example.domain.User" column="uid" select="selectUser"/>的select="selectUser"的实现以及findAll方法的实现,

<select id="selectUser" resultType="user">
        SELECT * FROM  USER WHERE ID = #{id};
    </select>
<select id="findAll" resultMap="accountUserMap">
       SELECT u.*,a.id AS aid,a.uid,a.money FROM USER u,account a WHERE a.UID= u.id;
    </select>

  这里我们有两个 select 查询语句:一个用来加载账户信息(Account),另外一个用来加载用户信息(User),而且accountUserMap的结果映射描述了应该使用 selectUser 语句加载它的 user属性,其它的列名和属性名相匹配的属性将会被自动加载。

    (5)查询测试

 @Test
    public void findAllTest(){
        List<User> userList = userDao.findAll();
        for (User user: userList){
            System.out.println(user);
        }
    }

测试结果:

  2.关联的嵌套结果映射实现1。

  (1)(2)步骤是上一方法是相同的。

  (3)主要是修改了上一种方式中第三步中的resultMap中的association关联属性,将其替换为:<association property="user" javaType="com.example.domain.User" column="uid" resultMap="userMap"/>,在association 中添加了resultMap="userMap"属性,userMap为结果映射的 ID,可以将此关联的嵌套结果集映射到一个合适的对象中,也就是将关联属性user的结果映射到映射ID为userMap的resultMap中。userMap的声明如下:

 <resultMap id="userMap" type="com.example.domain.User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="birthday" column="birthday" jdbcType="DATE"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
    </resultMap>

(4)AccountDao.xml的findAll方法的映射则只需要findAll方法,不再需要上一个方式中的selectUser映射的方法

<select id="findAll" resultMap="accountUserMap">
       SELECT u.*,a.id AS aid,a.uid,a.money FROM USER u,account a WHERE a.UID= u.id;
    </select>

(5)(6)查询代码以及测试结果不再贴出

 3.关联的嵌套结果映射实现2。

   第二种实现方式中使用了外部的结果映射元素来映射关联。这使得 User的结果映射可以被重用。 然而,如果我们不需要重用它(在上个例子中他是userMap),或者你更喜欢将你所有的结果映射放在一个具有描述性的结果映射元素中。 你可以直接将结果映射作为子元素嵌套在内。

  (1)(2)步骤是上一方法是相同的。

  (3)仍然是修改了上一种方式中第三步中的resultMap结果映射中的association关联属性,将其替换如下:

<!--关联的嵌套的结果映射2-->
        <association property="user" javaType="com.example.domain.User">
            <id property="id" column="id"/>
            <result property="username" column="username"/>
            <result property="birthday" column="birthday" jdbcType="DATE"/>
            <result property="sex" column="sex"/>
            <result property="address" column="address"/>
        </association>

这样实现与第二种实现大同小异,只是将关联对象的属性配置直接在association中进行了配置。

(4)AccountDao.xml的findAll方法的映射的findAll方法

<select id="findAll" resultMap="accountUserMap">
       SELECT u.*,a.id AS aid,a.uid,a.money FROM USER u,account a WHERE a.UID= u.id;
    </select>

(5)(6)查询代码以及测试结果不再贴出

  总结:通过上述例子可以初步窥探了Mybatis中多表联查(一对一)的使用方式,主要是通过resultMap的高级结果映射来实现的,在本例中最关键的属性是resultMap的关联属性association,association也是我们告诉Mybatis对象之间的关系的桥梁,同时也介绍了resultMap的属性的说明,通过解释其属性再加上Demo可以更好的理解结果映射的含义以及使用,这只是最简单的一种使用方式,以后会详细介绍一对多、多对多、多对一等复杂情况在Mybatis中的如何查询映射。

  参考网址:mybatis中文官网  http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html

原文地址:https://www.cnblogs.com/hopeofthevillage/p/11406649.html

时间: 2024-12-07 08:21:50

Mybatis多表查询之一对一查询的多种实现-XML配置的相关文章

第八节:mybatis关联查询之一对一查询

一对一也就是 A 表的一条记录对应 B 表的一条记录,下面的测试数据中,从employee 表来看,一个员工对应一个部门,是一对一关系,如果从部门角度来看,则是一对多的关系,一个部门对应多个员工,本节主要研究一对一的关系. 1,数据表建立 新建数据表department,有两个字段,插入两条数据如下: id dept_name 1 CIA 2 FSB 新建数据表employee,有三个字段,其中dept_id是外键,关联department表的主键id.插入数据如下: id last_name

Mybatis两表连接(一对一)

OrderJudgeMapper.xml <?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.data

Django学习【第7篇】:Django之ORM跨表操作(聚合查询,分组查询,F和Q查询等)

django之跨表查询及添加记录 一:创建表 书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);     一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many). 创建一对一的关系:OneToOne("要绑定关系的表名") 创建一对多的关系:ForeignKey("要绑定关系的表名") 创建多对多的关系:ManyToMany(&qu

Django之ORM跨表操作(聚合查询,分组查询,F和Q查询等)

一:创建表 书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);     一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many). 创建一对一的关系:OneToOne("要绑定关系的表名") 创建一对多的关系:ForeignKey("要绑定关系的表名") 创建多对多的关系:ManyToMany("要绑定关系的表名"

mybatis 关联表查询

这段时间由于项目上的需求:需要将数据库中两表关联的数据查询出来展示到前端(包含一对一,一对多): (1)一对一: 在实体类中维护了另一个类的对象: 这里我以用户(User)和产品(Product)为例:其中get和set的方法我就没有生成了,请自行生成: 实体类: public class User { private String id; private String name; private String password; private Product product; } publi

【MyBatis学习08】高级映射之一对一查询

从这一篇博文开始,将总结一下mybatis中的几个高级映射,即一对一.一对多.多对多查询,这篇先总结一下mybatis中的一对一查询. 为了模拟这些需求,事先要建立几个表,不同的表之间将对应上面提到的不同的映射,为此,我建立4个表,如下: DROP TABLE IF EXISTS `items`; DROP TABLE IF EXISTS `orders`; DROP TABLE IF EXISTS `user`; DROP TABLE IF EXISTS `orderdetail`; /*it

7.MyBatis 关联查询(一对一)

1 关联查询映射     1.1 分析数据模型         思路 : 每张表记录的数据内容 每张表重要的字段(主键,外键,非空字段) 表与表之间的关系(外键关系) 表与表之间的业务关系(建立在某个业务意义基础上去分析) 如图:     1.2 一对一查询 有三种实现方法: resultType resultMap中的association嵌套查询 resultMap中的嵌套结果 需求: 进行订单查询,包括用户的姓名和地址信息  SQL语句: SELECT orders.id,orders.u

mybatis学习笔记(10)-一对一查询

mybatis学习笔记(10)-一对一查询 mybatis学习笔记10-一对一查询 resultType实现 resultMap实现 resultType和resultMap实现一对一查询小结 本文使用两种方式(resultType和resultMap)实现一对一查询,查询订单信息,关联查询创建订单的用户信息 resultType实现 sql语句 确定查询的主表:订单表 确定查询的关联表:用户表 关联查询使用内连接?还是外连接? 因为orders表中有一个外键(user_id),通过外键关联查询

MyBatis 多表联合查询及优化

序 这篇文章我打算来简单的谈谈 mybatis 的多表联合查询.起初是觉得挺简单的,没必要拿出来写,毕竟 mybatis 这东西现在是个开发的都会用,而且网上的文章也是一搜罗一大堆,根本就用不着我来重复.但是吧,就我前几天在做一个多表联合查询的时候,竟然出了很多意想不到的问题,而且这些问题的出现,并不是对 mybatis 不了解,而是在用的过程中会或多或少的忽略一些东西,导致提示各种错误. 背景 老规矩,开始之前,还是要先说说这件事的背景.也就是最近几天,公司要做一个后台的管理平台,由于之前的一