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().