13

I've started development of a new Spring 3.2.4 application and am trying to use Java based configuration instead of XML files as I have used in the past. However, I am having trouble making the transition.

Using XML, I would code it as follows:

<!-- application datasource -->
<bean id="dataSource.jndi" class="org.springframework.jndi.JndiObjectFactoryBean" scope="singleton" lazy-init="true">
    <property name="jndiName" value="java:comp/env/jdbc/liment" />
</bean>

<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
    <property name="persistenceUnitName" value="persistenceUnit"/>
    <property name="dataSource" ref="dataSource.jndi"/>
</bean>

However, I am very much stuck trying to figure out how to do this in Java. I'm trying to replicate the configuration, but running into trouble:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages={"com.ia"})
public class AppConfigJPA {
    @Bean
    public DataSource dataSource() {
        // configure and return the necessary JDBC DataSource
        JndiObjectFactoryBean dataSource = new JndiObjectFactoryBean();
        dataSource.setJndiName("java:comp/env/jdbc/liment");
        try {
            dataSource.afterPropertiesSet();
        } catch (IllegalArgumentException | NamingException e) {
            // rethrow
            throw new RuntimeException(e);
        }
        return (DataSource)dataSource.getObject();
    }

    @Bean
    public EntityManagerFactory entityManagerFactory(){
        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setPersistenceUnitName("persistenceUnit");
        emf.setDataSource(dataSource());
            emf.afterPropertiesSet
        return emf.getObject();
    }

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

}

However, I get the following error message:

Caused by: java.lang.IllegalStateException: No persistence exception translators found in bean factory. Cannot perform exception translation.
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.detectPersistenceExceptionTranslators(PersistenceExceptionTranslationInterceptor.java:142)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.setBeanFactory(PersistenceExceptionTranslationInterceptor.java:117)
    at org.springframework.data.repository.core.support.PersistenceExceptionTranslationRepositoryProxyPostProcessor.<init>(PersistenceExceptionTranslationRepositoryProxyPostProcessor.java:44)
    at org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport.setBeanFactory(TransactionalRepositoryFactoryBeanSupport.java:85)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeAwareMethods(AbstractAutowireCapableBeanFactory.java:1502)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1470)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:521)
    ... 33 more

What am I missing or doing wrong?

EDIT

Following @SotiriosDelimanolis response, I have modified my code to read the following:

@Autowired DataSource dataSource;
@Autowired EntityManagerFactory entityManagerFactory;


@Bean
public JndiObjectFactoryBean dataSource() {
    // configure and return the necessary JDBC DataSource
    JndiObjectFactoryBean dataSource = new JndiObjectFactoryBean();
    dataSource.setJndiName("java:comp/env/jdbc/josak");
    return dataSource;
}

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
    LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
    emf.setPersistenceUnitName("persistenceUnit");
    emf.setDataSource(dataSource);
    return emf;
}

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

But am getting Autowired exceptions instead now:

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: javax.sql.DataSource com.ia.system.configuration.AppConfigJPA.dataSource; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:514)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:285)
    ... 31 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:988)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:858)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:770)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:486)
    ... 33 more
Eric B.
  • 23,425
  • 50
  • 169
  • 316
  • Can you verify that the reference returned by `dataSource.getObject();` is `null`? That shouldn't happen if you called `afterPropertiesSet()`. And please post the longer stack trace. – Sotirios Delimanolis Oct 17 '13 at 16:03
  • @SotiriosDelimanolis Actually, I just caught a mistake in the code before I saw your comment. I had forgotten an "afterPropertiesSet()" call in the entityManagerFactory(). But now, I get a different Exception relating to a persistence exception translator. – Eric B. Oct 17 '13 at 16:07

2 Answers2

25

please refer below link -

http://forum.spring.io/forum/spring-projects/container/724356-how-to-use-javaconfig-to-declare-a-jndi-datasource

the datasource need to be created like this -

@Bean
    public DataSource dataSource() {
        final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
        dsLookup.setResourceRef(true);
        DataSource dataSource = dsLookup.getDataSource("jdbc/yourJdbcGoesHere");
        return dataSource;
    } 
Raj
  • 1,698
  • 4
  • 22
  • 39
  • 1
    `setResourceRef` is not necessary in this context as JndiDataSourceLookup sets it to true. The code is simply `new JndiDataSourceLookup().getDataSource("jdbc/yourJdbcGoesHere")` – bric3 Aug 19 '16 at 13:25
  • You should actually use @Bean(destroyMethod="") – Ales Dolecek Feb 26 '19 at 10:58
1

This is a weird design (for PersistenceExceptionTranslator) that I don't immediately understand, but here is the solution.

Your LocalContainerEntityManagerFactoryBean is a FactoryBean but also a PersistenceExceptionTranslator (implements both). But you aren't putting the LocalContainerEntityManagerFactoryBean into your context, you are only getting its created object.

Instead of

@Bean
public EntityManagerFactory entityManagerFactory(){
    LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
    emf.setPersistenceUnitName("persistenceUnit");
    emf.setDataSource(dataSource());
        emf.afterPropertiesSet
    return emf.getObject();
}

do

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
    LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
    emf.setPersistenceUnitName("persistenceUnit");
    emf.setDataSource(dataSource());
    return emf;
}

@Autowired
private EntityManagerFactory entityManagerFactory;

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

Spring will take care of calling afterPropertiesSet() and getObject() to put a EntityManagerFactory bean into the context.

Basically you end up with two beans, a EntityManagerFactory and a LocalContainerEntityManagerFactoryBean. Your JPA configuration requires a PersistenceExceptionTranslator bean in the context. That will be satisfied by LocalContainerEntityManagerFactoryBean.


FYI, you can do the same thing for your JndiObjectFactoryBean or any other FactoryBean.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • The JpaTransactionManager is expecting an EntityManagerFactory as its constructor type. If entityManagerFactory() returns a LocalContainerEntityManagerFactoryBean, how do I instantiate the transactionManager()? Similarly, if I return a JndiObjectFactoryBean from dataSource(), how do I set the emf.setDataSource() with teh JndiFactoryBean? – Eric B. Oct 17 '13 at 17:32
  • @EricB. Spring is magic that way. It proxies the `@Bean` method calls and returns the `FactoryBean` created bean instead of the `FactoryBean` itself. – Sotirios Delimanolis Oct 17 '13 at 17:33
  • I don't understand. Returning the factory bean makes the compiler complain on `return new JpaTransactionManager(entityManagerFactory())` : `The constructor JpaTransactionManager(LocalContainerEntityManagerFactoryBean) is undefined` – Eric B. Oct 17 '13 at 17:35
  • @EricB. Oh that. My bad. A workaround is to `@Autowired` beans of the type of object your `FactoryBean` beans create into some `@Configuration` class fields. Then use them instead of calling their respective methods. – Sotirios Delimanolis Oct 17 '13 at 17:41
  • This seems very complicated. Can you post an example please? I tried what you suggested, but obviously am doing something wrong as I am getting NoSuchBeanDefnExceptions when it is trying to autowire my DataSource bean. I'm editing the question above to post my new code and exception – Eric B. Oct 17 '13 at 17:48
  • Thanks for the update, but still no dice. I'm getting circular bean referencing errors: `Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'entityManagerFactory': Requested bean is currently in creation: Is there an unresolvable circular reference?` – Eric B. Oct 17 '13 at 18:07
  • @ericB. At worst, you can call the `@Bean` methods and call the `getObject()` method on their returned references. You will have to cast the return value of `JndiObjectFactoryBean`. [Something similar](http://stackoverflow.com/questions/5541094/factorybeans-and-the-annotation-based-configuration-in-spring-3-0). A `FactoryBean` is typically meant to return the same instance on each call. – Sotirios Delimanolis Oct 17 '13 at 19:03