Hibernate, don't flush the Session after an exception occurs 问题

Hibernate, don't flush the Session after an exception occurs problem

我遇到了 Hibernate 和 JPA 存储库实现的问题。

我收到以下错误:

"don't flush the Session after an exception occurs"

它来自部分,当 -> 在我保存模型之前,我检查是否存在于数据库中。

留言table:

@Entity
@Table(name="message")
public class Message {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "type")
    private MessageType type;

    @Column(name = "date")
    private Timestamp date;

    @Column(name = "message")
    private String message;

    @ManyToOne(cascade= {CascadeType.PERSIST, CascadeType.MERGE,
            CascadeType.DETACH, CascadeType.REFRESH})
    @JoinColumn(name="user_id")
    private User User;

    //constructor, empty constructor, getter & setter

}

用户table:

@Entity
@Table(name = "user")
public class User {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "city")
    private String city;

    @Column(name = "name")
    private String name;

    @OneToMany(mappedBy = "user",
               cascade = {CascadeType.PERSIST, CascadeType.MERGE,
                        CascadeType.DETACH, CascadeType.REFRESH})
    private List<Message> message;

    //constructor, empty constructor, getter & setter

}

服务:

@Service
@Transactional
public class MessageUserDataService implements MessageUserService {


public void saveToDatabase(String data, String type, String message) {
    long userId;
    if (data.containsKey(userCode)) {
        userId = findUserId(userCode);
    } 
    MessageData messageData = new MessageData();
    User user = UserSystemService.findByUserId(id);
    messageData.setUser(user);
    messageData.setType(type);
    messageData.setMessage(message);

    messageDataService.save(messageData);
}

public long findUserId(Long id) {
    try {
        User user = UserSystemService.findByUserId(id);
        return user.getId();
    } catch (Exception e) {
        log("findUserId->id: " + id);
        throw e;
    }
}

可能这就是问题所在 - 它是异常。 session 仍然打开。当然,我在服务中有事务注释。

以及执行日志:

29.07.2019 21:43:12org.hibernate.AssertionFailure: null id in com.test.app.model.Message entry (don't flush the Session after an exception occurs)
29.07.2019 21:43:12 at org.hibernate.event.internal.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:60)
29.07.2019 21:43:12 at org.hibernate.event.internal.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:175)
29.07.2019 21:43:12 at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:135)
29.07.2019 21:43:12 at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:216)
29.07.2019 21:43:12 at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:85)
29.07.2019 21:43:12 at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:44)
29.07.2019 21:43:12 at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1398)
29.07.2019 21:43:12 at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1483)
29.07.2019 21:43:12 at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1445)
29.07.2019 21:43:12 at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1414)
29.07.2019 21:43:12 at org.hibernate.query.internal.AbstractProducedQuery.getSingleResult(AbstractProducedQuery.java:1463)
29.07.2019 21:43:12 at org.springframework.data.jpa.repository.query.JpaQueryExecution$SingleEntityExecution.doExecute(JpaQueryExecution.java:214)
29.07.2019 21:43:12 at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:91)
29.07.2019 21:43:12 at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:136)
29.07.2019 21:43:12 at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:125)
29.07.2019 21:43:12 at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:590)
29.07.2019 21:43:12 at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:578)
29.07.2019 21:43:12 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
29.07.2019 21:43:12 at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59)
29.07.2019 21:43:12 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
29.07.2019 21:43:12 at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
29.07.2019 21:43:12 at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
29.07.2019 21:43:12 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
29.07.2019 21:43:12 at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
29.07.2019 21:43:12 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
29.07.2019 21:43:12 at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:135)
29.07.2019 21:43:12 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
29.07.2019 21:43:12 at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
29.07.2019 21:43:12 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
29.07.2019 21:43:12 at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
29.07.2019 21:43:12 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
29.07.2019 21:43:12 at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
29.07.2019 21:43:12 at com.test.app.model.service.UserService.findUserId(UserService.java:14)

很可能在调用 findUserId() 方法之前,您在其他地方用 catch 块抑制了异常。当您调用 findUserId() 时会报告该错误,因为它是 Hibernate 决定在 com.test.app.model.Message 实体上执行 auto-flush 的后续调用。

您应该考虑检查您的会话维护策略。如果是会话绑定到请求线程的常见 Web 场景,则修复会有所不同,而当您有一个独立应用程序时,会话会保持打开数小时。

来自您问题中的以下代码:

public long findUserId(Long id) {
    try {
        User user = UserSystemService.findByUserId(id);
        return user.getId();
    } catch (Exception e) {
        log("findUserId->id: " + id);
        throw e;
    }
}

您正在从数据库中获取记录并将其保存在实体中。然后,在 catch 块中,您记录错误并重新抛出异常。目前很好。但是,您正在刷新会话或在某处提交事务。您没有在问题中详细提及您的工作单元策略。

问题在于,您正试图刷新会话或在异常后提交事务。你不应该那样做。异常后,您应该丢弃会话实例。

Hibernate 异常后为什么不Flush

Unit Of Work 是一个非常复杂的系统。它跟踪所有加载实体的变化。 Hibernate 在 内存中 实体副本与底层 RDBMS 保持一致方面做得很好。 Flush(或其他自动刷新方式)将使用内存中的更改更新 RDBMS。当抛出 Hibernate 异常时,不再保证此内存中状态处于一致状态。这就是 Hibernate 建议关闭会话并从头开始的原因。

您在问题中提到的异常:

org.hibernate.AssertionFailure: null id in com.test.app.model.Message entry (don't flush the Session after an exception occurs)

这不是真正的例外。真正的例外发生在早些时候。您正在 catch 块中记录异常。如果发生异常,您的 catch 块应该关闭会话——如果未提交,它也会回滚事务。

如果出现异常,您应该关闭会话。由于您的代码中关于会话如何实现的信息不多,以下仅作为示例:

catch (Exception e) {
        log("findUserId->id: " + id);
        session.close();//<--close the session. This will also rollback the transaction if not committed
        throw e;
    }

但是,正如我上面所说,这不是真正的解决方案。您的代码的其他部分某处有错误。您必须诊断并修复它。我通常把 session.flush() 放在我怀疑的代码块后面;这样,就可以轻松找到引发真正异常的确切代码行。