Quarkus JPA Issues

186 views
Skip to first unread message

David Hoffer

unread,
Jan 14, 2021, 2:06:48 PM1/14/21
to Quarkus Development mailing list
We are having some issues with JPA in our Quarkus port of a Thorntail JAX-RS application.  There are a few different issues (listed below) but the basic issue is that we are getting unwanted extra DB select calls.

For all below these are in JAX-RS GET endpoints.  We use an EntityManager for each endpoint/query with DB RLS (row level security) enabled...so users will only get the data they are authorized to view.

We always optimize our queries so the full response is in one select statement.  We do this with a combination of JPA Eagar/Lazy fetch config plus named entity graphs per query.     

Problem Case 1:  If for some reason the single query is not optimized to fetch in one select then we would like the framework to not try to get any additional data.  E.g. If N+1 loading occurs just get the 1 query and not any of the N.

Problem Case 2: We have seen cases where Quarkus thinks that the results of the query is dirty after serialization so it thinks it has to commit the changes back to the DB.  In fact there is no change to the query results but even if there was since the context here is a GET request we NEVER want the DB data to change.  How to configure the EntityManager so that it never does an update after a read?

Problem Case 3: In some cases where RLS has restricted the results set for child entities or child collections of entities, Quarkus is creating additional select statements for the data that was excluded in the first/main select statement.  E.g. It was correct that the initial result set was missing the data that RLS excluded but now during serialization Quarkus is creating yet another set of select statements for ALL the missing data that it does not have access to again.  Note that in some cases the parent entity has the ID of the child entity and in some cases it does not but in both cases would be referenced via @JoinColumn() relationship.  In Thorntail when RLS blocked a record we would get a LazyInit exception for that enity calling any method so we could catch that and just return null.  In Quarkus it creates the entity with the ID set (as it does have access to that) and then when we call another method to try to get an error we get an javax.persistence.EntityNotFoundException but this is after making the select statement to try to get what we already know it does not have access to.  So we have a chicken/egg problem here.

So in all cases the bottom line is we want the EntityManger to do just the read/fetch with the one and only select statement and never make more queries and never update the DB.

How to do this?

Note in case it matters here is how we get our EntityManager...

// Each JAX-RS service class...
@Transactional(Transactional.TxType.REQUIRED)
@ApplicationScoped
@Path("xyz")

// This is class field in base class of above service. 
@Inject
@PersistenceUnit(APP_PU)
EntityManagerFactory emf;

Then in each REST endpoint we have:
EntityManager em = emf.createEntityManager();
// Then em RLS is configured here for the user's context.
// Then do query

-Dave

Sanne Grinovero

unread,
Jan 14, 2021, 3:07:41 PM1/14/21
to David Hoffer, Quarkus Development mailing list
Hi David,

for P.1: I'm not sure how to help here: Hibernate's responsibility is
to provide a faithful, transactional representation of the data in the
database - having it ignore some portion of existing data based on
some totally arbitrary rule is going to clash with the tool. Could you
be more specific about why some data should not be loaded, and maybe
frame the use case description which we could possibly evolve into a
proposal for an API improvement?

for P.2 : you have lots of options here, however most will need using
the Hibernate API, or hint properties via JPA. One is to open the
Hibernate Session into "read-only" mode. Another option is to set the
Session's flush mode to `FlushMode.MANUAL` (and then not flush). Or
you could clear it just after the query was performed: this would
effectively detach all loaded entities, and therefore we'll also stop
monitoring them for changes, making sure there's nothing to flush at
commit time. For a pure read-only scenario, the optimal solution is to
open the Session in read-only mode.

session.setHibernateFlushMode(FlushMode.MANUAL);
session.setDefaultReadOnly(true);

Query also exposes `setReadOnly`, in case you only want to set to
read-only the results of a specific query.

But I'd love to have more details about why this happens, there might
be an issue with dirty checking; compared to Thorntail it's the same
Hibernate ORM engine, but clearly it's a different version and a
different configuration; in particular a significant difference in
this area is that Quarkus enforces the use of build time enhancement.

P.3 : catching exceptions for flow control wasn't a good idea on
Thorntail either. The right solution is to use filters:
- https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#pc-filtering

Thanks,
Sanne
> --
> You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/quarkus-dev/f9393d88-12a7-4f3a-b2bf-946d6f09399fn%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages