jakartaee / persistence

https://jakartaee.github.io/persistence/
Other
191 stars 55 forks source link

define persistence.xml elements for use in dependency injection #460

Closed gavinking closed 5 months ago

gavinking commented 11 months ago

Draft spec for direct CDI integration.

see #377, #456, #309 and even #3, #46, #72

scottmarlow commented 9 months ago

Am I correct that as the title mentions, this is for the EE/JPA container to implement? Or is it better for each Persistence Provider to implement as per more recent comments?

arjantijms commented 9 months ago

Or is it better for each Persistence Provider to implement as per more recent comments?

To my mind it would be much better if the Persistence Provider (Jakarta Persistence implementation) implemented this. E.g. Hibernate, OpenJPA and EclipseLink should be able to implement this.

If this has to be implemented by the EE container's internal integration code (e.g. that of WildFly, TomEE, GlassFish) then IMHO it would mean the Jakarta EE APIs (Jakarta CDI, Jakarta Persistence, etc) are missing something.

scottmarlow commented 9 months ago

Or is it better for each Persistence Provider to implement as per more recent comments?

To my mind it would be much better if the Persistence Provider (Jakarta Persistence implementation) implemented this. E.g. Hibernate, OpenJPA and EclipseLink should be able to implement this.

I'm not exactly sure of how Hibernate, EclipseLink, OpenJPA, DataNucleus would deal with the various chicken/egg timing issues that require specialized EE/JPA container code for doing what the EE containers have been doing for a long time in terms of deployment (WildFly does work better with Hibernate due to supported extensions as Steve mentioned earlier). Thanks for helping me to remember this. :-)

arjantijms commented 9 months ago

I'm not exactly sure of how Hibernate, EclipseLink, OpenJPA, DataNucleus would deal with the various chicken/egg timing issues that require specialized EE/JPA container code for doing what the EE containers have been doing for a long time

Well, for one, I'm still not convinced that such "specialized EE/JPA container code for doing what the EE containers have been doing for a long time" should be absolutely needed. What is that specialised code really doing when you take a closer look?

I know we have it in GlassFish, but when reimplementing integration in Piranha I found there barely was a need for it. The Jakarta Persistence implementation (EclipseLink in our case) could do everything just as well. The only small detail was that it defaults to SE mode, where the transactional settings are different by default. A simple patch lets it return the persistence unit in EE mode, without any involvement of tons of Piranha internal code.

But actually, that all is another issue; it's why containers historically needed (or think they need?) tons and tons of container specific code.

The issue here is much smaller than that.

This issue doesn't seek to get rid of all that integration code. It just asks why Jakarta Persistence implementations can't take care of the @Inject EntityManager case.

scottmarlow commented 9 months ago

Well, for one, I'm still not convinced that such "specialized EE/JPA container code for doing what the EE containers have been doing for a long time" should be absolutely needed. What is that specialised code really doing when you take a closer look?

In addition to parsing and dealing with the persistence.xml to prepare for calling https://jakarta.ee/specifications/persistence/3.1/apidocs/jakarta.persistence/jakarta/persistence/spi/persistenceprovider#createContainerEntityManagerFactory(jakarta.persistence.spi.PersistenceUnitInfo,java.util.Map) and dealing with https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1#container-responsibilities, WildFly is coordinating the right time for certain other things that Steve mentioned above that there are separate issues for standardizing (or not if others aren't interested which was mostly the case 8 years ago except for a few vendors). The extensions deal with calling a Hibernate callback after the CDI Bean Manager is started and available for use. As well as coordinating with application defined datasource (e.g. WFLY-2727) initialization in a way that ensures that bytecode enhancement occurs.

I know we have it in GlassFish, but when reimplementing integration in Piranha I found there barely was a need for it. The Jakarta Persistence implementation (EclipseLink in our case) could do everything just as well. The only small detail was that it defaults to SE mode, where the transactional settings are different by default. A simple patch lets it return the persistence unit in EE mode, without any involvement of tons of Piranha internal code.

IMO, that would be mostly a SE mode type integration that works with JTA which is interesting but not exactly a new thing as EE applications can do that via SE mode bootstrapping, except you are dealing with the injection but not in a EE compatible way.

But actually, that all is another issue; it's why containers historically needed (or think they need?) tons and tons of container specific code.

You are comparing EE vs SE application requirements but still there isn't tons and tons of container specific code, but there is a contract that defines the EE/Persistence container contract versus the persistence provider contract that is important for EE application compatibility, which I think we would lose if we follow what you pushing for here.

The issue here is much smaller than that.

This issue doesn't seek to get rid of all that integration code. It just asks why Jakarta Persistence implementations can't take care of the @Inject EntityManager case.

I think applications would lose the benefits of https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1#container-responsibilities which means their applications won't work the same. Application compatibility is as I understand it, very important.

arjantijms commented 9 months ago

In addition to parsing and dealing with the persistence.xml

I'm not really sure why the container has to do this. Why would an implementation not be capable of parsing persistence.xml?

In Jakarta Faces, it's the implementation that does exactly this. E.g. Mojarra or MyFaces parses faces-config.xml.

arjantijms commented 9 months ago

IMO, that would be mostly a SE mode type integration that works with JTA which is interesting but not exactly a new thing as EE applications can do that via SE mode bootstrapping, except you are dealing with the injection but not in a EE compatible way.

"the injection but not in a EE compatible way.". Could you elaborate on that?

In a Jakarta Faces implementation we also do injection in non-CDI beans (injecting CDI, EJB, Resource), but we don't need to have knowledge of Jakarta Faces in the container.

(we did had some SPIs that the container could provide implementations of for scanning and injection, but these are barely needed anymore these days)

arjantijms commented 9 months ago

I think applications would lose the benefits of https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1#container-responsibilities which means their applications won't work the same.

I don't understand that at all.

First if @Inject EntityManager would only be a small wrapper over getting the EntityManager "the old way", why would anything change? Which benefits would applications lose, and why would they not work the same?

Injecting the EntityManager via CDI is a new feature, so how can anything change? That code would always be new, right?

And secondly, the "container-responsibilities" seem rather mundane anyway. With CDI, a transaction scope, and Interceptors around, why can't the Jakarta Persistence implementation not simply create and close an entity manager? Why is this something a container would only be able to do?

I've been scanning a little through the integration code in GlassFish, and most things seem to be from the early JPA days and focussed on EJB (the "container-responsibilities" text also uses EJB terminology).

If there might be timing or ordering issues with interceptors and transactions, should we not focus on fixing these? This is what I meant earlier with IFF something would only be possible at the container level, it might signal a bug or omission in the Jakarta APIs.

scottmarlow commented 9 months ago

@arjantijms I am referring to the current Persistence contracts which I linked. It sounds like you are more in favor of applications using application-managed entity manager via @Inject EntityManagerFactory which gives the application the same full control over any created EntityManager as discussed in https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1#a11465

sebersole commented 9 months ago

It's only pseudo code. The EntityManagerFactory itself, I guess, will be either statically available, or reside within application scope.

I mean, if the expected "solution" is to hold the EMF in a static variable (singleton) or some such, I just don't see that as a solution.

sebersole commented 9 months ago

The problem is with enhancement of JPA entity classes. Hibernate/JPA has to register a ClassTransformer before any user code can run, which is the first phase of the Hibernate bootstrap. Then, after CDI booted completely i.e. when the AfterDeploymentValidation event is fired and the BeanManager can be used to retrieve beans, the second phase of the Hibernate bootstrap has to be initiated, which is to actually eagerly build an EntityManagerFactory.

That's a different topic.

How the CDI integration works is what's being discussed here. The problem there is not enhancement. It's a question of handling the chicken-egg problem of CDI needs an EMF, but the EMF needs CDI

scottmarlow commented 9 months ago

@arjantijms I am referring to the current Persistence contracts which I linked. It sounds like you are more in favor of applications using application-managed entity manager via @Inject EntityManagerFactory which gives the application the same full control over any created EntityManager as discussed in https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1#a11465

Responding to myself to state that I am favor of the current pull request which refers to container-managed entity manager being available for injection via CDI. For applications that do not want a container-managed entity manager should inject a EntityManagerFactory instead so they obtain an application-managed entity manager which would be equivalent to a SE bootstrap style persistence context.

scottmarlow commented 9 months ago

I'm giving my +1 approval of this pr and https://github.com/jakartaee/platform/pull/746 as I like the idea of the change, regardless of which pull request is used.

arjantijms commented 9 months ago

I am referring to the current Persistence contracts which I linked. It sounds like you are more in favor of applications using application-managed entity manager via @Inject EntityManagerFactory which gives the application the same full control over any created EntityManager as discussed

No, that's something else really.

I'm just wondering why the implementation code for the existing contracts HAS to reside within WildFly, GlassFish, etc and cannot reside in Hibernate, EclipseLink, etc.

I guess the "simple" reason is that when these contracts were designed, there were no public APIs in Java EE to do what they asked. So it had to be implemented with integration code.

But with today's transaction scope and CDI, it's much easier to create an entity manager once during a transaction upon first access. That is exactly what the create() method of a Bean does. Same for closing it.

arjantijms commented 9 months ago

It's a question of handling the chicken-egg problem of CDI needs an EMF, but the EMF needs CDI

Is that really a chicken-egg problem?

If a Bean creates an EntityManager using the EnitityManagerFactory, CDI is available at that point, and the EnitityManagerFactory can use it. The resulting EntityManager can then be returned.

In Jakarta Security a similar thing happens. CDI needs an AuthenticationMechanism, and the AuthenticationMechanism needs CDI:

authenticationMechanismBean = new CdiProducer<HttpAuthenticationMechanism>()
                    .scope(ApplicationScoped.class)
                    .types(Object.class, HttpAuthenticationMechanism.class)
                    .addToId(FormAuthenticationMechanismDefinition.class)
                    .create(e -> {
                        FormAuthenticationMechanism authMethod = CdiUtils.getBeanReference(FormAuthenticationMechanism.class);

                        authMethod.setLoginToContinue(
                            LoginToContinueAnnotationLiteral.eval(formAuthenticationMechanismDefinition.loginToContinue()));

                        return authMethod;
                    });
arjantijms commented 9 months ago

Responding to myself to state that I am favor of the current pull request which refers to container-managed entity manager being available for injection via CDI.

It might be that you're mixing up thing here.

It's not about the difference between a container-manager entity manager and an application-managed entity managed.

The difference is where the implementation code to handle a container-manager entity manager should or can reside, and specifically if an implementation like Hibernate is capable of providing a container-manager entity manager via @Inject.

If it's really true that only special container code can add a Bean for @Inject to inject, and a component library such as Hibernate cannot use the CDI API for this, we really have other problems in our APIs.

But I simply don't think this is the case.

I think we're just misunderstanding each other here.

arjantijms commented 9 months ago

It's only pseudo code. The EntityManagerFactory itself, I guess, will be either statically available, or reside within application scope.

I mean, if the expected "solution" is to hold the EMF in a static variable (singleton) or some such, I just don't see that as a solution.

It was just an example. It has to be kept "somewhere". Where do you keep the EntityManagerFactory currently?

arjantijms commented 9 months ago

IMO, that would be mostly a SE mode type integration that works with JTA which is interesting but not exactly a new thing as EE applications can do that via SE mode bootstrapping, except you are dealing with the injection but not in a EE compatible way.

"the injection but not in a EE compatible way.". Could you elaborate on that?

@scottmarlow I feel this is still a very important question. What exactly is "injection in an EE compatible way" here?

scottmarlow commented 9 months ago

IMO, that would be mostly a SE mode type integration that works with JTA which is interesting but not exactly a new thing as EE applications can do that via SE mode bootstrapping, except you are dealing with the injection but not in a EE compatible way.

"the injection but not in a EE compatible way.". Could you elaborate on that?

@scottmarlow I feel this is still a very important question. What exactly is "injection in an EE compatible way" here?

Is it okay if we discuss on the Persistence mailing list as I think others may be interested in the discussion and I think we are getting off topic with regard to this issue IMO.

arjantijms commented 9 months ago

Is it okay if we discuss on the Persistence mailing list as I think others may be interested in the discussion and I think we are getting off topic with regard to this issue IMO.

Sure, looking forward to the discussion there :)

beikov commented 9 months ago

The problem is that no JPA managed type class may be loaded until the JPA provider has called PersistenceUnitInfo#addTransformer, because that ClassTransformer is what does the bytecode enhancement on demand when loading classes. This method is called from within PersistenceProvider#createContainerEntityManagerFactory which creates the entity manager factory and hence validates and boots up the JPA provider. During that boot process, the JPA provider must be able to access beans through the BeanManager for CDI integration to work.

Since CDI potentially loads JPA managed classes, it must be ensured that PersistenceUnitInfo#addTransformer is called before that happens, so the JPA bootstrap must happen first, before CDI does any class loading. This means the JPA bootstrap will run with a "partial" BeanManager and it is not possible to lookup beans. This is the reason why there exists a special integration in Wildfly and potentially also other containers.

mnriem commented 7 months ago

My understanding here is that @arjantijms is asking for the extension / integration points to be formalized so how the integration is done is specified and well understood from the perspective of an integrator.

gavinking commented 5 months ago

@lukasj I've made the changes we discussed. WDYT?

gavinking commented 5 months ago

Thanks man.