Hibernate +Spring Boot JPA 利用@Where或@Filter过滤软删的子对象

场景

一个老师(Teacher)有很多个学生(Student)和有很多课本(book),一个学生有很多选修课程(Course)。查询老师列表的时候会把老师名下的学生和课本罗列出来,但是不需要展示被删除掉或者状态不对的数据。Teacher和Student通过关键字关联,Teacher和Book通过中间关联表关联,Student和Course通过关键字关联。

数据

Teacher:

 uuid 
------
 t1
 t2
(2 rows)

Student:

 uuid | deleted |  status  | teacher 
------+---------+----------+---------
 s1   |         | active   | t1
 s2   | deleted | inactive | t1

Course:

 uuid | deleted |  status  | student 
------+---------+----------+---------
 c1   |         | active   | s1
 c2   | deleted | inactive | s1
(2 rows)

Book:

 uuid | deleted |  status  
------+---------+----------
 b1   |         | active
 b2   | deleted | inactive
(2 rows)

teacher_book

 teacher_id | book_id 
------------+---------
 t1         | b1
 t1         | b2
(2 rows)

@Where

利用@Where注解过滤掉软删的学生、书、课程。

Entity:

Teacher:

@Data
@Entity
@Table(name = "teacher")
@EqualsAndHashCode(of = {"id"})
public class Teacher {

	@Column(name = "uuid")
	@Id
	private String id;
	
	@ManyToMany(fetch = FetchType.LAZY)
	@JoinTable(name = "teacher_book", 
	joinColumns = { @JoinColumn(name = "teacher_id") }, 
	inverseJoinColumns = { @JoinColumn(name = "book_id") })
	@Where(clause = " deleted is null ")
	private Set<Book> books;
	
	@OneToMany(mappedBy = "teacher", fetch = FetchType.LAZY)
	@Where(clause = " deleted is null ")
	private Set<Student> students;
}

Student:

@Data
@Entity
@Table(name = "student")
@EqualsAndHashCode(of = {"id"})
public class Student {

	@Column(name = "uuid")
	@Id
	private String id;
	
	@Column(name = "deleted")
	private String deleted;
	
	@Column(name = "status")
	private String status;
	
	@ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "teacher")
	@JsonIgnore
	private Teacher teacher;
	
	@OneToMany(mappedBy = "student", fetch = FetchType.LAZY)
	@Where(clause = " deleted is null ")
	private Set<Course> courses;
}

Course:

@Data
@Entity
@Table(name = "course")
@EqualsAndHashCode(of = {"id"})
public class Course {

	@Column(name = "uuid")
	@Id
	private String id;
	
	@Column(name = "deleted")
	private String deleted;
	
	@Column(name = "status")
	private String status;

	@ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "student")
	@JsonIgnore
	private Student student;
}

Book:

@Data
@Entity
@Table(name = "book")
@EqualsAndHashCode(of = {"id"})
public class Book {

	@Column(name = "uuid")
	@Id
	private String id;
	
	@Column(name = "status")
	private String status;
	
	@Column(name = "deleted")
	private String deleted;
}

Repository

@Repository
public interface TeacherRepository extends CrudRepository<Teacher, String>{

	@Query("select t from Teacher t "
			+ "left join fetch t.students s "
			+ "left join fetch s.courses c "
			+ "left join fetch t.books b")
	public Set<Teacher> findTeachersU();
}

Service

@Service
public class TeacherService {

	@Autowired
	TeacherRepository teacherRepository;
	
	public Set<Teacher> findTeachers(){
		return teacherRepository.findTeachersU();
	}
}

Controller

@RestController
public class TeacherController {

	@Autowired
	TeacherService teacherService;
	
	@GetMapping("/teachers")
	public Set<Teacher> findTeachers() String status){
		return teacherService.findTeachers();
	}
}

测试

发送/teachers可得到以下结果:

[
    {
        "id": "t1",
        "books": [
            {
                "id": "b1",
                "status": "active",
                "deleted": null
            },
            {
                "id": "b2",
                "status": "inactive",
                "deleted": "deleted"
            }
        ],
        "students": [
            {
                "id": "s1",
                "deleted": null,
                "status": "active",
                "courses": [
                    {
                        "id": "c1",
                        "deleted": null,
                        "status": "active"
                    }
                ]
            }
        ]
    },
    {
        "id": "t2",
        "books": [],
        "students": []
    }
]

