Jamie White
Jamie White

Reputation: 1620

Persisting JPA Entity using CrudRepository

Having issues saving JPA Entity through CrudRepository. It seems to return the original object that was passed to it without persisting the object to the database. I know this is not the correct way to do it but for testing purposes if I place an instance of TransactionSynchronizationManager before the save, it seems persist correctly. This leads me to think maybe there an issue the Transaction Manager?

In addition if I discard the repository and use the Entity Manager (em) I get the same result, however if I call em.flush(), I get the Exception "Transactionrequiredexception, No Transaction in Progress".

@Transactional
public class UserServiceImpl implements UserService{

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private EntityManagerFactory emf;

    @Override
    @Transactional(readOnly=false)
    public User save(User user) {

//  EntityManager em = emf.createEntityManager();
    //Object persists when adding following line
//  TransactionSynchronizationManager.bindResource(emf , new EntityManagerHolder(em)); 
        return  userRepository.save(user);
    }

}


@ComponentScan(basePackages = {"..."})
@Import(value={DataContextConfig.class,SecurityConfig.class})
public class AppConfig{
}

@Configuration
@ComponentScan(basePackages = {".."})
@Import(DataConfig.class)
public class DataContextConfig {

}

@Configuration
@EnableTransactionManagement(mode=AdviceMode.ASPECTJ)
@EnableJpaRepositories(value={"com.repository"}, entityManagerFactoryRef="entityManagerFactory", transactionManagerRef="transactionManager")
@PropertySource("classpath:data.properties")
public class DataConfig {
... 
    @Bean
    public PlatformTransactionManager transactionManager() { 
        JpaTransactionManager txManager = new JpaTransactionManager(); 
        txManager.setEntityManagerFactory((EntityManagerFactory) entityManagerFactory());
        return txManager; 
    }
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {      
        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        factory.setPackagesToScan("...");
        factory.setJpaPropertyMap(jpaProperties());
        factory.setDataSource(dbSource());
        return factory; 
    }


        @Bean
        public DriverManagerDataSource dbSource(){  
            DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
            driverManagerDataSource.setDriverClassName(environment.getRequiredProperty("jdbc.driverClassName"));
            driverManagerDataSource.setUrl(environment.getRequiredProperty("jdbc.url"));
            driverManagerDataSource.setUsername(environment.getRequiredProperty("jdbc.username"));
            driverManagerDataSource.setPassword(environment.getRequiredProperty("jdbc.password"));      
            return driverManagerDataSource;
        }

    }

I have uploaded a small project that isolates the exception. Zip and run AccountTester.class http://www14.zippyshare.com/v/81636273/file.html

Upvotes: 0

Views: 5768

Answers (2)

Jamie White
Jamie White

Reputation: 1620

It turns out there were multiple @EnableTransactionManagement(mode=AdviceMode.ASPECTJ) annotations within the application. This was cause of the problem, after removing the one within the Neo4j @Configuration Class the problem went away.

Upvotes: 0

Donovan Muller
Donovan Muller

Reputation: 3842

As per your sample project. Below are the only classes modified to avoid your TransactionRequiredException: no transaction is in progress problem and successfully insert an account:

package com.jpa.base.repository;

import com.jpa.base.entity.Account;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface AccountRepository extends CrudRepository<Account, Long> {

    public Account findByEmailAddress(@Param(value = "emailAddress") String emailAddress);

    public Account findByAccountId(@Param(value = "accountId") Long accountId);

}

Original for reference

You need to mark your Spring Data JPA repository with @Repository (Not your service). See here

package com.jpa.base.service.impl;

import com.jpa.base.entity.Account;
import com.jpa.base.repository.AccountRepository;
import com.jpa.base.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service("accountService")
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountRepository accountRepository;

    @Override
    @Transactional(readOnly = true)
    public Account findByAccountId(Long accountId) {
        return accountRepository.findByAccountId(accountId);
    }

    @Override
    @Transactional(readOnly = true)
    public Account findByEmailAddress(String emailAddress) {
        return accountRepository.findByEmailAddress(emailAddress);
    }

    @Override
    @Transactional
    public Account save(Account account) {
        return accountRepository.save(account);
    }

}

Original for reference

Note the changes of removing @Repository from the service (needs to be on your Spring Data JPA repository interface) and using the accountRepository to persist your entity in your save(...) method.

Not sure why you were trying to use the EntityManagerFactory to create a new EntityManager (if you really need an EntityManager instance you should just inject the configured EntityManager, not the factory). This also happens to be the reason for your TransactionRequiredException.

Anyhoo, why bother with all that when you can just use your repository to persist your entity. Running your AccountTester now produces the desired functionality:

...
Hibernate: insert into account (email_address, name, version) values (?, ?, ?)
INFO : com.jpa.base.entity.AccountTester - Account Saved: Account Id: 3, Email Address:[email protected], Name: James Brown, Version: 0

Upvotes: 1

Related Questions