0%

Mybatis中的一对多关系,用collection。

一个学生表,Student,一个老师表,Teacher。一个老师下面有多个学生。学生表有一个字段 teacher_id。

学生表对应的类

1
2
3
4
5
6
7
8
9
10
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Student {

private Integer id;
private String name;
private Integer teacherId;
}

教师类

1
2
3
4
5
6
7
8
9
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {

private Integer id;
private Integer age;
}

我们要查出教师及其学生的列表。

教师及学生列表类

1
2
3
4
5
6
7
8
9
10
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class TeacherInfo {

private Integer id;
private String name;
private List<Student> students;
}

获取所以教师及学生列表信息的 DAO 层接口。

1
2
3
4
public interface TeacherDao {

List<TeacherInfo> findTeachers();
}

上述接口对应的 xml 文件的核心内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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="gy.finolo.dao.TeacherDao">

<resultMap id="teacherInfoMap" type="gy.finolo.model.TeacherInfo">
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="age" jdbcType="INTEGER" property="age"/>
<collection property="students" ofType="gy.finolo.model.Student">
<!-- <id column="student_id" property="id" /> -->
<result column="name" property="name" />
</collection>
</resultMap>

<select id="findTeachers" resultMap="teacherInfoMap">
SELECT t.id, t.age, s.name FROM t_teacher t INNER JOIN t_student s ON (t.id = s.teacher_id);
</select>

</mapper>

需要说明的有以下几点:

  1. 主表和副表的 id 标签都可以不要,这时只是属性里面没有 id 的值而已。我之前以为是以 id 来判断两条记录是否相同的,现在看来并不是。

  2. 如果需要获取学生id值,为了避免column字段重复,所以在SQL语句写别名,同时在xml也做相应修改。

1
<id column="student_id" property="id" />
1
2
3
<select id="findTeachers" resultMap="teacherInfoMap">
SELECT t.id, t.age, s.id student_id, s.name FROM t_teacher t INNER JOIN t_student s ON (t.id = s.teacher_id);
</select>

在返回给客户端前,通过 AOP 给返回的类做处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Aspect
@Component
public class ResponseAspect {

// 定义切点
@Pointcut("execution(public * gy.finolo..*.*(..))")
public void afterResponse() {
}

@AfterReturning(returning = "object", pointcut = "afterResponse()")
public void doAfterReturning(Object object) {
if (object instanceof Response) {
// logic
}
}
}

AOP 有如下5种通知类型,@Around, @Before, @After, @AfterReturning 和 @AfterThrowing。

  • 如果在通知内都未调用 proceedingJoinPoint.proceed(),那执行顺序为如下:

Around -> After -> AfterReturning (正常返回) | AfterThrowing (异常返回)

  • 如果在通知内部调用了 proceedingJoinPoint.proceed(),此句代码相当于是执行 Before(如果有的话) 和 目标对象的方法 逻辑。

Pointcut注解

Pointcut 表达式除了使用 execution 以外,还可以使用 annotation,比如:

定义切点

@Pointcut("@annotation(gy.finolo.anno.AopAnnotation)")

我们再创建一个注解

1
2
3
4
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AopAnnotation {
}

在方法上面只要是被 @AopAnnotation 注解了,那逻辑都会被切入。

在 java 中经常会使用到枚举类。类名可以使用 Enum 结尾。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@AllArgsConstructor
public enum UserTypeEnum {

USER_TYPE_TEACHER (1, "教师"),
USER_TYPE_STUDENT (2, "学生")
;

@Setter
@Getter
private Integer type;

@Setter
@Getter
private String description;
}

循环遍历枚举类的每一个值

1
2
3
4
for (UserTypeEnum userType : UserTypeEnum.values()) {
// userType.getType();
// userType.getDescription();
}