本次案例主要以最为简单的用户和账户的模型来分析
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());
} }