JPA 2.0 Criteria Lazy Container

Starting a new thread to discuss the
JPA 2.0 Criteria Lazy Container
add-on, which is now approaching release candidate maturity.

Briefly, this add-on allows building a database container without writing any SQL, in a type-safe fashion, using the standard Java JPA 2.0 Criteria API. Arbitrary queries and groupings are supported and whatever columns returned by the query are automatically present in the container. It is also possible to perform arbitrary joins and use any property from any of the joined entites in the container. The container builds upon the well-known LazyQueryContainer.

== Release 0.4.6 ==

  • CriteriaContainer now supports nested retrieval
    container.addContainerProperty(“assignee.class”,…);
    table.setVisibleColumn(“assignee.class”);
    The items of the container will then retrieve the value of assignee.getClass() when
    accessing the “assignee.class” property.
    Any string that can be understood by Jakarta PropertyUtils.getProperty can be
    used (e.g. person.address.zipcode or even person.children[0]
    )
    However, there must be an accessor (getX() or isX()) for this to work.

== Release 0.4.5 ==

  • setKeyPropertyId() can be used to state that the container should return a key
    from the underlying data source instead of the index in the container. For example
    taskContainer.setKeyPropertyId(“taskId”) would return the taskId column. This is
    normally used when using the container with a Select, where it is useful to store
    a foreign key. NOTE: the Vaadin 6.5 implementation of Select performs a getItemIds()
    call, and is NOT lazy. This means that Select cannot be used for large data sets.
    If selecting from a large dataset, you probably need to use a Table, which does NOT
    attempt to get all the itemIds at once.

Hi Jean-François.

First of all congratulations on a very nice add-on.
Tested it for the last fiew days and I’m very pleased.

I’ve been experiencing some “detached entity passed to persist” errors while updating an entity and I’ve traced the problem to the saveItems method of the CriteriaItemHelper class.

Now this probably has to do with something that I did earlier but still I was wondering why you used :

for (Item item : modifiedItems) {
	entityManager.persist(fromItem(item));
}

Instead of:

for (Item item : modifiedItems) {
	entityManager.merge(fromItem(item));
}

The merge seams to do the trick for me but like I mentioned I’m probably doing something wrong earlier in the process.

Best Regards

Gonçalo Trindade

Oddly enough, the code was actually copied directly from the LazyQueryContainer routine it overrides, and I remember that someone had made the same comment to the original author. But since the code remained as is, and the semantics of merge() and persist() are sometimes extremely subtle, I never challenged the code. I will look at this in more detail tomorrow, prior to pushing a small bug fix I just did.

Thank you for your kind words.

Thank you for the prompt response.

As you suggested, I took a peek at LazyContainer’s EntityQuery class and the saveItems method looks like this:

  public void saveItems(final List<Item> addedItems, final List<Item> modifiedItems, final List<Item> removedItems) {
        if (applicationTransactionManagement) {
            entityManager.getTransaction().begin();
        }
        try {
            for (Item item : addedItems) {
                if (!removedItems.contains(item)) {
                    entityManager.persist(fromItem(item));
                }
            }
            for (Item item : modifiedItems) {
                if (!removedItems.contains(item)) {
                    Object entity = fromItem(item);
                    if (queryDefinition.isDetachedEntities()) {
                        entity = entityManager.merge(entity);
                    }
                    entityManager.persist(entity);
                }
            }
            for (Item item : removedItems) {
                if (!addedItems.contains(item)) {
                    Object entity = fromItem(item);
                    if (queryDefinition.isDetachedEntities()) {
                        entity = entityManager.merge(entity);
                    }
                    entityManager.remove(entity);
                }
            }
            if (applicationTransactionManagement) {
                entityManager.getTransaction().commit();
            }
        } catch (Exception e) {
            if (applicationTransactionManagement) {
                if (entityManager.getTransaction().isActive()) {
                    entityManager.getTransaction().rollback();
                }
            }
            throw new RuntimeException(e);            
        }
    }

So i’ve reproduced this in the CriteriaItemHelper class and added “private boolean detachedEntities;” to the AbstractCriteriaQueryDefinition in order to control if detached entites are used at the container’s creation (just like in LazyQueryContainer).

This works for me.

Best Regards

Gonçalo Trindade

== Release 0.5.0 ==
  * Added support for isDetachedEntities() as in LazyQueryContainer 1.2.8. If this parameter
    is set, the entities loaded are immediately detached, and a merge() is performed on
    update and remove.  Added corresponding constructors to BeanTupleQueryDefinition,
    CriteriaQueryDefinition and CriteriaContainer.
  * Improved caching of query size to avoid unnecessary calls to select count()
  * Improved defensive programming if asking to load empty or negative count of items.
  * Improved runtime error message if a CriteriaQueryDefinition is used on a BeanTupleContainer,
    since the conventions for property names are different and this can lead to hard to diagnose
    problems.

