I have marked this as CRITICAL because although there probably is no database integrity problem, the inability to robustly query relationship fields in a web application renders the ObjectDB system seemingly useless.
objectdb-2.2.5_10
NetbeansIDE6.9.1
Glassfish3.01
client-server mode.
odb file written to db-files/greendesk/greendesk.odb (see persistence.xml), copy of odb output attached.
Sorry this is a complex one, but it is extremely important. I have taken great trouble to make it simpler to understand with careful output logging to the Glassfish logs, and also with a diagnostic table showing the query result in indexTest.xhtml.
The problems is illustrated by running the attached JavaEE Web App and viewing both the Glassfish output log and the single served JSF page indexTest.xthml. For a number of different List fields, a query performed immediately after creating and persisting many Element objects gives expected results, but the same query performed later form a served JSF page via a querying @EJB gives many fields null (and not even empty, I mean truly null).
In the following explanation I will refer to the (bi-directional) ownedElements List of the Element class, but the same problem happens to uni-directional @OneToMany fields, and also there seem to be some simple @OneToOne relationship fields that likewise become null:
private List<Element> ownedElements = new ArrayList<Element>(); @OneToMany(mappedBy = "owner", cascade = CascadeType.ALL) public List<Element> getOwnedElements() { return ownedElements; } private Element owner; @ManyToOne(fetch = FetchType.LAZY) @JoinTable(name = "Element_owns_Element", joinColumns = @JoinColumn(name = "owner_ID", referencedColumnName = "ID"), inverseJoinColumns = @JoinColumn(name = "ownedElement_ID", referencedColumnName = "ID")) public Element getOwner() { return owner; }
(For completeness I've left the old ORM annotations in, but there are other cases without these that also exhibit the problem. Also, I don't think the FetchType is the culprit.)
INSTRUCTIONS TO REPRODUCE PROBLEM
The entities (mostly Element or Element subclass objects) are created in the @PostConstruct createData() method of ConfigBean. We will focus here on the case of one object of class TestDeepValues. On creation the logs show:
INFO: RequestBean []: Created: com.greensoft.entity.test.TestDeepValues [null](TEST: test deep values)
INFO: RequestBean []: Persisted: com.greensoft.entity.test.TestDeepValues [45](TEST: test deep values)
INFO: RequestBean []: Updated: com.greensoft.entity.test.TestDeepValues [45](TEST: test deep values)
Then at the end of createData() I perform an echoed query on all elements and interrogate the ownedElements list, which gives the expected results (corresponds with the database, too, see screenshot attached):
protected void testQuery() { String $i = "testQuery"; TypedQuery<Element> query = em.createQuery("SELECT e FROM Element e", Element.class); List<Element> elements = query.getResultList(); for (Element e : elements) { log_info($i,"---"); log_info($i,"element",e); log_info($i,"element.project",e.getProject()); List<Element> ownedElements = e.getOwnedElements(); log_info($i,"element.ownedElements",ownedElements); if (ownedElements != null) { for (Element owned: ownedElements) { log_info($i,"element.ownedElement",owned); } } } log_info($i,"---"); }
INFO: ConfigBean []: testQuery: element([class=TestDeepValues][project=TEST: project 1][id=45][name=TEST: test deep values])
INFO: ConfigBean []: testQuery: element.project([class=Project][id=1][name=TEST: project 1])
INFO: ConfigBean []: testQuery: element.ownedElements([[class=FloatQuantity][project=TEST: project 1][id=46][name=deep float quantity (length)], [class=IntegerQuantity][project=TEST: project 1][id=47][name=deep integer quantity (number of rooms)], [class=BooleanValue][project=TEST: project 1][id=48][name=deep boolean value (no formaldehyde)], [class=StringValue][project=TEST: project 1][id=49][name=deep string value (building name)]])
INFO: ConfigBean []: testQuery: element.ownedElement([class=FloatQuantity][project=TEST: project 1][id=46][name=deep float quantity (length)])
INFO: ConfigBean []: testQuery: element.ownedElement([class=IntegerQuantity][project=TEST: project 1][id=47][name=deep integer quantity (number of rooms)])
INFO: ConfigBean []: testQuery: element.ownedElement([class=BooleanValue][project=TEST: project 1][id=48][name=deep boolean value (no formaldehyde)])
INFO: ConfigBean []: testQuery: element.ownedElement([class=StringValue][project=TEST: project 1][id=49][name=deep string value (building name)])
The following reported failed is the same whether or not I perform the above post-construct query.
When I serve the query via and @EJB TestQuery and @RequestScoped Page JSF to a simple XHTML page the list field values becomes null (in my larger real-world application, lots of other @OneToMany and @OneToOne fields are also null).
In all cases lists that are given in the explored database either empty or populated are served as null (!).
The query is performed by an @EJB:
@Stateless
public class TestQuery {
@PersistenceContext
private EntityManager em;
public List<Element> findElements() {
String $i = "testQuery";
TypedQuery<Element> query = em.createQuery("SELECT e FROM Element e", Element.class);
List<Element> elements = query.getResultList();
return elements;
}
}
And the JSF backingbean:
@ManagedBean
@RequestScoped
public class Page extends All_ {
/** Creates a new instance of Page */
public Page() {
}
@EJB
private TestQuery testQuery;
public List<Element> getElements() {
String $i = "Page.getElements";
List<Element> elements = testQuery.findElements();
for (Element e : elements) {
log_info($i,"---");
log_info($i,"element",e);
log_info($i,"element.project",e.getProject());
List<Element> ownedElements = e.getOwnedElements();
log_info($i,"element.ownedElements",ownedElements);
if (ownedElements != null) {
for (Element owned: ownedElements) {
log_info($i,"element.ownedElement",owned);
}
}
}
log_info($i,"---");
return elements;
}
}
Note how I am tediously logging the result to Glassfish log (so can test whether problem is at XHTML level, which it is not). Because it gets pulled more than once during the various JSF phases the output is quite copius, but one can easily extract the relevant object diagnostics thus, which shows the error, that the ownedElements field is now given as null:
INFO: Page []: Page.getElements: element([class=TestDeepValues][project=TEST: project 1][id=45][name=TEST: test deep values])
INFO: Page []: Page.getElements: element.project([class=Project][id=1][name=TEST: project 1])
INFO: Page []: Page.getElements: element.ownedElements(null)
There is also a copy of the indexTest.xhtml output with a table of the query results attached.
Now please note well. I have run this on EclipseLink and it works, completely.
There is either some setting I'm not familiar with in ObjectDB that explains this strange behaviour, or there is another genuine bug.
Either way, this has once again prevented my attempts to complete an evaluation of ObjectDB against my real-world project that works with EclipseLink, and it has once again taken many hours of my time.
Please attend to this as soon as possible and fix this carefully.
I look forward to having this fixed and to having a robust ObjectDB working on my problem, as I am greatly encouraged by experiments last night that lead to me being able to see large portions of my web application suite loaded in the odb Explorer.
regards,
Darren Kelly (Webel IT Australia, Sydney)