9-Mybatis 多表查询之一对多(含一对一)

本文详细介绍了MyBatis中的一对一和一对多查询。通过实例展示了如何通过定义PO类和resultMap来处理查询结果,包括查询所有账户并获取用户信息,以及查询所有用户及其关联账户。示例涵盖了查询语句编写、Dao接口定义、XML配置以及测试用例的实现。
摘要由CSDN通过智能技术生成
本次案例主要以最为简单的用户和账户的模型来分析 Mybatis 多表关系。用户为 User 表,账户为 Account
表。一个用户( User )可以有多个账户( Account )。具体关系如下:

一、一对一查询(多对一)

1.需求

查询所有账户信息,关联查询下单用户信息。
注意:
因为一个账户信息只能供某个用户使用,所以从查询账户信息出发关联查询用户信息为一对一查询。如
果从用户信息出发查询用户下的账户信息则为一对多查询,因为一个用户可以有多个账户。

2.方式一 (定义专门的 po 类作为输出类型

a.定义账户信息的实体类

@Data
public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;
}

b.编写 Sql 语句

实现查询账户信息时,也要查询账户所对应的用户信息。
SELECT
account.*,
user.username,
user.address
FROM
account,
`user`
WHERE account.uid = user.id;

MySQL 中测试的查询结果如下:

c.定义 AccountUser

为了能够封装上面 SQL 语句的查询结果,定义 AccountUser 类中要包含账户信息同时还要包含用户信
息,所以我们要在定义 AccountUser 类时可以继承 Account 类。
@Data
public class AccountUser extends Account implements Serializable {
    private String username;
    private String address;
}

d.定义账户的持久层 Dao 接口

public interface IAccountDao {
    /**
     * 查询所有账户,同时获取账户的所属用户名称以及它的地址信息
     * @return
     */
    List<AccountUser> findAll();
}

e.定义 IAccountDao.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="cn.oesoft.mybatis01.dao.IAccountDao">
    <!-- 配置查询所有操作-->
    <select id="findAll" resultType="accountuser">
        select a.*, u.username, u.address
        from account a,
             user u
        where a.uid = u.id;
    </select>
</mapper>
注意:因为上面查询的结果中包含了账户信息同时还包含了用户信息,所以我们的返回值类型 returnType
的值设置为 AccountUser 类型,这样就可以接收账户信息和用户信息了。

f.创建 AccountTest 测试类

/**
 * @ClassName: AccountTest
 * @Author: linxin
 * @Date: 2021/6/13 19:12
 * @Company: http://www.oesoft.cn
 * @Description: 一对多账户的操作
 */
public class AccountTest {
    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession session;
    private IAccountDao accountDao;

    @Before//在测试方法执行之前执行
    public void init() throws Exception {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建构建者对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //3.创建 SqlSession 工厂对象
        factory = builder.build(in);
        //4.创建 SqlSession 对象
        session = factory.openSession();
        //5.创建 Dao 的代理对象
        accountDao = session.getMapper(IAccountDao.class);
    }

    @After//在测试方法执行完成之后执行
    public void destroy() throws Exception {
        session.commit();
        //7.释放资源
        session.close();
        in.close();
    }

    @Test
    public void testFindAll() {
        //6.执行操作
        List<AccountUser> accountusers = accountDao.findAll();
        for (AccountUser au : accountusers) {
            System.out.println(au);
        }
    }
}

g.小结

定义专门的 po 类作为输出类型,其中定义了 sql 查询结果集所有的字段。此方法较为简单,企业中使用普
遍。

3.方式二(定义专门的 resultMap 用于映射一对一查询结果

使用 resultMap ,定义专门的 resultMap 用于映射一对一查询结果。
通过面向对象的 (has a) 关系可以得知,我们可以在 Account 类中加入一个 User 类的对象来代表这个账户
是哪个用户的。

a.修改 Account

Account 类中加入 User 类的对象作为 Account 类的一个属性。

private User user;

b.修改 AccountDao 接口中的方法

/**
* 查询所有账户,同时获取账户的所属用户名称以及它的地址信息
* @return
*/
List<Account> findAll();
注意:第二种方式,将返回值改 为了 Account 类型。
因为 Account 类中包含了一个 User 类的对象,它可以封装账户所对应的用户信息。

c.重新定义 IAccountDao.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="cn.oesoft.mybatis01.dao.IAccountDao">
    <!-- 建立对应关系 -->
    <resultMap type="account" id="accountMap">
        <id column="aid" property="id"/>
        <result column="uid" property="uid"/>
        <result column="money" property="money"/>
        <!-- 它是用于指定从一方表引用的实体属性的 -->
        <association property="user" javaType="user">
            <id column="id" property="id"/>
            <result column="username" property="username"/>
            <result column="sex" property="sex"/>
            <result column="birthday" property="birthday"/>
            <result column="address" property="address"/>
        </association>
    </resultMap>
    <select id="findAll" resultMap="accountMap">
        select u.*, a.id as aid, a.uid, a.money
        from account a,
             user u
        where a.uid = u.id;
    </select>
</mapper>

d.在 AccountTest 类中修改测试方法

@Test
    public void testFindAll() {
        List<Account> accounts = accountDao.findAll();
        for(Account au : accounts) {
            System.out.println(au);
            System.out.println(au.getUser());
        } }

二、一对多查询

需求:
查询所有用户信息及用户关联的账户信息。
分析:
用户信息和他的账户信息为一对多关系,并且查询过程中如果用户没有账户信息,此时也要将用户信息
查询出来,我们想到了左外连接查询比较合适。

1.编写 SQL 语句

SELECT
u.*, acc.id id,
acc.uid,
 acc.money
FROM
`user` u
LEFT JOIN account acc ON u.id = acc.uid;

2.User 类加入 List<Account>

private List<Account> accounts; 

3.用户持久层 Dao 接口中加入查询方法

/**
     * 查询所有用户,同时获取出每个用户下的所有账户信息
     * @return
     */
    List<User> findAllWithAccounts();

4.用户持久层 Dao 映射文件配置

<resultMap type="user" id="userMap">
        <id column="id" property="id"></id>
        <result column="username" property="username"/>
        <result column="address" property="address"/>
        <result column="sex" property="sex"/>
        <result column="birthday" property="birthday"/>
        <!-- collection 是用于建立一对多中集合属性的对应关系
        ofType 用于指定集合元素的数据类型
        -->
        <collection property="accounts" ofType="account">
            <id column="aid" property="id"/>
            <result column="uid" property="uid"/>
            <result column="money" property="money"/>
        </collection>
    </resultMap>
    <!-- 配置查询所有操作 -->
    <select id="findAllWithAccounts" resultMap="userMap">
        select u.*, a.id as aid, a.uid, a.money
        from user u
                 left outer join account a on u.id = a.uid
    </select>
collection
部分定义了用户关联的账户信息。表示关联查询结果集
property = "accounts"
关联查询的结果集存储在 User 对象的上哪个属性。
ofType = "account"
指定关联查询的结果集中的对象类型即 List 中的对象类型。此处可以使用别名,也可以使用全限定名。

5.测试方法

 @Test
    public void testFindAllWithAccounts() {
        //6.执行操作
        List<User> users = userDao.findAllWithAccounts();
        for(User user : users) {
            System.out.println("-------每个用户的内容---------");
            System.out.println(user);
            System.out.println(user.getAccounts());
        } }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值