小言_互联网的博客

JavaWeb——MyBatis框架之多表查询操作实战案例总结(MyBatis1对1查询,MyBatis1对多查询,MyBatis多对多查询)

618人阅读  评论(0)

目录

1 MyBatis的多表查询

1.1 MyBatis的1对1查询操作

1.2 MyBatis的1对多查询操作

1.3 MyBatis的多对多查询操作


多表之间的关系,分为一对一、一对多(多对一)、多对多,具体的不再赘述了,在数据库专栏-MySQL数据中有总结过。我们这里详细总结下MyBatis中的多表操作,所使用的工程源码会在文章末尾附上。

1 MyBatis的多表查询

1.1 MyBatis的1对1查询操作

以一个用户和账户的示例说明多表查询的实现步骤:

【需求】:一个用户可有多个账户(多个账户也可属于一个用户),一个账户只能属于一个用户;

【步骤】:

  • 1)建立数据库表:用户表、账户表,在账户表添加外键,实现一对多关系
  • 2)建立两个实体类:用户实体类、账户实体类,实体类要体现一对多的关系;
  • 3)建立两个配置文件:用户配置文件、账户配置文件;
  • 4)实现配置:查询账户时,可以得到其对应的用户信息(1对1);查询用户时可同时得到其对应的所有账户信息(1对多)。

【代码实现】:

此处实现的就是:查询账户时,可以查到其对应的用户信息,典型的1对1关系。

1)建立数据库表


  
  1. CREATE TABLE USER (
  2. id INT( 11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  3. username VARCHAR( 32) NOT NULL COMMENT '用户名称',
  4. birthday DATETIME DEFAULT NULL COMMENT '生日',
  5. sex VARCHAR( 1) DEFAULT NULL COMMENT '性别',
  6. address VARCHAR( 256) DEFAULT NULL COMMENT '地址'
  7. ) ENGINE= INNODB DEFAULT CHARSET=utf8;
  8. INSERT INTO USER( id,username,birthday,sex,address) VALUES ( 1, '张三', '2019-02-27', '男', '北京'),( 2, '李四', '2020-02-09', '男', '上海'),( 3, '王五', '2020-01-29', '女', '南京');
  9. CREATE TABLE account (
  10. id INT( 11) NOT NULL PRIMARY KEY COMMENT '编号',
  11. uid INT( 11) DEFAULT NULL COMMENT '用户编号',
  12. money DOUBLE DEFAULT NULL COMMENT '金额',
  13. FOREIGN KEY (uid) REFERENCES USER( id)
  14. ) ENGINE= INNODB DEFAULT CHARSET=utf8;
  15. INSERT INTO account( id,uid,money) VALUES ( 1, 1, 100),( 2, 2, 100),( 3, 2, 200);

2)建立两个实体类:User、Account

注意,这里要体现两个实体类之间的对应关系,User主表,Account从表:

  • 从表Account实体类中要包含一个主表实体的对象引用;

  
  1. public class User implements Serializable {
  2. private Integer id;
  3. private String username;
  4. private String address;
  5. private String sex;
  6. private Date birthday;
  7. public Integer getId() {
  8. return id;
  9. }
  10. public void setId(Integer id) {
  11. this.id = id;
  12. }
  13. public String getUsername() {
  14. return username;
  15. }
  16. public void setUsername(String username) {
  17. this.username = username;
  18. }
  19. public String getAddress() {
  20. return address;
  21. }
  22. public void setAddress(String address) {
  23. this.address = address;
  24. }
  25. public String getSex() {
  26. return sex;
  27. }
  28. public void setSex(String sex) {
  29. this.sex = sex;
  30. }
  31. public Date getBirthday() {
  32. return birthday;
  33. }
  34. public void setBirthday(Date birthday) {
  35. this.birthday = birthday;
  36. }
  37. @Override
  38. public String toString() {
  39. return "User{" +
  40. "id=" + id +
  41. ", username='" + username + '\'' +
  42. ", address='" + address + '\'' +
  43. ", sex='" + sex + '\'' +
  44. ", birthday=" + birthday +
  45. '}';
  46. }
  47. }

  
  1. public class Account implements Serializable {
  2. private Integer id;
  3. private Integer uid;
  4. private Double money;
  5. //从表实体类中要包含一个主表实体的对象引用
  6. private User user;
  7. public Integer getId() {
  8. return id;
  9. }
  10. public void setId(Integer id) {
  11. this.id = id;
  12. }
  13. public Integer getUid() {
  14. return uid;
  15. }
  16. public void setUid(Integer uid) {
  17. this.uid = uid;
  18. }
  19. public Double getMoney() {
  20. return money;
  21. }
  22. public void setMoney(Double money) {
  23. this.money = money;
  24. }
  25. public User getUser() {
  26. return user;
  27. }
  28. public void setUser(User user) {
  29. this.user = user;
  30. }
  31. @Override
  32. public String toString() {
  33. return "Account{" +
  34. "id=" + id +
  35. ", uid=" + uid +
  36. ", money=" + money +
  37. '}';
  38. }
  39. }

3)建立两个配置文件:用户配置文件UserDao.xml、账户配置文件AccountDao.xml;

UserDao.xml:


  
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.winter.dao.UserDao">
  6. <!-- 查询所有-->
  7. <select id="findAll" resultType="com.winter.domain.User">
  8. select *from user;
  9. </select>
  10. </mapper>

AccountDao.xml:这里注意有两点比较重要

  • 定义封装两个类的resultMap:因为要用findAll方法查询账户的同时,获取对应的用户信息,所以封装的结果包含了User类和Account类,需要使用resultMap标签进行封装,并在其中使用association标签进行1对1的关系映射,配置封装User的内容,其中的property属性代表要封装的对象,column属性代表是根据哪个属性查询的,javaType与property一致。
  • 定义sql查询语句

注意:这里涉及到的类没有用全名称,是因为在主配置文件中用了<typeAliases>标签起别名了。


  
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.winter.dao.AccountDao">
  6. <!-- 封装Account和User的resultMap -->
  7. <resultMap id="accountUserMap" type="account">
  8. <id property="id" column="aid"> </id>
  9. <result property="uid" column="uid"> </result>
  10. <result property="money" column="money"> </result>
  11. <!-- 1对1映射,封装User -->
  12. <association property="user" column="uid" javaType="user">
  13. <id property="id" column="id"> </id>
  14. <result property="username" column="username"> </result>
  15. <result property="address" column="address"> </result>
  16. <result property="sex" column="sex"> </result>
  17. <result property="birthday" column="birthday"> </result>
  18. </association>
  19. </resultMap>
  20. <!-- 查询所有-->
  21. <select id="findAll" resultMap="accountUserMap">
  22. select u.*,a.id as aid,a.uid,a.money from account a,user u where a.id = u.id;
  23. </select>
  24. </mapper>

4)代码测试:与预期效果一致


  
  1. //测试MyBatis的CRUD
  2. public class AccountTest{
  3. private InputStream in;
  4. private SqlSession sqlSession;
  5. private AccountDao accountDao;
  6. @Before //在测试方法执行前执行
  7. public void init() throws Exception{
  8. //1、读取配置文件,生成字节输入流
  9. in = Resources.getResourceAsStream( "SqlMapConfig.xml");
  10. //2、获取SqlSessionFactory
  11. SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
  12. //3、获取SqlSession对象
  13. sqlSession = factory.openSession();
  14. //4、获取dao的代理对象
  15. accountDao = sqlSession.getMapper(AccountDao.class);
  16. }
  17. @After //测试方法执行后执行
  18. public void destroy() throws Exception{
  19. //提交事务
  20. sqlSession.commit();
  21. //6、释放资源
  22. sqlSession.close();
  23. in.close();
  24. }
  25. //测试查询所有
  26. @Test
  27. public void testFindAll() {
  28. //5、执行查询所有方法
  29. List<Account> accounts = accountDao.findAll();
  30. for (Account account : accounts) {
  31. System.out.println(account);
  32. System.out.println(account.getUser());
  33. }
  34. }
  35. }

