Page 1 of 2 12 LastLast
Results 1 to 10 of 19

Thread: Transforming JPA related beans to JavaConfig

  1. #1
    Join Date
    Aug 2008
    Location
    Billings, Montana
    Posts
    47

    Default 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. #2
    Join Date
    Apr 2007
    Posts
    307

    Default

    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.
    Chris Beams
    Spring Framework committer, VMware
    http://github.com/cbeams

  3. #3
    Join Date
    Aug 2008
    Location
    Billings, Montana
    Posts
    47

    Default

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

  4. #4
    Join Date
    Apr 2007
    Posts
    307

    Default

    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
    Chris Beams
    Spring Framework committer, VMware
    http://github.com/cbeams

  5. #5
    Join Date
    Aug 2008
    Location
    Billings, Montana
    Posts
    47

    Default

    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.

  6. #6
    Join Date
    Apr 2007
    Posts
    307

    Default

    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.
    Chris Beams
    Spring Framework committer, VMware
    http://github.com/cbeams

  7. #7
    Join Date
    Aug 2008
    Location
    Billings, Montana
    Posts
    47

    Arrow

    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

  8. #8
    Join Date
    Apr 2007
    Posts
    307

    Default

    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
    Chris Beams
    Spring Framework committer, VMware
    http://github.com/cbeams

  9. #9
    Join Date
    Aug 2008
    Location
    Billings, Montana
    Posts
    47

    Default

    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

  10. #10
    Join Date
    Aug 2008
    Location
    Billings, Montana
    Posts
    47

    Default

    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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •