I cannot solve following issue. My Spring + JPA + hibernate + Oracle DB application properly reads data from the database, but doesn't save them. I found similar issues searching the Internet (also this forum) but unfortunately was not able to fix it in my code.
One of the idea is to add entityManager.flush()
after calling the persist()
method but then I am getting an Exception javax.persistence.TransactionRequiredException: no transaction is in progress
.
On the beginnig I also used EntityManagerFactory
in my repository class, but based on different comments I've migrated it to use EntityManager
with @PersistenceContext
annotation.
I will be appreciate for any help.
Here is my code:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>hr_db</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>oralce_hr_hibernate</name>
<description>Oracle HR database</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</dependency>
<!-- http://stackoverflow.com/questions/9898499/oracle-jdbc-ojdbc6-jar-as-a-maven-dependency -->
<!-- mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=11.2.0.3 -Dpackaging=jar -Dfile=ojdbc6.jar -DgeneratePom=true -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.4</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Job.java:
package com.example.entities;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "JOBS")
public class Job {
@Id
@Column(name = "JOB_ID")
String jobId;
@Column(name = "JOB_TITLE")
String jobTitle;
@Column(name = "min_salary")
int minSalary;
@Column(name = "max_salary")
int maxSalary;
public Job() {
}
public Job(String id) {
jobId = id;
}
public String getJobId() {
return jobId;
}
public void setJobId(String jobId) {
this.jobId = jobId;
}
public String getJobTitle() {
return jobTitle;
}
public void setJobTitle(String jobTitle) {
this.jobTitle = jobTitle;
}
public int getMinSalary() {
return minSalary;
}
public void setMinSalary(int minSalary) {
this.minSalary = minSalary;
}
public int getMaxSalary() {
return maxSalary;
}
public void setMaxSalary(int maxSalary) {
this.maxSalary = maxSalary;
}
}
JobRepository.java:
package com.example.repositories.interfaces;
import org.springframework.data.repository.Repository;
import com.example.entities.Job;
public interface JobRepository extends Repository<Job, String> {
public Job findByJobId(String id);
public void saveOrUpdate(Job job);
public void delete(Job job);
public void deleteByJobId(String jobId);
public void persistJob(Job job);
}
JobRepositoryImpl.java:
package com.example.repositories;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.example.entities.Job;
import com.example.repositories.interfaces.JobRepository;
@Transactional(propagation = Propagation.REQUIRED)
public class JobRepositoryImpl implements JobRepository {
@PersistenceContext
private EntityManager entityManager;
public JobRepositoryImpl() {
}
public EntityManager getEntityManager() {
return entityManager;
}
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
@Override
public Job findByJobId(String id) {
return (Job) entityManager.find(Job.class, id);
}
@Override
public void saveOrUpdate(Job job) {
entityManager.merge(job);
}
@Override
public void delete(Job job) {
entityManager.remove(job);
}
@Override
public void deleteByJobId(String jobId) {
entityManager.remove(findByJobId(jobId));
}
@Override
public void persistJob(Job job) {
entityManager.persist(job);
// this is causing javax.persistence.TransactionRequiredException: no transaction is in progress
// entityManager.flush();
}
}
application-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<context:property-placeholder
location="file:///${app.properties.dir}/db.properties" />
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.example.entities" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan">
<list>
<value>com.example.entities</value>
</list>
</property>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="persistenceUnitName" value="HR" />
</bean>
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="ORACLE" />
<property name="showSql" value="true" />
<property name="generateDdl" value="false" />
<property name="databasePlatform" value="org.hibernate.dialect.OracleDialect" />
</bean>
<bean id="dataSource" class="oracle.jdbc.pool.OracleDataSource"
destroy-method="close">
<property name="URL" value="${test.oracle.url}" />
<property name="user" value="${test.oracle.username}" />
<property name="password" value="${test.oracle.password}" />
<property name="connectionCachingEnabled" value="true" />
</bean>
</beans>
repositories-impl-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="jobRepository" class="com.example.repositories.JobRepositoryImpl">
</bean>
</beans>
JobRepositoryTest.java:
package com.example.repositories;
import static org.junit.Assert.assertEquals;
import java.util.logging.Logger;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.example.entities.Job;
import com.example.repositories.interfaces.JobRepository;
@Transactional(propagation = Propagation.REQUIRED)
public class JobRepositoryTest {
private static final String TEST_JOB_ID = "TEST_JOB";
JobRepository repository;
ConfigurableApplicationContext context;
Logger logger = Logger.getLogger(JobRepositoryTest.class.getName());
@Before
public void setUp() {
// Create the test configuration for the application from two files
context = new ClassPathXmlApplicationContext("classpath:/com/example/application-config.xml",
"classpath:/com/example/repositories/repositories-impl-config.xml");
// Get the bean to use to invoke the application
repository = context.getBean(JobRepository.class);
}
// Test is passing
@Test
public void testFindById() {
Job job = repository.findByJobId("AD_PRES");
assertEquals("President", job.getJobTitle());
assertEquals(20080, job.getMinSalary());
assertEquals(40000, job.getMaxSalary());
}
// Doesn't save the job
@Test
@Rollback(false)
public void testSaveOrUpdateJob() {
Job job = createTestJob();
logger.info("BEFORE");
repository.persistJob(job);
logger.info("AFTER");
}
private Job createTestJob() {
Job testJob = new Job(TEST_JOB_ID);
testJob.setJobTitle("Test job title");
testJob.setMinSalary(0);
testJob.setMaxSalary(9999);
return testJob;
}
}
Output from console:
INFO: BEFORE
23:13:04.579 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.transaction.interceptor.TransactionInterceptor#0'
23:13:04.581 [main] DEBUG org.springframework.transaction.annotation.AnnotationTransactionAttributeSource - Adding transactional method 'JobRepositoryImpl.persistJob' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
23:13:04.586 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'transactionManager'
23:13:04.591 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Creating new transaction with name [com.example.repositories.JobRepositoryImpl.persistJob]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
23:13:04.652 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Opened new Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList@2add4d24 updates=org.hibernate.engine.spi.ExecutableList@3f93e4a8 deletions=org.hibernate.engine.spi.ExecutableList@12b5454f orphanRemovals=org.hibernate.engine.spi.ExecutableList@5445f5ba collectionCreations=org.hibernate.engine.spi.ExecutableList@1431267b collectionRemovals=org.hibernate.engine.spi.ExecutableList@342726f1 collectionUpdates=org.hibernate.engine.spi.ExecutableList@c808207 collectionQueuedOps=org.hibernate.engine.spi.ExecutableList@77134e08 unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
23:13:04.653 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Preparing JDBC Connection of Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList@2add4d24 updates=org.hibernate.engine.spi.ExecutableList@3f93e4a8 deletions=org.hibernate.engine.spi.ExecutableList@12b5454f orphanRemovals=org.hibernate.engine.spi.ExecutableList@5445f5ba collectionCreations=org.hibernate.engine.spi.ExecutableList@1431267b collectionRemovals=org.hibernate.engine.spi.ExecutableList@342726f1 collectionUpdates=org.hibernate.engine.spi.ExecutableList@c808207 collectionQueuedOps=org.hibernate.engine.spi.ExecutableList@77134e08 unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
23:13:04.653 [main] DEBUG org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Obtaining JDBC connection
23:13:04.653 [main] DEBUG org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Obtained JDBC connection
23:13:04.654 [main] DEBUG org.hibernate.engine.transaction.spi.AbstractTransactionImpl - begin
23:13:04.655 [main] DEBUG org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction - initial autocommit status: true
23:13:04.655 [main] DEBUG org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction - disabling autocommit
23:13:04.656 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Exposing Hibernate transaction as JDBC transaction [oracle.jdbc.driver.LogicalConnection@2264e43c]
23:13:04.658 [main] DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils - Opening JPA EntityManager
23:13:04.686 [main] DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils - Registering transaction synchronization for JPA EntityManager
23:13:04.697 [main] DEBUG org.hibernate.event.internal.AbstractSaveEventListener - Generated identifier: TEST_JOB, using strategy: org.hibernate.id.Assigned
23:13:04.730 [main] DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager
23:13:04.732 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Initiating transaction commit
23:13:04.732 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Committing Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList@2add4d24 updates=org.hibernate.engine.spi.ExecutableList@3f93e4a8 deletions=org.hibernate.engine.spi.ExecutableList@12b5454f orphanRemovals=org.hibernate.engine.spi.ExecutableList@5445f5ba collectionCreations=org.hibernate.engine.spi.ExecutableList@1431267b collectionRemovals=org.hibernate.engine.spi.ExecutableList@342726f1 collectionUpdates=org.hibernate.engine.spi.ExecutableList@c808207 collectionQueuedOps=org.hibernate.engine.spi.ExecutableList@77134e08 unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
23:13:04.732 [main] DEBUG org.hibernate.engine.transaction.spi.AbstractTransactionImpl - committing
23:13:04.734 [main] DEBUG org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction - committed JDBC Connection
23:13:04.734 [main] DEBUG org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction - re-enabling autocommit
23:13:04.735 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Closing Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList@2add4d24 updates=org.hibernate.engine.spi.ExecutableList@3f93e4a8 deletions=org.hibernate.engine.spi.ExecutableList@12b5454f orphanRemovals=org.hibernate.engine.spi.ExecutableList@5445f5ba collectionCreations=org.hibernate.engine.spi.ExecutableList@1431267b collectionRemovals=org.hibernate.engine.spi.ExecutableList@342726f1 collectionUpdates=org.hibernate.engine.spi.ExecutableList@c808207 collectionQueuedOps=org.hibernate.engine.spi.ExecutableList@77134e08 unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] after transaction
23:13:04.735 [main] DEBUG org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Releasing JDBC connection
23:13:04.735 [main] DEBUG org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Released JDBC connection
Jul 20, 2016 11:13:04 PM com.example.repositories.JobRepositoryTest testSaveOrUpdateJob
INFO: AFTER