Why didn't #Transactional work with #Autowired EntityManagerFactory? - java

I had a problem with persisting objects to the database using Spring 4.3, JPA 2.1 and Hibernate 5.
Figured out something was wrong with transactions.
Here is my configuration:
#Configuration
#EnableTransactionManagement
public class PersistenceConfig {
/**
* most bean methods skipped, left only the relevant ones
**/
#Bean
#Autowired
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, JpaVendorAdapter jpaVendorAdapter){
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter);
entityManagerFactoryBean.setPackagesToScan("com.company");
entityManagerFactoryBean.setJpaProperties(jpaProperties());
return entityManagerFactoryBean;
}
#Bean
#Autowired
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
Here is my service. The code has run, no exceptions were thrown. But the object was not persisted to the database. I have intuitively understood that either something was wrong with a transaction creation (as the logger didn't show any transactions) or data was not committed to the database. EntityManagerFactory was not null.
#Service
public class Manager {
#Autowired
private EntityManagerFactory entityManagerFactory;
#Transactional
public void persist(Entity entity){
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.persist(entity);
}
}
After I replaced #Autowired EntityManagerFactory with #javax.persistence.PersistenceContext EntityManager, everything worked fine.
#Service
public class Manager {
#javax.persistence.PersistenceContext
private EntityManager entityManager;
#Transactional
public void persist(Entity entity){
entityManager.persist(entity);
}
}
Why doesn't it work with #Autowired EntityManagerFactory?

You are using Spring for transaction management and as such you want to get the current transactional EntityManager. If you are injecting the EntityManagerFactory and use it to get an EntityManager you have a good change you end up with a new one, this new one isn't bound to the started transaction.
Instead inject the EntityManager using #PersistenceContext
#PersistenceContext
private EntityManager em.
If you really want to inject the EntityMangerFactory you have to use #PersistenceUnit instead of #Autowired. The #PersistenceUnit is handled different then a plain #Autowired.
#PersistenceUnit
private EntityManagerFactory emf;

Related

#PersistenceContext on two different LocalContainerEntityManagerFactoryBean throws TransactionRequiredException

I have two LocalContainerEntityManagerFactoryBeans set up, one with #Primary annotation and one without. This is because I have to communicate with two distinct databases, and if I remove the #Primary decoration I get a duplicate bean error. Both of them are constructed in the exact same way, here's one for reference:
package com.myorg.rest.config.dba;
import ...
#Configuration
#EnableJpaRepositories(
basePackages = "com.myorg.rest.dao.dbA",
entityManagerFactoryRef = "dbAEntityManager",
transactionManagerRef = "dbATransactionManager"
)
#EnableTransactionManagement
public class DbADataSourceConfig {
#Autowired
private EnvProperties settings;
#Bean
#Primary
public DataSource prjDataSource() {
DataSourceProperties ds = settings.getdbADatasource();
return ds.initializeDataSourceBuilder().type(BasicDataSource.class).build();
}
#Bean(name = "dbAEntityManager")
#Primary
public LocalContainerEntityManagerFactoryBean dbAEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(prjDataSource());
em.setPackagesToScan(new String[] { "com.myorg.model.entities.dba" });
em.setPersistenceUnitName("dbAUnit");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
// em.afterPropertiesSet();
return em;
}
#Bean(name = "dbATransactionManager")
#Primary
public PlatformTransactionManager dbATransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(dbAEntityManager().getObject());
return transactionManager;
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return properties;
}
}
Both of them are injected inside DAOs with #PersistenceContext(unitName = "dbAUnit") and #PersistenceContext(unitName = "dbBUnit"), and they are both successfully bootstrapping the corresponding EntityManager inside. I can create in both of them and then retrieve the created entity. But when I go to retrieve all entities with the following:
public List<T> findAll() {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<T> entityQuery = criteriaBuilder.createQuery(clazz);
entityQuery.from(clazz);
return entityManager.createQuery(entityQuery).getResultList();
}
One of them works flawlessly and the other one doesn't (it returns 0 entities). After debugging, I've realized that one of them was issuing the insert into... sql command, while the other one wasn't. I tried to force with entityManager.flush() and got
Caused by:
javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:413)
at org.hibernate.internal.SessionImpl.checkTransactionNeededForUpdateOperation(SessionImpl.java:3398)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1355)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1350)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:366)
at com.sun.proxy.$Proxy95.flush(Unknown Source)
I then tried to decorate the DAOs with #Transactional, and further decorate each individual method with #Transactional. Also tried injecting with #PersistenceContext(unitName = "dbAUnit", type = PersistenceContextType.TRANSACTION), but all of those attempts result in the exact same error.
Why is the 'secondary' #PersistenceContext not putting the transactions in?
When using multiple transaction managers with #Transactional, you need to declare the transaction manager to use explicitly, otherwise the #Primary manager always gets selected, and your secondary EntityManager cannot possibly join its transaction.
Try using #Transactional("dbBTransactionManager") wherever the second persistence context is injected, to see if the problem goes away.

