Programmatically loading Entity classes with JPA 2.0?
Solution 1:
With the help of Spring I did this in a JPA compliant way.
My "persistence.xml" looks empty, with no entities listed within the <persistence-unit>
element.
I then wrote a class that implemented PersistenceUnitPostProcessor like so:
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
import org.reflections.Reflections;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
public class ReflectionsPersistenceUnitPostProcessor implements PersistenceUnitPostProcessor {
private String reflectionsRoot;
private Logger log = LoggerFactory.getLogger(ReflectionsPersistenceUnitPostProcessor.class);
@Override
public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) {
Reflections r = new Reflections(this.reflectionsRoot, new TypeAnnotationsScanner());
Set<String> entityClasses = r.getStore().getTypesAnnotatedWith(Entity.class.getName());
Set<String> mappedSuperClasses = r.getStore().getTypesAnnotatedWith(MappedSuperclass.class.getName());
for (String clzz : mappedSuperClasses)
{
pui.addManagedClassName(clzz);
}
for (String clzz : entityClasses)
{
pui.addManagedClassName(clzz);
}
}
public String getReflectionsRoot() {
return reflectionsRoot;
}
public void setReflectionsRoot(String reflectionsRoot) {
this.reflectionsRoot = reflectionsRoot;
}
}
Then I adjusted my spring context xml like this:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" />
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
</bean>
</property>
<property name="persistenceUnitName" value="GenericPersistenceUnit"/>
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml"/>
<property name="persistenceUnitPostProcessors">
<list>
<bean class="com.austinmichael.core.repository.ReflectionsPersistenceUnitPostProcessor">
<property name="reflectionsRoot" value="com.austinmichael"/>
</bean>
</list>
</property>
</bean>
Note the registration of the ReflectionsPersistenceUnitPostProcessor
in the persistenceUnitPostProcessors setting.
And that's it. Every class with a JPA Entity or MappedSuperclass
annotation on the classpath is added to the classpath. I had to give reflections the prefix of a package name to scan through which is why com.austinmichael
is there at all. You could register a second ReflectionsPersistenceUnitPostProcessor
with a different package name prefix if you want if your entities don't share a common package name prefix.
But, this is now JPAVendor agnostic.
Solution 2:
Is there a way to do the same thing - programmatically loading your Entity classes - in a JPA 2.0 compliant way?
No, this is not supported by JPA so you'll have to do this in a provider specific way. James Sutherland described the process for EclipseLink in this thread like this:
You can access the EclipseLink
ServerSession
from theEntityManagerFactoryImpl
(getServerSession()
), and use its'addDescriptor(ClassDescriptor)
oraddDescriptors()
API to add EclipseLinkClassDescriptor
. You will need to build theClassDescriptor
meta-data objects directly yourself (or use the Mapping Workbench to create them), as loading from JPA annotations or orm.xml would be more difficult.
Also have a look at this more recent thread for more code sample (the API looks like a bit verbose).
References
- Re: [eclipselink-users] Close to get it. Just a little help
- Re: JPA: adding entities to EntityManagerFactory programmatically