Announcement Announcement Module
Collapse
No announcement yet.
@configuration of JpaRepository with Transactions Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • @configuration of JpaRepository with Transactions

    I'm having trouble getting a JpaRepository to save entities.

    setup:
    * maven-configured project
    * SQL Server 2008 R2
    * jTDS 1.2.5
    * hibernate-entitymanager 3.6.3.Final
    * spring-data-jpa 1.0.0.M2

    goals:
    * command-line application
    * as little XML configuration as possible
    * create repository, insert one new entity

    my main looks like this:
    Code:
    		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    		ctx.register(SpringConfig.class);
    		ctx.refresh();
    		
    		ThingRepository repo = ctx.getBean(ThingRepository.class);
    		
    		System.out.println( repo.count() );
                    //prints "0" as expected
    
    		ThingDatabase tdb = ctx.getBean(ThingDatabase.class);
    		Thing t = tdb.newThing("ThingName");
                    // Exception here "no transaction"
    
    		System.out.println(t);
    		System.out.println( repo.count() );
    seems simple enough but I can't get transactions working. I always get:

    Code:
    Exception in thread "main" javax.persistence.TransactionRequiredException: no transaction is in progress
    	at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:793)
    	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:597)
    	at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:365)
    	at $Proxy25.flush(Unknown Source)
    	at org.springframework.data.jpa.repository.support.SimpleJpaRepository.flush(SimpleJpaRepository.java:361)
    	at org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush(SimpleJpaRepository.java:326)
    	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:597)
    	at org.springframework.data.repository.support.RepositoryFactorySupport$QueryExecuterMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:329)
    	at org.springframework.data.repository.support.RepositoryFactorySupport$QueryExecuterMethodInterceptor.invoke(RepositoryFactorySupport.java:310)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    	at $Proxy26.saveAndFlush(Unknown Source)
    	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:597)
    	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    	at $Proxy27.saveAndFlush(Unknown Source)
    	at ThingDatabase.newThing(ThingDatabase.java:17)
    	at ThingDatabase$$FastClassByCGLIB$$376f7b3e.invoke(<generated>)
    	at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191)
    	at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:688)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    	at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:621)
    	at ThingDatabase$$EnhancerByCGLIB$$e1b52a01.newThing(<generated>)
    	at JPATest.main(JPATest.java:56)
    From which I can tell that the TransactionInterceptor is being invoked by then I don't understand why its not creating a transaction.

    I'll follow up excepts from my code and config
    Last edited by ipc; May 5th, 2011, 03:57 PM. Reason: was old code; I pulled the insertion into a Bean that was explicitly marked @Transactional

  • #2
    My SpringConfig looks like this:

    Code:
    @Configuration
    @ImportResource("tx.xml")
    public class SpringConfig {
    
    	@Bean 
    	public EntityManagerFactory entityManagerFactory() {
    		return Persistence.createEntityManagerFactory("artifact");		
    	}
    	
    	@Bean 
    	public EntityManager artifactEntityManager() {
            return entityManagerFactory().createEntityManager();
    	}
    
    	@Bean
    	public JpaRepositoryFactory artifactRepositoryFactory() {
    		return new JpaRepositoryFactory( artifactEntityManager() );
    	}
    	
        @Bean
        public ThingRepository thingRepository() {
            return artifactRepositoryFactory().getRepository(ThingRepository.class);
        }
        
        @Bean
        public ThingDatabase tdb() {
        	return new ThingDatabase();
        }
    	
        @Bean 
        public PlatformTransactionManager transactionManager() {
        	return new JpaTransactionManager(entityManagerFactory());
        }
        
    }
    Where "tx.xml" was necessary to pick up:
    Code:
    	<tx:annotation-driven />
    because I'm not using 3.1 (but I could, if that would actually help. I tried that at one point and the @Feature didn't help either).

    Then ThingDatabase looks like:
    Code:
    public class ThingDatabase {
    	
    	@Autowired
    	ThingRepository repo;
    
    	@Transactional
    	public Thing newThing(String name) {
    		Thing t = new Thing();
    		t.setName(name);
    		repo.saveAndFlush(t);
    		return t;
    	}
    
    }
    I started out with almost all of my config in Java and moved more and more of it into XML because those were the best examples that I had but I can't get transactions to work at all.
    Last edited by ipc; May 5th, 2011, 04:08 PM. Reason: changed to my Java-based config as that's closer to what I want in production

    Comment


    • #3
      You need to associate your transaction with the entityManagerFactory.

      It's better that you create a xml configuration file with the entityManagerFactory and transaction declaration.

      Comment


      • #4
        Originally posted by gwa View Post
        You need to associate your transaction with the entityManagerFactory.

        It's better that you create a xml configuration file with the entityManagerFactory and transaction declaration.
        Isn't that what
        Code:
            @Bean 
            public PlatformTransactionManager transactionManager() {
            	return new JpaTransactionManager(entityManagerFactory());
            }
        does?

        I think I figured out what the problem is. It's how I instantiate the repository itself:

        Code:
            @Bean
            public ThingRepository thingRepository() {
                return artifactRepositoryFactory().getRepository(ThingRepository.class);
            }
        I thought that was the equivalent of the <jpa:repositories base-package="blah"> feature but its not... after I added that statement to my XML then my @Transactional statements worked as I expected them to.

        Now I have to figure out how to programmatically do what <jpa:repositories base-package="blah"> does.
        Last edited by ipc; May 6th, 2011, 08:31 AM. Reason: TOEFL

        Comment


        • #5
          Originally posted by ipc View Post
          Now I have to figure out how to programmatically do what <jpa:repositories base-package="blah"> does.
          Ok, I have a solution; I think it's the right way to do things.

          The problem I was having was going from something like 'EntityManagerFactoryBean' to an actual instance of EntityManagerFactory.

          I was creating the Bean with 'new' and then calling the after properties set and the getObject() myself. Bad idea. Instead, return the bean after setting the properties. Then @autowire your bean-created instance.

          Here's my final setup:

          The repo I want to use is this:
          Code:
          public interface ThingRepository extends JpaRepository<Thing, Long> {
          
          }
          my META-INF/tx.xml has the following content:

          Code:
          	
          <context:property-placeholder location="META-INF/database.properties" />
          <tx:annotation-driven />
          The my Config class looks like this:
          Code:
          @Configuration
          @ImportResource("META-INF/tx.xml")
          public class SpringConfig {
          
          	private @Value("${javax.persistence.jdbc.driver}")
          	String jdbcDriver;
          	private @Value("${javax.persistence.jdbc.url}")
          	String jdbcUrl;
          	private @Value("${javax.persistence.jdbc.user}")
          	String jdbcUsername;
          	private @Value("${javax.persistence.jdbc.password}")
          	String jdbcPassword;
          
          	public @Bean
          	DataSource dataSource() {
          		DriverManagerDataSource dataSource = new DriverManagerDataSource(
          				jdbcUrl, jdbcUsername, jdbcPassword);
          		dataSource.setDriverClassName(jdbcDriver);
          		return dataSource;
          	}
          
          	@Bean
          	public JpaDialect jpaDialect() {
          		return new HibernateJpaDialect();
          	}
          
          	@Bean
          	public JpaVendorAdapter jpaVendorAdaptor() {
          		return new HibernateJpaVendorAdapter();
          	}
          
          	@Bean
          	public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
          		LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
          		emfb.setPersistenceUnitName("PersistenceUnit");
          		emfb.setJpaDialect(jpaDialect());
          		emfb.setJpaVendorAdapter(jpaVendorAdaptor());
          		emfb.setDataSource(dataSource());
          		return emfb;
          	}
          
          	@Bean
          	public JpaRepositoryFactoryBean<ThingRepository,Thing,Long> getJpaRepositoryFactoryBean() {
          		JpaRepositoryFactoryBean<ThingRepository, Thing, Long> rfb = new JpaRepositoryFactoryBean<LogRepository, Thing, Long>();
          		rfb.setRepositoryInterface(ThingRepository.class);
          		return rfb;
          	}
          	
          	
          	@Autowired 
          	public EntityManagerFactory entityManagerFactory;
          
          	@Bean
          	public PlatformTransactionManager transactionManager() {
          		JpaTransactionManager txm = new JpaTransactionManager();
          		txm.setEntityManagerFactory(entityManagerFactory);
          		txm.setDataSource(dataSource());
          		txm.setJpaDialect(jpaDialect());
          		return txm;
          	}
          }
          Then in main:
          Code:
          		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
          		ctx.register(SpringConfig.class);
          		ctx.scan("com.allstontrading.options.metrics");
          		ctx.refresh();
          
          		ThingRepository repo = ctx.getBean(ThingRepository.class);
          
          		Thing t = new Thing();
          		t.setName("ThingName");
          		repo.saveAndFlush(t);

          so that's cool. Again the point is that if you have somthing that has to be configured for BlahBean then create and return that in your @Bean annotated accessor and use @Autowired to get the Blah that BlahBean creates.

          As someone who hasn't used Spring since, like, 1.x, that wasn't obvious to me even after pouring over documentation, google, blogs, etc. I certainly like this whole @Configuration thing!

          Comment


          • #6
            Originally posted by ipc View Post
            Ok, I have a solution; I think it's the right way to do things.

            The problem I was having was going from something like 'EntityManagerFactoryBean' to an actual instance of EntityManagerFactory.

            I was creating the Bean with 'new' and then calling the after properties set and the getObject() myself. Bad idea. Instead, return the bean after setting the properties. Then @autowire your bean-created instance.

            Here's my final setup:

            The repo I want to use is this:
            Code:
            public interface ThingRepository extends JpaRepository<Thing, Long> {
            
            }
            my META-INF/tx.xml has the following content:

            Code:
            	
            <context:property-placeholder location="META-INF/database.properties" />
            <tx:annotation-driven />
            The my Config class looks like this:
            Code:
            @Configuration
            @ImportResource("META-INF/tx.xml")
            public class SpringConfig {
            
            	private @Value("${javax.persistence.jdbc.driver}")
            	String jdbcDriver;
            	private @Value("${javax.persistence.jdbc.url}")
            	String jdbcUrl;
            	private @Value("${javax.persistence.jdbc.user}")
            	String jdbcUsername;
            	private @Value("${javax.persistence.jdbc.password}")
            	String jdbcPassword;
            
            	public @Bean
            	DataSource dataSource() {
            		DriverManagerDataSource dataSource = new DriverManagerDataSource(
            				jdbcUrl, jdbcUsername, jdbcPassword);
            		dataSource.setDriverClassName(jdbcDriver);
            		return dataSource;
            	}
            
            	@Bean
            	public JpaDialect jpaDialect() {
            		return new HibernateJpaDialect();
            	}
            
            	@Bean
            	public JpaVendorAdapter jpaVendorAdaptor() {
            		return new HibernateJpaVendorAdapter();
            	}
            
            	@Bean
            	public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
            		LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
            		emfb.setPersistenceUnitName("PersistenceUnit");
            		emfb.setJpaDialect(jpaDialect());
            		emfb.setJpaVendorAdapter(jpaVendorAdaptor());
            		emfb.setDataSource(dataSource());
            		return emfb;
            	}
            
            	@Bean
            	public JpaRepositoryFactoryBean<ThingRepository,Thing,Long> getJpaRepositoryFactoryBean() {
            		JpaRepositoryFactoryBean<ThingRepository, Thing, Long> rfb = new JpaRepositoryFactoryBean<LogRepository, Thing, Long>();
            		rfb.setRepositoryInterface(ThingRepository.class);
            		return rfb;
            	}
            	
            	
            	@Autowired 
            	public EntityManagerFactory entityManagerFactory;
            
            	@Bean
            	public PlatformTransactionManager transactionManager() {
            		JpaTransactionManager txm = new JpaTransactionManager();
            		txm.setEntityManagerFactory(entityManagerFactory);
            		txm.setDataSource(dataSource());
            		txm.setJpaDialect(jpaDialect());
            		return txm;
            	}
            }
            Then in main:
            Code:
            		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
            		ctx.register(SpringConfig.class);
            		ctx.scan("com.allstontrading.options.metrics");
            		ctx.refresh();
            
            		ThingRepository repo = ctx.getBean(ThingRepository.class);
            
            		Thing t = new Thing();
            		t.setName("ThingName");
            		repo.saveAndFlush(t);

            so that's cool. Again the point is that if you have somthing that has to be configured for BlahBean then create and return that in your @Bean annotated accessor and use @Autowired to get the Blah that BlahBean creates.

            As someone who hasn't used Spring since, like, 1.x, that wasn't obvious to me even after pouring over documentation, google, blogs, etc. I certainly like this whole @Configuration thing!

            Hi,

            I have following configuration in my java config file for JpaRepository
            <Code>
            @Bean
            public JpaRepositoryFactory jpaRepositoryFactory() {

            return new JpaRepositoryFactory(entityManagerFactory().getObj ect().createEntityManager());
            }

            @Bean
            public UserJpaDAO userJpaDAO() {
            return jpaRepositoryFactory().getRepository(UserJpaDAO.cl ass);
            }
            <Code>

            My DAo interface extends JpaRepository interface

            @Transactional(readOnly = true)
            public interface UserJpaDAO extends JpaRepository<User, String> {}

            I am trying to autowire UserJpaDAO in my service class.

            When i am deploying my application i am getting the below error :

            Could not autowire field: com.acs.compassserver.repository.UserJpaDAO com.user.bo.impl.UserBOImpl.userJpaDAO; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'userJpaDAO' defined in class com.config.ApplicationConfig: Instantiation of bean failed;

            ava.lang.NoSuchMethodError: javax.persistence.EntityManager.getMetamodel()Ljav ax/persistence/metamodel/Metamodel;
            at org.springframework.data.jpa.repository.utils.JpaC lassUtils.getMetadata(JpaClassUtils.java:97)
            at org.springframework.data.jpa.repository.support.Jp aRepositoryFactory.getEntityInformation(JpaReposit oryFactory.java:159)
            at org.springframework.data.jpa.repository.support.Jp aRepositoryFactory.getTargetRepository(JpaReposito ryFactory.java:88)
            at org.springframework.data.jpa.repository.support.Jp aRepositoryFactory.getTargetRepository(JpaReposito ryFactory.java:69)
            at org.springframework.data.repository.core.support.R epositoryFactorySupport.getRepository(RepositoryFa ctorySupport.java:136)
            Truncated. see log file for complete stacktrace

            So could you please let me know what is the wrong in my code.

            Comment


            • #7
              You seem to have a JPA 1.0 JAR in your classpath. Spring Data JPA relies on JPA 2.0. Beyond that you shouldn't pipe the EntityManager created by the EntityManagerFactory as this is a stateful object and can not safely be used in various threads. You better use SharedEntityManagerCreator.createEntityManager(Ent ityManagerFactory) which will create a thread-bound proxy.

              Comment

              Working...
              X