Reputation: 1620
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
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
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);
}
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);
}
}
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