14

I'm writing a custom implementation for a Spring Data JPA repository. So I have:

  • MyEntityRepositoryCustom => interface with the custom methods
  • MyEntityRepositoryUmpl => implementation of the above interface
  • MyEntityRepository => standard interface which extends JpaRepository and MyEntityRepositoryCustom

My problem is this: within the implementation of MyEntityRepositoryUmpl I need to access the entity manager that was injected into Spring Data. How to get it? I can use @PersistenceContext to get it autowired, but the problem is that this repository must work in an application that sets up more than one persistence units. So, to tell Spring which one I need, I would have to use @PersistenceContext(unitName="myUnit"). However, since my repositories are defined in a reusable service layer, I can't know at that point what will be the name of the persistence unit that the higher-level application layer will configure to be injected into my repositories.

In other words, what I would need to do is to access the entity manager that Spring Data itself is using, but after a (not so quick) look at Spring Data JPA documentation I couldn't find anything for this.

Honestly, the fact that the Impl classes are totally unaware of Spring Data, although described as a strength in Spring Data manual, is actually a complication whenever you need to access something that is usually provided by Spring Data itself in your custom implementation (almost always, I would say...).

Mauro Molinari
  • 1,246
  • 2
  • 14
  • 24

3 Answers3

23

Since version Spring Data JPA 1.9.2 you have access to EntityManager through JpaContext, see: http://docs.spring.io/spring-data/jpa/docs/1.9.2.RELEASE/reference/html/#jpa.misc.jpa-context. Example:

@Component
public class RepositoryUtil
{
    @Autowired
    private JpaContext jpaContext;

    public void deatach(T entity)
    {
        jpaContext.getEntityManagerByManagedType(entity.getClass()).detach(entity);
    }
}

P.S. This approach will not work if you have more than one EntityManager candidate for some Class, see implementation of JpaContext#getEntityManagerByManagedType -> DefaultJpaContext#getEntityManagerByManagedType.

  • 1
    Yes, this is the result of the JIRA issue [DATAJPA-669](https://jira.spring.io/browse/DATAJPA-669) I opened for this. Still wondering why a simple `getEntityManager()` method in `JpaRepository` was not provided, it would have had no ambiguities. I added a comment on this in that JIRA issue. – Mauro Molinari Jan 20 '16 at 09:15
  • 1
    Isn't this supposed to be the accepted answer as the jira issue DATAJPA-669 is resolved? – yaswanth Mar 22 '17 at 05:41
  • Doesn't this give you a singular `EntityManager` for all the entity classes defined in a single persistent context? – Malvon Mar 24 '19 at 19:01
5

The best I could find is to set up a "convention": my repositories declare that they expect a persistence unit named myConventionalPU to be made available. The application layer then assigns that alias to the entity manager factory that it sets up and injects into Spring Data, so my custom implementations can receive the correct EMF with autowiring by using that alias. Here's an excerpt of my application context:

<bean id="myEntityManagerFactory" name="myConventionalPU" 
  class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  [...]
</bean>

<jpa:repositories base-package="com.example"
  entity-manager-factory-ref="myEntityManagerFactory"
  transaction-manager-ref="transactionManager" />

And within my custom implementation:

@PersistenceContext(unitName = "myConventionalPU")
private EntityManager em;

I opened DATAJPA-669 with this requirement.

Mauro Molinari
  • 1,246
  • 2
  • 14
  • 24
4

Spring Data JPA uses Auto configuration classes to auto generate entityManagerFactory, dataSource and transactionManager.

If you want get access to entityManager and control the instantiation and settings, you need to define your own PersistenceConfiguration. Below is the sample code using Java Config

    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(basePackages = { "com.test.repositories.*" })
    public class PersistenceJpaConfig {

        @Autowired
        JpaVendorAdapter jpaVendorAdapter;

        @Bean
        public DataSource dataSource() {
            return new EmbeddedDatabaseBuilder()
                    .setName("testdb")
                    .setType(EmbeddedDatabaseType.HSQL)
                    .build();
        }

        @Bean
        public EntityManager entityManager() {
            return entityManagerFactory().createEntityManager();
        }

        @Bean
        public EntityManagerFactory entityManagerFactory() {
            LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
            lef.setDataSource(dataSource());
            lef.setJpaVendorAdapter(jpaVendorAdapter);
            lef.setPackagesToScan("com.test.domain.*");
            lef.afterPropertiesSet();
            return lef.getObject();
        }

        @Bean
        public PlatformTransactionManager transactionManager() {
            return new JpaTransactionManager(entityManagerFactory());
        }
    }

If you have multiple data sources, follow this article.

Nitin Arora
  • 2,650
  • 1
  • 26
  • 27
  • I do know how to configure Spring Data repositories (I prefer XML over Java config, BTW), the problem is I can't know from inside a custom implementation what has been injected to Spring Data, unless I "hard code" in it the knowledge of which persistence unit will be in use. – Mauro Molinari Feb 02 '15 at 11:06