Java 在使用SpringDataJPA时,如何避免分离对象或未初始化的Hibernate代理?

Java 在使用SpringDataJPA时,如何避免分离对象或未初始化的Hibernate代理?,java,spring-data-jpa,Java,Spring Data Jpa,我使用的是Spring数据JPA,分离的对象有问题。我想知道如何解决这个问题 我正在处理两个对象:请求和事件。 请求有一个生命周期,并且在其生命周期内被多次处理。 每次处理请求,都会生成一个事件,并将其存储在表中。因此,事件具有对请求的引用和基数多通 请求:-------------------------------------------------------------> 事件:-------------------------------------------------------

我使用的是
Spring数据JPA
,分离的对象有问题。我想知道如何解决这个问题

我正在处理两个对象:
请求
事件
。 请求有一个生命周期,并且在其生命周期内被多次处理。 每次处理
请求
,都会生成一个
事件
,并将其存储在表中。因此,
事件
具有对
请求的引用
和基数
多通

请求:------------------------------------------------------------->

事件:---------------------------------------------------------------------------------------------------------------------------------------------------------->

下面是
事件类的一部分

public class Event implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    @Getter @Setter
    private long id;
    @ManyToOne(cascade = {CascadeType.ALL})
    @JoinColumn(name = "REQUESTID")
    @Getter @Setter
    private Request request;
    ...
请求
如下所示:

public class Request implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    @Getter @Setter
    private long id;
    @Column(name = "AMOUNT")
    @Getter @Setter
    private BigInteger amount;

}
以下是关系的SQL表示形式:

CREATE TABLE IF NOT EXISTS REACTIVEENGINE.EVENT (
    id int AUTO_INCREMENT NOT NULL ,
    requestId int NOT NULL ,
    domain varchar(50) NOT NULL ,
    step varchar(150) NOT NULL ,
    result varchar(20) NOT NULL ,
    timelog timestamp(3) NOT NULL ,
    alertId int NULL ,
    primary key (id),
    CONSTRAINT FK_EVENT_REQUEST_ID foreign key (requestId) references REQUEST (id) ON DELETE CASCADE ,
    CONSTRAINT FK_EVENT_ALERT_ID foreign key (alertId) references ALERT (id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4;
当我想处理
请求时,我遇到了这个问题。下面是一个方法:

   executorService.scheduleAtFixedRate( () -> {

        Flux<Event> eventFlux = getEventFlux();
        eventFlux.subscribe( event -> {
            Request request = requestDao.getOne(event.getRequest().getId());
            Event fundingEvent = Event.builder()
                    .domain(Domain.FUNDING)
                    .step(FundingStep.FUNDABILITY_CHECK.name())
                    .request(request)
                    .result(Event.Status.SUCCESS)
                    .timelog(LocalDateTime.now())
                    .build();
            eventDao.save(fundingEvent);
        });

    }, 0, 30, TimeUnit.SECONDS);
问题是请求现在已分离:

reactor.core.Exceptions$ErrorCallbackNotImplemented: org.springframework.dao.InvalidDataAccessApiUsageException: uninitialized proxy passed to persist(); nested exception is org.hibernate.PersistentObjectException: uninitialized proxy passed to persist()
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: uninitialized proxy passed to persist(); nested exception is org.hibernate.PersistentObjectException: uninitialized proxy passed to persist()
我尝试了许多解决方案,将
级联。所有
添加到事件实体。
我还尝试将
@PersistenceContext
添加到我的类中,以便在将请求对象注入事件对象之前刷新/合并请求对象(但它不起作用,因为持久性上下文仍然为null)

你能提出什么解决方案


提前感谢。

您使用
getOne
的目的是什么?是否将事件与
请求
关联,而不从DB加载
请求

如果是这样,没有事务,这将无法工作。之所以出现异常,是因为
getOne
返回一个代理,并且由于没有周围的持久性上下文,代理会立即“分离”,无法初始化它(因为创建它的上下文不再存在)

解决方案是,只需使用原始的
event.getRequest()
,或者,如果出于某种奇怪的原因坚持“刷新”
Request
状态,则使用
findById
而不是
getOne


另一个解决方案(假设您使用的是JPA的默认提供程序,即Hibernate)是从
Event.request中删除
CascadeType.ALL
(因为它对
@ManyToOne
没有任何意义-您可以将其替换为级联选项的某些子集,但排除
合并
删除
),只需使用
新请求(event.getRequest().getId())
。对于此场景,原始
事件.getRequest()
也同样有效。

您使用
getOne
的意图是什么?是否将事件与
请求关联,而不从DB加载
请求

如果是这样,那么如果没有事务,这将无法工作。您将获得异常,因为
getOne
返回一个代理,并且由于没有周围的持久性上下文,代理将立即“分离”,无法初始化它(因为创建它的上下文不再存在)

解决方案是,只需使用原始的
event.getRequest()
,或者,如果出于某种奇怪的原因坚持“刷新”
Request
状态,则使用
findById
而不是
getOne


另一个解决方案(假设您使用的是JPA的默认提供程序,即Hibernate)是从
Event.request中删除
CascadeType.ALL
(因为它对
@ManyToOne
没有任何意义-您可以将其替换为级联选项的某些子集,但排除
合并
删除
),只需使用
新请求(event.getRequest().getId())
。对于此场景,原始
事件.getRequest()
也同样有效。

谢谢您的时间和帮助

getOne(…)
”是Spring Data JPA命名“
findById(id)
”方法的方式。开始时没有调用
requestDao.getOne(event.getRequest().getId());

在上一个
事件
对象中只有一个
请求
对象,我只想将它从上一个传递到下一个。
分离的
对象问题出现了,当时我只想使用从
事件a
中提取的相同
请求
对象注入
事件B

这就是为什么我测试从数据库中获取一个“新的”
请求,但是“
分离的
”问题已经被“
代理
”问题所取代,没有更多的持久性上下文。我尝试的
@Transactional
注释也没有解决问题

事实上,你对
级联
的看法是正确的,这在我的情况下是完全无用和没有意义的。我认为它可以帮助我节省一些时间和代码行,以避免N次调用
save
方法,每个对象一次。我想在同一个动作中完成这一切,这就是我使用
级联
工具的原因

我不清楚Hibernate上下文中“
Proxy
”的概念,但我知道“
requestDao.getOne(…)
”方法不足以创建持久持久性上下文,后者将由
事件的dao层使用

我还了解到,
级联
原则的使用绝对不适合我的情况。当必须同时创建两个对象时,
级联.PERSIST
是可以的。但在我的情况下,
请求
对象已经创建,只是被一个新的
事件
对象重复使用。但Hibernate不是能够持久化其中一个,只需合并/更新另一个

最后,我抑制了
Cascade
关键字a
reactor.core.Exceptions$ErrorCallbackNotImplemented: org.springframework.dao.InvalidDataAccessApiUsageException: uninitialized proxy passed to persist(); nested exception is org.hibernate.PersistentObjectException: uninitialized proxy passed to persist()
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: uninitialized proxy passed to persist(); nested exception is org.hibernate.PersistentObjectException: uninitialized proxy passed to persist()