MyBatis联合查询 - 注解篇

文章目录

前言

在日常开发中,我们经常需要查询多张表的数据,传统的XML配置方式虽然功能强大,但有时候显得比较繁琐。MyBatis的注解方式为我们提供了一种更简洁、更直观的解决方案 - 注解

数据库表

sql 复制代码
-- 用户表
CREATE TABLE user (
    id BIGINT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(100),
    dept_id BIGINT
);

-- 部门表  
CREATE TABLE department (
    id BIGINT PRIMARY KEY,
    dept_name VARCHAR(50),
    location VARCHAR(100)
);

对应的实体类:

java 复制代码
@Data
public class User {
    private Long id;
    private String name;
    private String email;
    private Long deptId;
    private Department department; 
}

@Data
public class Department {
    private Long id;
    private String deptName;
    private String location;
}

一对一关联查询

方式一:

最常用的方式是直接在SQL中进行关联查询:

java 复制代码
@Mapper
public interface UserMapper {
    
    @Select("SELECT *" + 
            "FROM user u LEFT JOIN department d ON u.dept_id = d.id " +
            "WHERE u.id = #{id}")
    @Results({
        @Result(property = "id", column = "id"),
        @Result(property = "name", column = "name"),
        @Result(property = "email", column = "email"),
        @Result(property = "deptId", column = "dept_id"),
        @Result(property = "department.id", column = "dept_id"),
        @Result(property = "department.deptName", column = "dept_name"),
        @Result(property = "department.location", column = "location")
    })
    User getUserWithDept(Long id);
}

这种方式简单直接,一次查询就能获取所有需要的数据,性能比较好。

方式二:

如果你更喜欢分步查询的方式,可以使用@One注解:

java 复制代码
@Mapper
public interface UserMapper {
    
    @Select("SELECT * FROM user WHERE id = #{id}")
    @Results({
        @Result(property = "id", column = "id"),
        @Result(property = "name", column = "name"),
        @Result(property = "email", column = "email"),
        @Result(property = "deptId", column = "dept_id"),
        @Result(property = "department", column = "dept_id",
                one = @One(select = "com.example.mapper.DepartmentMapper.getDeptById"))
    })
    User getUserWithDept2(Long id);
}

@Mapper
public interface DepartmentMapper {
    
    @Select("SELECT * FROM department WHERE id = #{id}")
    Department getDeptById(Long id);
}

这种方式会执行两次SQL查询,先查用户信息,再根据部门ID查询部门信息。虽然查询次数多了,但逻辑更清晰,复用性也更好。

一对多关联查询

假设我们要查询部门及其下属的所有员工:

java 复制代码
@Data
public class Department {
    private Long id;
    private String deptName;
    private String location;
    private List<User> users; 
}

使用@Many注解

java 复制代码
@Mapper
public interface DepartmentMapper {
    
    @Select("SELECT * FROM department WHERE id = #{id}")
    @Results({
        @Result(property = "id", column = "id"),
        @Result(property = "deptName", column = "dept_name"),
        @Result(property = "location", column = "location"),
        @Result(property = "users", column = "id",
                many = @Many(select = "com.example.mapper.UserMapper.getUsersByDeptId"))
    })
    Department getDeptWithUsers(Long id);
}

@Mapper
public interface UserMapper {
    
    @Select("SELECT * FROM user WHERE dept_id = #{deptId}")
    List<User> getUsersByDeptId(Long deptId);
}

多对多关联查询

多对多关系是最复杂的关联关系,通常需要一张中间表来维护关系。我们以用户和角色的关系为例:

sql 复制代码
-- 角色表
CREATE TABLE role (
    id BIGINT PRIMARY KEY,
    role_name VARCHAR(50),
    description VARCHAR(200)
);

-- 用户角色关联表
CREATE TABLE user_role (
    user_id BIGINT,
    role_id BIGINT,
    PRIMARY KEY (user_id, role_id)
);

对应的实体类:

java 复制代码
@Data
public class User {
    private Long id;
    private String name;
    private String email;
    private Long deptId;
    private Department department;
    private List<Role> roles; 
}

@Data
public class Role {
    private Long id;
    private String roleName;
    private String description;
    private List<User> users;
}

方式一:

java 复制代码
@Mapper
public interface UserMapper {
    
    @Select("SELECT * " +
            "FROM user u " +
            "LEFT JOIN user_role ur ON u.id = ur.user_id " +
            "LEFT JOIN role r ON ur.role_id = r.id " +
            "WHERE u.id = #{userId}")
    @Results({
        @Result(property = "id", column = "id"),
        @Result(property = "name", column = "name"),
        @Result(property = "email", column = "email"),
        @Result(property = "roles", column = "id",
                many = @Many(select = "com.example.mapper.RoleMapper.getRolesByUserId"))
    })
    User getUserWithRoles(Long userId);
}

@Mapper
public interface RoleMapper {
    
    @Select("SELECT r.* FROM role r " +
            "INNER JOIN user_role ur ON r.id = ur.role_id " +
            "WHERE ur.user_id = #{userId}")
    List<Role> getRolesByUserId(Long userId);
}

方式二:

java 复制代码
@Mapper
public interface RoleMapper {
    
    @Select("SELECT r.id, r.role_name, r.description " +
            "FROM role r WHERE r.id = #{roleId}")
    @Results({
        @Result(property = "id", column = "id"),
        @Result(property = "roleName", column = "role_name"),
        @Result(property = "description", column = "description"),
        @Result(property = "users", column = "id",
                many = @Many(select = "com.example.mapper.UserMapper.getUsersWithDeptByRoleId"))
    })
    Role getRoleWithUsersAndDept(Long roleId);
}

@Mapper 
public interface UserMapper {
    
    @Select("SELECT * "+
            "FROM user u " +
            "INNER JOIN user_role ur ON u.id = ur.user_id " +
            "LEFT JOIN department d ON u.dept_id = d.id " +
            "WHERE ur.role_id = #{roleId}")
    @Results({
        @Result(property = "id", column = "id"),
        @Result(property = "name", column = "name"),
        @Result(property = "email", column = "email"),
        @Result(property = "deptId", column = "dept_id"),
        @Result(property = "department.deptName", column = "dept_name"),
        @Result(property = "department.location", column = "location")
    })
    List<User> getUsersWithDeptByRoleId(Long roleId);
}

总结

MyBatis的注解方式为我们提供了灵活的联合查询解决方案。虽然在复杂查询方面可能不如XML配置那样强大,但对于大部分常见场景来说已经足够了。