mybatis中处理 一对一、一对多、多对多 (实体)

  • Post author:
  • Post category:其他


感悟一下:

好记性不如烂笔头,因为最近面试,看太多java特性原理,项目大脑开始过滤了,导致问了这个问题不知道在说啥。。回顾一下



一、基本环境配置



1.依赖导入
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.atguigu</groupId>
    <artifactId>mybatis_plus</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mybatis_plus</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1</version>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.mybatis.spring.boot</groupId>-->
<!--            <artifactId>mybatis-spring-boot-starter</artifactId>-->
<!--            <version>2.1.4</version>-->
<!--        </dependency>-->
        <!--mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--lombok用来简化实体类-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-commons</artifactId>
            <version>2.3.7.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>



2.配置信息
#数据库的相关配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis1x?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root

# MyBatis Mapper所对应的XML文件位置
mybatis-plus.mapper-locations= classpath*:/static/*Mapper.xml
mybatis-plus.type-aliases-package= com.atguigu.mybatis_plus.entity
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.configuration.map-underscore-to-camel-case=true
#全局设置主键生成策略
#mybatis-plus.global-config.db-config.id-type=auto


3.数据库创建
/*
 Navicat Premium Data Transfer

 Source Server         : localhost_3306
 Source Server Type    : MySQL
 Source Server Version : 80017
 Source Host           : localhost:3306
 Source Schema         : mybatis1x

 Target Server Type    : MySQL
 Target Server Version : 80017
 File Encoding         : 65001

 Date: 24/11/2021 17:01:36
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for account
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account`  (
  `a_id` int(11) NOT NULL AUTO_INCREMENT,
  `money` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `u_id` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`a_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES (1, '100', 1);
INSERT INTO `account` VALUES (2, '200', 2);
INSERT INTO `account` VALUES (3, '300', 3);
INSERT INTO `account` VALUES (4, '400', 4);
INSERT INTO `account` VALUES (5, '110', 1);

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (
  `role_id` int(11) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `role_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  PRIMARY KEY (`role_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, '总裁', '管理公司');
INSERT INTO `role` VALUES (2, '经理', '管理部门');
INSERT INTO `role` VALUES (3, '组长', '管理小组');
INSERT INTO `role` VALUES (4, '县长', '管理县');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `sex` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, '牛一', '海南', '男');
INSERT INTO `user` VALUES (2, '牛二', '海南', '女');
INSERT INTO `user` VALUES (3, '京一', '北京', '男');
INSERT INTO `user` VALUES (4, '京二', '北京', '女');

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NULL DEFAULT NULL,
  `role_id` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1, 1, 1);
INSERT INTO `user_role` VALUES (2, 1, 4);
INSERT INTO `user_role` VALUES (3, 2, 2);
INSERT INTO `user_role` VALUES (4, 3, 3);
INSERT INTO `user_role` VALUES (5, 4, 4);

SET FOREIGN_KEY_CHECKS = 1;



二、一对一



1.Account实体类
package com.atguigu.mybatis_plus.entity;

import lombok.Data;

@Data
public class Account {
    private Integer aId;
    private Integer uId;
    private Double money;

    //一个账号只有一个用户。1-1
    private User user;//对应association property="user" user指的就是这个对象

    @Override
    public String toString() {
        return "Account{" +
                "aId=" + aId +
                ", uId=" + uId +
                ", money=" + money +
                '}';
    }
}



2.User实体类
package com.atguigu.mybatis_plus.entity;

import lombok.Data;

@Data
public class User {
    private Integer id;
    private String name;
    private String address;
    private String sex;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", address='" + address + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
}



3.AccountMapper接口
package com.atguigu.mybatis_plus.mapper;

import com.atguigu.mybatis_plus.entity.Account;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

@Mapper
@Repository
public interface AccountMapper {
    //查找所有账号,每个账号对应一个用户
    List<Account> findAll();
}


4.AccountMapper.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.atguigu.mybatis_plus.mapper.AccountMapper">
    <!--定义封装account和user的resultmap-->
    <resultMap id="accountUserMap" type="account">
        <id property="aId" column="aid"/>
        <result property="uId" column="uid"/>
        <result property="money" column="money"/>
        <!-- 一对一的关系映射:配置封装user的内容-->
        <association property="user" column="uid" javaType="user">
            <id property="id" column="id"/>
            <result column="name" property="name"/>
            <result column="address" property="address"/>
            <result column="sex" property="sex"/>
        </association>
    </resultMap>

    <select id="findAll" resultMap="accountUserMap">
    select u.*,a.a_id as aid,a.u_id as uid,a.money from account a , user u where u.id = a.u_id
    </select>
</mapper>


5.测试类
package com.atguigu.mybatis_plus;

import com.atguigu.mybatis_plus.entity.Account;
import com.atguigu.mybatis_plus.entity.User1;
import com.atguigu.mybatis_plus.mapper.AccountMapper;
import com.atguigu.mybatis_plus.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Arrays;
import java.util.List;

@SpringBootTest
class MybatisPlusApplicationTests {
    @Autowired
    private AccountMapper accountMapper;

    @Test
    public void findAll() {
        List<Account> list= accountMapper.findAll();
//        System.out.println(list);
        for (Account a:list) {
            a.toString();
    System.out.println(a.getUser());
        }
    }
}


结果

在这里插入图片描述



三、一对多



1.在User类中添上
//一个用户对应多个账户,也就是一个User有多个Account对象。
private List<Account> account;


2.UserMapper接口
package com.atguigu.mybatis_plus.mapper;


import com.atguigu.mybatis_plus.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;


@Mapper
@Repository
public interface UserMapper  {
   //查询所有操作,同时获取到用户下所有账户的信息
    List<User> findAll();

    //根据用户id找某个用户信息
    User findById(int id);
}


3.UserMapper.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.atguigu.mybatis_plus.mapper.UserMapper">
    <!--定义封装account和user的resultmap-->
    <resultMap id="userAccountMap" type="user">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
        <collection property="account"  ofType="account">
            <id property="aId" column="a_id"/>
            <result property="money" column="money"/>
            <result property="uId" column="u_id"/>
        </collection>
    </resultMap>

    <select id="findAll" resultMap="userAccountMap">
    select id,name,address,sex,a.* from user left outer join account a on user.id = a.u_id
    </select>

    <select id="findById" resultMap="userAccountMap" parameterType="integer">
    select id,name,address,sex,a.* from user left outer join account a on user.id = a.u_id WHERE id = #{id}
    </select>

</mapper>


4.测试类加上方法
@Test
    public void userfind() {
        List<User> list= userMapper.findAll();
        for (User a:list) {
            a.toString();
            System.out.println(a.getAccount());
        }

        User user = userMapper.findById(1);
        System.out.println(user);
        System.out.println(user.getAccount());
    }


结果

在这里插入图片描述



四、多对多



1.Role实体类
package com.atguigu.mybatis_plus.entity;

import lombok.Data;

import java.util.List;

@Data
public class Role {
    private Integer roleId;
    private String roleName; //名称
    private String roleDesc; //管啥的
    
    //一个角色可以分配给多个用户
    private List<User> users; //

    @Override
    public String toString() {
        return "Role{" +
                "roleId=" + roleId +
                ", roleName='" + roleName + '\'' +
                ", roleDesc='" + roleDesc + '\'' +
                '}';
    }
}


2.在User类中添上
//一个用户有多个角色
    private List<Role> roles;


3.RoleMapper接口
package com.atguigu.mybatis_plus.mapper;

import com.atguigu.mybatis_plus.entity.Role;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

@Mapper
@Repository
public interface RoleMapper {
    List<Role> findRoles();
}



4.RoleMapper.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.atguigu.mybatis_plus.mapper.RoleMapper">

    <resultMap id="roleUserMap" type="role">
        <id property="roleId" column="role_id"/>
        <result property="roleName" column="role_name"/>
        <result property="roleDesc" column="role_desc"/>
        <collection property="users"  ofType="user">
            <id property="id" column="id"/>
            <result property="name" column="name"/>
            <result property="sex" column="sex"/>
            <result property="address" column="address"/>
        </collection>
    </resultMap>

    <select id="findRoles" resultMap="roleUserMap">
    select role.*,user.* from user,role, user_role WHERE role.role_id=user_role.role_id and user.id=user_role.user_id
    </select>
</mapper>


5.UserMapper添加
    //查找用户和他的角色
    List<User> findRole();


6.UserMapper.xml中添加
 <!--定义封装Role和user的resultmap-->
    <resultMap id="userRole" type="user">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
        <collection property="roles"  ofType="role">
            <id property="roleId" column="role_id"/>
            <result property="roleName" column="role_name"/>
            <result property="roleDesc" column="role_desc"/>
        </collection>
    </resultMap>

    <select id="findRole" resultMap="userRole">
    select user.*,role.* from user,role, user_role WHERE role.role_id=user_role.role_id and user.id=user_role.user_id
    </select>


7.测试类加上方法
@Test
    public void roleUserfind() {
        List<Role> list= roleMapper.findRoles();
        for (Role a:list) {
            a.toString();
            System.out.println(a.getUsers());
        }
    }
    @Test
    public void userRolefind() {
        List<User> list= userMapper.findRole();
        for (User a:list) {
            a.toString();
            System.out.println(a.getRoles());
        }
    }


结果:

UserMapper中查找

用户和他对应的权限

n:n

在这里插入图片描述

RoleMapper中查找

权限对应的用户

n:n

在这里插入图片描述



五、总结



1.association

在我们实体关系对应1-1的时候,可以采用

在这里插入图片描述

association对应的实体只有一个对象

在这里插入图片描述


可以理解为association是为了打开这层User对象,进去映射sql查到User相关的结果。



2.collection

在我们实体关系对应1-n的时候,可以采用

反过来n-1也一样的道理。

在这里插入图片描述

collection对应的实体可以有很多对象,是个List容器

在这里插入图片描述


!!!


如果上层id和collection中的id对应有多个一样的,只会存找到的第一个。


比如查找到的数据

<== Row: 1, 总裁, 管理公司, 1, 牛一, 海南, 男

<== Row: 1, 县长, 管理县, 1, 牛一, 海南, 男

<== Row: 2, 经理, 管理部门, 2, 牛二, 海南, 女

<== Row: 3, 组长, 管理小组, 3, 京一, 北京, 男

关键字有两组11,只会存第一个。



可以理解为collection拿了个袋子,给每个顾客挑选关键部位不一样的数据放进去。



希望能给迷惑的你带来一些帮助~



六、拓展问题

在我们

1对多查询的

过程中,如果需要分页,就会出现

分页效果不理想

怎么个不理想呢?

先普及一下:


limit 0,2

代表从下标0开始,往后找2条数据


limit 2

代表从头开始找,往后找2条数据

举个User的例子:比如我们分页应该是,一页里面有几个用户。我们根据SQL的分页规则来控制数据量。回溯到我们之前查询User以及其账号的 sql语句

  select id,name,address,sex,a.* from user left outer join account a on user.id = a.u_id 

尝试分页:

select id,name,address,sex,a.* from user left outer join account a on user.id = a.u_id limit 0,2 #查询两条

在这里插入图片描述

可以看到结果 牛一 的数据丢失了一个。

解决方案:

1️⃣从sql处解决,查询语句中改为聚合类型,用

GROUP_CONCAT()

配合

group by

去拼接 多数据字段。

select id,name,address,sex,GROUP_CONCAT(a.a_id) a_id,GROUP_CONCAT(a.money) money,GROUP_CONCAT(a.u_id) u_id from user left outer join account a on user.id = a.u_id GROUP BY id LIMIT 0,2

在这里插入图片描述

这样牛一和牛二的数据都有了,而且牛一的数据没有丢失。



!注意:


这样查询的话就

不属于 1:n,不能使用collection去接收

类型的查询了。需要重新定义接收的实体等。

2️⃣ 从serviceImpl层解决,因为我们可以通过1:n的查询方式,获得每个User实体的所有数据(都封装在一个User类中),所以我们在serviceImpl层去获取所有的结果后,只取其中的分页数据就行。

  // limit begin,sum  比如每页2条数据,第一次 limit 0,2  第二次 limit 2,2 .....
        int begin=0; //从哪开始找
        int sum=2; //找几条
        int index = begin; //定位
        List<User> allList = userMapper.findAll();
        List<User> list = new ArrayList<>();
        //获取其中第一第二条的。
        for (int i = 0; i <sum; i++) {
            list.add(allList.get(index));
            index++;
        }
        //展示
        for (User a : list) {
            a.toString();
            System.out.println(a.getAccount());
        }

结果:

在这里插入图片描述



版权声明:本文为qq_45684867原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。