Skip to content

Propagate LCEMFB.packagesToScan to native PersistenceProvider to pick up annotated packages [SPR-10910] #15538

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

Closed
spring-projects-issues opened this issue Sep 12, 2013 · 6 comments
Assignees
Labels
in: data Issues in data modules (jdbc, orm, oxm, tx) type: enhancement A general enhancement
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Sep 12, 2013

Joris Kuipers opened SPR-10910 and commented

I'm using LocalContainerEntityManagerFactoryBean#setPackagesToScan without a persistence.xml in combination with Hibernate. This works great for annotated entities, but fails to pick up annotated packages. Case in point: I'm using an @FilterDef that I don't want to have to repeat on multiple entities, so I've placed it on a package.
This works with a persistence.xml as Hibernate does some additional work when building the PersistenceUnitInfo then, but I would like to remain persistence.xml-less if possible.

From what I can see, the JPA PersistenceUnitInfo (which Spring creates for me in this case) simply doesn't provide support to pass in annotated packages. However, there is (at least for Hibernate) a workaround: from inside the HibernatePersistenceProvider you can call Ejb3Configuration#addPackage to have Hibernate pick up the annotated packages. So, for now I'm subclassing the HibernatePersistenceProvider and override createContainerEntityManagerFactory(PersistenceUnitInfo, Map) like this:

public class SmartHibernatePersistence extends HibernatePersistence {
	
	private String[] annotatedPackages = new String[0];
	
	/**
	 * Overridden to allow specifying annotated packages. Without this, when you use Spring's
	 * {@link LocalContainerEntityManagerFactoryBean#setPackagesToScan(String...)} package-level
	 * annotations are not picked up. 
	 */
	public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map properties) {
		Ejb3Configuration cfg = new Ejb3Configuration();
		for (String annotatedPackage: annotatedPackages) {
			cfg.addPackage(annotatedPackage);
		}
		Ejb3Configuration configured = cfg.configure( info, properties );
		return configured != null ? configured.buildEntityManagerFactory() : null;
	}
	
	public void setAnnotatedPackages(String... annotatedPackages) {
		this.annotatedPackages = annotatedPackages;
	}

}

and then call LCEMFB#setPersistenceProvider with a configured instance of this class.

That does however require me to specify all my annotated packages (there is no recursive scanning here).
It would be way cooler if Spring would recursively scan for annotated packages when using LCEMFB#setPackagesToScan and would then pass them on to a Spring-provided extension of the native JPA provider's PersistenceProvider implementation that knows how to register these annotated packages: for example, by having it's own subtype of the PersistenceProvider interface that is 'package aware' that is supported in addition to the regular PersistenceProvider, with implementations for the common JPA providers.

So, basically, that's my feature request ;)

I haven't checked how this works for other JPA providers, but I assume they should provide similar ways to deal with annotated packages when building up their own EntityManagerFactoryBean from their configuration.


Issue Links:

Referenced from: commits 591f795, 0232739

4 votes, 7 watchers

@spring-projects-issues
Copy link
Collaborator Author

Joris Kuipers commented

BTW, after upgrading to the latest Hibernate (4.3.0.Final) I updated my code to this, as the Ejb3Configuration class has been removed and the HibernatePersistence is deprecated in favor of HibernatePersistenceProvider:

public class SmartHibernatePersistence extends HibernatePersistenceProvider {

    private String[] annotatedPackages = new String[0];

    /**
     * Overridden to allow specifying annotated packages. Without this, when you use Spring's
     * {@link LocalContainerEntityManagerFactoryBean#setPackagesToScan(String...)} package-level
     * annotations are not picked up.
     */
    @Override
    public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map properties) {
        return new EntityManagerFactoryBuilderImpl(new PersistenceUnitInfoDescriptor(info), properties) {
            @Override
            public Configuration buildHibernateConfiguration(ServiceRegistry serviceRegistry) {
                Configuration configuration = super.buildHibernateConfiguration(serviceRegistry);
                for (String annotatedPackage : annotatedPackages) {
                    configuration.addPackage(annotatedPackage);
                }
                return configuration;
            }
        }.build();
    }

    public void setAnnotatedPackages(String[] annotatedPackages) {
        this.annotatedPackages = annotatedPackages;
    }

}

@spring-projects-issues
Copy link
Collaborator Author

Gunnar Hillert commented

Thanks Joris for posting this! Ran into this myself after removing persistence.xml for a project that had Package-level Hibernate annotations. Your work-around worked great with Hibernate 4.3.5.Final

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

Implemented through a getManagedPackages() method in our SmartPersistenceUnitInfo now, with DefaultPersistenceUnitManager populating it, and HibernateJpaVendorAdapter setting a custom PersistenceProvider subclass which evaluates it. To bridge between Hibernate 3.6-4.2 and 4.3 there, we have distinct SpringHibernate[Ejb/Jpa]PersistenceProvider classes, reflectively chosen by HibernateJpaVendorAdapter depending on the version of Hibernate on the classpath.

This hasn't been tested outside of our test suite yet. Would be great if you could give it a try with the next 4.1 snapshot, Joris and Gunnar...

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Joris Kuipers commented

I just tested this using the project that I developed this extension for, and I can confirm (I added some breakpoints in the new Spring code) that my annotated package is picked up correctly and passed on to the SmartPersistenceUnitInfo. The code in the SpringHibernateJpaPersistenceProvider (and SpringHibernateEjbPersistenceProvider) is the code that I wrote, so that was battle-tested already ;)

Maybe the LCEMFB#setPacakgesToScan JavaDoc should mention that annotated packages will be picked up when using Hibernate as the JPA provider. currently it states that only entity classes will be scanned for.

Thanks for addressing this feature request!

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

Good point about the javadocs: added it to setPackagesToScan as well as to HibernateJpaVendorAdapter's class-level description. Also, you're officially a co-author of the SpringHibernate*PersistenceProvider subclasses now :-)

BTW, it seems that neither EclipseLink nor OpenJPA support package-level metadata to begin with. As a consequence, there's no need for managed package support in EclipseLinkJpaVendorAdapter and OpenJpaVendorAdapter either.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

Interestingly, Hibernate 4.3 supports package names in a PersistenceUnitInfo's getManagedClassNames List: It simply tries to resolve it as a class name first, then as a package name (finding a package-info.class file). So in principle, we could design SpringHibernateJpaPersistenceProvider to use a PersistenceUnitInfoDescriptor subclass and merge the getManagedClassNames and getManagedPackages result there, exposing the result to Hibernate via the getManagedClassNames List that it receives eventually.

The resulting code isn't nicer than the current arrangement. It's just likely going to be more future-proof: In the current Hibernate 5.0 snapshot, there is no buildHibernateConfiguration method (and no equivalent thereof either) for use in SpringHibernateJpaPersistenceProvider anymore... But there's still the code that processes package names in the getManagedClassNames List. So we may eventually have to switch to that variant in SpringHibernateJpaPersistenceProvider, once Hibernate 5.0 goes final, in order to avoid a third variant of our PersistenceProvider subclass there: After all, SpringHibernateJpaPersistenceProvider can happily serve for both Hibernate 4.3 and 5.0 then. FWIW, Hibernate 5.0 alpha release are not likely to appear before autumn, so probably straightforward to track in our Spring Framework 4.2 timeline.

Juergen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: data Issues in data modules (jdbc, orm, oxm, tx) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants