简体   繁体   中英

org.hibernate.AssertionFailure: collection was processed twice by flush()

I have a problem. I get the following Exception:

ERROR [org.hibernate.AssertionFailure] (http-0.0.0.0-80-10) an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
org.hibernate.AssertionFailure: collection was processed twice by flush()
    at org.hibernate.engine.Collections.prepareCollectionForUpdate(Collections.java:225)
    at org.hibernate.engine.Collections.processReachableCollection(Collections.java:208)
    at org.hibernate.event.def.FlushVisitor.processCollection(FlushVisitor.java:60)
    at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:124)
    at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:84)
    at org.hibernate.event.def.AbstractVisitor.processEntityPropertyValues(AbstractVisitor.java:78)
    at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:161)
    at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:219)
    at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:99)
    at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:58)
    at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:998)
    at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1143)
    at org.hibernate.impl.QueryImpl.list(QueryImpl.java:102)
    at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:94)
    at mypackage.findAll(DAOjava:36)

After this Exception Hibernate is deleting records on association tables in my Database. I can't understand why this happens? It's only execution of select statement. On Stacktrace I see AutoFlush. Maybe this is causing the problem. The biggest problem is that I can't reproduce it constantly. Error comes from time to time.

We use EntityManager in Application. With findAll() method we get separate List of Entities that have relations over the association tables.

I have a Richfaces suggestionbox on one tab. I can add and remove elements from this list.

<rich:tab label="${messages['tab.chiled']}" name="childTab"
        rendered="#{parentsController.selectedParent.ParentStatus == 'READY'}" switchType="ajax" 
        actionListener="#{parentController.initParentSelections()}">
         <s:decorate template="tabchildinfo.xhtml">
            <ui:param name="selection" value="#{parentController.childeSelection}" />
            <ui:param name="allowEdit" value="#{s:hasRole('assignChildesToParent')}" />
        </s:decorate>
    </rich:tab>

Tab code for suggestionbox.

     <h:panelGrid columns="2">

       <a4j:commandButton value="" alt="" styleClass="refreshButton rightSpacing" action="#{parentsController.refreshChildSelection()}" reRender="#{idPrefix}addElement, #{idPrefix}AssignedElementsTable"/>

        <s:decorate id="#{idPrefix}addElement" styleClass="selectionAddElement"
            template="/WEB-INF/templates/edit.xhtml" rendered="#{allowEdit}">

            <ui:param name="float" value="true" />
            <ui:define name="label">#{addLabel}:</ui:define>
            <h:inputText value="#{selection.addElementText}"
                id="#{idPrefix}addElementText"
                onclick="#{rich:component(idPrefix.concat('addElementTextSuggest'))}.callSuggestion(true);"
                styleClass="selectionAddText"
                alt="#{messages['selection.add.empty']}" />
            <rich:suggestionbox id="#{idPrefix}addElementTextSuggest"
                suggestionAction="#{selection.suggest}" var="element"
                for="#{idPrefix}addElementText" minChars="0"
                nothingLabel="#{messages['selection.noResult']}" width="600">

                <h:column width="80px">
                    <f:facet name="header">
                        <h:outputText value="${messages['child.product']}" />
                    </f:facet>
                    <h:outputText value="#{element.product.id}"
                        title="#{element.product.id}" />
                </h:column>
                <h:column width="200px">
                    <f:facet name="header">
                        <h:outputText value="${messages['child.version']}" />
                    </f:facet>
                    <h:outputText value="#{element.productVersion}"
                        title="#{element.productVersion}" />
                </h:column>
                <h:column rendered="#{selection.suggestSearchCounter > 0}">
                    <f:facet name="header">
                        <a4j:commandButton value="${messages['button.addAll']}" onclick="parentEntityChanged();"
                            action="#{selection.addSuggestSearchElements()}"
                            reRender="#{idPrefix}AssignedElementsTable,#{idPrefix}addElement" />
                    </f:facet>
                </h:column>

                <a4j:support event="onselect"
                    action="#{selection.addElement(element)}"
                    onsubmit="parentEntityChanged();"
                    reRender="#{idPrefix}AssignedElementsTable,#{idPrefix}addElement"
                    oncomplete="#{rich:element(idPrefix.concat('addElementText'))}.focus();" />
            </rich:suggestionbox>
        </s:decorate>
    </h:panelGrid> 

    <rich:dataTable value="#{selection.assignedElements}" var="element" id="#{idPrefix}AssignedElementsTable"
        reRender="#{idPrefix}AssignedElementsTableScroller" styleClass="topSpacing">

            <rich:column width="100px" sortExpression="#{element.product.id}">
                <f:facet name="header">
                    <h:outputText value="${messages['child.product']}" />
                </f:facet>
                <h:outputText value="#{element.product.id}" title="#{element.product.id}" />
            </rich:column>
            <rich:column  width="300px" sortExpression="#{child.version}">
                <f:facet name="header">
                    <h:outputText value="${messages['child.version']}" />
                </f:facet>
                <h:outputText value="#{element.productVersion}" title="#{element.productVersion}" />
            </rich:column>

        <rich:column rendered="#{allowEdit}">
            <a4j:commandLink action="#{selection.removeElement(element)}" title="${messages['button.remove']}"
                reRender="#{idPrefix}AssignedElementsTable" onclick="parentEntityChanged();"> 
                <img src="/root/resources/img/icons/Delete/Delete_16x16.gif"
                    alt="${messages['button.remove']}" />
            </a4j:commandLink>
        </rich:column>

        <f:facet name="footer">
            <rich:datascroller align="center" for="#{idPrefix}AssignedElementsTable" maxPages="20"
                id="#{idPrefix}AssignedElementsTableScroller" renderIfSinglePage="false" />
        </f:facet>
    </rich:dataTable>

