JPAContainer with CDI provided EntityManager

Hi all

I’m porting a
code
base that’s based on standard Java EE 6 with CDI, JSF2 etc over to a Vaadin UI. I’m a big fan of CDI, and even if I weren’t I’d be very reluctant to try to remove it from this codebase.

I’m using a custom CDI-enabled vaadin servlet, though I’m about to test the
CDI-Utils addon
after I found it earlier today. The servlet provides me with injection into the application and related classes, but doesn’t help with JPAContainer.

I’m trialling JPAContainer to use our existing mappings and entities with Vaadin. The app currently
@Provides EntityManager
instances using CDI alternatives to switch between different data sources and configurations for production vs test. More importantly, other parts of the app work with these entity managers - I don’t want a separate
EntityManagerFactory
just for Vaadin JPAContainer with its own separate pool of entities; the state of entities in the rest of the app should be reflected in JPAContainer. Creating a Vaadin specific
EntityManagerFactory
will also prevent injection into JPA2
EntityListener
from working once that comes out in JPA 2.2 (yay!) and waste resources on duplicate entities, mappings, etc.

So: ideally I’d like to continue to fetch our entity managers from CDI for JPAContainer. The Vaadin JPAContainer
com.vaadin.addon.jpacontainer.EntityProvider
interface doesn’t have a plug-in factory for
EntityManager
, but you can override it and provide your own
getEntityManager
impl so I’m hoping I can just subclass it, add an
@Inject EntityManager
member, have
getEntityManager
return that and have
setEntityManager
throw. If I then
@Inject
my factory subclass it should be enriched properly.

EDIT: Because a Class instance for the entity type is required, it’s better to use the EntityProvider implementation from vaadin unchanged, and supply an injected EntityManager from your Container subclass, as per the code at the end.

My main concern with this approach is that the JPAContainer documentation doesn’t say anything about whether it wants an extended or standard persistence context, what the transaction lifetimes are, etc. There’s some info in the EntityProvider subclasses, but it’s still quite limited. It’s hard to know how to design the app to interact with the container when its use of the EntityManager is somewhat opaque. I’m going to pull the sources (yay for sources!) but am hoping for an experienced opinion too.

I’ll test the simplistic approach now and see how it works. Opinions and suggestions welcome. Here’s how I’m hoping it can work:

Given some imaginary entity Customer, this might work but hasn’t been tested yet:


	public class CustomerContainer extends JPAContainer<Customer> {
		private static final long serialVersionUID = 1L;
		
		@Inject
		private EntityManager em;
		
		public CustomerContainer() {
			super(Customer.class);
		}
		
		@PostConstruct
		protected void init() {
			if (em == null) {
				throw new IllegalStateException("EntityManager not injected");
			}
			LocalEntityProvider<Customer> provider = new LocalEntityProvider<>(Customer.class, em);
			setEntityProvider(provider);
		}
	}

No go on the simple idea. It looks like JPAContainer is aggressive detaching entities or closing the entity manager (the latter seems unlikely), so lazy init doesn’t work even within the duration of the invocation of the event handler used to display the table. Demo project trace:


16:23:05,528 SEVERE [com.vaadin.Application]
 (http-localhost-127.0.0.1-8080-4) Terminal error:: com.vaadin.event.ListenerMethod$MethodException: Invocation of method buttonClick in com.example.MyToolbar failed.
	at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:530) [vaadin-6.8.0.jar:6.8.0]

	at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:164) [vaadin-6.8.0.jar:6.8.0]

	at com.vaadin.ui.AbstractComponent.fireEvent(AbstractComponent.java:1219) [vaadin-6.8.0.jar:6.8.0]

	at com.vaadin.ui.Button.fireClick(Button.java:567) [vaadin-6.8.0.jar:6.8.0]

	at com.vaadin.ui.Button.changeVariables(Button.java:223) [vaadin-6.8.0.jar:6.8.0]

	at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.changeVariables(AbstractCommunicationManager.java:1460) [vaadin-6.8.0.jar:6.8.0]

	at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.handleVariableBurst(AbstractCommunicationManager.java:1404) [vaadin-6.8.0.jar:6.8.0]

	at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.handleVariables(AbstractCommunicationManager.java:1329) [vaadin-6.8.0.jar:6.8.0]

	at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.doHandleUidlRequest(AbstractCommunicationManager.java:761) [vaadin-6.8.0.jar:6.8.0]

	at com.vaadin.terminal.gwt.server.CommunicationManager.handleUidlRequest(CommunicationManager.java:296) [vaadin-6.8.0.jar:6.8.0]

	at com.vaadin.terminal.gwt.server.AbstractApplicationServlet.service(AbstractApplicationServlet.java:501) [vaadin-6.8.0.jar:6.8.0]

	at javax.servlet.http.HttpServlet.service(HttpServlet.java:847) [jboss-servlet-api_3.0_spec-1.0.0.Final.jar:1.0.0.Final]

	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329) [jbossweb-7.0.13.Final.jar:]

	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]

	at org.jboss.weld.servlet.ConversationPropagationFilter.doFilter(ConversationPropagationFilter.java:62) [weld-core-1.1.5.AS71.Final.jar:2012-02-10 15:31]

	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:]

	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]

	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275) [jbossweb-7.0.13.Final.jar:]

	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161) [jbossweb-7.0.13.Final.jar:]

	at org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50) [jboss-as-jpa-7.1.1.Final.jar:7.1.1.Final]

	at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:153) [jboss-as-web-7.1.1.Final.jar:7.1.1.Final]

	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155) [jbossweb-7.0.13.Final.jar:]

	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [jbossweb-7.0.13.Final.jar:]

	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [jbossweb-7.0.13.Final.jar:]

	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368) [jbossweb-7.0.13.Final.jar:]

	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) [jbossweb-7.0.13.Final.jar:]

	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671) [jbossweb-7.0.13.Final.jar:]

	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930) [jbossweb-7.0.13.Final.jar:]

	at java.lang.Thread.run(Thread.java:722) [rt.jar:1.7.0_b147-icedtea]

Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.Customer.addressCollection, no session or session was closed
	at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:393) [hibernate-core-4.0.1.Final.jar:4.0.1.Final]

	at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:385) [hibernate-core-4.0.1.Final.jar:4.0.1.Final]

	at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:378) [hibernate-core-4.0.1.Final.jar:4.0.1.Final]

	at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:112) [hibernate-core-4.0.1.Final.jar:4.0.1.Final]

	at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:500) [hibernate-core-4.0.1.Final.jar:4.0.1.Final]

	at com.vaadin.addon.jpacontainer.JPAContainerItem$ItemProperty.toString(JPAContainerItem.java:181) [jpacontainer-addon-agpl-3.0-2.1.0.jar:2.1.0]

	at com.vaadin.ui.Table.formatPropertyValue(Table.java:3554) [vaadin-6.8.0.jar:6.8.0]

	at com.vaadin.ui.Table.getPropertyValue(Table.java:3506) [vaadin-6.8.0.jar:6.8.0]

	at com.vaadin.ui.Table.getVisibleCellsNoCache(Table.java:1915) [vaadin-6.8.0.jar:6.8.0]

	at com.vaadin.ui.Table.refreshRenderedCells(Table.java:1534) [vaadin-6.8.0.jar:6.8.0]

	at com.vaadin.ui.Table.attach(Table.java:3663) [vaadin-6.8.0.jar:6.8.0]

	at com.vaadin.ui.AbstractComponent.setParent(AbstractComponent.java:560) [vaadin-6.8.0.jar:6.8.0]

	at com.vaadin.ui.AbstractComponentContainer.addComponent(AbstractComponentContainer.java:211) [vaadin-6.8.0.jar:6.8.0]

	at com.vaadin.ui.AbstractSplitPanel.setSecondComponent(AbstractSplitPanel.java:159) [vaadin-6.8.0.jar:6.8.0]

	at com.example.ui.MyVaadinApplication.setScreen(MyVaadinApplication.java:69) [classes:]

	at com.example.ui.MyVaadinApplication$Proxy$_$$_WeldClientProxy.setScreen(MyVaadinApplication$Proxy$_$$_WeldClientProxy.java) [classes:]

	at com.example.ui.MyToolbar.buttonClick(MyToolbar.java:49) [classes:]

	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.7.0_b147-icedtea]

	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) [rt.jar:1.7.0_b147-icedtea]

	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [rt.jar:1.7.0_b147-icedtea]

	at java.lang.reflect.Method.invoke(Method.java:601) [rt.jar:1.7.0_b147-icedtea]

	at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:510) [vaadin-6.8.0.jar:6.8.0]

	... 28 more

Yes, JPAContainer is by default aggressive with respect to detaching entities.
Check the manual - there is an option to disable automatic detaching, but if I recall correctly you might have to e.g. handle transactions yourself in that case.

And there is a concept called LazyLoadDelagate that can be used to avoid lazy load exceptions with detached entities:
https://vaadin.com/book/-/page/jpacontainer.hibernate.html

Still, I’d consider EclipseLink. Much better suited for stateful apps.

cheers,
matti

Re the core issue of using the CDI-provided EntityManager, I’m getting good results with the approach given above, where I inject the entity manager into a JPAContainer subclass and have it create a new LocalEntityProvider with that injected entity manager during @PostConstruct. That works well, and the lazy loading issues appear unrelated to how the entity manager is provided.

