Mybatis两表查询

940 阅读4分钟

声明

本博客是看了黑马的课程之后总结来的,感兴趣的可以去看原教学视频,讲得挺好。 另外,各位读者有什么建议最好是留言,一起进步,加油!

1.一对一(多对一)查询

之前写的博客是对一个表进行查询。实际开发中经常会出现多表查询,下面就介绍任何操作。在学习的过程中一定要和前面的内容联系一起,会比较轻松。 另外应该有必要解释一下什么叫做一对一查询,就是主表的一个记录对应从表的一个记录;多对一就是主表的多条记录对应从表的一条记录

1.1 表的准备

下面的描述将根据两个简单的表,分别是user表和account表

image.png

同时要在Java工程中的domain文件夹内创建好这两个实体类。

public class User implements Serializable {

    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;
    
    //  还有各个属性的 getset 和toString方法  这里为了简便就不放在这里了。
}
public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private double money;

    //  还有各个属性的 getset 和toString方法  这里为了简便就不放在这里了。
}

1.2 需求分析

在这里我们要进行的多表联合查询的语句及结果是:

SELECT 
    account.*,
    user.username,
    user.address
FROM
    account,
    user
WHERE account.uid = user.id

image.png

1.3 方法一

分析上面图中的返回结果,按照之前的逻辑来解决的话,那就是用一个类来装返回的结果,这就是方式一 在domain中创建AccountUser类:

public class AccountUser extends Account{
    private String username;
    private String address;

    @Override
    public String toString() {
        return super.toString() + "       AccountUser{" +
                "username='" + username + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

1.4 常规操作

1.4.1 在dao文件夹中创建Dao接口

image.png

public interface IAccountDao {
    /**
     * 查询所有账户,并且带有用户名称和地址信息
     * @return
     */
    List<AccountUser> findAllAccount();

}

1.4.2 在resource下的dao包中创建对应的配置文件

image.png

    <!--查询所有同时包含用户名和地址信息-->
    <select id="findAllAccount" resultType="AccountUser">
        SELECT account.*,user.username,user.address FROM account,user WHERE account.uid = user.id
    </select>
  • 注意,这里不需要再去SqlMapConfig.xml文件中创建对应mapper,因为已经在SqlMapConfig.xml的<configuration> </configuration>中加入了:
<!--    配置映射文件的位置-->
    <mappers>
        <package name="com.zhouman.dao"/>
    </mappers>

可以直接写:resultType="AccountUser"是因为在SqlMapConfig.xml的<configuration> </configuration>中加入了:

    <typeAliases>
        <package name="com.zhouman.domain"/>
    </typeAliases>

1.4.3 在test包下创建测试类进行测试:

image.png

public class AccountTest {

    private InputStream inputStream;
    private SqlSession sqlSession;
    private IAccountDao accountDao;

    @Before
    public void init() throws Exception {
        //1.读取配置文件,生成字节输入流
        inputStream = Resource.class.getResourceAsStream("/SqlMapConfig.xml");
        //2.获取SqlSessionFactory对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(inputStream);
        //3.获取SqlSession对象
        sqlSession = factory.openSession();
        //4.通过SqlSession生产dao的代理对象
        accountDao = sqlSession.getMapper(IAccountDao.class);
    }

    @After
    public void destroy() throws Exception {
        //提交事务
        sqlSession.commit();

        inputStream.close();
        sqlSession.close();
    }

    /**
     * 查询所有账户,同时包含用户名称和地址
     * @throws Exception
     */
    @Test
    public void testFindAllAccountUser() {
        //5.执行方法
        List<AccountUser> accountUsers = accountDao.findAllAccount();
        for (Account accountUser : accountUsers){
            System.out.println(accountUser);
        }
    }

1.5 方法二

下面总结一下方法一:该方法的逻辑就是用一个类来装结果。但是实际工程运用当中,这种方式用的不多。

更多的会去使用方法二:使用resultMap,定义专门的resultMap用于映射一对一查询结果

1.5.1 修改 Account类

public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;
    
    private User user;
    
    public User getUser() {
        return user;
    }
    
    public void setUser(User user) {
        this.user = user;
    }
    
    //  还有各个属性的 get 和 set 和toString方法  这里为了简便就不放在这里了。
    
}

1.5.2 添加AccountDao接口中的方法

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

1.5.3 重新定义AccountDao.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.zhouman.dao.IAccountDao">

    <!-- 定义封装account 和user 的  accountUserMap -->
    <resultMap id="accountUserMap" type="account">
        <id property="id" column="uid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!-- 一对一的关系映射:配置封装user的内容 -->
        <association property="user" column="uid" javaType="user">
            <id property="id" column="id"></id>
            <result property="username" column="username"></result>
            <result property="address" column="address"></result>
            <result property="ser" column="sex"></result>
            <result property="birthday" column="birthday"></result>
        </association>

    </resultMap>

    <!--查询所有-->
    <select id="findAll" resultMap="accountUserMap">
        <!--select * from account-->
        select a.*,u.username,u.address from account as a, user as u where u.id = a.uid
    </select>
    
</mapper>

1.5.4 在 AccountTest 类中加入测试方法

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

总结

实际运用中方式二用得更多,其实方式二相较而言也更加方便,需要改动的点不多。

2 一对多查询

一对多查询就是,主表的一条记录对应从表的多条记录 查询出来的效果大致如下图:

image.png

而Mybatis是可以将其封装在一起的

这次用的方法是上面所述的方法二,即resultMap

2.1 User 类加入 List<Account>

    ......

    private List<Account> accounts;
    
    public List<Account> getAccounts() {
        return accounts; 
    }
    
    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts; 
    }
    
    ......

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

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

值得注意的是这里返回的是 List 集合

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

    <!-- 定义User的resultMap -->
    <resultMap id="userAccountMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
        <!-- 配置user对象中accounts集合的映射 -->
        <collection property="accounts" ofType="account">
            <id property="id" column="aid"></id>
            <result property="uid" column="uid"></result>
            <result property="money" column="money"></result>
        </collection>
    </resultMap>

    <!--查询所有,同时获取到用户下所有账户的信息-->
    <select id="findAll" resultMap="userAccountMap">
        select * from user;
    </select>

这里的话,和前面一对一多表查询的差别在于,这里用的是<collection></collection>标签

2.4 测试方法

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