当在存储库查询中加载实体时,不完全使用规范和实体图,则缓存数据。但是只有当方法在REST端点上被调用时。当该方法被调用调度时,一切都很好,数据没有被缓存。
数据模型:
School
|_ id
|_ name
|_ SchoolClass[]
|______ id
|______ name@Entity
@NamedEntityGraphs(
value = [
NamedEntityGraph(
name = "School.classes",
attributeNodes = [
NamedAttributeNode(
"classes"
)
]
)
]
)
class School {
constructor(name: String) {
this.name = name
}
@Id
@GeneratedValue
var id: Long = 0
@Column(length = 30, nullable = false)
var name: String = ""
@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = false, mappedBy = "school")
var classes: MutableSet<SchoolClass> = mutableSetOf()
override fun toString(): String {
return "School(id=$id, name='$name', classes=${classes.joinToString { it.name }})"
}
}
interface SchoolRepository : JpaRepository<School, Long>, JpaSpecificationExecutor<School> {
@EntityGraph("School.classes")
override fun findAll(spec: Specification<School>?): List<School>
@Query(
"""
select s from School s where s.id in :ids
"""
)
@EntityGraph("School.classes")
fun findAllByIdWithClasses(@Param("ids") ids: Set<Long>): List<School>
}
object SchoolSpecifications {
fun name(name: String) =
Specification<School> { root, _, criteriaBuilder ->
criteriaBuilder.equal(root.get<String>("name"), name)
}
fun className(className: String) =
Specification<School> { root, _, criteriaBuilder ->
val classes = root.join<School, SchoolClass>("classes")
criteriaBuilder.equal(classes.get<String>("name"), className)
}
}@Entity
class SchoolClass {
constructor(name: String, school: School) {
this.name = name
this.school = school
}
@Id
@GeneratedValue
var id: Long = 0
@Column(length = 30, nullable = false)
var name: String = ""
@ManyToOne(optional = false)
var school: School? = null
override fun toString(): String {
return "SchoolClass(id=$id, name='$name', school=${school?.name})"
}
}
interface SchoolClassRepository : JpaRepository<SchoolClass, Long>@Service
class SchoolService
@Autowired
constructor(
private val schoolRepository: SchoolRepository,
private val schoolClassRepository: SchoolClassRepository
) {
fun initData() {
schoolClassRepository.deleteAll()
schoolRepository.deleteAll()
createSchool("Goethe-School", listOf("1A", "1B", "1C", "1D"))
createSchool("Schiller-School", listOf("5A", "6A", "5B", "6B"))
}
private fun createSchool(schoolName: String, classes: List<String>) {
val school = School(schoolName)
val savedSchool = schoolRepository.saveAndFlush(school)
classes.forEach {
val newClass = SchoolClass(it, savedSchool)
schoolClassRepository.saveAndFlush(newClass)
}
}
fun allFine(): List<School> {
return schoolRepository.findAll(SchoolSpecifications.name("Goethe-School"))
}
fun crazyStuff(): List<School> {
val schoolIds = schoolRepository.findAll(SchoolSpecifications.className("1C"))
.map { it.id }
.toSet()
return schoolRepository.findAllByIdWithClasses(schoolIds)
}
}有趣的部分是:
@RestController
class Controller {
@Autowired
lateinit var schoolService: SchoolService
@GetMapping("/")
fun query() {
printSchool()
}
@Scheduled(fixedDelay = 5000)
fun scheduled() {
printSchool()
}
private fun printSchool() {
val schools = schoolService.crazyStuff()
println("=crazy stuff= $schools")
val schools2 = schoolService.allFine()
println("=all fine= $schools2")
}
}业务逻辑是:用类'1C‘加载所有学校,提取id,然后用这些id的更新加载所有学校。
在REST-Controller中,输出是:
=crazy stuff= [School(id=1, name='Goethe-School', classes=1C)] =all fine= [School(id=1, name='Goethe-School', classes=1C)]
在计划的任务中,输出与预期的相同:
=crazy stuff= [School(id=1, name='Goethe-School', classes=1D, 1B, 1C, 1A)] =all fine= [School(id=1, name='Goethe-School', classes=1A, 1C, 1B, 1D)]
这种行为的原因是什么?以及如何使用规范查询和实体图禁用未满载实体的缓存?
发布于 2022-09-14 14:43:52
这肯定是由于事务范围的限制。
很难确定,但是由于您没有事务去标记,所以我将假设您使用的是视图反模式中的开放会话
这使得您在REST调用中使用所有发生在单个事务中的事情。因此,第一个加载的学校把它放在第一级缓存,它停留在那里,并在随后的调用返回。
在预定的调用中,您根本没有任何显式事务,OSIV也没有效果,因为这是绑定到不存在的web请求上的。因此,为每个存储库调用创建和关闭新事务,每次重新创建和销毁第一级缓存。因此,您实际上根本没有任何缓存。
https://stackoverflow.com/questions/73718342
复制相似问题