This should fix the issue reported by Gonçalo Trindade (thanks!). The improvement was made to both BeanTupleContainer and CriteriaContainer.

Also, an example has been added for a situation when you want to join two entities on a field, but do not want to declare a relationship to JPA (you don’t want to worry about fetching the linked entities, or this is an ad hoc query).

Hi Jean!

Im in troubles with Nested Properties.

If a nested property is null, it’s causing a null pointer exception, that I belive might be avoided by container. For example:

Person → Login Account → username.

Not every person needs to have a login account. It permits null on relationship.

On the Table, I wanna show the username, in the case that Person haves a Username, otherwise, shows the default value of property.

In this case, the, using addContainerProperty(loginAccount.username) causes a null pointer exception on the container.

And another little thing: Will be possible to order the table based on nested properties? For now, I can only order by directly properties.

Im using the 0.50 version, and CriteriaContainer class.

Many thanks by atention!

I just realized that a few persons had posted issues and enhancement requests to the Googlecode home for the project. Unfortunately I was not being warned on updates, something I have now fixed.

The issues were fortunately quite easy to fix and an update should be coming shortly.

The enhancements relate to filtering, something I am currently looking at in the context of Vaadin 6.6 and a current project also, so this may well work out in the coming weeks as well.

There is now an EclipseLink branch in the source tree on GoogleCode (criteriacontainer.googlecode.com) for early adopters. The checkout is a set of Eclipse projects with full Maven pom.xml, so it should build right away if you have m2eclipse installed. See the release notes under the doc directory for EclipseLink limitations - basically, EclipseLink generates very peculiar non-working SQL with multiiple Criteria Roots so you have to stick with joins (which is what most people do anyway).

I am currently working on implementing the new Filterable interface and will likely wait until that is stable to issue a real 1.0 release.

Support for the Filterable interface is now on the default branch in the source tree, for courageous souls. Vaadin filters are translated to JPA 2.0 Criteria API and processed as such. I’ve implemented all the filters present in 6.6 and 6.7; commented out the 6.7 specifics for the time being.

See the project criteriaapp_test for a couple simple example (BeanTupleContainerFilteringApplication and EntityContainerFilteringApplication).

Hello,

I have tried the EntityContainerFilteringApplication and a NullPointerException is killing me!
I’ve already tried ecliselink 2.3.0…2.4.0.

Thanks in advance!

java.lang.NullPointerException
at org.eclipse.persistence.internal.jpa.querydef.CriteriaQueryImpl.createCompoundQuery(CriteriaQueryImpl.java:543)
at org.eclipse.persistence.internal.jpa.querydef.CriteriaQueryImpl.translate(CriteriaQueryImpl.java:715)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1313)
at org.vaadin.addons.beantuplecontainer.BeanTupleQueryDefinition.getSelectQuery(BeanTupleQueryDefinition.java:214)
at org.vaadin.addons.beantuplecontainer.BeanTupleItemHelper.(BeanTupleItemHelper.java:75)
at org.vaadin.addons.criteriacontainer.CriteriaItemHelper.(CriteriaItemHelper.java:56)
at org.vaadin.addons.criteriacontainer.CriteriaQueryFactory.constructQuery(CriteriaQueryFactory.java:36)
at org.vaadin.addons.lazyquerycontainer.LazyQueryView.getQuery(LazyQueryView.java:348)
at org.vaadin.addons.lazyquerycontainer.LazyQueryView.size(LazyQueryView.java:181)
at org.vaadin.addons.beantuplecontainer.BeanTupleQueryView.init(BeanTupleQueryView.java:285)
at org.vaadin.addons.beantuplecontainer.BeanTupleQueryView.size(BeanTupleQueryView.java:274)
at org.vaadin.addons.lazyquerycontainer.LazyQueryContainer.size(LazyQueryContainer.java:159)
at org.vaadin.addons.beantuplecontainer.BeanTupleContainer.size(BeanTupleContainer.java:410)
at org.vaadin.addons.criteriacontainersample.AbstractEntityApplication.init(AbstractEntityApplication.java:97)
at com.vaadin.Application.start(Application.java:554)
at com.vaadin.terminal.gwt.server.AbstractApplicationServlet.startApplication(AbstractApplicationServlet.java:1208)
at com.vaadin.terminal.gwt.server.AbstractApplicationServlet.service(AbstractApplicationServlet.java:484)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1534)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:281)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:98)
at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:91)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:162)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:326)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:227)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:228)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:822)
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:719)
at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1013)
at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:225)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
at java.lang.Thread.run(Thread.java:662)
|#]

You need to use 2.4.0-SNAPSHOT (or 2.3.1-SNAPSHOT). EclipseLink fixed the bug on August 17 2011.
(the actual version numbers I tested with are listed in the pom.xml file. Even if you don’t use Maven, getting familiar enough with the “dependencies” section of the pom can be worthwhile).

Hello Jean-Francois

I’ve been testing your Criteria Lazy Continer for a bit. Thanks very much for all your excellent work. It appears to hold considerable promise for our application. I’ve been having some issues with detached entities.

This is the code sequence that is generating the problem:

        criteriaContainer.addEntity();

        criteriaContainer.commit();

The resulting error message is:

SEVERE: Terminal error:
com.vaadin.event.ListenerMethod$MethodException
Cause: java.lang.RuntimeException: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: helpwaredb.dbdeb1.data.TwsPatient
at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:510)
at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:164)
at com.vaadin.ui.AbstractComponent.fireEvent(AbstractComponent.java:1193)
at com.vaadin.ui.Button.fireClick(Button.java:539)
at com.vaadin.ui.Button.changeVariables(Button.java:206)
at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.handleVariableBurst(AbstractCommunicationManager.java:1299)
at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.handleVariables(AbstractCommunicationManager.java:1219)
at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.doHandleUidlRequest(AbstractCommunicationManager.java:735)
at com.vaadin.terminal.gwt.server.CommunicationManager.handleUidlRequest(CommunicationManager.java:296)
at com.vaadin.terminal.gwt.server.AbstractApplicationServlet.service(AbstractApplicationServlet.java:501)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:401)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:766)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:450)
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:945)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:756)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:410)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
Caused by: java.lang.RuntimeException: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: helpwaredb.dbdeb1.data.TwsPatient
at org.vaadin.addons.criteriacontainer.CriteriaItemHelper.saveItems(CriteriaItemHelper.java:172)
at org.vaadin.addons.lazyquerycontainer.LazyQueryView.commit(LazyQueryView.java:467)
at org.vaadin.addons.beantuplecontainer.BeanTupleQueryView.commit(BeanTupleQueryView.java:123)
at org.vaadin.addons.lazyquerycontainer.LazyQueryContainer.commit(LazyQueryContainer.java:476)
at org.vaadin.addons.beantuplecontainer.BeanTupleContainer.commit(BeanTupleContainer.java:348)
at helpwaredb.dbdeb1.TwsPatientAdmin.buttonClick(TwsPatientAdmin.java:306)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:490)
… 27 more
Caused by: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: helpwaredb.dbdeb1.data.TwsPatient
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1215)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1148)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1154)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:678)
at org.vaadin.addons.criteriacontainer.CriteriaItemHelper.saveItems(CriteriaItemHelper.java:142)
… 37 more
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: helpwaredb.dbdeb1.data.TwsPatient
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:127)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:61)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:808)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:782)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:786)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:672)
… 38 more

I would greatly appreciate your assistance in tracking this down. I’ve created a few different test modules. In some cases this seems to work fine, but in other cases it does not.

Thanks in advance for your aid.

Cheers,
Dana

Unless you explicitly use the “use detached entities” mode, the container does not detach entities. If you use that mode, the container will detach its entities on fetching, and remerge everything. I must confess not using that mode myself, it is there for compatibility with LazyQueryContainer.

If you create a container using an entity manager that is later closed, you cannot reuse that container, you need to create another one. So typically you end up with two kinds of containers – session (i.e. vaadin application) containers for things that persist from page to page, and request-level containers that get rebuilt on every invocation.

From the trace, it looks like you are editing an object; if it has been fetched from a table displayed on a previous http request, and you are editing it (on a new page), and your entity manager is allocated on each page, you may well be running into this problem.

When a (business) transaction straddles several HTTP requests, and can be performed in isolation of whatever else is happening in the database, you can allocate an entity manager for that business transactiion in that user’s application, and null it at the end. But you have to be aware that the entity manager is an object cache, and that you will need to deal with locking exceptions when you persist if you use optimistic locking. Just be aware that if you need to check a constraint on an object, you will need to refresh() it explicitly, or use another entity manager to look up the values you want “live”.

Thank you very much for your quick and thorough response. Your explanation is very helpful and much appreciated. It will be useful as I move forward with development.

For the record, it turns out that this specific issue resulted from incorrectly specifying a setter in the ENTITY definition class. It appears to be important to get those getters and setters right! Imagine that?

I was also having a similar problem in another class and that problem resulted from attempting to use an entity manager that had previously been closed.

Issue resolved, thanks to your help!