0

我有个问题。我得到以下异常:

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

4

1 回答 1

2

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

于 2013-10-03T12:16:44.760 回答