I am new to Spring and trying to integrate Spring Data, EclipseLink, and EJB on Weblogic 12c.
I want to use CDI to inject a Spring Data Repository into a stateless EJB so I followed the Spring Data CDI integration instruction and succeeded with single persistence unit.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpd.misc.cdi-integration
As the application requires two persistence units to connect two different databases, I configured two persistence units with a different name in persistence.xml.
Here comes the question: How can I create two Spring Data repository so that RepositoryA
uses persistence Unit A and RepositoryB
uses persistence Unit B?
persistence.xml
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">
<persistence-unit name="PRIMARY_PU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/EMP_DS</jta-data-source>
<class>com.smec.eis.example.springbooteval.model.Employee</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
</persistence-unit>
<persistence-unit name="SECONDARY_PU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/HR_DS</jta-data-source>
<class>com.smec.eis.example.springbooteval.model.Job</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
</persistence-unit>
</persistence>
Primary CDI Producer:
public class EntityManagerFactoryProducer {
@Produces
@ApplicationScoped
public EntityManagerFactory createEntityManagerFactory() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PRIMARY_PU");
return emf;
}
public void close(@Disposes EntityManagerFactory entityManagerFactory) {
entityManagerFactory.close();
}
@Produces
@Dependent
public EntityManager createEntityManager(EntityManagerFactory entityManagerFactory) {
return entityManagerFactory.createEntityManager();
}
public void close(@Disposes EntityManager entityManager) {
entityManager.close();
}
}
Use qualifiers to declare which repository should use which EntityManager
.
Spring Data JPA repositories are implemented by default on a single EntityManager
. The CDI extension propagates any qualifiers from the repository interface to its EntityManager
selection. Because the qualifiers are effectively empty (not counting in @Default
and @Any
), the extension uses the single EntityManager
from your code above.
Creating and adding own qualifier annotations will do the job for you:
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
@interface MyFirstDatabase {
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
@interface MySecondDatabase {
}
@MyFirstDatabase
public interface SomeRepository extends CrudRepository<MyEntity, Long> { … }
@MySecondDatabase
public interface SomeOtherRepository extends CrudRepository<OtherEntity, Long> { … }
public class MyComponent {
@Inject
@MyFirstDatabase
SomeRepository someRepo;
@Inject
@MySecondDatabase
SomeOtherRepository someOtherRepo;
}
EntityManagerFactoryProducer
:public class EntityManagerFactoryProducer {
@Produces
@ApplicationScoped
@MyFirstDatabase
public EntityManagerFactory createEntityManagerFactory() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PRIMARY_PU");
return emf;
}
@Produces
@ApplicationScoped
@MySecondDatabase
public EntityManagerFactory createEntityManagerFactory() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("SECONDARY_PU");
return emf;
}
public void close(@Disposes EntityManagerFactory entityManagerFactory) {
entityManagerFactory.close();
}
@Produces
@Dependent
@MyFirstDatabase
public EntityManager createEntityManager(@MyFirstDatabase EntityManagerFactory entityManagerFactory) {
return entityManagerFactory.createEntityManager();
}
@Produces
@Dependent
@MySecondDatabase
public EntityManager createEntityManager(@MySecondDatabase EntityManagerFactory entityManagerFactory) {
return entityManagerFactory.createEntityManager();
}
public void close(@Disposes EntityManager entityManager) {
entityManager.close();
}
}
The code above assumes that you work with entity types that are not the same across your two data sources. If you need to use the same entity type, then you would create a base repository interface, annotate it with @NoRepositoryBean
and two derived interfaces, similar to the code above.