Create DataSource lazily and transaction manager

I have a DB access module that interacts with multiple types of data sources.
Some higher modules use the DB module without interacting with JDBC data source. Therefore, I would like the data source to be defined as lazy. However, I also define a transaction manager which I can't define as lazy since it is not a direct dependency in my code. Since the transaction manager depends on the data source, the latter is always created.
#Configuration
#EnableTransactionManagement
public class SpringConfig {
#Bean(destroyMethod = "close")
#Lazy
#Autowired
public DataSource dataSource() throws Exception {
//create data source
}
#Bean
#Lazy
#Autowired
public PlatformTransactionManager txManager() throws Exception {
DataSourceTransactionManager txManager = new DataSourceTransactionManager(dataSource());
return txManager;
}
}
#Lazy
#Repository("FundRepository")
public class MyRepository {
private JdbcTemplate jdbcTemplate;
#Autowired
public void setDataSource(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
}
Any idea how to make this work?

Spring + Jboss7 #Transactional Not working

I am upgrading my JBoss server from 5 to 7 and am now incorporating Spring 4. I am having some trouble using Spring's #Transactional annotation. It does not appear to be working. I am also trying to use a java based configuration file instead of an xml file (I believe I can get away without using any xml, but correct me if I am wrong). The problem is that nothing is being saved in my db, leading me to believe that the #Transactional isn't working. Here is my config file:
#Configuration
#ComponentScan
#EnableTransactionManagement
public class SpringBeanConfiguration {
#Bean
public FirstTestBean firstTestBean() {
return new FirstTestBean();
}
#Bean
public TestService testService() {
return new TestServiceImpl();
}
#Bean
public SomethingDAO somethingDAO(){
return new SomethingDAOImpl();
}
#Bean
public GeneralDAO generalDAO(){
return new GeneralDAOImpl();
}
Here is a test class with the #Transactional method:
//#RequestScoped
#ManagedBean(name="firstTestBean")
#Component
public class FirstTestBean {
private EntityManager em;
private EntityManagerFactory emf;
#Transactional
public String transactionalTest() {
//ApplicationContext context = new AnnotationConfigApplicationContext(SpringBeanConfiguration.class);
Something something = new Something();
getEntityManager().persist(something);
return "dkljs";
}
public EntityManager getEntityManager() {
if (em == null) {
emf = Persistence.createEntityManagerFactory("xxx");
em = emf.createEntityManager();
}
return em;
}
I am also using Hibernate 4, which is compatible with Spring. I commented out the ApplicationContext because I have that running separately on the start up of JBoss. I was using that earlier to access a bean, but I have since simplified things in order to get the #Transactional working and thus do not need it here. The #ComponentScan does not need parameters because these classes are in the same package.
Any help would be greatly appreciated. Thanks!
Updates
I've made some of the changes suggested. Things appear to be moving in the right direction.
Here are my updated files:
#ManagedBean(name="firstTestBean")
public class FirstTestBean {
public String getTestString() {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringBeanConfiguration.class);
TestService testService = context.getBean(TestService.class);
testService.transactionalTest();
return "adfs";
}
public String test() {
return "firstTestBean works";
}
}
Note - some of these classes will be run outside of the Jboss application server as standalone applications, so for that reason, I am staying away from FacesContext when instantiating the TestService in FirstTestBean, as Spring beans work in standalone, but FacesContext beans do not.
#Component
#Transactional
public class TestServiceImpl implements TestService {
public GeneralDAO generalDAO;
//#Autowired
private EntityManager em;
//#Autowired
private EntityManagerFactory emf;
public TestServiceImpl(){}
public String transactionTest() {
Something something = new Something();
getEntityManager().persist(something);
return "dkljs";
}
#autowired on the EntityManager and EntityManagerFactory did not work - I received an error saying No Qualified Bean of type EntityManager when it was annotated with the #autowired like suggested.
#Configuration
#ComponentScan
#EnableTransactionManagement
public class SpringBeanConfiguration implements TransactionManagementConfigurer {
#Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
String hibernatePropsFilePath = "/[path]/hibernate.cfg.xml";
File hibernatePropsFile = new File(hibernatePropsFilePath);
org.hibernate.cfg.Configuration cfg = new org.hibernate.cfg.Configuration().configure(hibernatePropsFile);
SessionFactory sessionFactory = cfg.buildSessionFactory();
HibernateTransactionManager txManager = new HibernateTransactionManager(sessionFactory);
txManager.setNestedTransactionAllowed(true);
return txManager;
}
}
The error I am getting now is:Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.context.event.internalEventListenerProcessor': Initialization of bean failed
; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration':
Injection of autowired dependencies failed; nested exception is org.hibernate.service.UnknownUnwrapTypeException: Cannot unwrap to requested type [javax.sql.DataSource]
I take it this means that the #Transactional is at least being recognized, but I'm having some issues getting this to work. Any further advice would be greatly appreciated.
More Updates
Found this article: http://www.baeldung.com/the-persistence-layer-with-spring-and-jpa#javaconfig
and followed it. My new config file:
#Configuration
#ComponentScan
#EnableTransactionManagement
public class SpringBeanConfiguration { //implements TransactionManagementConfigurer {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "xxx.xxx.xxx" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
// em.setJpaProperties(additionalProperties());
return em;
}
#Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/jboss_test");
dataSource.setUsername( "root" );
dataSource.setPassword( "root" );
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
Good news is that I am no longer having deployment issues. The logging also suggests that my changes are having some effect on the server. Unfortunately, nothing is being saved, so it is still not quite working.
The first thing that draws my attention is the use of #Transactional and #Component on a ManagedBean.
JSF and Spring are definately made to work together but i never saw them used this way on many projects i was working on.
Im not sure if that is the cause of your problems but please consider changing that.
I would do like this:
a) Define some service layer where you would wrap your calls with transactions and inject the JPA classes:
#Component
#Transactional
class Service{
#Autowired
private EntityManager em;
#Autowired
private EntityManagerFactory emf;
public String serviceMethod(..){ .. }
}
b) Inject that to Jsf's ManagedBean while removing the unnecessary annotations:
#ManagedBean(name="firstTestBean")
public class FirstTestBean {
#ManagedProperty("#{service}")
private Service service;
public String transactionalTest() {
return service.serviceMethod();
}
}
Ok, so I finally got it (Check out my OP for all updates that led me to this point).
Here's my final config file:
#Configuration
#ComponentScan
#EnableTransactionManagement
public class SpringBeanConfiguration {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceUnitName("myPersistenceContext");
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "xxx.xxx.xxx" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
// em.setJpaProperties(additionalProperties());
return em;
}
#Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/jboss_test");
dataSource.setUsername( "root" );
dataSource.setPassword( "root" );
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
JtaTransactionManager transactionManager = new JtaTransactionManager();
// transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
So first, I added in
em.setPersistenceUnitName("myPersistenceContext");
This gave me the error:
Spring IllegalStateException: A JTA EntityManager cannot use getTransaction()
From here, I did some research (Spring IllegalStateException: A JTA EntityManager cannot use getTransaction())
and changed
JpaTransactionManager transactionManager = new JpaTransactionManager();
to
JtaTransactionManager transactionManager = new JtaTransactionManager();
Also, my persistence.xml file is:
<persistence-unit name="myPersistenceContext">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:jboss/datasources/myDS</jta-data-source>
<class>xxx.xxx.xxx.Something</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.cache.use_second_level_cache" value="true" />
<property name="hibernate.id.new_generator_mappings" value="false"/>
<property name="hibernate.classloading.use_current_tccl_as_parent" value="false"/>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.archive.autodetection" value="class, hbm" />
</properties>
</persistence-unit>
Thanks to Maciej Kowalski for your help - you pushed me in the right direction.

Spring Hibernate not committing data

I have a Spring MVC application we're developing, using Spring 4.2.3 and Hibernate 5.0.6. In some of our testing we found that some of our data is not being saved to the database (Oracle). The code is as follows:
#Service
#Transactional
public class MyService {
#Autowired
private MyDao dao;
public void saveCustomer(Customer c) {
dao.saveCustomer(c);
}
}
#Repository
#Transactional
public class MyDao {
#Autowired
private SessionFactory sessionFactory; // Hibernate's SessionFactory
public void saveCustomer(Customer cust) {
sessionFactory.getCurrentSession().clear();
sessionFactory.getCurrentSession().flush();
sessionFactory.getCurrentSession().save(cust);
}
}
To be honest, I just saw that #Transactional on MyDao, as I'm writing this. That probably is wrong, since #Transactional is already on the service layer.
We're using Java Config....showing the relevant beans....
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan({"my.package"})
public class Config extends WebMvcConfigurerAdapter {
#Bean(name = "sessionFactory")
public SessionFactory getSessionFactory(ComboPooledDataSource dataSource) {
LocalSessionFactoryBuilder sessionBuilder = new LocalSessionFactoryBuilder(dataSource);
sessionBuilder.scanPackages("my.entities");
sessionBuilder.addProperties(getHibernateProperties());
return sessionBuilder.buildSessionFactory();
}
#Bean(name = "transactionManager")
public HibernateTransactionManager getTransactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager transactionManager = new HibernateTransactionManager(sessionFactory);
return transactionManager;
}
}
My question is, other than the #Transactional on MyDao, which shouldn't be there, is there anything else that stands out to anyone as to why Customer does not save to the database? Are we even doing transactions correctly? I've never liked using Hibernate's SessionFactory, but that's the way we went. Are we using it correctly within a Spring Transaction?
Thanks!
Chris

