Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attempt to Support fror @Inject StatelessSession #8861

Closed

Conversation

maxandersen
Copy link
Member

Why:

  • Sometimes you just want to do raw data access

This change addreses the need by:

  • Add StatelessSession as a possible injection.

Fixes #7148

@boring-cyborg boring-cyborg bot added the area/hibernate-orm Hibernate ORM label Apr 26, 2020
@maxandersen
Copy link
Member Author

maxandersen commented Apr 26, 2020

For now this is incomplete - basically just setup the actual @Inject option but currently does not actually do anything - thats where I need @Sanne or someone with some jpa/quarkus internal knowledge :)

a) in JPAResourceReferenceProvider I need to create a StatelessSession; should I just duplicate the exact same infrastructure as done for EntityManager? i.e. TransationStatelessSessionManagers, ForwardingEntityManager, RequestScopeStatelessSessionHolder, etc. or is that overkill ?

b) for now I could only get injection to work if there is a Entity present - didn't find a good way to have it allow when 0 entites but I reckon we can do that in a future update.

@maxandersen maxandersen requested a review from Sanne April 26, 2020 08:54
@geoand
Copy link
Contributor

geoand commented May 15, 2020

@maxandersen what is missing now for this?

Why:

 * Sometimes you just want to do raw data access

This change addreses the need by:

 * Add StatelessSession as a possible injection.

Signed-off-by: Max Rydahl Andersen <manderse@redhat.com>
@maxandersen
Copy link
Member Author

I've updated to just include a basic producer method + basic docs.

Remaining two questions:

  1. will stateless session with just the mere producer automtically start or participate in active transaction ?
  2. currently this requires at least one Entity present - this is annoying since statelessSession and even EntityManager can be used for pure scalar/DTO queries and processing. How can we enable that ? /cc @Sanne

@maxandersen maxandersen marked this pull request as ready for review May 25, 2020 12:03
@maxandersen
Copy link
Member Author

marking as for review as conceptually its done but I need some feedback on how tx's are handled in these layers.

@maxandersen
Copy link
Member Author

@emmanuelbernard look here :)


@Transactional
public void queryGifts(String giftDescription) {
ScrollableResults gifts = session.getQuery("from Gift")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is getQuery?

=== Processing raw or large amount of data

`StatelessSession` are supported too and can be injected and used standalone or inter-mixed with your `EntityManager`.
The benefits of `StatelessSession` are that entities are not put in a session-level cache, allowing for more raw and in some cases higher performance
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think of it as read and forget.


`StatelessSession` are supported too and can be injected and used standalone or inter-mixed with your `EntityManager`.
The benefits of `StatelessSession` are that entities are not put in a session-level cache, allowing for more raw and in some cases higher performance
data access. Especially useful when reading/processing large amount of data.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and avoiding OutOfMemoryException while processing the data.

--
<.> Inject your stateless session and have fun; it will open or reuse the active transaction.
<.> Using `scroll()` means you can process the data without loading the complete resultset or list of data in memory.
<.> Since using a stateless session you need to explicitly call `update()` to save changes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<.> Since using a stateless session you need to explicitly call `update()` to save changes.
<.> a stateless session does not remember your objects, you need to explicitly call `update()` to save changes.

return transactionEntityManagers.getEntityManager(pc.unitName());
}
};
return () -> entityManager;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like something fishy here, shouldn't you return unwrap(SessionFactory.class).openStatelessSession(). in this case? I fail to see how the right object is injected otherwise.

CC @mkouba

Copy link
Contributor

@mkouba mkouba Jun 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this should be used for @PersistenceContext(unitName="foo") StatelessSession injection, right? But we don't support multiple PUs yet, correct?

If possible, I'd remove this code completely because ResourceReferenceProvider should only be used for non-CDI EE resource injection. In this case, it would be probably better to use a custom qualifier or something like that...

@maxandersen What the use case for @PersistenceContext StatelessSession?

@emmanuelbernard
Copy link
Member

To answer your question @maxandersen, yes I think your stateless session will join the existing transaction if I read the code correctly.

@emmanuelbernard
Copy link
Member

But you have me puzzled, I did not know StatelessSession was about "entity-less" Hibernate. Can you show examples of how it would be used?

@maxandersen
Copy link
Member Author

But you have me puzzled, I did not know StatelessSession was about "entity-less" Hibernate. Can you show examples of how it would be used?

To get access to do native queries with simple data mapping layer without having to setup/configure yet another framework. Hibernate has all the apis needed already.

I could technically use a Session/EntityManager to kick of most native queries but:

a) they come with an overhead that is not used for raw data access
b) they currently require at least 1 entity to be created to be activated.

With statelessSession have the following advantages:

a) I can do raw data access with no to little overhead with mapping to scalars and DTO's
b) I can do both hql and sql queries and map into already mapped entites but with much much higher throughput and low memory usage than a traditional session/entitymanager can do
c) it makes for nice usage of things like jooq as a query builder (also relevant for entitymanager but if performance and simplicty is your concern statelessSession wins)
d) I can reuse the mappings/queries in stateless as well as statefull sessions (I see it as mix and matching persistence/query approaches like we mix and match imperative and reactive)

Does that help or where you asking for some more code examples beyond what i put in the basic docs?

@Sanne
Copy link
Member

Sanne commented Jun 3, 2020

@maxandersen scalar queries won't return managed objects, so wouldn't you have the same benefits you listed with any regular Session ?

The Session implementation is mostly lazy, it will only allocate its state-holding infrastructure on demand.

@maxandersen
Copy link
Member Author

@maxandersen scalar queries won't return managed objects, so wouldn't you have the same benefits you listed with any regular Session ?

The Session implementation is mostly lazy, it will only allocate its state-holding infrastructure on demand.

I can't do raw update/insert with EntityManager/Session. thus batch/bulk operations not as effective by long shot.

@Sanne
Copy link
Member

Sanne commented Jun 3, 2020

I can't do raw update/insert with EntityManager/Session. thus batch/bulk operations not as effective by long shot.

I'm not questioning the usefulness of StatelessSession - but you won't do a "raw update/insert" on a scalar or an unmapped type? I'm not sure if you've got me completely confused on the idea, a runnable test would be great.

@emmanuelbernard
Copy link
Member

But you have me puzzled, I did not know StatelessSession was about "entity-less" Hibernate. Can you show examples of how it would be used?

To get access to do native queries with simple data mapping layer without having to setup/configure yet another framework. Hibernate has all the apis needed already.

I could technically use a Session/EntityManager to kick of most native queries but:

a) they come with an overhead that is not used for raw data access
b) they currently require at least 1 entity to be created to be activated.

With statelessSession have the following advantages:

a) I can do raw data access with no to little overhead with mapping to scalars and DTO's
b) I can do both hql and sql queries and map into already mapped entites but with much much higher throughput and low memory usage than a traditional session/entitymanager can do
c) it makes for nice usage of things like jooq as a query builder (also relevant for entitymanager but if performance and simplicty is your concern statelessSession wins)
d) I can reuse the mappings/queries in stateless as well as statefull sessions (I see it as mix and matching persistence/query approaches like we mix and match imperative and reactive)

Does that help or where you asking for some more code examples beyond what i put in the basic docs?

Thanks, that helps a lot and I would really like to see concrete code examples. It will hopefully make the value more concrete to me.

@maxandersen
Copy link
Member Author

I'm not questioning the usefulness of StatelessSession - but you won't do a "raw update/insert" on a scalar or an unmapped type? I'm not sure if you've got me completely confused on the idea, a runnable test would be great.

There are two main usescases for stateless session that EntityManager can't do - below they are in my personal "priority":

  1. doing batch operation (effeciently) on Entities
  2. gives a no-entities needed at all persistence API for those who like spring jdbc style approach

And yes, #2 can technically be done with Session/EntityManager but the API you get access to assumes you have entities majority of the time. Additionally my hope was that where we now don't start EntityManagerFactory at all when zero entities I was thinking that @Inject StatelessSession could be used to detect we should start up anyways.

#1 for me alone is more than enough to justify easy access to StatelessSession and not have every app implement a @produces StatelessSession ....

@Sanne
Copy link
Member

Sanne commented Jun 4, 2020

@maxandersen I agree that ther are two. I was just pointing out that in your explanation you seemed to imply that one needs the other, which is what I think was confusing Emmanuel.

doing batch operation (effeciently) on Entities

BTW we should really not call it "batch" as the NON-stateless Session can actually do batching, which is in fact a massive optimisation over what one can achieve via the stateless session, as it can reduce the number of DB roundtrips by orders of magnitude.

I think we agree on all points, just suggesting improvements on how to call these things and the explanations.

@emmanuelbernard
Copy link
Member

I'm not questioning the usefulness of StatelessSession - but you won't do a "raw update/insert" on a scalar or an unmapped type? I'm not sure if you've got me completely confused on the idea, a runnable test would be great.

There are two main usescases for stateless session that EntityManager can't do - below they are in my personal "priority":

1. doing batch operation (effeciently) on Entities

2. gives a no-entities needed at all persistence API for those who like spring jdbc style approach

And yes, #2 can technically be done with Session/EntityManager but the API you get access to assumes you have entities majority of the time. Additionally my hope was that where we now don't start EntityManagerFactory at all when zero entities I was thinking that @Inject StatelessSession could be used to detect we should start up anyways.

#1 for me alone is more than enough to justify easy access to StatelessSession and not have every app implement a @produces StatelessSession ....

The reason we don't start is is that we don't want ti to fail in the face of a new user. I forgot if it failed because zero entity were in or simply because without DB config, then Hibernate can't start.
If the later (most likely), then we can try and make it start if some StatelessSession or even EntityManager is injected. Because in the end that would be an error as the user expressed the will to get the (initialized) EntityManager.

@mkouba I wonder how at build time, we could detect that the application has @Inject EntityManager directly or indirectly. And when we will support multiple persistence units, we would need to take qualifiers into account. We make the decision today in HibernateOrmProcessor#configurationDescriptorBuilding which is a @BuildStep.

public class DefaultStatelessSessionProducer {

@Inject
EntityManagerFactory em;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would probably fail if a custom EntityManagerFactory producer is present (DefaultEntityManagerFactoryProducer is only registered if such producer does not exist).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would it fail?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, sorry. It would fail if a user-provided producer declares some qualifier... such as @Documents in:

@Produces @PersistenceUnit(unitName="documents") @Documents EntityManagerFactory factory;

But it's true that there is no reason to declare a producer like this if only one PU is supported...


@Transactional
public void queryGifts(String giftDescription) {
ScrollableResults gifts = session.getQuery("from Gift")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ScrollableResults gifts = session.getQuery("from Gift")
ScrollableResults gifts = session.createQuery("from Gift")

@xumk
Copy link
Contributor

xumk commented Sep 24, 2020

@gastaldi @emmanuelbernard @mkouba @Sanne
@maxandersen
Hello guys! Can I help you complete this PR or task #7148? It so more important for my work project. I can try to do it on the weekends

@Sanne
Copy link
Member

Sanne commented Sep 25, 2020

@gastaldi @emmanuelbernard @mkouba @Sanne
@maxandersen
Hello guys! Can I help you complete this PR or task #7148? It so more important for my work project. I can try to do it on the weekends

Hi! sure, a little help here would be awesome. I'm not sure why Max didn't finish it :)

I believe this is what's missing:

  • rebasing it (or starting over if you prefer)
  • make @Inject work (unfortunately I'm not good to help with this one but I assume it shouldn't be too hard, especially as other team members are always very helpful should you have question)
  • please take into account that Quarkus now supports multiple persistece units, so you'll need some way for injections to be able to use a qualifier
  • figure out how the StatelessSession you're opening is going to participate in the current transaction, and require checking for transaction; if you look how the injectiong of EntityManager works that might give an idea. Alternatively, maybe you don't want it to join the same transaction? But this is tomething that would require a little thinking and exploration, maybe some more tests would help?
  • Documentation and Integration tests

Specifically, I would recommend to not work on making it possible to start Hibernate ORM without any entities; Max wanted to do that as well but we should keep that as a separate issue, separate patch and separate discussion.

Thanks!

@maxandersen
Copy link
Member Author

I didn't finish it as awaiting response from @mkouba on how to do the inject work :)

If someone else want to give it a try feel very welcome!

@mkouba
Copy link
Contributor

mkouba commented Sep 29, 2020

@maxandersen I'm sorry what's the exact problem? I added some comments few months ago but I'm a bit lost in comments now... Also it seems that we do support multiple PUs now, or?

@gastaldi gastaldi added the triage/needs-rebase This PR needs to be rebased first because it has merge conflicts label Oct 13, 2020
@gastaldi
Copy link
Contributor

Closing as this needs to be rebased. Reopen when Sanne's comments in #8861 (comment) are addressed

@gastaldi gastaldi closed this Oct 20, 2020
@gsmet gsmet added the triage/invalid This doesn't seem right label Oct 27, 2020
@geoand
Copy link
Contributor

geoand commented Jan 25, 2023

figure out how the StatelessSession you're opening is going to participate in the current transaction, and require checking for transaction; if you look how the injectiong of EntityManager works that might give an idea

@Sanne any pointers on where this code is?

Unless I understood your list above incorrectly, I think this is something we can deliver without a terrible amount of effort.

@Sanne
Copy link
Member

Sanne commented Jan 25, 2023

@geoand have a look into how a Session is wrapped to get scoped via e.g. https://github.com/Sanne/quarkus/blob/bdc776daa8bda180948dce0baa1d06acde9cbd3e/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/session/TransactionScopedSession.java

N.B. this risks to be fairly different with ORM 6 - perhaps it's the worst time to want to revive this issue :/

@geoand
Copy link
Contributor

geoand commented Jan 25, 2023

N.B. this risks to be fairly different with ORM 6 - perhaps it's the worst time to want to revive this issue :/

Understood. Let's discuss again when ORM 6 has been merged as I would really like to have this feature for Quarkus 3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/documentation area/hibernate-orm Hibernate ORM triage/invalid This doesn't seem right triage/needs-rebase This PR needs to be rebased first because it has merge conflicts
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Have a way to get access to EntityManager even without entities
8 participants