I have a method (initChildSelections()) in my controller class. It select all children for logged user.

Java Code Controller:

 @Name("parentsController") @Scope(ScopeType.PAGE) @Restrict("#{s:hasRole('viewParents')}") @Synchronized(timeout = 10000L) public class ParentsController { private final Logger log = LoggerFactory.getLogger(ParentsController.class); @Out(required = false) private Parent selectedParent; @In private EntityManager entityManager; // NOSONAR @In private AuditLogEntryDao auditLogEntryDao; @In private ParentDao parentDao; private TableFilter<Parent> filter = new TableFilter<Parent>(); private boolean entityChanged; private boolean loadFullHistory = false; private ChildSelection childSelection; private List<Child> allChilds; private ParentData editParentData; ... @Create public void init() { parents = parentDao.findVisibleForCurrentUser(); filter.putRenderer("name", new TableRenderer<Parent>() { private ViewHelper viewHelper = ParentsController.this.viewHelper; @Override public String render(Parent parent) { return viewHelper.getName1(parent); } }); filter.putRenderer("country", new TableRenderer<Parent>() { private ViewHelper viewHelper = ParentsController.this.viewHelper; @Override public String render(Parent parent) { return viewHelper.getCountry(parent); } }); filter.putRenderer("salesOrg", new TableRenderer<Parent>() { private ViewHelper viewHelper = ParentsController.this.viewHelper; @Override public String render(Parent parent) { return viewHelper.getSalesOrganisationId(Parent) + " (" + viewHelper.getSalesOrganisationName(Parent) + ")"; } }); if ("edit".equals(paramMode)) { for (Parent parent : parents) { if (parent.getSID().equals(paramSid)) { edit(parent, paramTab); } } } } public void edit(Parent selectedParent) { edit(selectedParent, "detailsTab"); } public void edit(Parent selectedParent, String selectedTab) { log.info("Parent selected: {}", selectedParent.getId()); salesOrganisations = salesOrganisationDao.findAll(); salesOffices = salesOfficeDao.findAll(); entityManager.refresh(selectedParent); setSelectedTab(selectedTab); this.selectedParent = selectedParent; setEditData(this.selectedParent); entityChanged = false; childSelection = null; overwriteDateThreeMonths = DateUtils.addMonths(new Date(), 3); // NOSONAR overwriteDateCurrent = getSelectedParent().getManualContractOverwriteExpiresAt(); responseXmlTree = viewHelper.getXmlRichTree(selectedParent.getParentXML().getResponseXML()); requestXmlTree = viewHelper.getXmlRichTree(selectedParent.getParentXML().getRequestXML()); refreshAuditLogEntries(); } private ParentData setEditData(Parent selectedParent) { if (editParentData == null) { editParentData = new ParentData(); } // Info Parent if (selectedParent.getOverwriteParent() != null) { editParentData.setName1(selectedParent.getOverwriteParent().getName1()); editParentData.setName2(selectedParent.getOverwriteParent().getName2()); editParentData.setName3(selectedParent.getOverwriteParent().getName3()); editParentData.setName4(selectedParent.getOverwriteParent().getName4()); editParentData.setStras(selectedParent.getOverwriteParent().getStras()); editParentData.setPstlz(selectedParent.getOverwriteParent().getPstlz()); editParentData.setOrt01(selectedParent.getOverwriteParent().getOrt01()); editParentData.setLand1(selectedParent.getOverwriteParent().getLand1()); editParentData.setRemarks(selectedParent.getOverwriteParent().getRemarks()); editParentData.setSalesOffice(selectedParent.getOverwriteParent().getSalesOffice()); editParentData.setSalesOrganisation(selectedParent.getOverwriteParent().getSalesOrganisation()); } //Workflows editParentData.setIsReleaseParent(selectedParent.isIsReleaseParent()); editParentData.setIsInhouseParent(selectedParent.isIsInhouseParent()); editParentData.setIsFieldTestParent(selectedParent.isIsFieldTestParent()); editParentData.setIsControlledReleaseParent(selectedParent.isIsControlledReleaseParent()); //Contract Data editParentData.setManualContractOverwrite(selectedParent.getManualContractOverwrite()); editParentData.setManualContractOverwriteExpiresAt(selectedParent.getManualContractOverwriteExpiresAt()); editParentData.setManualContractOverwriteUserId(selectedParent.getManualContractOverwriteUserId()); editParentData.setManualContractOverwriteRemark(selectedParent.getManualContractOverwriteRemark()); editParentData.setAllowDistribution(selectedParent.getAllowDistribution()); return editParentData; } private Parent fillEditData(ParentData editParentData) { // Info Parent if (selectedParent.getOverwriteParent() != null) { selectedParent.getOverwriteParent().setName1(editParentData.getName1()); selectedParent.getOverwriteParent().setName2(editParentData.getName2()); selectedParent.getOverwriteParent().setName3(editParentData.getName3()); selectedParent.getOverwriteParent().setName4(editParentData.getName4()); selectedParent.getOverwriteParent().setStras(editParentData.getStras()); selectedParent.getOverwriteParent().setPstlz(editParentData.getPstlz()); selectedParent.getOverwriteParent().setOrt01(editParentData.getOrt01()); selectedParent.getOverwriteParent().setLand1(editParentData.getLand1()); selectedParent.getOverwriteParent().setRemarks(editParentData.getRemarks()); selectedParent.getOverwriteParent().setSalesOffice(editParentData.getSalesOffice()); selectedParent.getOverwriteParent().setSalesOrganisation(editParentData.getSalesOrganisation()); } //Workflows selectedParent.setIsReleaseParent(editParentData.getIsReleaseParent()); selectedParent.setIsInhouseParent(editParentData.getIsInhouseParent()); selectedParent.setIsFieldTestParent(editParentData.getIsFieldTestParent()); selectedParent.setIsControlledReleaseParent(editParentData.getIsControlledReleaseParent()); //Contract Data selectedParent.setManualContractOverwrite(editParentData.getManualContractOverwrite()); selectedParent.setManualContractOverwriteExpiresAt(editParentData.getManualContractOverwriteExpiresAt()); selectedParent.setManualContractOverwriteUserId(editParentData.getManualContractOverwriteUserId()); selectedParent.setManualContractOverwriteRemark(editParentData.getManualContractOverwriteRemark()); selectedParent.setAllowDistribution(editParentData.getAllowDistribution()); return selectedParent; } public void initChildSelections() { if (childSelection == null) { if (allChilds == null) { ||||EXCEPTION |||| allChilds = childDao.findVisibleForCurrentUser(); } entityManager.refresh(selectedParent); List<Template> templates = groupDao.findForCurrentUser(); childSelection = new ChildSelection(viewHelper, identity, user.getSalesOrganisations(), new ChildFilter( selectedParent), templates); childSelection.init(selectedParent.getChilds(), allChilds); } } public void refreshChildSelection() { if (childSelection != null) { allChilds = childDao.findVisibleForCurrentUser(); entityManager.refresh(selectedParent); List<Child> addedChilds = new ArrayList<Child>(); for (Child pkg : childSelection.getAddedAssignedElements()) { if (!selectedParent.getChilds().contains(pkg)) { addedChilds.add(pkg); } } List<Child> removedChilds = new ArrayList<Child>(); for (Child pkg : childSelection.getRemovedAssignedElements()) { if (selectedParent.getChilds().contains(pkg)) { removedChilds.add(pkg); } } childSelection.refresh(selectedParent.getChilds(), addedChilds, removedChilds, allChilds); } } @Restrict("#{s:hasRole('manageParents') or s:hasRole('assignChildsToParent')}") public void saveChanges() { // dummy change to always force update on parent if (entityChanged) { refreshChildSelection(); if (childSelection != null) { selectedParent.getChilds().addAll(childSelection.getAddedAssignedElements()); selectedParent.getChilds().removeAll(childSelection.getRemovedAssignedElements()); } //Set info changes selectedParent = fillEditData(this.editParentData); selectedParent.setLastModifiedAt(new Date()); try { parentDao.persist(selectedParent); } catch (InvalidStateException e) { for (InvalidValue invalidValue : e.getInvalidValues()) { log.info("Instance of bean class: " + invalidValue.getBeanClass().getSimpleName() + " has an invalid property: " + invalidValue.getPropertyName() + " with message: " + invalidValue.getMessage()); } } } entityChanged = false; } @Restrict("#{s:hasRole('manageParents') or s:hasRole('assignChildsToParent')}") public void reset() { if (allChilds != null) { childSelection.init(selectedParent.getChilds(), allChilds); } edit(this.selectedParent); } .... } 

Hibernate mapping:

PARENT

 @Entity @Table(name = "PARENT") public class Parent implements Serializable, EntityBase { private static final long serialVersionUID = 1L; private String id; private List<ChildInstallInfo> childInstallInfos; private ParentXML parentXML; private List<Child> childs; private List<Template> groups; private Date lastModifiedAt; @PrePersist @PreUpdate public void prePersist() { setLastModifiedAt(new Date()); } @GenericGenerator(name = "generator", strategy = "SequenceGenerator", parameters = { @Parameter(name = "sequence", value = "PO_ID_SEQ"), @Parameter(name = "prefix", value = "PARENT_") }) @Id @GeneratedValue(generator = "generator") @Column(name = "PARENT_ID", unique = true, nullable = false, length = ID_COL_LENGTH) @Length(max = ID_COL_LENGTH) public String getId() { return id; } public void setId(String id) { this.id = id; } ..... @ManyToMany @JoinTable(name = "CHILD2PARENT", joinColumns = { @JoinColumn(name = "PARENT_ID") }, inverseJoinColumns = { @JoinColumn(name = "CHILD_ID") }) public List<Child> getChilds() { return childs; } public void setChilds(List<Child> childs) { this.childs = childs; } @ManyToMany @JoinTable(name = "PARENT2TEMPLATE", joinColumns = { @JoinColumn(name = "PARENT_ID") }, inverseJoinColumns = { @JoinColumn(name = "TEMPLATE_ID") }) public List<Template> getGroups() { return groups; } public void setGroups(List<Template> groups) { this.groups = groups; } @OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY) public List<ChildInstallInfo> getChildInstallInfo() { return childInstallInfos; } public void setChildInstallInfo( List<ChildInstallInfo> childInstallInfos) { this.childInstallInfos = childInstallInfos; } @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) @PrimaryKeyJoinColumn public SapParentInfo getSapParentInfo() { return sapParentInfo; } // We will never set the SAP data. // It will be maintain in SAP only public void setOverwriteParent(OverwriteParentInfo overwriteParent) { this.overwriteParent = overwriteParent; } /** * Compares the entity with another object. * * @return <code>true</code> if, and only if, the other object is of the * same type and has the same ID (when the key is null, the equal * check is delegated to the super class), <code>false</code> * otherwise. */ @Override public boolean equals(Object obj) { if (obj instanceof Parent) { Parent other = (Parent) obj; if (StringUtils.isEmpty(other.getId()) || StringUtils.isEmpty(getId())) { return super.equals(obj); } else { return getId().equals(other.getId()); } } else { return false; } } /** * Generates a hash code based on the ID of the entity. Delegates to the * super class when no ID is set. * * @return a hash code as described above. */ @Override public int hashCode() { if (StringUtils.isEmpty(getId())) { return getId().hashCode(); } else { return super.hashCode(); } } } 