I was using EclipseLink with Glassfish, but moved over to Hibernate when I switched to JBoss AS 7 as it’s the default provider. I’ve certainly seen that it’s more focused on high throughput short transaction workloads, so I’ll give EclipseLink a go. Unlike when I started with AS7 I now know it’s pretty trivial to
bundle it up in an AS7 module and add it to WEB-INF/jboss-deployment-structure.xml
so I don’t have to redeploy the provider every time I deploy the app.

Even if EclipseLink can lazy-load behind the scenes, though, that’s not something I want to have to rely on.

  1. The lazy fetch is likely to be in another transaction, so isolation is broken and inconsistent data may be fetched; and
  2. Lazy fetching is very slow compared to fetching what you want from the start, esp. as you can’t batch lazy fetches

So: If you have an entity you’re using with a JPAContainer and you know that certain lazily loaded properties are
guaranteed
to be required by the container, how do you ensure they’re loaded? Say a 1:1 mapped summary table entity or a collection you’re using to generate a column - something you can’t afford to eagerly load for all uses of the entity everywhere. Do you rely on EclipseLink loading them lazily, with all the overhead and loss of consistency guarantees involved in that? Do you extend JPAContainer to get it to eagerly load them (and if so, hints on how you do it) ? Do you give up and mark 'em eagerly loaded in the mapping, suffering big performance penalties across the rest of the app?

Hi,

I think you should be able to use QueryModifierDelegate in your container to explicitly do joins and force “eager loading”.

cheers,
matti

Do you use EclipseLink on JBoss AS 7? Or are you using it on Glassfish or some other container it’s well integrated with?

I ask because I’ve just had the most “interesting” time getting EclipseLink to play on JBoss AS 7. JTA integration didn’t work because
EclipseLink was looking the TransactionManager up in the wrong place
. Some of the docs are
pretty dodgy
. Other docs suggest that dynamic weaving doesn’t work and you
need to use static weaving
. You also have to explicitly name all the entity classes used in the PU because EclipseLink doesn’t find them using false when on AS7.

All in all, it feels like a configuration nobody uses that isn’t ready yet, and I’m wondering if it’s really worth the hassle when the code is currently persistence-provider agnostic barring a couple of Hibernate annotations.

Much appreciated, that looks ideal.

Using a QueryModifierDelegate is considerably more complex than I’d hoped because the delegate doesn’t get told enough about the query.

It appears that a JPAContainer will run more than one query for any given container access. At minimum it runs a count query followed by the proper query to fetch the desired entities. No information is passed to the delegate to tell the delegate what the purpose of a query is or what it’s expected to return, so unless that information is available elsewhere the delegate has a lot of guessing to do.

A fetch join can’t be added unconditionally to all queries. Not only would that be slow and ugly, but it breaks the count(…) aggregate queries. The delegate needs a way to tell when the entities are actually being fetched.

It appears that no type is passed to CriteriaBuilder.createQuery(…) when the query is initially created, so the result type can’t be retrieved via query.getResultType() on the CriteriaQuery<?> passed to DefaultQueryModifierDelegate.queryHasBeenBuilt(CriteriaBuilder criteriaBuilder, CriteriaQuery<?> query) . It just returns java.lang.Object, which is the specified result when no type is passed. That means you can’t tell the difference between queries intended to return the entity and unrelated queries based on the result type.

You also can’t reliably identify when entities are being fetched by examining the select list. At least in my tests, query.getSelection() just returned a org.hibernate.ejb.criteria.path.SingularAttributePath with Java result type class java.lang.Integer and no alias. This turns out to be the primary key of the table containing the entity, but it doesn’t exactly leap out and say “I’m the key for EntityType”. The count aggregate’s type is a JPA provider specific class that you can’t reliably match or test for; its only public API ancestor from the JPA spec is javax.persistence.criteria.Expression as you can see in the
Hibernate sources for org.hibernate.ejb.criteria.expression.function.AggregationFunction
.

The query root does have useful type info, so for any given Root<?> root the result of root.getModel().getJavaType() is the entity type, so the target entity can be tested for with .equals(EntityType.class). The count() queries use the same root, of course, so you can’t distinguish them that way.

For now, I’m using a couple of rules:

  • If the query root isn’t the expected entity type, presume we don’t understand what’s going on. Log a warning and take no action.
  • If query.getSelection() has isCompoundSelection() == false and is instanceof javax.persistence.criteria.Expression, presume it’s a count( ) or something else we shouldn’t mess with. Take no action.
  • If query.getResultType() is the java.lang.Object class, or if it’s the entity class, assume we’re fetching the entity. Add a fetch join.
  • Otherwise, scream for help in the logs and take no action

