JPA using alternative "persistence.xml"
I know that with the instruction:
Persistence.createEntityManagerFactory("persistence-unit-name");
The JPA persistence mechanism reads the "persistence.xml" file, looks for the persistence unit calle开发者_如何学编程d "persistence-unit-name", and constructs the EntityManagerFactory based on it.
My question is, how can I force JPA to take a file different from "persistence.xml"?? for example, "persistence-test.xml".
There is no standardized JPA way to do this, although individual JPA providers may provide a way. I would suggest the standard way to have a different class path for main and test.
For example, if you use Maven, and you have two META-INF/persistence.xml
files, one in src/main/resources
and one in src/test/resources
, tests will pick up the one in src/test/resources
, because Maven puts test classes / resources ahead of main classes / resources in the classpath. You can easily configure Ant to work in similar ways.
If you need more advanced logic, consider using Spring's JPA support. It will let you deal with advanced situations like integrating multiple persistence.xml files.
In EclipseLink you can do the following:
Properties pros = new Properties();
pros.setProperty(PersistenceUnitProperties.ECLIPSELINK_PERSISTENCE_XML,
"META-INF/persistence-alternative.xml");
EntityManagerFactory factory =
Persistence.createEntityManagerFactory("pu-name", pros);
I don't think you can. The long way of doing this is:
- Create a Factory that will read your
persistence-test.xml
and render aMap<String, String>
properties, and. - Call
Persistence.createEntityManagerFactory(persistenceUnitName, Map properties)
. That way, it reads from theproperties
map instead of it reading frompersistence.xml
.
So we want to have 2 separate persistence.xml
files. One should be read only by production configuration, and the other for tests. The idea is to rename or hide the production files.
War solution
Don't put persistence.xml
in src\main\resources\META-INF\
. Instead put it into src\main\webapp\WEB-INF\classes\META-INF\
. That's the location where the file should be and in this place it won't be visible by tests.
This solution works for me in gradle environment, but I hope in others would do too.
Jar solution
Rename the production file to persistence-ee.xml
. Now we're done with the test configuration. For production we must employ some processing. Each environment may have its own way, that's how I do it in gradle:
tasks.withType(Jar) {
rename { fileName ->
fileName == "persistence-ee.xml" ? "persistence.xml" : fileName;
}
}
In my applications having the persistence.xml
file for production is necessary only at deployment time, that is in jar/war packages. And these are the only places where this file is visible. Without double persistence
I couldn't start my database. The main reason was using jta-data-source
, the other: 2 separate named persistence units.
If you were using OpenEJB to drive your testing, you could do exactly what you want using whichever JPA provider you want. Similar question and the related answer here:
How to instruct Maven to ignore my main/resources/persistence.xml in favor of test/...?
You can configure Hibernate without using persistence.xml at all in Spring like like this:
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean()
{
Map<String, Object> properties = new Hashtable<>();
properties.put("javax.persistence.schema-generation.database.action",
"none");
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabasePlatform("org.hibernate.dialect.MySQL5InnoDBDialect"); //you can change this if you have a different DB
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(adapter);
factory.setDataSource(this.springJpaDataSource());
factory.setPackagesToScan("package name");
factory.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
factory.setValidationMode(ValidationMode.NONE);
factory.setJpaPropertyMap(properties);
return factory;
}
Since you are not using persistence.xml, you should create a bean that returns DataSource which you specify in the above method that sets the data source:
@Bean
public DataSource springJpaDataSource()
{
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl("jdbc:mysql://localhost/SpringJpa");
dataSource.setUsername("tomcatUser");
dataSource.setPassword("password1234");
return dataSource;
}
Then you use @EnableTransactionManagement
annotation over this configuration file. Now when you put that annotation, you have to create one last bean:
@Bean
public PlatformTransactionManager jpaTransactionManager()
{
return new JpaTransactionManager(
this.entityManagerFactoryBean().getObject());
}
Now, don't forget to use @Transactional
Annotation over those method that deal with DB.
Lastly, don't forget to inject EntityManager
in your repository (This repository class should have @Repository
annotation over it).
精彩评论