我有个问题。我得到以下异常:
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)
在此异常之后,Hibernate 正在删除我的数据库中关联表上的记录。我不明白为什么会这样?它只是执行选择语句。在 Stacktrace 上,我看到了 AutoFlush。也许这是导致问题的原因。最大的问题是我不能不断地重现它。错误时有发生。
我们在应用程序中使用 EntityManager。使用 findAll() 方法,我们得到单独的实体列表,这些实体与关联表有关系。
我在一个选项卡上有一个 Richfaces 建议框。我可以从这个列表中添加和删除元素。
<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>
建议框的标签代码。
<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>
我的控制器类中有一个方法(initChildSelections())。它为登录用户选择所有孩子。
Java 代码控制器:
@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);
}
....
}
休眠映射:
家长
@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();
}
}
}
孩子
@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();
}
}
我们使用:JBOSS 5 + Hibernate 3.3.0 + Richfaces 3.3.3 + JBOSS Seam 2.2.2