【MyBatis学习笔记 六】MyBatis关联查询和集合查询

简介: 【MyBatis学习笔记 六】MyBatis关联查询和集合查询

之前我们做的都是单表查询,那么问题来了,如果我们想要做多表关联查询该怎么处理呢,本篇Blog来探索关联查询(多对一关系)和集合查询(一对多关系)的实现方式。同样还是在之前的Person表基础上做一些扩展。

数据及代码准备

首先我们预设一个场景:一条人员Person数据对应多个账户BankAccount数据,然后分别进行账户数据的查询时获取到关联的人员数据【多对一关联查询】和通过人员获取账户数据【一对多关联查询】。

1 数据表准备

首先我们需要准备两张数据表,首先Person表是之前准备好的,我们还是用之前预设的三条数据:

然后我们配置一个账户表并且在账户表中关联好人员id以及初始化一些数据:

2 数据Model准备

我们沿用之前的数据Model,Person,并且新增一个BankAccount的Model:

Person

package com.example.MyBatis.dao.model;
import lombok.Data;
/*
 * person表对应对象
 * */
@Data
public class Person {
    private int id;
    private String username;
    private String password;
    private int age;
    private int phone;
    private String email;
    private String interests;
}

BankAccount

package com.example.MyBatis.dao.model;
import lombok.Data;
/*
 * bank_account表对应对象
 * */
@Data
public class BankAccount {
    private int id;
    private String bankName;
    private String accountName;
    private Person person;
    private int personId;
}

3 DAO接口类准备

我们提前准备好PersonDao以及BankAccountDao,其中PersonDao中的接口维持之前的定义,BankAccountDao新定义暂无接口

PersonDao

package com.example.MyBatis.dao.mapper;
import com.example.MyBatis.dao.model.Person;
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.util.Map;
public interface PersonDao {
    List<Person> getPersonList();
    Person getPersonByUsername(@Param("username")String username, @Param("password")String password);
    Person getPersonByMap(Map<String,Object> map);
    int addPerson(Person person);
    int updatePerson(Person person);
    int deletePerson(@Param("id")int id);
    List<Person> getPersonListLike(@Param("username")String username);
    List<Person> getPersonListPage(Map<String,Integer> map);
}

PersonDao

package com.example.MyBatis.dao.mapper;
import com.example.MyBatis.dao.model.BankAccount;
import java.util.List;
public interface BankAccountDao {
}

4 Mapper文件编写及集成

我们需要配置personMapper以及bankAccountMapper,其中personMapper沿用之前的,bankAccountMapper为新增的

personMapper

