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()
放在我怀疑的代码块后面;这样,就可以轻松找到引发真正异常的确切代码行。
我遇到了 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()
放在我怀疑的代码块后面;这样,就可以轻松找到引发真正异常的确切代码行。