Vojtěch
Vojtěch

Reputation: 12416

Injecting one of the two @PersistenceContext

Consider having two entity manager factories:

<bean id="writeEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">...</bean>
<bean id="readOnlyEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">...</bean>

Then I want to have two Beans to which I would inject the correct persistence context:

<bean id="readOnlyManager" class="..MyDatabaseManager">
<bean id="writeManager" class="..MyDatabaseManager">

The bean would look something like:

public class MyDatabaseManager {

    private javax.persistence.EntityManager em;

    public EntityManager(javax.persistence.EntityManager em) {
        this.em = em;
    }
    ...
}

This obviously doesn't work, because EntityManager is not a bean and cannot be injected in this way:

No qualifying bean of type 'javax.persistence.EntityManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

How can I qualify correct EntityManager in the bean? I used to use @PersistenceContext annotation, but this is not usable as I need to inject it.

How can I specify the PersistenceContext for such Bean?

UPDATE: My question is how to inject PersistenceContext with qualifier via XML, not via annotation.

Upvotes: 10

Views: 3766

Answers (3)

Yu Tian Toby
Yu Tian Toby

Reputation: 419

I suppose you are trying to inject an entityManager into your manager class

public class MyDatabaseManager {

// where you need to qualify your injection    
private javax.persistence.EntityManager em;

    public EntityManager(javax.persistence.EntityManager em) {
        this.em = em;
    }
    ...
}

here are the working codes for my project

  1. Persistence.xml in META-INF folder

     <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
         <persistence-unit name="PERSISTENCE_UNIT_T" transaction-type="JTA">
             <provider>foo.bar.PersistenceProvider</provider>
             <jta-data-source>jdbc/DB2</jta-data-source>
    
         </persistence-unit>
    
          <persistence-unit name="PERSISTENCE_UNIT_P" transaction-type="JTA">
             <provider>foo.bar.PersistenceProvider</provider>
             <jta-data-source>jdbc/DB2</jta-data-source>
    
         </persistence-unit> 
     </persistence>
    
  2. Qualifier interface class SelectDataSource

 @Qualifier
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD })
    public @interface SelectDataSource{}
  1. EntityManager class

    public class EntityManager {

         public EntityManager() {
         }
    
         @Inject
         @SelectDataSource
         private EntityManager entityManager;
    
         // other entity manager methods
    
     }
    
  2. Entity Manager Provider class to provide persistence unit based on context

    public class EntityManagerProvider {

         @PersistenceContext(unitName = "PERSISTENCE_UNIT_T")
         private EntityManager entityManagerT;
    
         @PersistenceContext(unitName = "PERSISTENCE_UNIT_P")
         private EntityManager entityManagerP;
    
    
         @Produces
         @SelectDataSource
         EntityManager createEntityManager(InjectionPoint injectionPoint) {
    
             if (System.ENV.equalsIgnoreCase("p")) {
                 if (entityManagerP != null) {
    
                     return entityManagerP;
                 }
             }
    
             return entityManagerT;
         }
    
     }
    

then the EntityManager class can be injected to wherever you like with @Inject

Below is a nice post on how to use Context Dependency Injection to create managed entity manager https://www.sitepoint.com/cdi-weld-inject-jpa-hibernate-entity-managers/#usingaproducermethod

Upvotes: 0

Bhushan Uniyal
Bhushan Uniyal

Reputation: 5703

persistence.xml (2 persistence unit for 2 different entitymanager, based on your persistence provider here i ma using HibernatePersistence)

<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
   <persistence-unit name="pu1">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
   </persistence-unit>

   <persistence-unit name="pu2">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
   </persistence-unit>   
</persistence>

Make sure you are assigning the persistence-unit to entity manager using property persistenceUnitName

    <bean id="writeEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
            <property name="persistenceXmlLocation" value="classpath: of yout persistence xml" />
            <property name="persistenceUnitName" value="pu1" />  
             .....other configuration of em..  
    </bean>

same for other em.

Now, use constructor injection for MyDatabaseManager to inject EntityManager (using qualifier name ex. writeEntityManagerFactory )

<bean id="mdm" class="MyDatabaseManager">
     <constructor-arg ref = "writeEntityManagerFactory"/>
</bean>

Upvotes: 1

Angelo Immediata
Angelo Immediata

Reputation: 6944

Assuming that you are using spring in order to manage transactions what I would do is 2 different transaction managers and then in my services i would use the most appropriate transaction manager like this:

Configuration section

@Bean
public LocalContainerEntityManagerFactoryBean writeEntityManagerFactory() {

    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    //Your configuration here
    return factory;
}

@Bean(name={"writeTx"})
public PlatformTransactionManager writeTransactionManager() {

    JpaTransactionManager txManager = new JpaTransactionManager();
    txManager.setEntityManagerFactory(writeEntityManagerFactory().getObject());
    return txManager;
}

@Bean
public LocalContainerEntityManagerFactoryBean readEntityManagerFactory() {

    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    //Your configuration here
    return factory;
}

@Bean(name={"readTx"})
public PlatformTransactionManager readTransactionManager() {

    JpaTransactionManager txManager = new JpaTransactionManager();
    txManager.setEntityManagerFactory(readEntityManagerFactory().getObject());
    return txManager;
}

Service layer

@Transactional(value="readTx")
public List<Object> read(){
    //Your read code here
}

@Transactional(value="writeTx")
public void write(){
    //Your write code here
}

UPDATED ANSWER I misunderstood the question.

In your configuration class you can define:

@Bean
    public LocalContainerEntityManagerFactoryBean writeEntityManagerFactory() {

        LocalContainerEntityManagerFactoryBean em  = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource());
        em.setPackagesToScan(new String[] { "models" });
        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaProperties(hibProps());
        em.setPersistenceUnitName("writer");
        return em;
    }
    @Bean
    public LocalContainerEntityManagerFactoryBean readEntityManagerFactory() {

        LocalContainerEntityManagerFactoryBean em  = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource());
        em.setPackagesToScan(new String[] { "models" });
        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaProperties(hibProps());
        em.setPersistenceUnitName("reader");
        return em;
    }   

Please see the PersistenceUnitName values

Then you can injecting them by doing:

@PersistenceContext(unitName="writer")
private EntityManager emWriter;

@PersistenceContext(unitName="reader")
private EntityManager emReader;

I just tested it and all worked pretty good

Angelo

Upvotes: 2

Related Questions