休眠,发生异常后不要刷新会话

时间:2019-07-29 20:21:10

标签: mysql hibernate spring-boot spring-data-jpa flush

我在Hibernate和JPA存储库实现方面遇到问题。

我收到有关以下内容的错误:

“发生异常后不要刷新会话”

这是从节开始的->保存模型之前,先检查数据库中是否存在。

消息表:

@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

}

用户表:

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

可能是问题所在-这是异常。会话仍处于打开状态。当然,我在使用事务性注释。

执行日志:

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)

2 个答案:

答案 0 :(得分:1)

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

您应该考虑查看会话维护策略。如果是通常的Web场景,会话绑定到请求线程,则修复方法会有所不同;与之相比,如果您使用一个独立的应用程序将会话保持打开状态数小时,则此修复方法将有所不同。

答案 1 :(得分:1)

从下面的问题代码中:

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

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

问题是,您试图以某种方式在异常之后刷新会话或提交事务。你不应该那样做。发生异常后,您应该丢弃该会话实例。

为什么在休眠异常后不Flush

工作单元是非常复杂的系统。它跟踪所有已加载实体的更改。 Hibernate所做的出色工作可将此保留在内存中实体副本与基础RDBMS保持一致。 Flush(或其他自动刷新方式)将使用内存更改来更新RDBMS。引发Hibernate异常时,不能保证此内存中状态不再处于一致状态。这就是为什么Hibernate建议关闭会话并从头开始的原因。

您提到的异常:

  

org.hibernate.AssertionFailure:com.test.app.model.Message条目中的ID为null(发生异常后请勿刷新Session)

这不是真正的例外。真正的例外发生在那之前。您正在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()放在我怀疑的代码块后面;这样,查找引发实际异常的确切代码行变得容易。

相关问题