简体   繁体   中英

Spring Data JPA CDI integration with multiple persistence units

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();
    }
}

TL; DR;

Use qualifiers to declare which repository should use which EntityManager .

Explanation

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:

Qualifiers

@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 {

}

Repository interfaces

@MyFirstDatabase
public interface SomeRepository extends CrudRepository<MyEntity, Long> { … }

@MySecondDatabase
public interface SomeOtherRepository extends CrudRepository<OtherEntity, Long> { … }

Client-side usage interfaces

public class MyComponent {

    @Inject
    @MyFirstDatabase 
    SomeRepository someRepo;

    @Inject    
    @MySecondDatabase 
    SomeOtherRepository someOtherRepo;
}

Your 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.

Based on mp911de's answer. The EntityManagerFactoryProducer requires two more close methods otherwise deployment fails on Weblogic.

public class EntityManagerFactoryProducer {

    @Produces
    @ApplicationScoped
    @PrimaryEM
    public EntityManagerFactory createEntityManagerFactory() {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("PRIMARY_PU");
        return emf;
    }

    @Produces
    @Dependent
    @PrimaryEM
    public EntityManager createEntityManager(@PrimaryEM EntityManagerFactory entityManagerFactory) {
        EntityManager em = entityManagerFactory.createEntityManager();
        return em;
    }

    @Produces
    @ApplicationScoped
    @SecondaryEM
    public EntityManagerFactory createSecondaryEntityManagerFactory() {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("SECONDARY_PU");
        return emf;
    }

    @Produces
    @Dependent
    @SecondaryEM
    public EntityManager createSecondaryEntityManager(@SecondaryEM EntityManagerFactory entityManagerFactory) {
        EntityManager em = entityManagerFactory.createEntityManager();
        return em;
    }

    public void close(@Disposes @PrimaryEM EntityManager entityManager) {
        entityManager.close();
    }


    public void close(@Disposes @PrimaryEM EntityManagerFactory entityManagerFactory) {
        entityManagerFactory.close();
    }

    public void closeSecondary(@Disposes @SecondaryEM EntityManager entityManager) {
        entityManager.close();
    }

    public void closeSecondary(@Disposes @SecondaryEM EntityManagerFactory entityManagerFactory) {
        entityManagerFactory.close();
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM