Embeddable Objects (EJB3) not working properly
niesar Jul 17, 2006 11:11 AMHi guys,
in a Seam/JSF/Facelets evaluation project I ran into the following problem. Looks like there is a problem if you use embeddable Objects in Entity beans.
Consider the following entity bean containing an embedded object:
@Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.STRING) @Table (name = "CLassAB") @Name("classA") public class ClassA implements java.io.Serializable { @Id @GeneratedValue @Column (name = "Id") private int id; @Column (name = "Var1", nullable=false, length=3) @NotNull @Length(min=2, max=3) private String var1; @Embedded // <------- trouble maker private ClassE classEInst; @Basic private Double latitude; @Basic private Double longitude; public int getId() { return id; } protected void setId(int id) { this.id = id; } public String getVar1() { return var1; } public void setVar1(String var1) { this.var1 = var1; } public ClassE getClassEInst() { return classEInst; } public void setClassEInst(ClassE classEInst) { this.classEInst = classEInst; } public Double getLatitude() { return latitude; } public void setLatitude(Double latitude) { this.latitude = latitude; } public Double getLongitude() { return longitude; } public void setLongitude(Double longitude) { this.longitude = longitude; } public GeoPoint getLocation() { return new GeoPoint(latitude,longitude); } public String toString() { return var1; } }Please disregard the inheritance. You don't need to bother with it for this issue.
ClassE just contains a single integer in this test case
@Embeddable public class ClassE implements java.io.Serializable { private int classEVar; public ClassE() { } public ClassE(int classEContents) { this.classEVar = classEContents; } public int getClassEVar() { return classEVar; } public void setClassEVar(int classEVar) { this.classEVar = classEVar; }
The problem arises in a JSF page where I want to edit instances of entity bean ClassA (classAEdit.xhtml):
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:s="http://jboss.com/products/seam/taglib" xmlns:o="http://www.optimizer.de/jsf" template="template.xhtml"> <!-- content --> <ui:define name="content"> <div class="section"> <h1>Edit ClassA - Test</h1> <h:form> <fieldset> <o:classA-input classAInst="#{classASearch.selectedClassAInst}" showId="#{false}" /> // <------------- (1) <div class="entry errors"><h:messages globalOnly="true" /></div> <div class="entry"> <div class="input"> <h:commandButton value="Save" action="#{classAEdit.update}" class="button"/> <h:commandButton value="Delete" action="#{classAEdit.delete}" class="button"/> <s:link value="Cancel" action="classAList" linkStyle="button" buttonClass="button"/> </div> </div> </fieldset> </h:form> </div> </ui:define> </ui:composition>
Take a look at (1).
tag classA-input is defined in a facelets tag lib (classA-input.xhtml)
<!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:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:c="http://java.sun.com/jstl/core" xmlns:fn="http://java.sun.com/jsp/jstl/functions" xmlns:s="http://jboss.com/products/seam/taglib" xmlns:o="http://www.optimizer.de/jsf"> <ui:composition> <div class="entry"> <div class="label"><h:outputLabel for="id">ID:</h:outputLabel></div> <div class="output"><h:outputText id="id">#{classAInst.id}</h:outputText></div> </div> <div class="entry"> <div class="label"><h:outputLabel for="classEInst">Class E Instance (embedded):</h:outputLabel></div> <div class="input"> <h:inputText id="classEInst" value="#{classAInst.classEInst}" /><br/> // <------------------ (2) <span class="errors"><h:message for="classEInst" /></span> </div> </div> <div class="entry"> <div class="label"><h:outputLabel for="latitude">Latitude:</h:outputLabel></div> <div class="input"> <h:inputText id="latitude" value="#{classAInst.latitude}" /><br/> <span class="errors"><h:message for="latitude" /></span> </div> </div> <div class="entry"> <div class="label"><h:outputLabel for="longitude">Longitude:</h:outputLabel></div> <div class="input"> <h:inputText id="longitude" value="#{classAInst.longitude}" /><br/> <span class="errors"><h:message for="longitude" /></span> </div> </div> </ui:composition> </html>In (1) an input field with contents `{{classASearch.selectedClassAInst} is created.
Here is the relevant part of classASearch:
@Stateful @Name("classASearch") @Scope(ScopeType.SESSION) public class ClassASearchAction implements ClassASearch { @PersistenceContext private EntityManager em; private String searchString; @DataModel private List<ClassA> classAInstList; @DataModelSelection @Valid private ClassA selectedClassAInst; // <-------------------- [...] }
The interesting part is that everything works fine if the ClassE instance is displayed in (2). However, if you change the contents and want to save, you'll get an error - apparingly from MyFaces:
/WEB-INF/facelets/tags/classA-input.xhtml @20,68 value="#{classAInst.classEInst}": Exception setting property classEInst of base with class test.ClassB
[hint: classA-input.xhtml @20,68 is (2)]
Since the code works fine for Strings or - as you can see above - doubles, but fails with embedded objects, I guess MyFaces is not compatible with @Embedded Objects created by EJB3/Hibernate.
However, I don't know where to file the bug. Is MyFaces supposed to deal correctly with an EJB3 embedded obejct? Or is Hibernate/EJB3 implementing the embedded object in a strange way so that MyFaces can't deal correctly with the code? Or are JBoss and MyFaces just not as compatible as they should be?
So is this a bug or a feature request? File a bug in the JBoss or in the Apache JIRA???
---
BTW, the problem seems to arise in PropertyResolverImpl.setValue(). In http://myfaces.apache.org/impl/xref/org/apache/myfaces/el/PropertyResolverImpl.html you can see the error is thrown in the catch (RuntimeException e) part in setValue().