Lazy loading exception when using JSF Converter (collection link)
This is my first post after doing a lot of research on this issue.
This example works under Jboss 7.1 with 3.1 seam (solder + persistence + faces) with seam driven persistence context
I ran into a problem, a classic one, failed to lazily initialize a collection, no session or session was closed: org.hibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed
when using a converter to Entity beans. The goal is to stay 100% OO by reusing the JPA model.
in beans.xml, org.jboss.seam.transaction.TransactionInterceptor
activated
Entity beans:
@Entity
public class Member implements Serializable {
@Id
@GeneratedValue
private Long id;
private String name;
private String email;
@Column(name = "phone_number")
private String phoneNumber;
@ManyToMany
private List<Statut> listeStatut = new ArrayList<Statut>();
// getters, setters, hashcode, equals
}
@Entity
public class Statut implements Serializable {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToMany(mappedBy="listeStatut")
private List<Member> members = new ArrayList<Member>();
// getters, setters, hashcode, equals
}
JSF page:
<h:form>
<h:selectManyCheckbox id="stat" value="#{memberModif.member.listeStatut}">
<f:converter converterId="statutConverter"/>
<f:selectItems value="#{memberModif.statutsPossibles}" var="statut" itemValue="#{statut}" itemLabel="#{statut.name}" />
</h:selectManyCheckbox>
<h:commandLink id="register" action="#{memberModif.modifier()}" value="Modifier">
<f:param name="cid" value="#{javax.enterprise.context.conversation.id}"/>
</h:commandLink>
</h:form>
Bean support (i tried using ConversationScoped after SessionScoped -> same problem)
@ConversationScoped
@Named
public class MemberModif implements Serializable {
private static final long serialVersionUID = -291355942822086126L;
@Inject
private Logger log;
@Inject
private EntityManager em;
@Inject Conversation conversation;
private Member member;
@SuppressWarnings("unused")
@PostConstruct
private void init() {
if (conversation.isTransient()) {
conversation.begin();
}
}
public String modifier() {
em.merge(member);
}
public Member getMember() {
if (member == null) {
member = em.createQuery("from Member m where m.id=:id",Member.class).setParameter("id", new Long(0)).getSingleResult();
}
return member;
}
public List<Statut> getStatutsPossibles() {
return em.createQuery("from Statut", Statut.class).getResultList();
}
}
And a converter (heavily inspired by seam ObjectConverter
):
@FacesConverter("statutConverter")
public class StatutConverter implements Converter, Serializable {
final private Map<String, Statut> converterMap = new HashMap<String, Statut>();
final private Map<Statut, String> reverseConverterMap = new HashMap<Statut, String>();
@Inject
private transient Conversation conversation;
private final transient Logger log = Logger.getLogger(StatutConverter.class);
private int incrementor = 1;
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (this.conversation.isTransient()) {
log.warn("Conversion attempted without a long running conversation");
}
return this.converterMap.get(value);
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (this.conversation.isTransient()) {
log.warn("Conversion attempted without a long running conversation");
}
if (this.reverseConverterMap.containsKey(value)) {
return this.reverseConverterMap.get(value);
} else {
final String incrementorStringValue = String.valueOf(this.incrementor++);
this.converterMap.put(incrementorStringValue, (Statut)value);
this.reverseConverterMap.put( (Statut)value, incrementorStringValue);
return incrementorStringValue;
}
}
}
Note that I put this converter here to avoid searching the net for the seam implementation, but this is the same as using the tag <s:objectConverter/>
instead<f:converter converterId="statutConverter"/>
Any help would be greatly appreciated.
source to share
You should be able to access the objects in one transaction. If you are sure you are doing this already, you can try getting the entitymanager by viewing it in context instead of typing it in. I had a simulated problem that was solved this way. You can also initialize the collection in a transaction when you first got your reference to it.
Hibernate.initialize(yourCollection);
source to share
Take a look at this: selectManyCheckbox LazyInitializationException while checking process
Try:
<f:attribute name="collectionType" value="java.util.ArrayList" />;
on<h:selectManyCheckbox>
source to share