Announcement Announcement Module
Collapse

JavaConfig forum decommissioned in favor of Core Container

As described at

http://static.springsource.org/sprin...fig/README.TXT

key features of the Spring JavaConfig project have been migrated into the core Spring Framework as of version 3.0.

Please see the Spring 3.0 documentation on @Configuration and @Bean support:

http://static.springsource.org/sprin...tml#beans-java

For any questions related to @Configuration classes and @Bean methods in Spring 3.0, please post in the dedicated 'Core Container' forum at

http://forum.springsource.org/forumdisplay.php?f=26
See more
See less
Transforming JPA related beans to JavaConfig Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Transforming JPA related beans to JavaConfig

    I am working on creating a dynamic spring config for my project using Java Config. How about translating the following XML using JavaConfig?

    LocalContainerEntityManagerFactoryBean is not related to javax.persistence.EntityManagerFactory. So, I am having trouble configuring entityManagerFactory using Java Config.

    <bean id="personDao" class="impl.PersonDaoImpl">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerE ntityManagerFactoryBean">
    <property name="persistenceUnitName" value="personPU" />
    <property name="dataSource" ref="dataSource"/>
    <property name="jpaVendorAdapter">
    <bean class="org.springframework.orm.jpa.vendor.Hibernat eJpaVendorAdapter">
    <property name="showSql" value="${jdbc.showSql}"/>
    <property name="generateDdl" value="${jdbc.generateDdl}"/>
    <property name="databasePlatform" value="${jdbc.databasePlatform}"/>
    </bean>
    </property>
    </bean>
    <bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionM anager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
    <property name="dataSource" ref="dataSource"/>
    </bean>

    I appreciate any insight into this issue.

    Thank you,
    Arul

  • #2
    Hi Arul,

    The following should get you started. First, make sure that you upgrade to the latest nightly snapshot release. Instructions for this can be found at http://springframework.org/javaconfig.

    Code:
    @Configuration
    @AnnotationDrivenTx
    @PropertiesValueSource(locations="classpath:path/to/db.properties")
    @Import(DataSourceConfig.class)
    public abstract class JpaConfiguration {
    
        abstract @ExternalValue("jdbc.showSql") String showSql();
        abstract @ExternalValue("jdbc.generateDdl") String generateSql();
        abstract @ExternalValue("jdbc.databasePlatform") String databasePlatform();
    
        abstract @ExternalBean DataSource dataSource();
    
        public @Bean PersonDao personDao() {
            PersonDaoImpl personDao = new PersonDaoImpl();
            personDao.setEntityManagerFactory(entityManagerFactory());
        }
    
        public @Bean EntityManagerFactory entityManagerFactory() {
            LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
            em.setPersistenceUnitName("personPU");
            em.setDataSource(dataSource());
            em.setJpaVendorAdapter(jpaVendorAdapter());
        }
    
        public @Bean HibernateJpaVendorAdapter jpaVendorAdapter() {
            HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
            adapter.setShowSql(showSql());
            adapter.setGenerateDdl(generateDdl());
            adapter.setDatabasePlatform(databasePlatform());
        }
    
        public @Bean PlatformTransactionManager transactionManager() {
            JpaTransactionManager txManager = new JpaTransactionManager();
            txManager.setEntityManagerFactory(entityManagerFactory());
            txManager.setDataSource(dataSource());
        }
    
    }
    Notes:

    @AnnotationDrivenTx behaves exactly like <tx:annotation-driven/>. It assumes that you have a bean named 'transactionManager' (and in the above configuration, you do). See the JavaDoc for full details.

    @Import(DataSourceConfig.class) assumes you have another @Configuration class defined named DataSourceConfig that defines a @Bean of type DataSource.

    You'll notice that the combination of @PropertiesValueSource and @ExternalBean replace what you're used to with <contextroperty-placeholder/> / PropertyPlaceholderConfigurer

    Again, this is just a start. You may encounter additional issues, please ask if you do, and when possible include a complete set of sample code. As several of these features are unreleased, there is no official documentation, but in most cases you'll find sufficient Javadoc in the code.

    Comment


    • #3
      Thanks Chris for your detailed explanation. I am really excited about this project due to its expressiveness.

      In fact, I had almost similar configuration. Here is the slightly updated code. I have added return statements to all the methods. I am not able to return from entityManagerFactory as LocalContainerEntityManagerFactoryBean does not belong to this type.
      Code:
      @Configuration
      @AnnotationDrivenTx
      @PropertiesValueSource(locations = "classpath:path/to/db.properties")
      @Import(DataSourceConfig.class)
      public abstract class JpaConfiguration {
      
        abstract @ExternalValue("jdbc.showSql") boolean showSql();
        abstract @ExternalValue("jdbc.generateDdl") boolean generateDdl();
        abstract @ExternalValue("jdbc.databasePlatform") String databasePlatform();
      
        abstract @ExternalBean  DataSource dataSource();
      
        public @Bean PersonDao personDao() {
          PersonDaoImpl personDao = new PersonDaoImpl();
          personDao.setEntityManagerFactory(entityManagerFactory());
          return personDao;
        }
      
        public @Bean EntityManagerFactory entityManagerFactory() {
          LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
          em.setPersistenceUnitName("personPU");
          em.setDataSource(dataSource());
          em.setJpaVendorAdapter(jpaVendorAdapter());
          return em;//incompatible types: required javax.persistence.EntityManagerFactory
        }
      
        public @Bean HibernateJpaVendorAdapter jpaVendorAdapter() {
          HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
          adapter.setShowSql(showSql());
          adapter.setGenerateDdl(generateDdl());
          adapter.setDatabasePlatform(databasePlatform());
          return adapter;
        }
      
        public @Bean PlatformTransactionManager transactionManager() {
          JpaTransactionManager txManager = new JpaTransactionManager();
          txManager.setEntityManagerFactory(entityManagerFactory());
          txManager.setDataSource(dataSource());
          return txManager;
        }
      
      }
      I was trying to pull the snapshot using the following in my POM.

      Code:
          <repositories>
              <!-- Necessary if using snapshot builds of SpringSource products (as above) -->
              <repository>
                  <id>SpringSource Enterprise Bundle Repository - External Bundle Snapshots</id>
                  <url>repository.springsource.com/maven/bundles/snapshot</url>
              </repository>
      
              <!-- Required, as Spring JavaConfig has dependencies on released versions of SpringSource products -->
              <repository>
                  <id>SpringSource Enterprise Bundle Repository - SpringSource Bundle Releases</id>
                  <url>repository.springsource.com/maven/bundles/release</url>
              </repository>
      
              <!-- Required, as Spring JavaConfig has dependencies on External OSGi bundles -->
              <repository>
                  <id>SpringSource Enterprise Bundle Repository - External Bundle Releases</id>
                  <url>repository.springsource.com/maven/bundles/external</url>
              </repository>      
              <repository>
                  <id>spring-milestone</id>
                  <name>Spring Milestone Repository</name>
                  <url>s3browse.com/explore/repository.springsource.com/maven/bundles/snapshot</url>
              </repository>
          </repositories>
      
              <dependency>
                  <groupId>org.springframework.javaconfig</groupId>
                  <artifactId>org.springframework.config.java</artifactId>
                  <version>1.0.0.BUILD-SNAPSHOT</version>
              </dependency>
      But, I could not fetch the snapshot from the maven repo defined above.

      Am I missing something here?

      [Note: I had intentionally removed the http prefix from the repository URLs because the forum posting does not allow me to include URL until I have made 15 posts.]

      Comment


      • #4
        Regarding EntityManagerFactory, I was a bit hasty in the code above. Because LocalContainerEntityManagerFactory is a FactoryBean, it must be treated specially. Here is the revised code:

        Code:
        public abstract class JpaConfiguration extends ConfigurationSupport {
          public @Bean EntityManagerFactory entityManagerFactory() {
            LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
            em.setPersistenceUnitName("personPU");
            em.setDataSource(dataSource());
            em.setJpaVendorAdapter(jpaVendorAdapter());
            return (EntityManagerFactory) this.getObject(em);
          }
        }
        Note that the class now extends ConfigurationSupport, which provides a protected 'getObject()' method. This method internally calls the FactoryBean's getObject() method, which in your case returns an EntityManagerFactory. It also runs that returned object through the container lifecycle, in order to perform any bean post-processing, etc that may be required.


        Regarding the pom and dependencies, your configuration is close, but using the s3browse url will not work. Take a look at JavaConfig's petclinic sample pom:

        https://fisheye.springframework.org/...xml?r=796#l196

        Comment


        • #5
          Thanks Chris. I fixed the config and maven. It worked like a charm.

          My PersonDaoImpl extends JpaDaoSupport which provides "Spring JPA" benefits.

          In my PersonDaoImpl, I need to add the following to support it from the SJC:

          Code:
            private EntityManagerFactory entityManagerFactory;
          
            @PersistenceContext
            public void setEntityManager(EntityManagerFactory entityManagerFactory) {
                this.entityManagerFactory = entityManagerFactory;
            }
          Do you think this would hurt my DAO having multiple references to EMF?
          I was just curious to know if the below would return a new EMF or the one injected above.
          Code:
          getJpaTemplate().getEntityManagerFactory()
          Do you know if there is any timeframe I could see these features (AnnotationDrivenTx, PropertiesValueSource) part of a milestone release?

          Thanks once again for your timely support.

          Comment


          • #6
            Arul,

            Either of the approaches you mention should work fine. If one does not, and it appears related to SJC, please post here with the error, etc.

            With regard to the timeline for the features, you can always follow the roadmap in JIRA. These features are currently scheduled for 1.0.0.m4, which is due out shortly.

            Comment


            • #7
              Hi Chris,

              I have the following scenario where my service layer delegates to my DAO layer. Here the DAO is injected using autowiring and both the service and DAO impl are annotated with @Service and @Repository respectively.

              Code:
              =====Service layer======
              @Service("personService")
              public class PersonServiceImpl implements PersonService {
              
                /**
                 * Person Dao.
                 */
                private
                @Autowired
                PersonDao personDao;
              
               @Transactional
               public void addPerson(Person p) {
                 personDao.save(p);
               }
              }
              
              =====Dao layer======
              @Repository("personDao")
              public class PersonDaoImpl implements PersonDao {
                public void save(Person entity) {
                  getEntityManager().persist(entity);
                }
              }
              
              }
              In my java config, I have the following definition for automatically locating the Service and DAO classes using ComponentScan.

              Code:
              @Configuration
              @AnnotationDrivenTx
              @PropertiesValueSource(locations = "classpath:jdbc.properties")
              @Import(DataSourceConfiguration.class)
              @ComponentScan({"dao.impl", "service.impl"})
              public abstract class JpaConfiguration extends ConfigurationSupport {
              //all the beans defined except service and DAO beans
              }
              In the above case, how do I get access to service and DAO beans? Do I need to define them explicitly in the above java configuration.

              Please clarify.

              Thanks!
              Arul

              Comment


              • #8
                Arul,

                You have a number of options:

                1) Declare an @ExternalBean method to pull the desired beans in:
                Code:
                @Configuration
                @AnnotationDrivenTx
                @PropertiesValueSource(locations = "classpath:jdbc.properties")
                @Import(DataSourceConfiguration.class)
                @ComponentScan({"dao.impl", "service.impl"})
                public abstract class JpaConfiguration {
                    //all the beans defined except service and DAO beans
                
                    abstract @ExternalBean PersonDao personDao();
                    
                    public @Bean Foo beanThatNeedsPersonDao() {
                        return new Foo(personDao());
                    }
                }
                2) Extend ConfigurationSupport and use the getBean() method:
                Code:
                @Configuration
                @AnnotationDrivenTx
                @PropertiesValueSource(locations = "classpath:jdbc.properties")
                @Import(DataSourceConfiguration.class)
                @ComponentScan({"dao.impl", "service.impl"})
                public class JpaConfiguration extends ConfigurationSupport {
                    //all the beans defined except service and DAO beans
                
                    public @Bean Foo beanThatNeedsPersonDao() {
                        return new Foo(getBean(PersonDao.class));
                        // or:
                        // return new Foo(getBean("personDao"));
                    }
                }
                3) Use @Autowired / @Resource
                Remember - @Configuration classes end up being "just another spring bean" in the container, so are therefore candidates for autowiring just like all the rest! Note that using @AnnotationDrivenConfig is required here:
                Code:
                @Configuration
                @AnnotationDrivenConfig
                @AnnotationDrivenTx
                @PropertiesValueSource(locations = "classpath:jdbc.properties")
                @Import(DataSourceConfiguration.class)
                @ComponentScan({"dao.impl", "service.impl"})
                public class JpaConfiguration {
                    //all the beans defined except service and DAO beans
                
                    private @Autowired PersonDao personDao;
                    
                    public @Bean Foo beanThatNeedsPersonDao() {
                        return new Foo(personDao);
                    }
                }
                I'll be interested to hear which approach you find most compelling

                Comment


                • #9
                  Thanks for all your help. SJC is very powerful indeed as I can see all the magic wiring happening behind the scenes and user is at rejoice.

                  I chose option 2 as I am already extending ConfigurationSupport. As usual, it worked like a charm.

                  Thanks Chris once again.

                  -Arul

                  Comment


                  • #10
                    Chris,

                    How about defining the following jndi config in SJC?

                    Code:
                    <jee:jndi-lookup id="dataSource" name="java:comp/env/jdbc/OracleDS">
                    I am not finding anything equivalent in SJC javadocs.

                    -Arul

                    Comment


                    • #11
                      There is no equivalent as yet to jee:jndi-lookup, though it is planned for. It would be helpful if you would create a feature request in JIRA for this. You can follow the example of http://jira.springframework.org/browse/SJC-96 when you do this.

                      In the meantime, you can directly use http://static.springframework.org/sp...ctoryBean.html. This is a more low-level approach, but will work just fine.

                      Comment


                      • #12
                        Chris,

                        I submitted a reply and it did not show up till now. So re-posting it.

                        I logged a JIRA for this issue (jira.springframework.org/browse/SJC-197).

                        Do you think the following should work?

                        Code:
                          public @Bean EntityManagerFactory entityManagerFactory() {
                            LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
                            em.setPersistenceUnitName("flux");
                            em.setJpaProperties(getJpaProperties());
                            em.setDataSource(jndiDataSource());
                            em.setJpaVendorAdapter(jpaVendorAdapter());
                            em.setLoadTimeWeaver(loadTimeWeaver());
                            return (EntityManagerFactory) this.getObject(em);
                          }
                        
                          public @Bean DataSource jndiDataSource() {
                            JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
                            jndiObjectFactoryBean.setJndiName("java:comp/env/jdbc/MySqlDataSource");
                            jndiObjectFactoryBean.setCache(true);
                            jndiObjectFactoryBean.setLookupOnStartup(false);
                            jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
                            return (DataSource) this.getObject(jndiObjectFactoryBean);
                          }
                        Thanks!
                        Arul

                        Comment


                        • #13
                          At a glance, yes it should work. Also note that ConfigurationSupport.getObject() now has a generics-enabled variant. See the last line below (saves you the cast):

                          public @Bean DataSource jndiDataSource() {
                          JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
                          jndiObjectFactoryBean.setJndiName("java:comp/env/jdbc/MySqlDataSource");
                          jndiObjectFactoryBean.setCache(true);
                          jndiObjectFactoryBean.setLookupOnStartup(false);
                          jndiObjectFactoryBean.setProxyInterface(javax.sql. DataSource.class);
                          return this.getObject(jndiObjectFactoryBean, DataSource.class);
                          }

                          Comment


                          • #14
                            Thanks Chris for the tip.

                            I currently have the following bean configurations.

                            Code:
                              @Bean
                              public DataSource dataSource() {
                                ComboPooledDataSource dataSource = new ComboPooledDataSource();
                                try {
                                  dataSource.setDriverClass(driverClass());
                                } catch (PropertyVetoException pve) {
                            
                                }
                                dataSource.setJdbcUrl(jdbcUrl());
                                dataSource.setUser(user());
                                dataSource.setPassword(password());
                                dataSource.setMinPoolSize(minPoolSize());
                                dataSource.setAcquireIncrement(acquireIncrement());
                                dataSource.setMaxPoolSize(maxPoolSize());
                                return dataSource;
                              }
                            
                              @Bean
                              public DataSource jndiDataSource() {
                                JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
                                jndiObjectFactoryBean.setJndiName(dataSourceJndiName());//dynamic
                                jndiObjectFactoryBean.setCache(true);
                                jndiObjectFactoryBean.setLookupOnStartup(false);
                                jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
                                return this.getObject(jndiObjectFactoryBean, DataSource.class);
                              }
                            Is it possible to dynamically use one of these configuration at runtime? If the user provide standalone datasource, I need to use dataSource() config. If the user provides jndi datasource, I need to use jndiDataSource() config.

                            Please clarify.

                            Comment


                            • #15
                              I have the following bean configuration.

                              Code:
                              @EnvironmentValueSource
                              @Configuration
                              public abstract class ApplicationConfiguration {
                              
                                  abstract
                                  @ExternalValue("database_type")
                                  String databaseType();
                              }
                              
                              @Configuration
                              @AnnotationDrivenTx
                              @PropertiesValueSource(locations = "classpath:config.properties")
                              @Import({DataSourceConfiguration.class, ApplicationConfiguration.class})
                              @ComponentScan({"dao.impl", "service.impl"})
                              public abstract class JpaConfiguration extends ConfigurationSupport {
                              
                                  abstract
                                  @ExternalBean
                                  String databaseType();
                                  //omitted other methods for clarity
                              }
                              I am getting the following error when I try to access the bean which uses databaseType(). I have imported the external bean too. Not sure what I am missing here.

                              Code:
                              Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public final java.lang.String config.JpaConfiguration$$EnhancerByCGLIB$$7c12f771.databasePlatform()] threw exception; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'databaseType' is defined
                              	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:127)
                              	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:435)
                              	... 147 more
                              Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'databaseType' is defined
                              	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:387)
                              	at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:968)
                              	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:246)
                              	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
                              	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:168)
                              	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:238)
                              	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
                              	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
                              	at org.springframework.config.java.internal.enhancement.CglibConfigurationEnhancer$ExternalBeanMethodInterceptor.doIntercept(CglibConfigurationEnhancer.java:188)
                              	at org.springframework.config.java.internal.enhancement.CglibConfigurationEnhancer$AbstractMethodInterceptor.intercept(CglibConfigurationEnhancer.java:170)
                              	at config.JpaConfiguration$$EnhancerByCGLIB$$7c12f771.databaseType(<generated>)
                              	at config.JpaConfiguration.databasePlatform(JpaConfiguration.java:146)
                              	at config.JpaConfiguration$$EnhancerByCGLIB$$7c12f771.CGLIB$databasePlatform$21(<generated>)
                              	at config.JpaConfiguration$$EnhancerByCGLIB$$7c12f771$$FastClassByCGLIB$$d9791c9c.invoke(<generated>)
                              	at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:167)
                              	at org.springframework.config.java.internal.enhancement.CglibConfigurationEnhancer$BeanMethodInterceptor.doIntercept(CglibConfigurationEnhancer.java:285)
                              	at org.springframework.config.java.internal.enhancement.CglibConfigurationEnhancer$AbstractMethodInterceptor.intercept(CglibConfigurationEnhancer.java:170)
                              	at config.JpaConfiguration$$EnhancerByCGLIB$$7c12f771.databasePlatform(<generated>)
                              	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                              	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                              	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                              	at java.lang.reflect.Method.invoke(Method.java:585)
                              	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:115)
                              	... 148 more
                              Appreciate any help.

                              Thanks!
                              Arul

                              Comment

                              Working...
                              X