Spring REST控制器和Hibernate会话生存期
假设我有一个类Spring REST控制器和Hibernate会话生存期,spring,hibernate,jpa,jackson,lazy-initialization,Spring,Hibernate,Jpa,Jackson,Lazy Initialization,假设我有一个类A和B,它们具有从A到B的一对一关联 @Entity class A { @Id Long id; @OneToOne(fetch = LAZY) B b; // getters, setters } @Entity class B { @Id Long id; } 使用SpringJPA,我有一个a的存储库,如下所示 @Repository public interface ARepository extends JpaRepository&l
A
和B
,它们具有从A
到B
的一对一关联
@Entity
class A {
@Id Long id;
@OneToOne(fetch = LAZY) B b;
// getters, setters
}
@Entity
class B {
@Id Long id;
}
使用SpringJPA,我有一个a
的存储库,如下所示
@Repository
public interface ARepository extends JpaRepository<A, Long> {
A findById(Long id);
void delete(Long id);
}
问题
假设我已将id=1的A
实例保存到数据库中,当我向/A/1
发送GET请求时,它会毫无问题地返回A
的JSON表示形式
但是当我试图通过向/a/1
发送删除请求来删除实例时,我得到了com.fasterxml.jackson.databind.JsonMappingException
,其根本原因如下
org.hibernate.LazyInitializationException:延迟初始化集合失败,无法初始化代理-无会话
问题:
我的理解是,发生此错误是因为Jackson在方法deleteA()
完成后,试图通过调用a.getB()
序列化实例,在该方法中,Hibernate会话的生存期已结束,是否正确
如果是这样,我不明白为什么在方法
getA()
中没有发生此错误,我认为Hibernate会话应该在repo.findById(id)完成后立即结束
,对吗?问题很可能与Jackson试图初始化A.b进行序列化有关
即使启用了“在视图中打开”,错误仍将持续,因为您试图初始化不再存在的实体的字段
如果<代码> A.B确实应该被懒洋洋地取出,请考虑使用<代码> @ JSONNEELION/COD>(如果另一方面,<代码> A.B.<代码>应被序列化并发送到响应体中,与<代码> A <代码>无关,使它懒惰地获取没有意义,只需使用默认<代码> FETCHYTYPE。p> 我不喜欢
在视图中打开
,因为我觉得这是一个谎言,你应该知道你是如何访问数据库的,并且在初始事务之后不会有意外的访问
我也不喜欢使用FetchType.EAGER
出于同样的原因,您应该知道如何访问数据库FetchType.EAGER
可能会导致您不期望的连接
这里一个可能的解决方案是在存储库中创建特定的方法,并用命名图对它们进行注释。例如
@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
}
及
这样,您就知道自己在数据库中做什么了。这将导致一个优化的查询,而不是在访问
实体
的各个子实体时执行的多个查询。您是否正在为控制器使用spring boot?我不明白为什么在方法getA()
中不会出现此错误,(如果您正在使用spring boot),您会发现相同的问题。对于第一个问题,恕我直言,在deleteA(Long)
方法中不应该有lazyiinitializationexception
,因为Session
将用于获取与getA()
方法类似的B
数据。@O.Badr,谢谢您的回答。我用弹簧靴。我按照您建议的链接,尝试将视图中的打开设置为false,然后方法getA()
变为给出错误。但是当我将openinview
设置为true时,getA()
不会给出错误,但是deleteA()
会给出错误。我尝试过删除repo.delete(id)代码>从deleteA()
开始工作。所以我怀疑这可能与repo.delete(id)有关代码>方法?我已经测试了您的代码,deleteA()
毫无例外地工作,您使用的是哪个spring启动版本?尝试在deleteA()
mehod中不返回a
对象(而是使用和HttpStatus
返回,例如ResponseEntity.ok()
)?
@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;
}
}