目录
多表之间的关系,分为一对一、一对多(多对一)、多对多,具体的不再赘述了,在数据库专栏-MySQL数据中有总结过。我们这里详细总结下MyBatis中的多表操作,所使用的工程源码会在文章末尾附上。
1 MyBatis的多表查询
1.1 MyBatis的1对1查询操作
以一个用户和账户的示例说明多表查询的实现步骤:
【需求】:一个用户可有多个账户(多个账户也可属于一个用户),一个账户只能属于一个用户;
【步骤】:
- 1)建立数据库表:用户表、账户表,在账户表添加外键,实现一对多关系;
- 2)建立两个实体类:用户实体类、账户实体类,实体类要体现一对多的关系;
- 3)建立两个配置文件:用户配置文件、账户配置文件;
- 4)实现配置:查询账户时,可以得到其对应的用户信息(1对1);查询用户时可同时得到其对应的所有账户信息(1对多)。
【代码实现】:
此处实现的就是:查询账户时,可以查到其对应的用户信息,典型的1对1关系。
1)建立数据库表
-
CREATE
TABLE
USER (
-
id
INT(
11)
NOT
NULL PRIMARY
KEY AUTO_INCREMENT,
-
username
VARCHAR(
32)
NOT
NULL
COMMENT
'用户名称',
-
birthday DATETIME
DEFAULT
NULL
COMMENT
'生日',
-
sex
VARCHAR(
1)
DEFAULT
NULL
COMMENT
'性别',
-
address
VARCHAR(
256)
DEFAULT
NULL
COMMENT
'地址'
-
)
ENGINE=
INNODB
DEFAULT
CHARSET=utf8;
-
-
INSERT
INTO
USER(
id,username,birthday,sex,address)
VALUES (
1,
'张三',
'2019-02-27',
'男',
'北京'),(
2,
'李四',
'2020-02-09',
'男',
'上海'),(
3,
'王五',
'2020-01-29',
'女',
'南京');
-
-
CREATE
TABLE
account (
-
id
INT(
11)
NOT
NULL PRIMARY
KEY
COMMENT
'编号',
-
uid
INT(
11)
DEFAULT
NULL
COMMENT
'用户编号',
-
money
DOUBLE
DEFAULT
NULL
COMMENT
'金额',
-
FOREIGN
KEY (uid)
REFERENCES
USER(
id)
-
)
ENGINE=
INNODB
DEFAULT
CHARSET=utf8;
-
-
INSERT
INTO
account(
id,uid,money)
VALUES (
1,
1,
100),(
2,
2,
100),(
3,
2,
200);
2)建立两个实体类:User、Account
注意,这里要体现两个实体类之间的对应关系,User主表,Account从表:
- 从表Account实体类中要包含一个主表实体的对象引用;
-
public
class User implements Serializable {
-
private Integer id;
-
private String username;
-
private String address;
-
private String sex;
-
private Date birthday;
-
-
public Integer getId() {
-
return id;
-
}
-
-
public void setId(Integer id) {
-
this.id = id;
-
}
-
-
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;
-
}
-
-
public String getSex() {
-
return sex;
-
}
-
-
public void setSex(String sex) {
-
this.sex = sex;
-
}
-
-
public Date getBirthday() {
-
return birthday;
-
}
-
-
public void setBirthday(Date birthday) {
-
this.birthday = birthday;
-
}
-
-
@Override
-
public String toString() {
-
return
"User{" +
-
"id=" + id +
-
", username='" + username +
'\'' +
-
", address='" + address +
'\'' +
-
", sex='" + sex +
'\'' +
-
", birthday=" + birthday +
-
'}';
-
}
-
}
-
public
class Account implements Serializable {
-
private Integer id;
-
private Integer uid;
-
private Double money;
-
-
//从表实体类中要包含一个主表实体的对象引用
-
private User user;
-
-
public Integer getId() {
-
return id;
-
}
-
-
public void setId(Integer id) {
-
this.id = id;
-
}
-
-
public Integer getUid() {
-
return uid;
-
}
-
-
public void setUid(Integer uid) {
-
this.uid = uid;
-
}
-
-
public Double getMoney() {
-
return money;
-
}
-
-
public void setMoney(Double money) {
-
this.money = money;
-
}
-
-
public User getUser() {
-
return user;
-
}
-
-
public void setUser(User user) {
-
this.user = user;
-
}
-
-
@Override
-
public String toString() {
-
return
"Account{" +
-
"id=" + id +
-
", uid=" + uid +
-
", money=" + money +
-
'}';
-
}
-
}
3)建立两个配置文件:用户配置文件UserDao.xml、账户配置文件AccountDao.xml;
UserDao.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.winter.dao.UserDao">
-
<!-- 查询所有-->
-
<select id="findAll" resultType="com.winter.domain.User">
-
select *from user;
-
</select>
-
-
</mapper>
AccountDao.xml:这里注意有两点比较重要
- 定义封装两个类的resultMap:因为要用findAll方法查询账户的同时,获取对应的用户信息,所以封装的结果包含了User类和Account类,需要使用resultMap标签进行封装,并在其中使用association标签进行1对1的关系映射,配置封装User的内容,其中的property属性代表要封装的对象,column属性代表是根据哪个属性查询的,javaType与property一致。
- 定义sql查询语句
注意:这里涉及到的类没有用全名称,是因为在主配置文件中用了<typeAliases>标签起别名了。
-
<?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.winter.dao.AccountDao">
-
-
<!-- 封装Account和User的resultMap -->
-
<resultMap id="accountUserMap" type="account">
-
<id property="id" column="aid">
</id>
-
<result property="uid" column="uid">
</result>
-
<result property="money" column="money">
</result>
-
<!-- 1对1映射,封装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="sex" column="sex">
</result>
-
<result property="birthday" column="birthday">
</result>
-
</association>
-
</resultMap>
-
-
<!-- 查询所有-->
-
<select id="findAll" resultMap="accountUserMap">
-
select u.*,a.id as aid,a.uid,a.money from account a,user u where a.id = u.id;
-
</select>
-
-
</mapper>
4)代码测试:与预期效果一致
-
//测试MyBatis的CRUD
-
public
class AccountTest{
-
private InputStream in;
-
private SqlSession sqlSession;
-
private AccountDao accountDao;
-
-
@Before
//在测试方法执行前执行
-
public void init() throws Exception{
-
//1、读取配置文件,生成字节输入流
-
in = Resources.getResourceAsStream(
"SqlMapConfig.xml");
-
//2、获取SqlSessionFactory
-
SqlSessionFactory factory =
new SqlSessionFactoryBuilder().build(in);
-
//3、获取SqlSession对象
-
sqlSession = factory.openSession();
-
//4、获取dao的代理对象
-
accountDao = sqlSession.getMapper(AccountDao.class);
-
}
-
@After
//测试方法执行后执行
-
public void destroy() throws Exception{
-
//提交事务
-
sqlSession.commit();
-
//6、释放资源
-
sqlSession.close();
-
in.close();
-
}
-
//测试查询所有
-
@Test
-
public void testFindAll() {
-
//5、执行查询所有方法
-
List<Account> accounts = accountDao.findAll();
-
for (Account account : accounts) {
-
System.out.println(account);
-
System.out.println(account.getUser());
-
}
-
}
-
}
1.2 MyBatis的1对多查询操作
此处要实现的是:查询所有用户,同时得到其对应的所有账户信息。
代码实现相比于1.1节有重复的地方,就不重复贴代码了,文章末尾有工程源码,这里主要看下几个重点地方:
1)User实体类体现一对多关系映射,主表实体类包含从表实体的集合引用
-
public
class User implements Serializable {
-
private Integer id;
-
private String username;
-
private String address;
-
private String sex;
-
private Date birthday;
-
-
//1对多关系映射,主表实体类包含从表实体的集合引用
-
private List<Account> accounts;
-
-
public List<Account> getAccounts() {
-
return accounts;
-
}
-
-
public void setAccounts(List<Account> accounts) {
-
this.accounts = accounts;
-
}
-
//其他的getter、setter、toString方法就不再贴了
-
}
2)用户配置文件UserDao.xml,这里注意:
- resultMap中,配置User对象中accounts集合的映射,使用的是<collection>标签,其中的ofType指定是集合中元素的类型;
- sql语句使用左外连接查询,即查询左表中所有数据及其交集部分,温故而知新,查看原来的博文的简单总结:)
-
<?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.winter.dao.UserDao">
-
<!-- 定义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集合的映射 ,ofType指定是集合中元素的类型-->
-
<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 u left join account a on u.id = a.uid;
-
</select>
-
</mapper>
3)测试结果:满足预期
-
//测试查询所有
-
@Test
-
public void testFindAll() {
-
//5、执行查询所有方法
-
List<User> users = userDao.findAll();
-
for (User user : users) {
-
System.out.println(user);
-
System.out.println(user.getAccounts());
-
}
-
}
1.3 MyBatis的多对多查询操作
多对多,以用户和角色的示例说明:
【需求】:一个用户可有多个角色,一个角色又属于多个用户;
【步骤】:
- 1)建立数据库表:用户表、角色表,使用中间表,实现多对多关系,中间表包含各自的主键,在中间表中是外键;
- 2)建立两个实体类:用户实体类、角色实体类,实体类要体现多对多的关系,各自包含对方的集合引用;
- 3)建立两个配置文件:用户配置文件、角色配置文件;
- 4)实现配置:查询角色时,可以同时得到角色赋予的用户信息;查询用户时,可以同时得到用户包含的角色信息;
【代码实现】:具体是一个家庭的例子,有四个人,角色为2个爸爸、1个妈妈、还有1个是其中一个爸爸的妹妹。
1)建立数据库表:person、role、person_role(中间表),中间表2个字段分别对应前两个表的id。
-
CREATE
TABLE person (
-
id
INT(
11)
NOT
NULL PRIMARY
KEY AUTO_INCREMENT,
-
personname
VARCHAR(
32)
NOT
NULL
COMMENT
'姓名',
-
birthday DATETIME
DEFAULT
NULL
COMMENT
'生日',
-
sex
VARCHAR(
1)
DEFAULT
NULL
COMMENT
'性别',
-
address
VARCHAR(
256)
DEFAULT
NULL
COMMENT
'地址'
-
)
ENGINE=
INNODB
DEFAULT
CHARSET=utf8;
-
INSERT
INTO person(
id,personname,birthday,sex,address)
VALUES (
1,
'张三',
'2019-02-27',
'男',
'北京'),(
2,
'李四',
'2020-02-09',
'女',
'上海'),(
3,
'张小妹',
'2020-01-29',
'女',
'南京'),(
4,
'王五',
'2020-08-09',
'男',
'南京');
-
-
CREATE
TABLE
role (
-
id
INT(
11)
NOT
NULL PRIMARY
KEY
COMMENT
'编号',
-
rolename
VARCHAR(
30)
DEFAULT
NULL
COMMENT
'角色名称',
-
roledesc
VARCHAR(
60)
DEFAULT
NULL
COMMENT
'角色描述'
-
)
ENGINE=
INNODB
DEFAULT
CHARSET=utf8;
-
INSERT
INTO
role(
id,rolename,roledesc)
VALUES (
1,
'爸爸',
'赚钱'),(
2,
'妈妈',
'赚钱还照顾家'),(
3,
'哥哥',
'姑姑的大哥');
-
-
CREATE
TABLE person_role (
-
pid
INT(
11)
NOT
NULL
COMMENT
'用户编号',
-
rid
INT(
11)
NOT
NULL
COMMENT
'角色编号',
-
PRIMARY
KEY (pid,rid),
-
FOREIGN
KEY (rid)
REFERENCES
role (
id),
-
FOREIGN
KEY (pid)
REFERENCES person (
id)
-
)
ENGINE=
INNODB
DEFAULT
CHARSET=utf8;
-
INSERT
INTO person_role(pid,rid)
VALUES (
1,
1),(
2,
2),(
1,
3),(
4,
1);
对应关系像这样的:
姓名 | 角色 |
张三 | 爸爸 |
李四 | 妈妈 |
张小妹 | 爸爸的妹妹 |
王五 | 爸爸(张小妹的老公,同时也是一个爸爸) |
2)创建实体类:Person、Role,先实现查询角色时,可以同时得到角色赋予的用户信息;
Person类(getter、setter、toString方法就不写了)
-
public
class Person implements Serializable {
-
private Integer id;
-
private String personName;
-
private String address;
-
private String sex;
-
private Date birthday;
-
}
Role类(getter、setter、toString方法就不写了),注意加入List集合实现一个角色对应多个人的映射关系
-
public
class Role implements Serializable {
-
private Integer id;
-
private String roleName;
-
private String roleDesc;
-
//多对多关系映射,一个角色对应多个人
-
private List<Person> persons;
-
}
3)映射配置文件:注意resultMap中column对应的是数据库表的列名称,sql语句中起别名的话要和别名一致。这里比较难理解 的是sql语句,多表查询,用到了左外连接。
-
<?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.winter.dao.RoleDao">
-
-
<!-- 封装Role的resultMap -->
-
<resultMap id="roleMap" type="role">
-
<id property="id" column="rid">
</id>
-
<result property="roleName" column="rolename">
</result>
-
<result property="roleDesc" column="roledesc">
</result>
-
<collection property="persons" ofType="person">
-
<id property="id" column="id">
</id>
-
<result property="personName" column="personname">
</result>
-
<result property="address" column="address">
</result>
-
<result property="sex" column="sex">
</result>
-
<result property="birthday" column="birthday">
</result>
-
-
</collection>
-
</resultMap>
-
-
<!-- 查询所有-->
-
<select id="findAll" resultMap="roleMap">
-
SELECT p.*,r.id AS rid,r.rolename,r.roledesc FROM role r
-
LEFT OUTER JOIN person_role pr ON r.id = pr.rid
-
LEFT OUTER JOIN person p ON p.id = pr.pid;
-
</select>
-
-
</mapper>
4)测试效果:满足预期
-
//测试查询所有
-
@Test
-
public void testFindAll() {
-
//5、执行查询所有方法
-
List<Role> roles = roleDao.findAll();
-
for (Role role : roles) {
-
System.out.println(role);
-
System.out.println(role.getPersons());
-
}
-
}
5)查询用户时,可以同时得到用户包含的角色信息的实现不再赘述了,主要完善下Person实体类List集合,还有映射配置文件,sql语句反过来即可,具体可见源码,测试结果如下:
———————————————————————————————————
本文为博主原创文章,转载请注明出处!
若本文对您有帮助,轻抬您发财的小手,关注/评论/点赞/收藏,就是对我最大的支持!
祝君升职加薪,鹏程万里!
转载:https://blog.csdn.net/w464960660/article/details/108665737