Most of the time you will find JPA’s auto-scan mechanism for annotated entities very limited. It only scans paths
starting from the parent of classpath:META-INF/ folder from which persistence.xml is loaded. If you want to use a
persistence.xml file located in a different place, for example, in a jar, your annotated entities won’t be scanned
because JPA will only process paths in that jar file. It is still problematic if you locate your persistence.xml into
classpath:META-INF but want to load entities not in any of the folders under the parent folder of META-INF.
Well, what is the solution then? You have the option to list entities to be loaded in fully qualified names, and jar
files to be processed in your persistence.xml. However, this approach is not very flexible in terms of unit testing
your code and then running it in your container without a change in paths of those listed resources.
Well, I have a better solution for this JPA entity scan problem. The solution is based on Spring’s
PersistenceUnitPostProcessor interface. It can be used to add additional class names and jar files during the
construction of the EntityManagerFactory object.
With our solution, it is possible to define locations using ant-style patterns and exclude some entities identified in
those locations. You can use ant-style patterns in the exclude list as well. This solution also provides a mechanism to
automatically locate persistent entities when you use persistence.xml files located in places other than the root
classpath:/META-INF folder. For example, in your web application you may use a persistence.xml in a jar file located
in WEB-INF/lib, and persistent entities can be in WEB-INF/classes folder at the same time. In this case, you don’t
need to state WEB-INF/classes in your location patterns. Our solution only needs a special hook file to be created in
your classpath (for example, WEB-INF/classes/META-INF/.entityScanPath), and if it finds one, it will scan the folder
starting from the parent folder of that hook file. It is also possible to apply this scanning process only to specified
persistence units.
You can reach the full source code of this JPA EntityScanner solution from here. Let’s now look at how it is configured and used as a spring-managed bean.
<bean id="entityScanner" class="samples.EntityScanner">
<property name="locationPatterns">
<bean class="samples.DelimitedStringToListFactoryBean">
<property name="listElements">
<value>${entityScanner.locationPatterns}</value>
</property>
</bean>
</property>
<property name="targetPersistenceUnits">
<bean class="samples.DelimitedStringToListFactoryBean">
<property name="listElements">
<value>${entityScanner.targetPersistenceUnits}</value>
</property>
</bean>
</property>
<property name="classesToExclude">
<bean class="samples.DelimitedStringToListFactoryBean">
<property name="listElements">
<value>${entityScanner.classesToExclude}</value>
</property>
</bean>
</property>
<property name="entityScanPathHook" value=".myJpaScanPathHook"/>
</bean>
First, we need to provide it with locationPatterns, which indicates paths in which persistent entities are located. It
is possible to populate the list with String elements from a delimited string property by using
DelimitedStringToListFactoryBean, a simple generic utility used extensively in our projects. It is a FactoryBean that
gets a delimited string and converts it into a List object with string elements. With the help of Spring’s
PropertyPlaceholderConfigurer bean, we are able to externalize those properties in environment-specific properties file.
For example, entityScanner.locationPatterns property may have the following values in dev and prod properties files;
#project.dev.properties
entityScanner.locationPatterns=file:/samples.spring/**/WEB-INF/test-classes/,file:/samples.spring/WebContent/WEB-INF/lib/crank-crud*.jar
#project.prod.properties
entityScanner.locationPatterns=
In the development environment, in addition to entities accessible from the location of the loaded persistence.xml
file, entities used in test-classes and entities in crank-crud*.jar files will be discovered too.
There might be more than one persistence unit defined in the persistence.xml file, and it is possible to apply this
entityScanner post processor only to the selected persistence unit(s). You can list persistence units to be processed
in the targetPersistenceUnits list property.
Another feature in EntityScanner is classesToExclude property. We can decide to exclude some of the entities among
discovered entities. For example,
#project.dev.properties
entityScanPath.classesToExclude=org.crank.crud.controller.**
With the above property, entities that match with the above pattern will be excluded while scanning the path
file:/samples.spring/WebContent/WEB-INF/lib/crank-crud*.jar.
The last property that I want to mention about is entityScanPathHook. If we use a persistence.xml file located, for
example, in a jar file, then JPA won’t be able to discover entities in WEB-INF/classes folder. We can add
WEB-INF/classes path to the locationPatterns to include those entities. However, entityScanPathHook provides an
alternative to enlisting WEB-INF/classes folder in the locationPatterns. EntityScanner tries to find a file
specified by entityScanPathHook property (default value is .entityScanPath) in the classpath, and when it finds one,
entities starting from its parent folder are discovered automatically.
Finally, let’s see how entityScanner is used in conjunction with LocalContainerEntityManagerFactoryBean.
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
<property name="persistenceUnitPostProcessors">
<list>
<ref bean="entityScanner" />
</list>
</property>
</bean>
LocalContainerEntityManagerFactoryBean has a persistenceUnitPostProcessors property, and entityScanner should be
injected into it.