SpringBoot 使用 Mybatis 实现一对一关联表查询

435 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第22天,点击查看活动详情

1. 关联查询

MyBatis 作为 ORM 框架为项目中的对象模型和关系数据库之间建立联系,并提供机制实现程序对数据库中数据的操作。

对于简单的单表查询,MyBatis 提供了 JavaBean 和数据库 Table 的直接映射关系,可以很方便通过 bean 操作数据。

在实际业务中,关联数据查询也是比较常见的需求,如用户账户与详细信息关系、订单与明细的关系等,MyBatis 中同样提供了关联查询的能力。

2. 一对一关联

常见的系统中,用户登录账户仅作为登录的身份识别使用,登录后的用户信息存储在另外一个地方,两者建立一定的关联关系,且这种关系是唯一的,就是一对一关联关系。

2.1 定义关联表

自定义创建用户账户表和详细信息表,并使用 user_id 进行关联

CREATE TABLE `user` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT,
    `username` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
    `password` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

insert into `user`(`id`,`username`,`password`) values (1,'tom','abc123'),(2,'lily','123456');

CREATE TABLE `user_info` (
    `id`  bigint(20) NOT NULL AUTO_INCREMENT,
    `user_id`  bigint(20) DEFAULT NULL,
    `age` int(11) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
    `address` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

insert into `user`(`id`,`user_id`,`age`,`address`) values (1,'1','18','上海市'),(2,2,17,'北京市');

2.2 定义 JavaBean

分别为两个表定义对应的对象模型类

  • 对应 user 表
@Data
@Table("user")
public class User {
    private long id;
    private String username;
    private String password;
}
  • 对应 user_info 表
@Data
@Table("user_info")
public class Userinfo {
    private long id;
    private long userId;
    private Integer age;
    private String mobile;
}

3. 关联查询

使用 MyBaits 为数据模型和数据库表建立一个 Mapper 接口类,以此实现两者的关联,并提供操作数据库表中数据的能力。

3.1 业务代码关联

一对一关联查询,最常使用的方法是在业务代码中通过属性进行关联匹配 此种方法不需要使用 MyBatis 的关联支持,只需要分别对两个数据库进行单表查询,并使用查询结果中的关联字段进行匹配,最终得到关联合并的结果数据。

@Service
public class UserServiceImpl{
    @Override
    private Usermapper userMapper;
    
    @Override
    private UserInfomapper userInfoMapper;
    
    public UserModelDto getUserInfoByUserId(){
        User user = userMapper.queryUserById(userId);
        UserInfo userInfo = userInfoMapper.queryUserInfoByUserId(userId);
        UserModelDto userModelDto = new UserModelDto();
        userModelDto.setUserId(userId);
        userModelDto.setUserName(user.getUserName());
        userModelDto.setUserAge(user.getAge());
        userModelDto.setMobile(user.getMobile());
        return userModelDto;
    }
}
  • 在业务代码中对多个表的数据进行处理是可行的,但是对于比较简单的表数据的整合,MyBatis 或 MySQL 的 SQL 语句完全可以胜任,并且可以使得业务代码逻辑更简洁。

3.2 MyBatis 实现一对一关联

MyBatis 进行一对一表关联时可以使用动态 SQL 语句中提供的 association 标签,并通过其中的相关属性来将多个分别定义的 SQL 语句产生关联,最终得到整合的数据。

  • UserMapper.xml
<!-- 省略了 UserMapper.java 文件的定义-->
<mapper namespace="com.shone.manage.mapper.UserMapper">
    <select id="queryUserById" resultType="com.shone.manage.Entity.User">
        SELECT * FROM user WHERE id = #{id}
    </select>
</mapper>
  • UserInfoMapper.xml
<!-- 省略了 UserInfoMapper.java 文件的定义-->
<mapper namespace="com.shone.manage.mapper.UserInfoMapper">

    <resultMap type="com.shone.manage.dto.userModelDto" id="userInfoMap">
        <id property="id" column="id" />
        <result property="userId" column="user_id" />
        <result property="age" column="age" />
        <result property="mobile" column="mobile" />
        <!-- association 关联其他 SQL 实现一对一级联查询 -->
        <association property="userName" column="username"
        javaType="com.shone.manage.mapper.UserMapper"
   select="com.shone.manage.mapper.UserMapper.queryUserById" />
    </resultMap>
    
    <select id="queryUserInfoByUserId" resultMap="userInfoMap">
        SELECT * FROM user_info WHERE user_id = #{userId}
    </select>
</mapper>
  • 这样在执行外层 queryUserInfoByUserId 查询方法时,会根据对应的字段自动执行内部的 queryUserById 方法获取 username 字段内容

此时业务代码逻辑可以写成

@Service
public class UserServiceImpl{
    @Override
    private UserInfomapper userInfoMapper;
    
    public UserModelDto getUserInfoByUserId(){
        return userInfoMapper.queryUserInfoByUserId(userId);
    }
}

3.3 MySQL 关联表实现一对一关联

除了业务逻辑代码整合数据、MyBatis 动态 SQL 关联,还可以根据 MySQL 自带的表关联语法实现一对一关联,在少表小表关联上更加适用

  • 定义一个联合两张表的 UserJoinMapper.xml
<!-- 省略了 UserJoinMapper.java 文件的定义-->
<mapper namespace="com.shone.manage.mapper.UserJoinMapper">
    <!--定义查询语句使用 inner join on 关联两张表的数据-->
    <select id="queryUserModelDtoByUserId" resultType="com.shone.manage.dto.userModelDto">
        SELECT user.username, user_info.user_id, user_info.age, user_info.mobile 
        FROM user
        INNER JOIN user_info ON user.id = user_info.user_id
        WHERE user.id = #{id}
    </select>
</mapper>
  • 业务代码逻辑书写
@Service
public class UserServiceImpl{
    @Override
    private UserJoinMapper userJoinMapper;
    
    public UserModelDto getUserInfoByUserId(){
        return userJoinMapper.queryUserModelDtoByUserId(userId);
    }
}