假设我的课程A
和B
与A
到B
之间存在一对一的关联。
@Entity
class A {
@Id Long id;
@OneToOne(fetch = LAZY) B b;
// getters, setters
}
@Entity
class B {
@Id Long id;
}
使用Spring JPA,我有A
的存储库,如下所示
@Repository
public interface ARepository extends JpaRepository<A, Long> {
A findById(Long id);
void delete(Long id);
}
最后,一个REST控制器
@RestController
@RequestMapping("/a")
class AController {
@Autowired ARepository repo;
@GetMapping("{id}")
public A getA(@PathVariable Long id) {
return repo.findById(id);
}
@Transactional
@DeleteMapping("{id}")
public A deleteA(@PathVariable Long id) {
A a = repo.findById(id);
repo.delete(id);
return a;
}
}
假设我已将A
id = 1
的实例保存到数据库中,当我向/a/1
发送GET请求时,它返回A
的JSON表示没问题。
但是当我尝试通过向/a/1
发送DELETE请求来删除实例时,我得到了com.fasterxml.jackson.databind.JsonMappingException
以及根本原因
org.hibernate.LazyInitializationException:懒得初始化一个集合,无法初始化代理 - 没有Session
我的理解是,发生此错误是因为Jackson在完成Hibernate会话的生命周期结束的方法a.getB()
之后尝试通过调用deleteA()
来序列化实例,这是正确的吗? / p>
如果是这样,我不明白为什么在方法getA()
中没有出现此错误,我认为Hibernate会话应该在repo.findById(id);
完成后立即结束,对吗?
答案 0 :(得分:0)
问题很可能与杰克逊试图初始化A.b
序列化有关。
即使您启用open-in-view
,错误仍会存在,因为您正在尝试初始化不再存在的实体的字段。
如果A.b
确实应该延迟提取,请考虑使用@JsonIgnore
(另一方面,A.b
是否应序列化并与{{{}一起发送到响应正文中1}},没有必要让它懒洋洋地获取,只需使用默认的A
)。
答案 1 :(得分:0)
我不喜欢open-in-view
,因为我觉得这是谎言,你应该知道你是如何访问数据库的,并且在初始交易后没有意外访问。
出于同样的原因,我也不想将FetchType.EAGER
用于所有内容,您应该知道如何访问数据库。 FetchType.EAGER
可能导致您不期望加入。
这里可能的解决方案是在您的存储库中创建特定方法,并使用命名图注释它们。 E.g。
@Entity
@NamedEntityGraph(name = "A.fetchB",attributeNodes=@NamedAttributeNode("b"))
public class A implements Serializable {
private static final long serialVersionUID = 1L;
@Id Long id;
@OneToOne(fetch = FetchType.LAZY) B b;
// getters, setters
}
和
@Repository
public interface ARepository extends JpaRepository<A, Long> {
@EntityGraph(value="A.fetchB", type=EntityGraphType.FETCH)
A findAFetchBById(Long id);
}
然后
@RestController
@RequestMapping("/a")
public class AController {
@Autowired
private ARepository repo;
@GetMapping("{id}")
public A getA(@PathVariable Long id) {
A a = repo.findAFetchBById(id);
return a;
}
@Transactional
@DeleteMapping("{id}")
public A deleteA(@PathVariable Long id) {
A a = getA(id);
repo.delete(id);
return a;
}
}
这样您就知道自己在使用数据库做了些什么。这将导致单个优化查询,而不是在您访问Entity
的各个子项时执行的多个查询。