How to execute custom SQL query with spring-managed transactional EntityManager

I have an application built on Spring. I let the Spring do the all #Transactional magic and everything works fine as long as I operate on my entities that are mapped to Java objects.
However, when I want to do some custom job on a table that is not mapped to any of my Java entities, I'm stuck. Some time ago, I found a solution to execute a custom query like this:
// em is instance of EntityManager
em.getTransaction().begin();
Statement st = em.unwrap(Connection.class).createStatement();
ResultSet rs = st.executeQuery("SELECT custom FROM my_data");
em.getTransaction().commit();
When I try this with the entity manager injected from Spring with the #PersistenceContext annotation, I receive almost obvious exception:
java.lang.IllegalStateException:
Not allowed to create transaction on shared EntityManager -
use Spring transactions or EJB CMT instead
I finally managed to extract non-shared Entity Manager like this:
#Inject
public void myCustomSqlExecutor(EntityManagerFactory emf){
EntityManager em = emf.createEntityManager();
// the em.unwrap(...) stuff from above works fine here
}
Nevertheless, I find this solution neither comfortable nor elegant. I just wonder if there is any other way to run custom SQL queries in this Spring-transactional-driven environment?
For those who are curious - this problem appeared when I tried to create user accounts in my application and in the related forum at once - I did not want the forum's users table to be mapped to any of my Java entities.
You can use createNativeQuery to execute any arbitrary SQL on your database.
EntityManager em = emf.createEntityManager();
List<Object> results = em.createNativeQuery("SELECT custom FROM my_data").getResultList();
The above answer still holds true but I would like to edit in some additional information that may also be relevant to people looking at this question.
While it is true that you can use the createNativeQuery method to execute native queries through an EntityManager; there is an alternative (arguably better) way of doing it if you are using the Spring Framework.
The alternative method for executing queries with Spring (that will behave with the configured transactions) is to use the JDBCTemplate. It is possible to use both the JDBCTemplate and a JPA EntityManager within the same application. The configuration would look something like this:
InfrastructureConfig.class:
#Configuration
#Import(AppConfig.class)
public class InfrastructureConfig {
#Bean //Creates an in-memory database.
public DataSource dataSource(){
return new EmbeddedDatabaseBuilder().build();
}
#Bean //Creates our EntityManagerFactory
public AbstractEntityManagerFactoryBean entityManagerFactory(DataSource dataSource){
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(dataSource);
emf.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
return emf;
}
#Bean //Creates our PlatformTransactionManager. Registering both the EntityManagerFactory and the DataSource to be shared by the EMF and JDBCTemplate
public PlatformTransactionManager transactionManager(EntityManagerFactory emf, DataSource dataSource){
JpaTransactionManager tm = new JpaTransactionManager(emf);
tm.setDataSource(dataSource);
return tm;
}
}
AppConfig.class:
#Configuration
#EnableTransactionManagement
public class AppConfig {
#Bean
public MyService myTransactionalService(DomainRepository domainRepository) {
return new MyServiceImpl(domainRepository);
}
#Bean
public DomainRepository domainRepository(JdbcTemplate template){
return new JpaAndJdbcDomainRepository(template);
}
#Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
JdbcTemplate template = new JdbcTemplate(dataSource);
return template;
}
}
And an example repository that would use both JPA and JDBC:
public class JpaAndJdbcDomainRepository implements DomainRepository{
private JdbcTemplate template;
private EntityManager entityManager;
//Inject the JdbcTemplate (or the DataSource and construct a new JdbcTemplate)
public DomainRepository(JdbcTemplate template){
this.template = template;
}
//Inject the EntityManager
#PersistenceContext
void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
//Execute a JPA query
public DomainObject getDomainObject(Long id){
return entityManager.find(id);
}
//Execute a native SQL Query
public List<Map<String,Object>> getData(){
return template.queryForList("select custom from my_data");
}
}
You can use EntityManager.createNativeQuery(String sql) to use direct sql code or use EntityManager.createNamedQuery(String name) to execute precompiled query.
You still use spring-managed Entity manager, but working on non managed objects

Categories