Mybatis 一对多关联注解

时间:2025-01-27 08:47:38

文章目录

      • 两表关联(Student、Classes)
      • 三表关联查询(User、Role、User_Role)
        • 过程问题记录

最近学习mybatis,记录下一对多关联注解。

两表关联(Student、Classes)

这里用班级(Classes)和学生(Student)一对多关联,配置如下:

Student
Classes
Student0
Student1
Student2
...

public class Classes {
	public int id;
	public String name;
	public List<Student> studentList;
	//getter/setter
	}

public class Student {
	private int id;
	private String name;
	private Classes classes;
	//getter/setter
	}

public interface ClassesMapper {
	
	@Insert("insert into classes (name) values (#{name})")
	@Options(useGeneratedKeys=true,keyProperty="id")
	public void insertClasses(Classes classes);
	
	@Select("select * from classes where id=#{id}")
	@Results({
		@Result(id=true,property="id",column="id"),
		@Result(property="name",column="name"),
		@Result(property="studentList",column="id",javaType=,
		many=@Many(select=""))
	})
	public Classes selectClasses(int id);
	
	@Delete("delete from classes where id=#{id}")
	public void deleteClasses(int id);
	
}

public interface StudentMapper {
	
	@Insert("insert into student (name,classes_id) values(#{name},#{})")
	@Options(useGeneratedKeys=true,keyProperty="id")
	public void insertStudent(Student student);
	
	@Select("select * from student where id=#{id}")
	@Results({
		@Result(id=true,property="id",column="id"),
		@Result(property="name",column="name"),
		@Result(property="classes",column="classes_id",javaType=,
		one=@One(select=""))
	})
	public Student selectStudent(int id);
	
	@Select("select * from student where classes_id=#{classId}")
	@Results({
		@Result(id=true,property="id",column="id"),
		@Result(property="name",column="name"),
		@Result(property="classes",column="classes_id",javaType=,
		one=@One(select=""))
	})
	public List<Student> selectStudentByClass(int classId);
	
	@Delete("delete from student where id=#{id}")
	public void deleteStudent(int id);
}

这里StudentMapper中,Student中包含classes对象,是多端,通过

		@Result(property="classes",column="classes_id",javaType=,
		one=@One(select="")

把student与classes联系起来,这样查询student时,根据classes_id就可以加上classes查询并加入到student中

在ClassesMapper中查询studentList时,引用了StudentMapper中通过班级Id查询Student的select语句

@Result(property="studentList",column="id",javaType=,
		many=@Many(select="")

其中many语句需要在StudentMapper定义一个根据班级id查学生的select语句,上面的Result的many中column必须是班级表的id,把班级id传给select语句作为参数。

三表关联查询(User、Role、User_Role)

使用MybatisPlus直接生成三个表的bean跟Mapper,用户表、角色表跟用户角色关联表:

@Getter
@Setter
@TableName("t_user")
public class TUser implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "user_id", type = IdType.AUTO)
    private Integer userId;

    private String userName;

    private String password;

    private LocalDateTime createTime;

    private Short status;
}

@Getter
@Setter
@TableName("t_role")
public class TRole implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "role_id", type = IdType.AUTO)
    private Integer roleId;

    private String roleName;

    private String roleDesc;

    private Short status;
}

@Getter
@Setter
@TableName("t_user_role")
public class TUserRole implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "user_role_id", type = IdType.AUTO)
    private Integer userRoleId;

    private Integer userId;

    private Integer roleId;
}

修改过程如下:

  • 给User对象增加List属性,需要添加注解@TableField(exist = false),mybatiPlus自动生成的Mapper类,在使用其方法进行查询时会动态拼接bean属性到sql中,导致查询时表找不到roles字段报错
    @TableField(exist = false)
    private List<TRole> roles;
### Error querying database.  Cause: java.sql.SQLSyntaxErrorException: Unknown column 'roles' in 'field list'
### The error may exist in com/user/dao/TUserMapper.java (best guess)
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: SELECT user_id,user_name,password,create_time,status,roles FROM t_user WHERE user_id=?
### Cause: java.sql.SQLSyntaxErrorException: Unknown column 'roles' in 'field list'
; bad SQL grammar []] with root cause

java.sql.SQLSyntaxErrorException: Unknown column 'roles' in 'field list'
  • Mapper修改
  1. 增加通过username查询User的语句:selectByUserName
  2. 增加使用userId作为参数,通过Role表跟UserRole表关联查询到Role的语句:selectRolesByUserId
  3. 增加ResultMap定义,其他Bean属性正常映射表字段,roles属性的column配置为user_id,many指向第2步定义的语句,代表使用user_id作为参数调用selectRolesByUserId
public interface TUserMapper extends BaseMapper<TUser> {
    @Select("")
    @Results(id = "UserResultMap",
            value = {
                    @Result(property = "userId", column = "user_id"),
                    @Result(property = "userName", column = "user_name"),
                    @Result(property = "password", column = "password"),
                    @Result(property = "createTime", column = "create_time"),
                    @Result(property = "status", column = "status"),
                    @Result(property = "roles", column = "user_id", many = @Many(select = "selectRolesByUserId"))
            }
    )
    TUser resultMap();

    @Select({"select u.* from t_user u",
            "where user_name = #{userName}"})
    @ResultMap("")
    TUser selectByUserName(String userName);

    @Select({"SELECT r.* FROM t_role r ",
            "RIGHT JOIN t_user_role ur ON r.role_id = ur.role_id",
            "WHERE ur.user_id = #{userId}"})
    @ResultMap("")
    List<TRole> selectRolesByUserId(Long userId);
}
过程问题记录
  1. 一开始想通过直接关联三个表查询出一个User对象,roles属性想通过role_id来查询对应的Role信息,但是这样查出来的数据会有多条,mybatis会报错说查出了多条数据,与预期的selectOne()不符合,经过反复折腾,后面脑子转过来了,user只查一个User表,再通过userId去关联查询Role信息,下面是一开始的错误修改:
public interface TUserMapper extends BaseMapper<TUser> {

    @Select({"select u.*,r.role_id from t_user u",
            "left join t_user_role ur on u.user_id = ur.user_id",
            "left join t_role r on r.role_id = ur.role_id",
            "where user_name = #{userName}"})
    @Results({
            @Result(property = "userId", column = "user_id"),
            @Result(property = "userName", column = "user_name"),
            @Result(property = "password", column = "password"),
            @Result(property = "createTime", column = "create_time"),
            @Result(property = "status", column = "status"),
            @Result(property = "roles", column = "role_id", javaType = List.class, many = @Many(select = "selectRoleById"))
    }
    )
    public TUser selectByUserName(String userName);

    @Select("select * from t_role where role_id = ${roleId}")
    @ResultMap("")
    public TRole selectRoleById(Integer roleId);
}
  1. 由于MybatisPlus是动态映射生成ResultMap,没有默认的ResultMap可以直接引用,所以在增加Role表相关查询时,需要能自己定义ResultMap,这个时候又想着复用它,于是修改RoleMapper类,在其中定义了一个@Results,给它加上id,@Results又必须定义在方法上,一开始随便定义了个方法 void resultMap(),测试查询时说找不到ResultObject的构造方法,debug后才发现方法的返回值不能定义为void,需要指定一个具体类,这里才修改为TRole这个类:
Caused by: org.apache.ibatis.executor.ExecutorException: No constructor found in void matching [java.lang.Integer, java.lang.String, java.lang.String, java.lang.Integer]

下面是一开始的错误修改,返回的是void,不是TRole类:

@Mapper
public interface TRoleMapper extends BaseMapper<TRole> {

    @Select("")
    @Results(id = "TRoleResultMap", value = {
            @Result(property = "roleId", column = "role_id"),
            @Result(property = "roleName", column = "role_name"),
            @Result(property = "roleDesc", column = "role_desc"),
            @Result(property = "status", column = "status")
    })
    void resultMap();
}
  1. 还是Results问题,一开始定义的@Results的方法上没有使用@Select(“”),导致@Results没有被扫描解析,引用时找不到resultMap,下面是一开始的错误修改:
```java
@Mapper
public interface TRoleMapper extends BaseMapper<TRole> {

    @Results(id = "TRoleResultMap", value = {
            @Result(property = "roleId", column = "role_id"),
            @Result(property = "roleName", column = "role_name"),
            @Result(property = "roleDesc", column = "role_desc"),
            @Result(property = "status", column = "status")
    })
    TRole resultMap();
}