<?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">
<!--namespace=绑定一个指定的Dao/Mapper接口-->
<mapper namespace="com.example.MyBatis.dao.mapper.PersonDao">
    <!-- id确定返回map, type为模型全类名 -->
    <resultMap id="PersonMap" type="com.example.MyBatis.dao.model.Person">
        <!-- id为主键 -->
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="age" property="age"/>
        <result column="phone" property="phone"/>
        <!-- column写错会因为查到数据匹配不上,给默认值, type写错会报错,找不到属性-->
        <result column="email" property="email"/>
        <!-- column是数据库表的列名 , property是对应实体类的属性名 -->
        <result column="hobby" property="interests"/>
    </resultMap>
    <select id="getPersonList" resultMap="PersonMap">
        select * from person
    </select>
    <select id="getPersonByUsername" resultMap="PersonMap" >
        select * from person where   password=#{password} and username = #{username}
    </select>
    <select id="getPersonByMap" resultMap="PersonMap"  parameterType="map">
        select * from person where   password=#{password} and username = #{username}
    </select>
    <insert id="addPerson" parameterType="com.example.MyBatis.dao.model.Person" >
        insert into person (id,username,password,age,phone,email,hobby) values (#{id},#{username},#{password},#{age},#{phone},#{email},#{interests})
    </insert>
    <update id="updatePerson" parameterType="com.example.MyBatis.dao.model.Person" >
        update person set username=#{username},phone=#{phone} where id=#{id}
    </update>
    <delete id="deletePerson" parameterType="int" >
        delete from person where id = #{id}
    </delete>
    <select id="getPersonListLike" resultMap="PersonMap" >
        select * from person where username like "%"#{username}"%"
    </select>
    <select id="getPersonListPage" parameterType="map" resultMap="PersonMap">
        select * from person limit #{startIndex},#{pageSize}
    </select>
</mapper>

bankAccountMapper

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

并且我们将两个mapper文件都添加到核心配置文件中:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心配置文件-->
<configuration>
    <!--导入properties文件-->
    <properties resource="properties/db.properties"/>
    <settings>
        <setting name="logImpl" value="log4j"/>
    </settings>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
   <mapper resource="mappers/personMapper.xml"/>
   <mapper resource="mappers/bankAccountMapper.xml"/>
<!-- <mapper class="com.example.MyBatis.dao.mapper.PersonDao"/>-->
    </mappers>
</configuration>

多对一关联查询

多对一的关联查询方式有两种,一种是子查询的方式,一种是联表查询的方式。

子查询实现方式

所谓子查询的方式就是在将要获取BankAccount对象中的Person对象时通过绑定的参数关系查一次,是一种查询嵌套方式。

1 BankAccountDao增加接口方法

首先我们在BankAccountDao中增加接口方法getBankAccountListBySelect

List<BankAccount> getBankAccountListBySelect();

2 bankAccountMapper增加实现配置

然后我们在bankAccountMapper中增加配置实现,配置内容如下:

<select id="getBankAccountListBySelect" resultMap="BankAccountPersonBySelectMap">
        select * from bank_account
    </select>
    <!-- id确定返回map, type为模型全类名 -->
    <resultMap id="BankAccountPersonBySelectMap" type="com.example.MyBatis.dao.model.BankAccount">
        <!-- id为主键 -->
        <id column="id" property="id"/>
        <result column="bank_name" property="bankName"/>
        <result column="account_name" property="accountName"/>
        <!--association关联属性 property属性名 javaType属性类型 column在多的一方的表中的列名-->
        <association column="person_id"  property="person" javaType="com.example.MyBatis.dao.model.Person" select="getPerson"/>
    </resultMap>
    <select id="getPerson" resultMap="PersonMap">
        <!--这里的参数就是上一个sql的column,-->
        select * from person where id = #{person_id}
    </select>
    <resultMap id="PersonMap" type="com.example.MyBatis.dao.model.Person">
        <!-- id为主键 -->
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="age" property="age"/>
        <result column="phone" property="phone"/>
        <!-- column写错会因为查到数据匹配不上,给默认值, type写错会报错,找不到属性-->
        <result column="email" property="email"/>
        <!-- column是数据库表的列名 , property是对应实体类的属性名 -->
        <result column="hobby" property="interests"/>
    </resultMap>

其实如果阅读过源码不难理解,当返回结果执行person映射的时候通过动态代理生成了javaType中对应的Person代理,然后执行方法getPerson获取到查询结果,返回时因为hobby字段不对应,所以我们还做了一次结果集映射。

3 测试实现效果

最后我们测试下实现效果,测试代码如下:

@Test
    public void testGetBankAccountListBySelect(){
        //1.获取SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //2.执行SQL
        BankAccountDao bankAccountDao = sqlSession.getMapper(BankAccountDao.class);
        List<BankAccount> bankAccounts = bankAccountDao.getBankAccountListBySelect();
        for (BankAccount bankAccount : bankAccounts) {
            System.out.println(bankAccount);
        }
        //关闭sqlSession
        sqlSession.close();
    }

测试结果如下:

联表查询实现方式

所谓联表查询的方式就是在一次性查完所有结果,将结果嵌套映射到结果集上。

1 BankAccountDao增加接口方法

首先我们在BankAccountDao中增加接口方法getBankAccountListByResult

List<BankAccount> getBankAccountListByResult();

2 bankAccountMapper增加实现配置

然后我们在bankAccountMapper中增加配置实现:

<select id="getBankAccountListByResult" resultMap="BankAccountPersonByResultMap">
        select ba.id id, ba.bank_name bank_name, ba.account_name account_name,ba.person_id person_id,p.id pid,p.username username,p.password password,p.age age,p.phone phone,p.email email,p.hobby hobby
        from bank_account ba,person p
        where ba.person_id = p.id
    </select>
    <!-- id确定返回map, type为模型全类名 -->
    <resultMap id="BankAccountPersonByResultMap" type="com.example.MyBatis.dao.model.BankAccount">
        <!-- id为主键 -->
        <id column="id" property="id"/>
        <result column="bank_name" property="bankName"/>
        <result column="account_name" property="accountName"/>
        <association column="person_id" property="person" javaType="com.example.MyBatis.dao.model.Person">
            <!-- id为主键 -->
            <id column="pid" property="id"/>
            <result column="username" property="username"/>
            <result column="password" property="password"/>
            <result column="age" property="age"/>
            <result column="phone" property="phone"/>
            <result column="email" property="email"/>
            <result column="hobby" property="interests"/>
        </association>
    </resultMap>

其实就是通过联表查询一次性把所有结果都查了出来,然后进行结果集的嵌套映射。

3 测试实现效果

最后我们测试下实现效果,测试代码编写如下:

@Test
    public void testGetBankAccountListByResult(){
        //1.获取SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //2.执行SQL
        BankAccountDao bankAccountDao = sqlSession.getMapper(BankAccountDao.class);
        List<BankAccount> bankAccounts = bankAccountDao.getBankAccountListByResult();
        for (BankAccount bankAccount : bankAccounts) {
            System.out.println(bankAccount);
        }
        //关闭sqlSession
        sqlSession.close();
    }

实现效果如下,可以看到打印日志如下:

一对多关联查询

一个人员Person拥有多个账户信息,是个一对多现象,也分为两种情况,按照子查询方式和按照结果查询方式。

子查询实现方式

所谓子查询的方式就是在将要获取BankAccount对象中的Person对象时通过绑定的参数关系查一次,是一种查询嵌套方式。

1 PersonDao增加接口方法

首先我们在PersonDao中增加接口方法getPersonByIdSelect

Person getPersonByIdSelect(@Param("id")int id);

2 personMapper增加实现配置

然后我们在personMapper中增加配置实现:

<select id="getPersonByIdSelect" resultMap="PersonBankAccountBySelectMap">
        select * from person where id = #{id}
    </select>
    <resultMap id="PersonBankAccountBySelectMap" type="com.example.MyBatis.dao.model.Person">
        <!-- id为主键 -->
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="age" property="age"/>
        <result column="phone" property="phone"/>
        <!-- column写错会因为查到数据匹配不上,给默认值, type写错会报错,找不到属性-->
        <result column="email" property="email"/>
        <!-- column是数据库表的列名 , property是对应实体类的属性名 -->
        <result column="hobby" property="interests"/>
        <!--column是一对多的外键 , 写的是一的主键的列名-->
        <collection column="id" property="bankAccounts" javaType="ArrayList" ofType="com.example.MyBatis.dao.model.BankAccount" select="getBankAccountByPersonId"/>
    </resultMap>
    <select id="getBankAccountByPersonId" resultMap="BankAccountResultMap">
        select * from bank_account where person_id = #{id}
    </select>
    <resultMap id="BankAccountResultMap" type="com.example.MyBatis.dao.model.BankAccount">
        <!-- id为主键 -->
        <id column="id" property="id"/>
        <result column="bank_name" property="bankName"/>
        <result column="account_name" property="accountName"/>
        <result column="person_id" property="personId"/>
    </resultMap>

JavaType和ofType都是用来指定对象类型的,JavaType是用来指定pojo中属性的类型,ofType指定的是映射到list集合属性中pojo的类型。

3 测试实现效果

最后我们测试下实现效果,测试代码编写如下:

@Test
    public void testGetPersonByIdSelect() {
        //1.获取SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //2.执行SQL
        PersonDao personDao = sqlSession.getMapper(PersonDao.class);
        Person person = personDao.getPersonByIdSelect(1);
        System.out.println(person);
        //关闭sqlSession
        sqlSession.close();
    }

实现效果如下,可以看到打印日志如下:

联表查询实现方式

所谓联表查询的方式就是在一次性查完所有结果,将结果嵌套映射到结果集上。

1 PersonDao增加接口方法

首先我们在PersonDao中增加接口方法getPersonByIdResult

Person getPersonByIdResult(@Param("id")int id);

2 personMapper增加实现配置

然后我们在personMapper中增加配置实现:

<select id="getPersonByIdResult" resultMap="PersonBankAccountByResultMap">
        select p.id id,p.username,username,p.password password,p.age age,p.phone phone,p.email email,p.hobby hobby,ba.id bid,ba.bank_name bank_name,ba.account_name account_name,ba.person_id person_id
        from person p,bank_Account ba
        where p.id=ba.person_id and p.id=#{id}
    </select>
    <resultMap id="PersonBankAccountByResultMap" type="com.example.MyBatis.dao.model.Person">
        <!-- id为主键 -->
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="age" property="age"/>
        <result column="phone" property="phone"/>
        <!-- column写错会因为查到数据匹配不上,给默认值, type写错会报错,找不到属性-->
        <result column="email" property="email"/>
        <!-- column是数据库表的列名 , property是对应实体类的属性名 -->
        <result column="hobby" property="interests"/>
        <collection  property="bankAccounts" javaType="ArrayList" ofType="com.example.MyBatis.dao.model.BankAccount">
            <result column="bid" property="id"  />
            <result column="bank_name" property="bankName"/>
            <result column="account_name" property="accountName"/>
            <result column="person_id" property="personId"/>
        </collection>
    </resultMap>

3 测试实现效果

最后我们测试下实现效果,测试代码编写如下:

@Test
    public void testGetPersonByIdResult() {
        //1.获取SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //2.执行SQL
        PersonDao personDao = sqlSession.getMapper(PersonDao.class);
        Person person = personDao.getPersonByIdResult(1);
        System.out.println(person);
        //关闭sqlSession
        sqlSession.close();
    }

实现效果如下,可以看到打印日志如下:

总结一下

多对一关联查询使用association,并且把配置写到多的一方;一对多集合查询使用collection,并且把配置写到一的一方;每种关联查询类型都有两种查询方式,一种是类似子查询的查询嵌套,另一种是类似联表查询的结果集映射。附加的属性类似于外键配置在多的一方。自我感觉,用子查询嵌套+一对多关联查询+在多的一方配置一的一方的主键id应该是我们常用的使用方式,但更多场景我觉得还是不要引入复合关联查询,让数据库的数据获取更纯粹,业务逻辑在上层处理更好理解也更好修改,基础查询只包含简单增删查改即可,除非这个复合查询使用场景很频繁可以定义一下。

相关文章
|
5月前
|
Java 数据库连接 数据库
mybatis查询数据,返回的对象少了一个字段
mybatis查询数据,返回的对象少了一个字段
377 8
|
1月前
|
XML Java 数据库连接
Mybatis一对一,一对多关联查询
## MyBatis一对一、一对多关联查询详解 MyBatis是一款优秀的持久层框架,提供了灵活的SQL映射功能,支持复杂的数据库操作。本文将详细介绍MyBatis中一对一和一对多关联查询的实现。 ### 一对一关联查询 一对一关联关系指的是一个表中的一条记录与另一个表中的一条记录相关联。例如,一个用户有一个地址信息。 #### 数据库表设计 假设有两个表:`user`和 `address`。 ``` CREATE TABLE user ( id INT PRIMARY KEY, name VARCHAR(50) ); CREATE TABLE address
37 18
|
29天前
|
SQL Java 数据库连接
【潜意识Java】MyBatis中的动态SQL灵活、高效的数据库查询以及深度总结
本文详细介绍了MyBatis中的动态SQL功能,涵盖其背景、应用场景及实现方式。
96 6
|
2月前
|
XML Java 数据库连接
Mybatis实现RBAC权限模型查询
通过对RBAC权限模型的理解和MyBatis的灵活使用,我们可以高效地实现复杂的权限管理功能,为应用程序的安全性和可维护性提供有力支持。
73 5
|
2月前
|
SQL Java 数据库连接
spring和Mybatis的各种查询
Spring 和 MyBatis 的结合使得数据访问层的开发变得更加简洁和高效。通过以上各种查询操作的详细讲解,我们可以看到 MyBatis 在处理简单查询、条件查询、分页查询、联合查询和动态 SQL 查询方面的强大功能。熟练掌握这些操作,可以极大提升开发效率和代码质量。
162 3
|
3月前
|
SQL 安全 Java
MyBatis-Plus条件构造器:构建安全、高效的数据库查询
MyBatis-Plus 提供了一套强大的条件构造器(Wrapper),用于构建复杂的数据库查询条件。Wrapper 类允许开发者以链式调用的方式构造查询条件,无需编写繁琐的 SQL 语句,从而提高开发效率并减少 SQL 注入的风险。
61 1
MyBatis-Plus条件构造器:构建安全、高效的数据库查询
|
4月前
|
SQL Java 数据库连接
mybatis如何仅仅查询某个表的几个字段
【10月更文挑战第19天】mybatis如何仅仅查询某个表的几个字段
151 1
|
5月前
|
SQL XML Java
mybatis复习04高级查询 一对多,多对一的映射处理,collection和association标签的使用
文章介绍了MyBatis中高级查询的一对多和多对一映射处理,包括创建数据库表、抽象对应的实体类、使用resultMap中的association和collection标签进行映射处理,以及如何实现级联查询和分步查询。此外,还补充了延迟加载的设置和用法。
mybatis复习04高级查询 一对多,多对一的映射处理,collection和association标签的使用
|
7月前
|
Java 数据库连接 mybatis
Mybatis查询传递单个参数和传递多个参数用法
Mybatis查询传递单个参数和传递多个参数用法
104 11
MyBatisPlus如何根据id批量查询?Required request parameter ‘id‘ for method 解决方法是看青戈大佬MybatisPlus的教程
MyBatisPlus如何根据id批量查询?Required request parameter ‘id‘ for method 解决方法是看青戈大佬MybatisPlus的教程