MySQL org.hibernate.AssertionFailure: 入口为空id(异常发生后不刷新Session)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10855542/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
org.hibernate.AssertionFailure: null id in entry (don't flush the Session after an exception occurs)
提问by Herzog
I have a hibernate and JSF2 application going to the deployment server and suddenly throwing an org.hibernate.AssertionFailure: null id in exception. I will provide the stack trace and code immediately but here are four important issues first:
我有一个 hibernate 和 JSF2 应用程序去部署服务器,突然抛出一个 org.hibernate.AssertionFailure: null id in exception。我将立即提供堆栈跟踪和代码,但首先有四个重要问题:
This happens only on the deployment server (Jboss & MySql running on Windows Sever 2008.) It does not happen on my development machine (Tomcat and MySql running on Windoes 7 Pro) and also not on the staging environment (Jboss and MySql running on Linux.)
Researching this, it seems that people get this error when trying to insert an object. But I get the error when I'm doing a simple query. (various different queries, actually, as the error pops up on several pages randomly.)
The error hits only every now and then. If I do a Jboss restart it goes away, but a time later returns. Also, it's not consistent, on some clicks it's there, on others it's not. Even when it hits, when I do a simple refresh of the page it returns fine.
I'm using c3p0 (config below)
这仅发生在部署服务器上(在 Windows Sever 2008 上运行的 Jboss 和 MySql。)它不会发生在我的开发机器上(在 Windoes 7 Pro 上运行的 Tomcat 和 MySql),也不会发生在登台环境(在 Linux 上运行的 Jboss 和 MySql) .)
对此进行研究,似乎人们在尝试插入对象时会遇到此错误。但是当我做一个简单的查询时我得到了错误。(实际上,各种不同的查询,因为错误会随机出现在几个页面上。)
错误只是偶尔出现。如果我重新启动 Jboss,它就会消失,但过一段时间又会回来。此外,它并不一致,在某些点击时它存在,而在其他点击时则不存在。即使它命中,当我对页面进行简单的刷新时,它也能正常返回。
我正在使用 c3p0(配置如下)
Any idea what's going on?
知道发生了什么吗?
The code details:
代码详情:
This happens on an address object. Here's the full hbm:
这发生在地址对象上。这是完整的 hbm:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.idex.auctions.model">
<class name="Address" table="address" lazy="true">
<id name="addressID" column="AddressID">
<generator class="native"/>
</id>
<property name="street" column="street"/>
<property name="city" column="city"/>
<property name="zip" column="zip"/>
<property name="state" column="state"/>
<property name="region" column="region"/>
<property name="country" column="country"/>
<many-to-one name="user"
class="com.idex.auctions.model.User"
column="userid"
unique="true"
cascade="save-update"/>
</class>
</hibernate-mapping>
The Java class is straight forward:
Java 类很简单:
public class Address implements Serializable {
private static final long serialVersionUID = 7485582614444496906L;
private long addressID;
private String street;
private String city;
private String zip;
private String state;
private String region;
private String country;
private User user;
public Address() {
}
public long getAddressID() {
return addressID;
}
public void setAddressID(long addressID) {
this.addressID = addressID;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
The c3p0 configuration:
c3p0 配置:
<property name="hibernate.c3p0.acquire_increment">1</property>
<property name="hibernate.c3p0.idle_test_period">1000</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.timeout">1800</property>
<property name="hibernate.c3p0.max_statements">0</property>
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
The versions used are
使用的版本是
hibernate3.jar
c3p0-0.9.1.2.jar
myfaces-api-2.1.4.jar
myfaces-impl-2.1.4.jar
mysql-connector-java-5.1.20-bin.jar
The full stacktrace
完整的堆栈跟踪
org.hibernate.AssertionFailure: null id in com.idex.auctions.model.Address entry
(don't flush the Session after an exception occurs)
org.hibernate.event.def.DefaultFlushEntityEventListener.checkId(
DefaultFlushEntityEventListener.java:78)
org.hibernate.event.def.DefaultFlushEntityEventListener.getValues(
DefaultFlushEntityEventListener.java:187)
org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(
DefaultFlushEntityEventListener.java:143)
org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(
AbstractFlushingEventListener.java:219)
org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(
AbstractFlushingEventListener.java:99)
org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(
DefaultAutoFlushEventListener.java:58)
org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:997)
org.hibernate.impl.SessionImpl.list(SessionImpl.java:1142)
org.hibernate.impl.QueryImpl.list(QueryImpl.java:102)
com.idex.auctions.manager.DatabaseManager.getAllObjects(DatabaseManager.java:464)
com.idex.auctions.ui.NavBean.gotoHome(NavBean.java:40)
sun.reflect.GeneratedMethodAccessor350.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
java.lang.reflect.Method.invoke(Unknown Source)
javax.el.BeanELResolver.invokeMethod(BeanELResolver.java:735)
javax.el.BeanELResolver.invoke(BeanELResolver.java:467)
javax.el.CompositeELResolver.invoke(CompositeELResolver.java:246)
org.apache.el.parser.AstValue.getValue(AstValue.java:159)
org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:189)
org.apache.myfaces.view.facelets.el.ContextAwareTagValueExpression.getValue(
ContextAwareTagValueExpression.java:96)
javax.faces.component._DeltaStateHelper.eval(_DeltaStateHelper.java:246)
javax.faces.component.UIOutcomeTarget.getOutcome(UIOutcomeTarget.java:50)
org.apache.myfaces.shared.renderkit.html.HtmlRendererUtils.getOutcomeTargetHref(
HtmlRendererUtils.java:1542)
org.apache.myfaces.shared.renderkit.html.HtmlLinkRendererBase.renderOutcomeLinkStart(
HtmlLinkRendererBase.java:908)
org.apache.myfaces.shared.renderkit.html.HtmlLinkRendererBase.encodeBegin(
HtmlLinkRendererBase.java:143)
javax.faces.component.UIComponentBase.encodeBegin(UIComponentBase.java:502)
javax.faces.component.UIComponent.encodeAll(UIComponent.java:744)
javax.faces.component.UIComponent.encodeAll(UIComponent.java:758)
javax.faces.component.UIComponent.encodeAll(UIComponent.java:758)
org.apache.myfaces.view.facelets.FaceletViewDeclarationLanguage.renderView(
FaceletViewDeclarationLanguage.java:1900)
org.apache.myfaces.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:285)
com.ocpsoft.pretty.faces.application.PrettyViewHandler.renderView(
PrettyViewHandler.java:163)
javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:59)
org.apache.myfaces.tomahawk.application.ResourceViewHandlerWrapper.renderView(
ResourceViewHandlerWrapper.java:93)
com.idex.auctions.ui.CustomViewHandler.renderView(CustomViewHandler.java:98)
org.apache.myfaces.lifecycle.RenderResponseExecutor.execute(RenderResponseExecutor.java:115)
org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:241)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:199)
com.ocpsoft.pretty.PrettyFilter.doFilter(PrettyFilter.java:126)
com.ocpsoft.pretty.PrettyFilter.doFilter(PrettyFilter.java:118)
回答by acdcjunior
The exception:
例外:
org.hibernate.AssertionFailure: null id in entry (don't flush the Session after an exception occurs)
org.hibernate.AssertionFailure: 入口为空id(异常发生后不刷新Session)
Tells us that that the session exception has happened beforethe point where this org.hibernate.AssertionFailure
is thrown.
告诉我们会话异常在org.hibernate.AssertionFailure
抛出this之前已经发生。
To be exact, the org.hibernate.AssertionFailure
is thrown when the session.flush()
is happening, not the point where the error ocurred.
确切地说,org.hibernate.AssertionFailure
是在session.flush()
发生时抛出的,而不是发生错误的点。
The above is a fact, thus a possible conclusion from it is: something could be suppressingthe original exception.
以上是事实,因此可能得出的结论是:某些东西可能会抑制原始异常。
So look for otherpossible points of error: A save()
or saveOrUpdate()
is possibly trying to persist an entity with a null
fieldwhere, in the table, the column is NOT NULL
?
因此,寻找其他可能的错误点: Asave()
或saveOrUpdate()
可能试图保留一个实体,其中的null
字段在表中,列是NOT NULL
?
TIP:To help in the debugging, try adding a session.flush()
after every interaction with the Session
object (e.g. session.save(obj)
, session.merge(obj)
, etc.), this will hopefully cause the org.hibernate.AssertionFailure
to happen earlier, closer to where the real problem is taking place. (Of course, after the debugging, remove those session.flush()
.)
提示:要在调试的帮助下,尝试添加一个session.flush()
与每一次交互后Session
的对象(例如session.save(obj)
,session.merge(obj)
等),希望这将导致org.hibernate.AssertionFailure
更早发生,更加接近其真正的问题正在发生。(当然,调试后,删除那些session.flush()
。)
In my case, the realexception was taking place inside a try/catch {}
block where the catch
suppressed the exception (didn't rethrow or warn me about it).
就我而言,真正的异常发生在抑制异常的try/catch {}
块内catch
(没有重新抛出或警告我)。
回答by Yves Martin
I would bet for a concurrency issue but it may occur at different levels:
我敢打赌并发问题,但它可能发生在不同的级别:
- a hibernate session may be shared between different users if the classical "open session in view" patternis not properly implemented
- an entity is shared between two user sessions because of improper hibernate cache settings
- a JDBC connection is shared between two different hibernate session (less likely)
- 如果未正确实现经典的“视图中打开会话”模式,则休眠会话可能会在不同用户之间共享
- 由于不正确的休眠缓存设置,实体在两个用户会话之间共享
- JDBC 连接在两个不同的休眠会话之间共享(不太可能)
Apart from these potential sources of troubles, I would remove c3p0 (maybe just rumors...) as your stack already provides DataSource
with connection pooling integrated with the transaction manager.
除了这些潜在的麻烦来源之外,我会删除 c3p0(也许只是谣言......),因为您的堆栈已经提供DataSource
了与事务管理器集成的连接池。
回答by Przemek Nowak
The @asdcjunior has answered correctly. Something has happened before the exception is thrown.
@asdcjunior 已正确回答。在抛出异常之前发生了一些事情。
In that kind of situations (it happens often on integration tests when you dealing with single transaction for one test - for example @Transaction annotation) I'm invoking the method:
在那种情况下(当您为一个测试处理单个事务时,它经常发生在集成测试中 - 例如 @Transaction 注释)我正在调用该方法:
session.clear()
It helps because all the 'dirty' objects are removed from current session so when the next flush is executed the problem does not appear.
它有帮助,因为所有“脏”对象都从当前会话中删除,因此在执行下一次刷新时不会出现问题。
Example flow:
示例流程:
- insert the assignment entity (many-to-many relation with constraint that could exist only single assignment) -> everything ok
- insert the same assignment entity one more time -> everything ok, controller in this case return some kind of bad request exception, under the hood Spring throws the IntegrityViolationException -> in test everything looks ok
- get the repository and execute findAll().size() to check the count of existed assigned to be sure that we have only single assignment -> the mentioned exception is thrown ;/ what happend? on the session exist still dirty object, normally the session would be destroyed (controller return error) but here we have the next assertions to check regarding database, so the solution here is additional session.clear() before next db related method executions
- 插入赋值实体(具有约束的多对多关系,只能存在单个赋值)-> 一切正常
- 再次插入相同的赋值实体 -> 一切正常,在这种情况下控制器返回某种错误的请求异常,在引擎盖下 Spring 抛出 IntegrityViolationException -> 在测试中一切看起来正常
- 获取存储库并执行 findAll().size() 以检查已存在分配的计数以确保我们只有单个分配-> 抛出了提到的异常;/发生了什么?在会话上仍然存在脏对象,通常会话会被销毁(控制器返回错误)但在这里我们有下一个断言要检查数据库,所以这里的解决方案是在下一个与数据库相关的方法执行之前附加 session.clear()
Example correct flow:
示例正确流程:
- insert the assignment entity
- insert the same assignment entity
- session.clear()
- get the repository and execute findAll().size()
- 插入赋值实体
- 插入相同的分配实体
- session.clear()
- 获取存储库并执行 findAll().size()
Hope it helps ;)
希望能帮助到你 ;)
回答by Shahid Hussain Abbasi
I was facing this issue I just add try catch block and in catch block I wrote seesion.clear(); now I can proceed with the rest of records to insert in database.
我遇到了这个问题,我只是添加了 try catch 块,在 catch 块中我写了 seesion.clear(); 现在我可以继续处理其余的记录以插入数据库。
回答by Old Pro
You are probably hitting some Hibernate bug. (I'd recommend upgrading to at least Hibernate 3.3.2.GA.)
您可能遇到了一些 Hibernate 错误。(我建议至少升级到 Hibernate 3.3.2.GA。)
Meanwhile, Hibernate does better when your ID is nullable so that Hibernate can always tell the difference between a new object that has not yet been persisted to the database and one that's already in the database. Changing the type of addressID
from long
to Long
will probably work around the problem.
同时,当您的 ID 可以为空时,Hibernate 会做得更好,这样 Hibernate 总是可以区分尚未持久化到数据库中的新对象和已经存在于数据库中的新对象。更改的类型addressID
从long
到Long
可能会解决此问题。
The stack trace you provided shows that you are seeing the problem on a query because your query is forcing buffered writes to be flushed to the database before the query is executed and that write is failing, probably with the same insert problem other people are seeing.
您提供的堆栈跟踪表明您在查询中看到了问题,因为您的查询在执行查询之前强制将缓冲写入刷新到数据库,并且写入失败,可能与其他人看到的插入问题相同。
回答by Gab
Problem flow :
问题流程:
- You create a new transient entity instance (here an Address instance)
- You persist it to the database (using save, merge or persist in hibernate Session / JPA EntityManager)
- As the entity identifier is generated by the database hibernate has to trigger the database insertion (it flushes the session) to retrieve the generated id
- The insert operation trigger an exception (or any pending unflushed change in the session)
- You catch the exception (without propagating it) and resume the execution flow (at this point your session still contains the unpersisted instance without the id, the problem is that hibernate seems to consider the instance as managed but the instance is corrupted as a managed object must have an id)
- you reach the end of your unit of work and the session is automatically flushed before the current transaction is committed, the flush fails with an assertion failure as the session contains a corrupted instance
- 您创建一个新的瞬态实体实例(这里是一个 Address 实例)
- 你将它持久化到数据库中(在休眠会话/JPA EntityManager 中使用保存、合并或持久化)
- 由于实体标识符是由数据库生成的,休眠必须触发数据库插入(它刷新会话)以检索生成的 id
- 插入操作触发异常(或会话中任何未决未刷新的更改)
- 您捕获异常(不传播它)并恢复执行流程(此时您的会话仍然包含没有 id 的非持久化实例,问题是 hibernate 似乎认为该实例是托管的,但该实例已损坏为托管对象必须有身)
- 您到达工作单元的末尾并且会话在提交当前事务之前自动刷新,刷新失败并出现断言失败,因为会话包含损坏的实例
You have many possible ways to mitigate this error :
您有许多可能的方法来减轻此错误:
- Simplest one and as hibernate stands "don't flush the Session after an exception occurs" ie. immediately give up and roll back the current transaction after a persistence exception.
- Manually evict (JPA : detach) the corrupted instance from the session after catching the error (at point 5, but if the error was triggered by another pending change instead of the entity insert itself, this will be useless)
- Don't let the database handle the id generation (use UUID or distributed id generation system, in this case the final flush will throw the real error preventing the persistence of the new instance instead of an hibernate assertion failure)
- 最简单的一个,作为休眠状态,“发生异常后不要刷新会话”,即。发生持久性异常后立即放弃并回滚当前事务。
- 捕获错误后从会话中手动驱逐(JPA:分离)损坏的实例(在第 5 点,但如果错误是由另一个挂起的更改而不是实体插入本身触发的,这将是无用的)
- 不要让数据库处理 id 生成(使用 UUID 或分布式 id 生成系统,在这种情况下,最终刷新将抛出真正的错误,阻止新实例的持久化,而不是休眠断言失败)
回答by Gray
org.hibernate.AssertionFailure: null id in entry (don't flush the Session after an exception occurs)
org.hibernate.AssertionFailure: 入口为空id(异常发生后不刷新Session)
This just happened to us and I thought I'd add some details for posterity. Turns out that we were trying to create an entity with a duplicate field that violated a condition:
这只是发生在我们身上,我想我会为后代添加一些细节。事实证明,我们试图创建一个实体,该实体具有违反条件的重复字段:
Caused by: org.hibernate.exception.ConstraintViolationException: Duplicate entry '' for key
'Index_schools_name'
This exception however was being masked because hibernate was trying to committhe session even though the create failed. When the created failed then the id was not set hence the assert error. In the stack trace we could see that hibernate was committing:
然而,这个异常被掩盖了,因为即使创建失败,hibernate 也试图提交会话。当创建失败时,则未设置 id,因此出现断言错误。在堆栈跟踪中,我们可以看到 hibernate 正在提交:
at org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit
(HibernateTransactionManager.java:480)
It should have been rolling back the session, not committing it. This turned out to be a problem with our rollback configuration. We are using the old XML configs and the exception path was incorrect:
它应该回滚会话,而不是提交它。结果证明这是我们回滚配置的问题。我们使用的是旧的 XML 配置,异常路径不正确:
<prop key="create*">PROPAGATION_REQUIRED,-org.x.y.LocalException</prop>
The LocalException
path was incorrect and hibernate didn't throw an error (or it was buried in the startup log spew). This would probably also be the case if you are using the annotations and don't specify the right exception(s):
该LocalException
路径是不正确的和Hibernate没有抛出一个错误(或者它被埋藏在启动日志析出)。如果您使用注释并且没有指定正确的异常,也可能是这种情况:
// NOTE: that the rollbackFor exception should match the throws (or be a subclass)
@Transactional(rollbackFor = LocalException.class)
public void create(Entity entity) throws AnotherException {
Once we fixed our hibernate wiring then we properly saw the "duplicate entry" exception and the session was properly being rolledback and closed.
一旦我们修复了我们的休眠接线,我们就会正确地看到“重复条目”异常,并且会话正确地被回滚和关闭。
One additional wrinkle to this was that when hibernate was throwing the AssertionFailure
, it was holding a transaction lock in MySQL that then had to be killed by hand. See: https://stackoverflow.com/a/39397836/179850
对此的另一个问题是,当 hibernate 抛出 时AssertionFailure
,它在 MySQL 中持有一个事务锁,然后必须手动终止。参见:https: //stackoverflow.com/a/39397836/179850
回答by Herzog
OK, I continued researching based among other things on other answers in this thread. But in the end, since we were up against a production deadline, I had to choose the emergency rout. So instead of figuring out hibernate I did these two things:
好的,我继续根据该线程中的其他答案进行研究。但最后,因为我们赶上了生产期限,我不得不选择紧急溃败。因此,我没有弄清楚 hibernate,而是做了以下两件事:
Removed a jQuery library I was using to grab focus on one of the forms. I did this because I read somewhere that this type of bug may happen due to a form posting a null value -- causing the null id down the line. I suspected the jQuery library may not sit well with PrimeFaces, and cause some form to malfunction. Just a hunch.
I killed the hibernate implemented relationship I had between user and address. (just one required, not one to many) and wrote the code myself when needed. Luckily it only affected one page significantly, so it wasn't much work.
删除了我用来集中关注其中一个表单的 jQuery 库。我这样做是因为我在某处读到这种类型的错误可能是由于表单发布空值而发生的 - 导致空 id 下降。我怀疑 jQuery 库可能不适合 PrimeFaces,并导致某些表单出现故障。只是一种预感。
我杀死了用户和地址之间的休眠实现关系。(只需要一个,而不是一对多)并在需要时自己编写代码。幸运的是,它只显着影响了一页,所以工作量不大。
The bottom line: we went live and the application has been running for several days without any errors. So this solution may not be pretty -- and I'm not proud of myself -- but I have a running app and a happy client.
最重要的是:我们上线了,应用程序已经运行了几天没有任何错误。所以这个解决方案可能并不漂亮——我并不为自己感到骄傲——但我有一个正在运行的应用程序和一个快乐的客户端。
回答by Wojciech Prusik
In my case the problem was the length parameter of an entity's field. When I tried to save an object with too long String value in one of its fields, I got the error. The solution was to set the proper value of parameter "length" in hibernate configuration.
在我的情况下,问题是实体字段的长度参数。当我尝试在其中一个字段中保存字符串值过长的对象时,出现错误。解决方案是在休眠配置中设置参数“长度”的正确值。
<property name="status" type="string" length="150" not-null="false" access="field"/>
It can also be done with annotation @Length like that:
也可以使用 @Length 注释来完成:
@Length(max=150)
private String status;
The hibernate exception's message was very misleading in my case, as was stacktrace. The fastest way to locate where the problem occures is to follow your code with debugger and evaluate session.flush();after every save()and saveOrUpdate()method.
在我的情况下,休眠异常的消息非常具有误导性,堆栈跟踪也是如此。定位问题发生位置的最快方法是使用调试器跟踪您的代码并评估session.flush(); 在每个save()和saveOrUpdate()方法之后。
回答by William
Changing the generator class:
更改生成器类:
<generator class="identity" />
to
到
<generator class="assigned" />