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;
    }
}