私は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を使用するか、プロパティを怠惰にフェッチします。私の問題はいくつかの理由で異なっていると思います。
- 私はすでにOpenSessionInViewを使用しています。さらに、ログには、問題が発生したときにトランザクションがまだ開いていることが実際に示されています。
- プロパティを怠惰にフェッチしようとしても役に立ち
themes
ません
コードを調べてみると、これはすべてのSelectMultiXXXウィジェットに共通であるように思われたので、SelectManyCheckboxを試してみました。それもうまくいきませんでした。また、ウィジェットで特定のコンバーターを使用するかどうかを試しましたが、結果は同じです。
最後に、他のすべてのList<>プロパティが同じcontrole.xhtmlで機能することを指摘したいと思います。ただし、違いは、これらがダイアログボックスを介して変更されることです(したがって、異なる要求)。
私が見ないものが表示されること、それがバグであることを確認できること、または回避策を提供してくれることを願っています。そうでない場合でも、その長い質問を読むために時間を割いていただき、ありがとうございます。
更新2012年11月29日:例外の時点でentitymanagerが実際に開いていると確信しています。さらに、問題は次の方法によるものだと確信しています。
_SharedRendererUtils.getConvertedUISelectManyValue(FacesContext, UISelectMany, String[], boolean) (line 143)
誰かがそれを試してみたい場合は、そのメソッドのデバッグからのいくつかのポインタがあります
Class<?> modelType = expression.getType(facesContext.getELContext());
はjava.util.List
Collection.class.isAssignableFrom(modelType)
本当ですcollectionTypeAttr != null
偽ですCollection.class.isAssignableFrom(modelType)
本当ですCollection<?> componentValue = (Collection<?>) component.getValue();
は、データ(および)PersistenBag
で正しく初期化されているように見えますが、nullで初期化されているようです。storedSnapshot
role
session
targetForConvertedValues = (componentValue != null ? componentValue.getClass() : modelType).newInstance();
最終PersistentBag
的にすべてがnull(データを含む)になります。それが問題でしょうか?boolean isArray = (targetForConvertedValues.getClass().isArray());
偽です((Collection) targetForConvertedValues).add(value);
すべてがうまくいかないところです。
何かご意見は ?