CHILD

 @Entity @Table(name = "CHILD") public class Child implements Serializable, EntityBase { private static final String CHILD_ID_COL = "CHILD_ID"; private static final long serialVersionUID = 1L; private String id; private List<Child2Phase> child2Phase; private List<Parent> parents; private List<SalesOrganisation> salesOrganizations; private List<ChildInstallInfo> installInfos; private Date lastModifiedAt; ..... @PrePersist @PreUpdate public void prePersist() { setLastModifiedAt(new Date()); } // -------------------------------- mappings // -------------------------------------------- @GenericGenerator(name = "generator", strategy = "SequenceGenerator", parameters = { @Parameter(name = "sequence", value = "PO_ID_SEQ"), @Parameter(name = "prefix", value = "PKG_") }) @Id @GeneratedValue(generator = "generator") @Column(name = CHILD_ID_COL, unique = true, nullable = false, length = ID_COL_LENGTH) @Length(max = ID_COL_LENGTH) public String getId() { return id; } public void setId(String id) { this.id = id; } ...... @ManyToOne(fetch = FetchType.EAGER, optional = true) @JoinColumn(name = "PRODUCT_ID", nullable = true) public Product getProduct() { return product; } public void setProduct(Product product) { this.product = product; } ...... @OneToMany(mappedBy = "pk.child", cascade = CascadeType.ALL, fetch = FetchType.EAGER) @OrderBy("pk.phase asc") public List<Child2Phase> getChild2Phase() { return child2Phase; } public void setChild2Phase(List<Child2Phase> child2Phase) { this.child2Phase = child2Phase; } @ManyToMany @JoinTable(name = "CHILD2PARENT", joinColumns = { @JoinColumn(name = CHILD_ID_COL) }, inverseJoinColumns = { @JoinColumn(name = "PARENT_ID") }) public List<Parent> getParents() { return parents; } public void setParents(List<Parent> parents) { this.parents = parents; } @OneToMany(mappedBy = "child", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY) public List<ChildInstallInfo> getInstallInfos() { return installInfos; } public void setInstallInfos(List<ChildInstallInfo> installInfos) { this.installInfos = installInfos; } @ManyToMany @JoinTable(name = "CHILD2VKORG", joinColumns = { @JoinColumn(name = CHILD_ID_COL) }, inverseJoinColumns = { @JoinColumn(name = "VKORG") }) public List<SalesOrganisation> getSalesOrganizations() { return salesOrganizations; } public void setSalesOrganizations(List<SalesOrganisation> salesOrganizations) { this.salesOrganizations = salesOrganizations; } @Version @Column(name = "LAST_MODIFIED_AT", nullable = false) public Date getLastModifiedAt() { return DateUtil.copyDate(lastModifiedAt); } public void setLastModifiedAt(Date lastModifiedAt) { this.lastModifiedAt = DateUtil.copyDate(lastModifiedAt); } /** * Compares the entity with another object. * * @return <code>true</code> if, and only if, the other object is of the * same type and has the same ID (when the key is null, the equal * check is delegated to the super class), <code>false</code> * otherwise. */ @Override public boolean equals(Object obj) { if (obj instanceof Child) { Child other = (Child) obj; if (StringUtils.isEmpty(other.getId()) || StringUtils.isEmpty(getId())) { return super.equals(obj); } else { return getId().equals(other.getId()); } } else { return false; } } /** * Generates a hash code based on the ID of the entity. Delegates to the * super class when no ID is set. * * @return a hash code as described above. */ @Override public int hashCode() { if (StringUtils.isEmpty(getId())) { return getId().hashCode(); } else { return super.hashCode(); } } } 

DAOS

 @Name("parentDao") @AutoCreate public class parentDao extends AbstractDao<Parent> { public ParentDao() { super(Parent.class); } } /** * Default DAO object that provides common methods. */ public abstract class AbstractDao<T> extends AbstractNoEditDao<T> { public AbstractDao(Class<T> entityClass) { super(entityClass); } public void persist(T entity) { entityManager.persist(entity); } public void persistAll(List<T> entities) { for (T entity : entities) { persist(entity); } } public void delete(T entity) { entityManager.remove(entity); } } public abstract class AbstractNoEditDao<T> { @In protected EntityManager entityManager; // NOSONAR @In protected Session session; // NOSONAR private final Class<T> entityClass; public AbstractNoEditDao(Class<T> entityClass) { this.entityClass = entityClass; } public void refresh(T entity) { entityManager.refresh(entity); } @SuppressWarnings("unchecked") ||||EXCEPTION||| public List<T> findAll() { return entityManager.createQuery("select e from " + EntityUtil.getEntityName(entityClass) + " e") .getResultList(); } } 

We use: JBOSS 5 + Hibernate 3.3.0 + Richfaces 3.3.3 + JBOSS Seam 2.2.2

我认为您正在同时从两个或多个线程访问一个Hibernate会话实例,并且您不应该这样做...我在使用C#nHibernate遇到了"deleting records on association tables"的相同问题是由2个线程使用同一会话引起的-完全在同一时间进行了更多SELECT查询(第二个SELECT在第一个会话在同一会话中完成之前开始)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM