3

私はJSF2(MyFaces2.1.7およびPrimefaces3.4.2)、CDI(Weld-servlet 1.1.10)、JPA 2(Hibernate 4.1.7)、およびLombok0.11.2を使用しています。これはすべてTomcat6および7で実行されます。

を介して実装されたOpenSessionInViewパターンを使用しFilterます。

@Advanced
@Data
@Slf4j
public class TransactionalFilter implements Filter, Serializable {

    private static final long serialVersionUID = 999173590695648899L;

    @Inject
    private EntityManager em;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        boolean newTransaction = false;
        EntityTransaction tx = em.getTransaction();
        if (!tx.isActive()) {
            tx.begin();
            newTransaction = true;
        }

        try {
            chain.doFilter(request, response);
            if (newTransaction && tx.isActive()) {
                tx.commit();
            }
        } catch (Exception e) {
            if (newTransaction && tx.isActive()) {
                tx.rollback();
            }
            throw new ServletException(e);
        }

    }
    (...)
}

注入されたものは私のRequestScoped EntityManagerによって提供され、私EntityManagerFactoryのすべてのサービスの最前線にもそれを提供します。

@ApplicationScoped
@Data
@Slf4j
public class TransactionalEntityManagerFactory implements Serializable {

    private static final String PU_NAME = "fr.senat.dosleg";
    private static final long serialVersionUID = -3595175390458199193L;
    private EntityManagerFactory emf;

    /** (...)
     * @return un nouvel EntityManager.
     */
    @Produces
    @RequestScoped
    public EntityManager getEntityManager() {
        if (emf == null) {
            emf = Persistence.createEntityManagerFactory(PU_NAME);
        }
        return emf.createEntityManager();
    }

    /**(...)
     * @param em le gestionnaire d'entité à libérer.
     */
    public void closeEntityManager(@Disposes EntityManager em) {
        if (em != null && em.getTransaction().isActive()) {
            em.getTransaction().rollback();
        }

        if (em != null && em.isOpen()) {
            em.close();
        }
    }
}

List<Theme>以下のControleエンティティに表示されているものを追加するまで、これはすべて正常に機能しました

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString(exclude = { "organisme", "groupePolitique", "lois", "livrables",
    "acteurs", "themes" })
public class Controle implements Serializable {

    private static final long serialVersionUID = -6471695606036735891L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @NotNull
    @Size(max = 256, message = "trop long.")
    private String libelle;

    @Pattern(regexp = Constants.URL_PATTERN, message = "pas au bon format")
    private String url;

    @NotNull
    @Type(type = "fr.senat.util.hibernate.usertype.OuiNonSmallType")
    private boolean initiativeDesGroupes;

    @NotNull
    @Type(type = "fr.senat.util.hibernate.usertype.OuiNonSmallType")
    private boolean courDesComptes;

    @NotNull
    private int anneeCreation;

    @Embedded
    private EcheanceControle echeance = new EcheanceControle();

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "ORGCOD")
    private Organisme organisme;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "GRPPOL")
    private GroupePolitique groupePolitique;

    @ManyToMany
    @JoinTable(name = "CONTROLE_LOI", joinColumns = @JoinColumn(name = "CON_ID"), inverseJoinColumns = @JoinColumn(name = "LOICOD"))
    private List<Loi> lois;

    @OneToMany(orphanRemoval = true, cascade = CascadeType.ALL)
    @JoinColumn(name = "CON_ID", nullable = false)
    private List<LivrableControle> livrables;

    @OneToMany(orphanRemoval = true, cascade = CascadeType.ALL)
    @JoinColumn(name = "CON_ID", nullable = false)
    private List<ActeurControle> acteurs;

    @ManyToMany
    @JoinTable(name = "THEME_CONTROLE", joinColumns = @JoinColumn(name = "CON_ID"), inverseJoinColumns = @JoinColumn(name = "THECLE"))
    private List<Theme> themes;

    (...)

}

ページを介して既存のControleを保存しようとするとcontrole.xhtml、ajax応答エラーが発生します。以下でありcontrole.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:p="http://primefaces.org/ui"
    xmlns:s="http://www.senat.fr/taglib/util"
    xmlns:ui="http://java.sun.com/jsf/facelets">
<ui:composition template="template.xhtml">
    <ui:define name="title">
        <h:outputText value="#{controle.controle.libelle}"
            rendered="#{not empty controle.controle.id}" />
        <h:outputText value="Création d'un contrôle"
            rendered="#{empty controle.controle.id}" />
    </ui:define>
    <ui:define name="specific_header">
        <h:outputScript library="fr.senat.util.primefaces"
            name="calendar-locale.js" />
    </ui:define>
    <ui:define name="content">
        <h:form id="controle">

            <p:messages />

            <p:panel>

                <h:panelGrid columns="2" columnClasses="label,">

                    <p:outputLabel for="libelle" value="Libellé" />
                    <p:inputText id="libelle" value="#{controle.controle.libelle}"
                        size="60" />

                    <p:outputLabel for="url" value="URL" />
                    <p:inputText id="url" value="#{controle.controle.url}" size="60" />

                    <h:outputText value="Acteurs" />
                    <h:panelGroup>
                        <h:panelGroup id="acteurs">
                            <p:dataTable var="a" value="#{controle.controle.acteurs}"
                                rendered="#{not empty controle.controle.acteurs}">
                                <p:column headerText="Rôle">
                                    <h:outputText value="#{a.role}" />
                                </p:column>

                                <p:column headerText="Nom">
                                    <h:outputText
                                        value="#{a.senateur.nomUsuel} #{a.senateur.prenomUsuel}" />
                                </p:column>

                                <p:column headerText="Enlever">
                                    <p:commandButton
                                        title="Enlever #{a.senateur.nomUsuel} #{a.senateur.prenomUsuel}"
                                        icon="ui-icon-trash"
                                        actionListener="#{controle.removeElement}"
                                        update=":controle:acteurs" immediate="true">
                                        <f:attribute name="ancien" value="#{a}" />
                                    </p:commandButton>
                                </p:column>
                            </p:dataTable>
                        </h:panelGroup>
                        <p:commandButton id="addActeur" value="Ajouter un acteur"
                            icon="ui-icon-plus" onclick="addActeurDialog.show()"
                            immediate="true" />
                    </h:panelGroup>

                    <p:outputLabel for="themes" value="Thèmes" />
                    <p:selectManyButton id="themes" value="#{controle.controle.themes}">
                        <f:selectItems value="#{controle.themes}" var="t"
                            itemLabel="#{t.libelle}" itemValue="#{t}" />
                    </p:selectManyButton>

                </h:panelGrid>

                <f:facet name="footer">
                    <p:commandButton onclick="deleteControleDialog.show()"
                        value="Supprimer" icon="ui-icon-trash"
                        styleClass="ui-priority-secondary" type="button" />
                    <p:button outcome="pretty:start" value="Annuler"
                        icon="ui-icon-cancel" />
                    <p:commandButton id="saveSubmit" value="Sauvegarder"
                        actionListener="#{controle.save}" icon="ui-icon-disk"
                        styleClass="ui-priority-primary" />
                </f:facet>
            </p:panel>
            <p:defaultCommand target="saveSubmit" />
        </h:form>

        <p:dialog header="Ajoute un nouvel acteur" id="addActeurDialog"
            widgetVar="addActeurDialog" modal="true" width="650">
            <h:form id="addActeurForm">
                <p:messages />
                <h:panelGrid columns="2">

                    <p:outputLabel for="newRole" value="Rôle" />
                    <p:autoComplete id="newRole" dropdown="true"
                        value="#{controle.newRole}"
                        completeMethod="#{controle.completeRole}" />

                    <p:outputLabel for="senateurSearch" value="Sénateur" />
                    <h:panelGroup>
                        <p:inputText id="senateurSearch"
                            value="#{controle.senateurSearch}" size="30" />
                        <p:commandButton id="senateurSearchSubmit" value="Chercher"
                            actionListener="#{controle.searchSenateurs}"
                            update="senateursFound" icon="ui-icon-search"
                            oncomplete="addActeurDialog.initPosition()" />
                        <p:selectBooleanButton value="#{controle.senateurSearchActif}"
                            onLabel="en activité seulement" offLabel="en activité ou non" />
                    </h:panelGroup>
                </h:panelGrid>
                <h:panelGroup id="senateursFound">
                    <p:dataTable value="#{controle.senateurs}" var="s"
                        rendered="#{not empty controle.senateurs}" rows="10">
                        <p:column headerText="Nom">
                            <h:outputText value="#{s.nomCompletUsuel}" />
                        </p:column>

                        <p:column headerText="Ajout">
                            <p:commandButton title="Ajouter le sénateur #{s.nomCompletUsuel}"
                                icon="ui-icon-plus" actionListener="#{controle.addElement}"
                                oncomplete="addActeurDialog.hide()" update=":controle:acteurs">
                                <f:attribute name="nouveau" value="#{s}" />
                            </p:commandButton>
                        </p:column>
                    </p:dataTable>
                </h:panelGroup>

            </h:form>
        </p:dialog>

        <p:confirmDialog id="deleteControleDialog"
            message="Etes-vous sûr de vouloir supprimer ce contrôle ?"
            header="Suppression du contrôle" severity="alert"
            widgetVar="deleteControleDialog">
            <p:commandButton value="Annuler"
                onclick="deleteControleDialog.hide()" />
            <p:commandButton value="Confirmer la suppression"
                action="#{controle.deleteControle}" styleClass="ui-priority-primary" />
        </p:confirmDialog>
    </ui:define>
</ui:composition>
</html>

これがエラーです

<?xml version="1.0" encoding="utf-8"?>
<partial-response>
    <error>
        <error-name>org.hibernate.LazyInitializationException</error-name>
        <error-message><![CDATA[could not initialize proxy - no Session]]></error-message>
    </error>
</partial-response>

デバッガーを使用すると、例外が最初にスローされたときにスタックトレースを判別できました。

PersistentBag(AbstractPersistentCollection).withTemporarySessionIfNeeded(LazyInitializationWork<T>) line: 180   
PersistentBag(AbstractPersistentCollection).initialize(boolean) line: 520   
PersistentBag(AbstractPersistentCollection).write() line: 345   
PersistentBag.add(Object) line: 291 
_SharedRendererUtils.getConvertedUISelectManyValue(FacesContext, UISelectMany, String[], boolean) line: 339 
RendererUtils.getConvertedUISelectManyValue(FacesContext, UISelectMany, Object, boolean) line: 1088 
RendererUtils.getConvertedUISelectManyValue(FacesContext, UISelectMany, Object) line: 1056  
HtmlCheckboxRenderer(HtmlCheckboxRendererBase).getConvertedValue(FacesContext, UIComponent, Object) line: 525   
SelectManyButtonRenderer.getConvertedValue(FacesContext, UIComponent, Object) line: 36  
SelectManyButton(UISelectMany).getConvertedValue(FacesContext, Object) line: 402    
SelectManyButton(UIInput).validate(FacesContext) line: 584  
SelectManyButton(UISelectMany).validate(FacesContext) line: 393 
SelectManyButton(UIInput).processValidators(FacesContext) line: 274 
HtmlPanelGrid(UIComponentBase).processValidators(FacesContext) line: 1421   
Panel(UIComponentBase).processValidators(FacesContext) line: 1421   
Panel.processValidators(FacesContext) line: 297 
HtmlForm(UIForm).processValidators(FacesContext) line: 209  
HtmlBody(UIComponentBase).processValidators(FacesContext) line: 1421    
UIViewRoot(UIComponentBase).processValidators(FacesContext) line: 1421  
UIViewRoot._processValidatorsDefault(FacesContext) line: 1401   
UIViewRoot.access$500(UIViewRoot, FacesContext) line: 74    
UIViewRoot$ProcessValidatorPhaseProcessor.process(FacesContext, UIViewRoot) line: 1508  
UIViewRoot._process(FacesContext, PhaseId, UIViewRoot$PhaseProcessor) line: 1357    
UIViewRoot.processValidators(FacesContext) line: 799    
ProcessValidationsExecutor.execute(FacesContext) line: 38   
LifecycleImpl.executePhase(FacesContext, PhaseExecutor, PhaseListenerManager) line: 170 
LifecycleImpl.execute(FacesContext) line: 117   
CodiLifecycleWrapper.execute(FacesContext) line: 95 
FacesServlet.service(ServletRequest, ServletResponse) line: 197 
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 290  
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206  
PrettyFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 145   
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 235  
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206  
ApplicationDispatcher.invoke(ServletRequest, ServletResponse, ApplicationDispatcher$State) line: 646    
ApplicationDispatcher.processRequest(ServletRequest, ServletResponse, ApplicationDispatcher$State) line: 436    
ApplicationDispatcher.doForward(ServletRequest, ServletResponse) line: 374  
ApplicationDispatcher.forward(ServletRequest, ServletResponse) line: 302    
PrettyFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 137   
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 235  
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206  
TransactionalFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 60 
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 235  
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206  
SetUtf8CharacterEncodingFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 44  
(...)
Thread.run() line: 662  

今、私はSOに関する多くの投稿がその例外に関係していることに気づきました。すべての回答は通常、entitymanager(またはHibernate方言のセッション)が閉じられることを中心に展開されます。それらは2つの解決策を提供します:OpenSessionInViewを使用するか、プロパティを怠惰にフェッチします。私の問題はいくつかの理由で異なっていると思います。

  1. 私はすでにOpenSessionInViewを使用しています。さらに、ログには、問題が発生したときにトランザクションがまだ開いていることが実際に示されています。
  2. プロパティを怠惰にフェッチしようとしても役に立ちthemesません

コードを調べてみると、これはすべてのSelectMultiXXXウィジェットに共通であるように思われたので、SelectManyCheckboxを試してみました。それもうまくいきませんでした。また、ウィジェットで特定のコンバーターを使用するかどうかを試しましたが、結果は同じです。

最後に、他のすべてのList<>プロパティが同じcontrole.xhtmlで機能することを指摘したいと思います。ただし、違いは、これらがダイアログボックスを介して変更されることです(したがって、異なる要求)。

私が見ないものが表示されること、それがバグであることを確認できること、または回避策を提供してくれることを願っています。そうでない場合でも、その長い質問を読むために時間を割いていただき、ありがとうございます。

更新2012年11月29日:例外の時点でentitymanagerが実際に開いていると確信しています。さらに、問題は次の方法によるものだと確信しています。

_SharedRendererUtils.getConvertedUISelectManyValue(FacesContext, UISelectMany, String[], boolean) (line 143)

誰かがそれを試してみたい場合は、そのメソッドのデバッグからのいくつかのポインタがあります

  1. Class<?> modelType = expression.getType(facesContext.getELContext());java.util.List
  2. Collection.class.isAssignableFrom(modelType)本当です
  3. collectionTypeAttr != null偽です
  4. Collection.class.isAssignableFrom(modelType)本当です
  5. Collection<?> componentValue = (Collection<?>) component.getValue();は、データ(および)PersistenBagで正しく初期化されているように見えますが、nullで初期化されているようです。storedSnapshotrolesession
  6. targetForConvertedValues = (componentValue != null ? componentValue.getClass() : modelType).newInstance();最終PersistentBag的にすべてがnull(データを含む)になります。それが問題でしょうか?
  7. boolean isArray = (targetForConvertedValues.getClass().isArray());偽です
  8. ((Collection) targetForConvertedValues).add(value);すべてがうまくいかないところです。

何かご意見は ?

4

2 に答える 2

2

これは、Mojarra で見つけた非常に厄介なバグのように見えますが、このバージョンの MyFaces でも​​同じである可能性があります。基本は、検証を行うときにリストのコピーを行いますが、引数なしのコンストラクターを使用してコレクションを作成し、コレクションの具象型を使用します。休止状態の場合、この新しいリストにはすべての初期化コードが実行されておらず、セッションにリンクされていません。実際に何が起こっているのかを理解するために、私は長い間デバッグと Mojarra ソースを調べました。

collectionType 属性を使用して、それを java.util インターフェイス タイプに設定する必要があることがわかりました。使用するコレクション タイプを明示的に JSF に指示しない限り、コレクションに関しては何もしません。

于 2012-11-28T14:34:16.677 に答える
0

Hibernate のコレクションは、コレクションを含む db からオブジェクトを受け取ると、デフォルトで遅延ロードされます。 List<> または Set<> の代わりに、そのリストまたはセットのプロキシがあります。そのコレクションの getter メソッドを呼び出すと、取得されます。ただし、オブジェクトの発生元のセッションが閉じられているか、アクセスできなくなっている場合に問題が発生します (これは、OpenSessionInView フィルターの制約にあってはなりません)。

session.mergeオブジェクトが異なるセッションで処理されている場合は、オブジェクトを呼び出してみることができます。getterまたは、フェッチ中にリストの を手動で呼び出して、プロキシをトリガーすることもできます。または、コレクションに追加することもできFetchType.Eagerます。この場合、オブジェクトはプロキシではなく、フェッチ時でも実際のオブジェクトになります。

しかし、CDI に Weld を使用しているようですが、トランザクション管理をサポートする Seam Persistence モジュールを使用しないのはなぜですか? その後、オープン セッション イン ビュー アプローチを非常に迅速に実装できます。

http://www.seamframework.org/Seam3/PersistenceModule

于 2012-11-27T14:25:29.763 に答える