Needless to say this is ugly. It’d be greatly helped by having JPAContainer (specifically LocalEntityProvider) pass the entity type when it creates a Criteria query, which can be done backward compatibly without any changes to the QueryModifierDelegate interface. The LocalEntityProvider already has the query class passed to it in all possible constructor paths and accessible via entityClassMetadata.getMappedClass(), so it just has to pass that to createQuery(…) calls. I’ll send a patch.

It’d be even better if the entity provider passed an extra param that gave the QueryModifierDelegate a hint as to what the query is supposed to do - maybe an enum with entries like COUNT_ENTITIES, SELECT_ENTITIES, etc, but that requires changes to the delegate interface that’d be hard to make BC.

(BTW, it’s beginning to bug me that if I take the time to write and research a post properly I have to copy the text in my post, try to post and get a 403, reload the page, paste, and resubmit. Over-aggressive session management?)

This code correctly identifies which queries are for the entities and copes with the lack of an entity type passed to the criteria query. Unfortunately, once I have the Criteria query I’m struggling to work out any way to get @#$@#$ JPA2 to sanely eagerly load sub-entities.

It doesn’t seem like much of an ask. “For every loaded, load the normally lazily-loaded property <X.Y>”.

Preferably without forcing the database to produce a cartesian product, filter and sort it, and send the results to Hibernate for de-duplication; ie preferably by using any other strategy than left join fetch, like a subquery or a follow-up batch SELECT of the entities.

Argh, JPA. Anyhow, this at least matches the right query.

   public class EagerCustomerSummaryFetchingDelegate<EntityType> extends DefaultQueryModifierDelegate {
		
		private static final long serialVersionUID = 1L;
		
		private static final Logger logger = Logger.getLogger(EagerCustomerSummaryFetchingDelegate.class.getName());
		
		private final Class<EntityType> entityClass;
		
		public EagerCustomerSummaryFetchingDelegate(Class<EntityType> entityClass) {
			super();
			this.entityClass = entityClass;
		}
		
		@Override
		public void queryHasBeenBuilt(
				CriteriaBuilder criteriaBuilder,
				CriteriaQuery<?> query) 
		{
			// Make sure we're querying the expected entity type
			Set<Root<?>> roots = query.getRoots();
			if (roots.size() != 1) {
				logger.warning("Unhandled case: Multiple query roots " + roots);
			} else {
				Root<?> root = roots.iterator().next();
				Class<?> rootClass = root.getModel().getJavaType();
				if (!entityClass.isAssignableFrom(rootClass)) {
					logger.warning("Unexpected root type " + rootClass + ", expected entity type " + entityClass);
				} else {
					// We now know the root class is an EntityType or some EntityType subclass.
					@SuppressWarnings("unchecked")
					Root<EntityType> typedRoot = (Root<EntityType>)root;
					// The query could be for entities, or it could be an aggregate or other
					// query that doesn't return entities. Because LocalEntityProvider doesn't pass
					// the entity type to CriteriaBuilder.createQuery() we can't get the expected
					// result type with query.getResultType() and work out when entities are being
					// fetched. Instead we have to do some guesswork.
					//
					// This can be removed when getResultType() can be relied upon.
					//
					// For now: If the result type is the entity class or is exactly java.lang.Object, presume
					// entities are being fetched. Other expected results probably mean metadata queries
					// etc. This isn't very robust - we should be able to RELY on the entity class being
					// the result type - but at the moment we can't. The result type could be a specific
					// subclass of the target entity, so check for compatibility not identity.
					//
					Class<?> resultType = query.getResultType();
					if (resultType.isInstance(entityClass) || resultType.equals(java.lang.Object.class)) {
						// Best guess: we're fetching entities.
						// Add an additional query root to force eager fetching of the desired
						// properties.
						addEagerFetching(criteriaBuilder, query, typedRoot);
					}
				}
			}
			super.queryHasBeenBuilt(criteriaBuilder, query);
		}
		
		protected void addEagerFetching(CriteriaBuilder criteriaBuilder, CriteriaQuery<?> query, Root<EntityType> root) {
			// You'd hope this would work and do a 'left join fetch' in HQL terms, it fails with
                        //    org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list 
			//root.fetch("propertyToFetch");

                        // In addition, in the case of this query running a 'left join fetch' isn't viable. Combined with the sort,
                        // it takes the query from milliseconds to minutes to return a result. Again, we're stuck wanting
                        // a JPA2 equivalent of setFetchMode(SELECT), ie "fetch these with a separate query once you've
                        // got the ones you asked for.
                        //
                        // Stumped at present. 
		}
		
	}