1.2 MyBatis的1对多查询操作

此处要实现的是:查询所有用户,同时得到其对应的所有账户信息。

代码实现相比于1.1节有重复的地方,就不重复贴代码了,文章末尾有工程源码,这里主要看下几个重点地方:

1)User实体类体现一对多关系映射,主表实体类包含从表实体的集合引用


  
  1. public class User implements Serializable {
  2. private Integer id;
  3. private String username;
  4. private String address;
  5. private String sex;
  6. private Date birthday;
  7. //1对多关系映射,主表实体类包含从表实体的集合引用
  8. private List<Account> accounts;
  9. public List<Account> getAccounts() {
  10. return accounts;
  11. }
  12. public void setAccounts(List<Account> accounts) {
  13. this.accounts = accounts;
  14. }
  15. //其他的getter、setter、toString方法就不再贴了
  16. }

2)用户配置文件UserDao.xml,这里注意:

  • resultMap中,配置User对象中accounts集合的映射,使用的是<collection>标签,其中的ofType指定是集合中元素的类型;
  • sql语句使用左外连接查询,即查询左表中所有数据及其交集部分,温故而知新,查看原来的博文的简单总结:)

  
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.winter.dao.UserDao">
  6. <!-- 定义User的resultMap -->
  7. <resultMap id="userAccountMap" type="user">
  8. <id property="id" column="id"> </id>
  9. <result property="username" column="username"> </result>
  10. <result property="address" column="address"> </result>
  11. <result property="sex" column="sex"> </result>
  12. <result property="birthday" column="birthday"> </result>
  13. <!-- 配置User对象中accounts集合的映射 ,ofType指定是集合中元素的类型-->
  14. <collection property="accounts" ofType="account">
  15. <id property="id" column="aid"> </id>
  16. <result property="uid" column="uid"> </result>
  17. <result property="money" column="money"> </result>
  18. </collection>
  19. </resultMap>
  20. <!-- 查询所有-->
  21. <select id="findAll" resultMap="userAccountMap">
  22. select *from user u left join account a on u.id = a.uid;
  23. </select>
  24. </mapper>

3)测试结果:满足预期


  
  1. //测试查询所有
  2. @Test
  3. public void testFindAll() {
  4. //5、执行查询所有方法
  5. List<User> users = userDao.findAll();
  6. for (User user : users) {
  7. System.out.println(user);
  8. System.out.println(user.getAccounts());
  9. }
  10. }

1.3 MyBatis的多对多查询操作

多对多,以用户和角色的示例说明:

【需求】:一个用户可有多个角色,一个角色又属于多个用户;

【步骤】:

  • 1)建立数据库表:用户表、角色表,使用中间表,实现多对多关系,中间表包含各自的主键,在中间表中是外键
  • 2)建立两个实体类:用户实体类、角色实体类,实体类要体现多对多的关系,各自包含对方的集合引用;
  • 3)建立两个配置文件:用户配置文件、角色配置文件;
  • 4)实现配置:查询角色时,可以同时得到角色赋予的用户信息;查询用户时,可以同时得到用户包含的角色信息;

【代码实现】:具体是一个家庭的例子,有四个人,角色为2个爸爸、1个妈妈、还有1个是其中一个爸爸的妹妹。

1)建立数据库表:person、role、person_role(中间表),中间表2个字段分别对应前两个表的id。


  
  1. CREATE TABLE person (
  2. id INT( 11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  3. personname VARCHAR( 32) NOT NULL COMMENT '姓名',
  4. birthday DATETIME DEFAULT NULL COMMENT '生日',
  5. sex VARCHAR( 1) DEFAULT NULL COMMENT '性别',
  6. address VARCHAR( 256) DEFAULT NULL COMMENT '地址'
  7. ) ENGINE= INNODB DEFAULT CHARSET=utf8;
  8. 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', '男', '南京');
  9. CREATE TABLE role (
  10. id INT( 11) NOT NULL PRIMARY KEY COMMENT '编号',
  11. rolename VARCHAR( 30) DEFAULT NULL COMMENT '角色名称',
  12. roledesc VARCHAR( 60) DEFAULT NULL COMMENT '角色描述'
  13. ) ENGINE= INNODB DEFAULT CHARSET=utf8;
  14. INSERT INTO role( id,rolename,roledesc) VALUES ( 1, '爸爸', '赚钱'),( 2, '妈妈', '赚钱还照顾家'),( 3, '哥哥', '姑姑的大哥');
  15. CREATE TABLE person_role (
  16. pid INT( 11) NOT NULL COMMENT '用户编号',
  17. rid INT( 11) NOT NULL COMMENT '角色编号',
  18. PRIMARY KEY (pid,rid),
  19. FOREIGN KEY (rid) REFERENCES role ( id),
  20. FOREIGN KEY (pid) REFERENCES person ( id)
  21. ) ENGINE= INNODB DEFAULT CHARSET=utf8;
  22. INSERT INTO person_role(pid,rid) VALUES ( 1, 1),( 2, 2),( 1, 3),( 4, 1);

对应关系像这样的:

姓名 角色
张三 爸爸
李四 妈妈
张小妹 爸爸的妹妹
王五

爸爸(张小妹的老公,同时也是一个爸爸)

2)创建实体类:Person、Role,先实现查询角色时,可以同时得到角色赋予的用户信息;

Person类(getter、setter、toString方法就不写了)


  
  1. public class Person implements Serializable {
  2. private Integer id;
  3. private String personName;
  4. private String address;
  5. private String sex;
  6. private Date birthday;
  7. }

Role类(getter、setter、toString方法就不写了),注意加入List集合实现一个角色对应多个人的映射关系


  
  1. public class Role implements Serializable {
  2. private Integer id;
  3. private String roleName;
  4. private String roleDesc;
  5. //多对多关系映射,一个角色对应多个人
  6. private List<Person> persons;
  7. }

3)映射配置文件:注意resultMap中column对应的是数据库表的列名称,sql语句中起别名的话要和别名一致。这里比较难理解 的是sql语句,多表查询,用到了左外连接。


  
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.winter.dao.RoleDao">
  6. <!-- 封装Role的resultMap -->
  7. <resultMap id="roleMap" type="role">
  8. <id property="id" column="rid"> </id>
  9. <result property="roleName" column="rolename"> </result>
  10. <result property="roleDesc" column="roledesc"> </result>
  11. <collection property="persons" ofType="person">
  12. <id property="id" column="id"> </id>
  13. <result property="personName" column="personname"> </result>
  14. <result property="address" column="address"> </result>
  15. <result property="sex" column="sex"> </result>
  16. <result property="birthday" column="birthday"> </result>
  17. </collection>
  18. </resultMap>
  19. <!-- 查询所有-->
  20. <select id="findAll" resultMap="roleMap">
  21. SELECT p.*,r.id AS rid,r.rolename,r.roledesc FROM role r
  22. LEFT OUTER JOIN person_role pr ON r.id = pr.rid
  23. LEFT OUTER JOIN person p ON p.id = pr.pid;
  24. </select>
  25. </mapper>

4)测试效果:满足预期


  
  1. //测试查询所有
  2. @Test
  3. public void testFindAll() {
  4. //5、执行查询所有方法
  5. List<Role> roles = roleDao.findAll();
  6. for (Role role : roles) {
  7. System.out.println(role);
  8. System.out.println(role.getPersons());
  9. }
  10. }

5)查询用户时,可以同时得到用户包含的角色信息的实现不再赘述了,主要完善下Person实体类List集合,还有映射配置文件,sql语句反过来即可,具体可见源码,测试结果如下:

需要源码的传送门在此

———————————————————————————————————

本文为博主原创文章,转载请注明出处!

若本文对您有帮助,轻抬您发财的小手,关注/评论/点赞/收藏,就是对我最大的支持!

祝君升职加薪,鹏程万里!


转载:https://blog.csdn.net/w464960660/article/details/108665737
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场