I am using JPA in my application, and it works once I query for objects, however it throw error javax.persistence.TransactionRequiredException: No transactional EntityManager available
once I tried to save or update an object.
This is the java configuration:
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
@PropertySource("classpath:dao.properties")
public class JpaConfig {
@Autowired
private Environment env;
@Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
.....................
return dataSource;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.dialect", ...........)
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setJpaProperties(jpaProperties);
entityManagerFactoryBean.setPackagesToScan("com....");
return entityManagerFactoryBean;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
}
Note I use proxyTargetClass = true
in the @EnableTransactionManagement
, since I do not want to create the useless interfaces in my application.
And this is the concrete implemention of the dao:
@Transactional
@Repository
public abstract class AbstractJPADao<I, E> {
@Autowired
@PersistenceContext
protected EntityManager entityManager;
private Class<E> type;
public AbstractJPADao() {
type=....
}
@Override
public Result<E> find(I id) {
E e = entityManager.find(type, id);
return Result.newInstance().setContent(e);
}
@Override
public Result<E> find(Map<String, Object> condition) {
Query q = entityManager.createQuery(".......));
return Result.newInstance().setContent(q.getResultList());
}
@Override
public E save(E element) {
entityManager.persist(element);
return element;
}
@Override
public E update(E element) {
entityManager.merge(element);
return element;
}
@Override
public void delete(E element) {
entityManager.remove(element);
}
}
@Repository
@Transactional
public class DepartmentDao extends AbstractJPADao<String, Department> {
@Override
protected String selectCause(Map<String, Object> condition) {
return "";
}
}
And the controller as the client of the dao:
@Controller
@RequestMapping("/api/deps")
public class DepartmentCtrl {
@Autowired
private DepartmentDao departmentDao;
@RequestMapping(value = "", method = RequestMethod.POST)
public Result create(@Valid Department department, BindingResult bindingResult) {
if (!bindingResult.hasErrors()) {
departmentDao.save(department);
return Result.newInstance().setContent(department);
}
throw new RuntimeException("...");
}
}
Is there anything wrong?
dao.properties:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/proj
jdbc.username=root
jdbc.password=
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.hbm2ddl.auto=update
#hibernate.ejb.naming_strategy=true
hibernate.show_sql=true
hibernate.format_sql=true
You can choose between JPA's persist and merge and Hibernate's save and update methods. It seems like there are 2 pairs of 2 methods that do the same. You can use the methods persist and save to store a new entity and the methods merge and update to store the changes of a detached entity in the database.
We use the EntityManager. merge() method to update an entity. This method takes the entity to be saved as the parameter and return the merged entity back as the result.
You can modify or update the entities by defining the repository method using @Query and @Modifying annotations. All the update/delete operations are based on the transaction scope.
Try renaming method transactionManager
to txManager
in the class JpaConfig
Autowiring goes by the name txManager
Edit
Also the framework could be expecting a no argument method for txManager. Can you try changing to
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
As noticed Tiny, you have two annotations @Autowired and @PersistenceContext over the protected field of type EntityManager in AbstractJPADao. Try removing @Autowired. @PersistenceContext is enough to inject the EntityManager.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With