从结果可以看出软删掉的student和student名下的course已经被过滤掉了,而teacher名下的book并没有过滤掉。所以有中间表关联的子集使用@Where无法达到过滤的作用。

Hibernate: select teacher0_.uuid as uuid1_25_0_, students1_.uuid as uuid1_24_1_, courses2_.uuid as uuid1_5_2_, book4_.uuid as uuid1_3_3_, students1_.deleted as deleted2_24_1_, students1_.status as status3_24_1_, students1_.teacher as teacher4_24_1_, students1_.teacher as teacher4_24_0__, students1_.uuid as uuid1_24_0__, courses2_.deleted as deleted2_5_2_, courses2_.status as status3_5_2_, courses2_.student as student4_5_2_, courses2_.student as student4_5_1__, courses2_.uuid as uuid1_5_1__, book4_.deleted as deleted2_3_3_, book4_.status as status3_3_3_, books3_.teacher_id as teacher_1_26_2__, books3_.book_id as book_id2_26_2__ from teacher teacher0_ 
           left outer join student students1_ on teacher0_.uuid=students1_.teacher and (  students1_.deleted is null ) 
           left outer join course courses2_ on students1_.uuid=courses2_.student and (  courses2_.deleted is null )
           left outer join teacher_book books3_ on teacher0_.uuid=books3_.teacher_id
           left outer join book book4_ on books3_.book_id=book4_.uuid and (  book4_.deleted is null )

Hibernate: select book0_.uuid as uuid1_3_0_, book0_.deleted as deleted2_3_0_, book0_.status as status3_3_0_ from book book0_ where book0_.uuid=?

从sql语句中可以看到Hibernate将@Where的条件添加到了left join 的on条件而进行了过滤。

利用@Where进行的过滤是静态的,如果是变量的话就得利用@Filter了。

@Filter

利用@FilterDef定义变量的名称
@FilterDef(name = “过滤器的名字”, parameters = {@ParamDef(name=“sql中变量的名字”,type=“变量类型”)})

在需要过滤的字段添加@Filter进行过滤
@Filter(name = “过滤器的名字”, condition = " 过滤器中定义的变量=数据库表的字段 ")

Entity

Teacher

@Data
@Entity
@Table(name = "teacher")
@EqualsAndHashCode(of = {"id"})
public class Teacher {

	@Column(name = "uuid")
	@Id
	private String id;
	
	@ManyToMany(fetch = FetchType.LAZY)
	@JoinTable(name = "teacher_book", 
	joinColumns = { @JoinColumn(name = "teacher_id") }, 
	inverseJoinColumns = { @JoinColumn(name = "book_id") })
	@Filter(name = "BookStatusFilter", condition = " :status=status ")
	private Set<Book> books;
	
	@OneToMany(mappedBy = "teacher", fetch = FetchType.LAZY)
	@Filter(name = "StudentStatusFilter", condition = " :status=status ")
	private Set<Student> students;
}

Student:

@Data
@Entity
@Table(name = "student")
@EqualsAndHashCode(of = {"id"})
@FilterDef(name = "StudentStatusFilter", parameters = {@ParamDef(name="status",type="string")})
public class Student {

	@Column(name = "uuid")
	@Id
	private String id;
	
	@Column(name = "deleted")
	private String deleted;
	
	@Column(name = "status")
	private String status;
	
	@ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "teacher")
	@JsonIgnore
	private Teacher teacher;
	
	@OneToMany(mappedBy = "student", fetch = FetchType.LAZY)
	@Where(clause = " deleted is null ")
	@Filter(name = "CourseStatusFilter", condition = " :status=status ")
	private Set<Course> courses;
}

Course:

@Data
@Entity
@Table(name = "course")
@EqualsAndHashCode(of = {"id"})
@FilterDef(name = "CourseStatusFilter", parameters = {@ParamDef(name="status",type="string")})
public class Course {

	@Column(name = "uuid")
	@Id
	private String id;
	
	@Column(name = "deleted")
	private String deleted;
	
	@Column(name = "status")
	private String status;

	@ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "student")
	@JsonIgnore
	private Student student;
}

Book:

@Data
@Entity
@Table(name = "book")
@EqualsAndHashCode(of = {"id"})
@FilterDef(name = "BookStatusFilter", parameters = {@ParamDef(name="status",type="string")})
public class Book {

	@Column(name = "uuid")
	@Id
	private String id;
	
	@Column(name = "status")
	private String status;
	
	@Column(name = "deleted")
	private String deleted;
}

Repository

@Repository
public interface TeacherRepository extends CrudRepository<Teacher, String>{

	@Query("select t from Teacher t "
			+ "left join fetch t.students s "
			+ "left join fetch s.courses c "
			+ "left join fetch t.books b")
	public Set<Teacher> findTeachersU();
}

Service:
JPA中启用Filter

@Service
public class TeacherService {
	
	@Autowired
	@PersistenceContext()
	EntityManager entityManager;

	@Autowired
	TeacherRepository teacherRepository;
	
	public Set<Teacher> findTeachers(){
		return teacherRepository.findTeachersU();
	}
	
	public Set<Teacher> findTeachersByFilter(String status){
		Session session = entityManager.unwrap(Session.class);
		session.enableFilter("StudentStatusFilter").setParameter("status", status);
		session.enableFilter("CourseStatusFilter").setParameter("status", status);
		session.enableFilter("BookStatusFilter").setParameter("status", status);
		Set<Teacher> reSet = teacherRepository.findTeachersU();
		session.disableFilter("StudentStatusFilter");
		session.disableFilter("CourseStatusFilter");
		session.disableFilter("BookStatusFilter");
		return reSet;
	}
}

Controller

@RestController
public class TeacherController {

	@Autowired
	TeacherService teacherService;
	
	@GetMapping("/teachers")
	public Set<Teacher> findTeachers(@RequestParam(value = "status", required = false) String status){
		return teacherService.findTeachersByFilter(status);
	}
}

测试

利用/teachers?status=active动态传输过滤变量过滤。可以得到下列结果:

[
    {
        "id": "t1",
        "books": [
            {
                "id": "b1",
                "status": "active",
                "deleted": null
            },
            {
                "id": "b2",
                "status": "inactive",
                "deleted": "deleted"
            }
        ],
        "students": [
            {
                "id": "s1",
                "deleted": null,
                "status": "active",
                "courses": [
                    {
                        "id": "c1",
                        "deleted": null,
                        "status": "active"
                    }
                ]
            }
        ]
    },
    {
        "id": "t2",
        "books": [],
        "students": []
    }
]

可以看到status为inactive的student和course已经被过滤掉了,而inactive的book并没有被过滤掉。所以,与@Where一样,通过中间表关联的数据利用@Filter达不到过滤的效果。
查询的sql:

Hibernate: select teacher0_.uuid as uuid1_25_0_, students1_.uuid as uuid1_24_1_, courses2_.uuid as uuid1_5_2_, book4_.uuid as uuid1_3_3_, students1_.deleted as deleted2_24_1_, students1_.status as status3_24_1_, students1_.teacher as teacher4_24_1_, students1_.teacher as teacher4_24_0__, students1_.uuid as uuid1_24_0__, courses2_.deleted as deleted2_5_2_, courses2_.status as status3_5_2_, courses2_.student as student4_5_2_, courses2_.student as student4_5_1__, courses2_.uuid as uuid1_5_1__, book4_.deleted as deleted2_3_3_, book4_.status as status3_3_3_, books3_.teacher_id as teacher_1_26_2__, books3_.book_id as book_id2_26_2__ from teacher teacher0_ 
           left outer join student students1_ on teacher0_.uuid=students1_.teacher and  ?=students1_.status 
           left outer join course courses2_ on students1_.uuid=courses2_.student and  ?=courses2_.status  and (  courses2_.deleted is null ) 
           left outer join teacher_book books3_ on teacher0_.uuid=books3_.teacher_id 
           left outer join book book4_ on books3_.book_id=book4_.uuid and  ?=book4_.status

Hibernate: select book0_.uuid as uuid1_3_0_, book0_.deleted as deleted2_3_0_, book0_.status as status3_3_0_ from book book0_ where book0_.uuid=?

达到过滤功能的原理和@Where一样。

参考:
https://www.baeldung.com/hibernate-dynamic-mapping
https://stackoverflow.com/questions/49752429/using-a-hibernate-filter-with-spring-boot-jpa

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值