Reputation: 3166
I looked around for similar problems but couldn't find a solution for this:
I have a Spring Data JPA application that whenever I try to do a trasaction I get javax.persistence.TransactionRequiredException: no transaction is in progress
.
I believe it has something to do with the Transaction Manager or Entity Manager Factory, but can't put my finger on it.
The context files are here (latest checked in are here), but here is the part that matters:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSourceMySQL" />
<property name="persistenceUnitName" value="spring-jpa" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="true" />
<property name="showSql" value="true" />
<property name="database" value="MYSQL" />
</bean>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSourceMySQL"/>
</bean>
<bean id="dataSourceMySQL" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/dbname"/>
<property name="username" value="user"/>
<property name="password" value="pass"/>
</bean>
<jpa:repositories base-package="com.simplecash.dal.repository" />
A sample Repository is here and then created a Repository Factory here, which I'm not sure if I need... Then use it here (line 34).
public void populateWithTestData() {
Bank bank = new Bank();
bank.setName("ContentName");
bank.setCode("ContentCode");
RepositoryFactory.getEntityManager().getTransaction().begin();
BankRepository bankRepository = RepositoryFactory.getRepository(BankRepository.class);
bankRepository.save(bank);
bankRepository.flush();
RepositoryFactory.getEntityManager().getTransaction().commit();
}
A couple of things are wrong above, but I I've tried fixing it and can't:
bankRepository
should be @Autowired
, but when I do that I get null
. However, in this testcase it is Autowired and works.Has anyone faced a similar problem and knows what's going on? Thanks a ton for taking the time to read this. Hope the answer to this question will help other folks.
Upvotes: 1
Views: 15591
Reputation: 3136
I had a similar issue when combining spring-batch
and spring-jpa
. In my batch XML, I had this line:
<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager"/>
which caused an error since JPA needs a PlatformTransactionManager
.
Upvotes: 2
Reputation: 22549
What Spring does with your repository beans ( e.g. BankRepository
), it creates a proxy around it, and then it is ready to be injected into other collaborators, which in your case is DatabaseManagerDAO
. However if you create the object yourself like you do:
BankRepository bankRepository = RepositoryFactory.getRepository(BankRepository.class);
Instead of expected Spring's proxy ( that already does transaction management for you ), you are getting a simple object that is not aware of anything beyond it is immediate declaration.
What you need to do instead is to trust Spring to do the plumbing for you and just inject a bankRepository
bean into a DatabaseManagerDAO
(although I don't really think you need both DAO and Repository, since those terms really mean the same thing :)
No need for another abstraction. Just inject it as a bean to whatever component needs it.
bankRepository
should be @Autowired
, but when I do that I get null
. However, in this testcase it is Autowired and works.In a case where it works you run your test with AbstractTransactionalJUnit4SpringContextTests
, which knows about 'bankRepository' bean, hence autowires it. In your DatabaseManagerDAO
, I see neither autowiring nor a setter for the bankRepository
, in fact you create it manually from the factory.
What jpa:repositories
in your XML config really does => it scans the package and creates Spring beans for each component that is either annotated as @Repository
or implements a Repository
interface.
With that in mind, what you should do in order to use a BankRepository
repository in your DatabaseManagerDAO
is to inject it. You can do it via "autowiring":
@Service
public class DatabaseManagerDAO {
@Autowired
BankRepository bankRepository;
...
}
instead of manually creating it trough your factory.
Again, DatabaseManagerDAO
in your case is probably a service ( @Service
), and not a DAO
, but I'll leave it up to you to decide on that.
Notice that a DatabaseManagerDAO
should also be loaded by Spring in order for the autowiring to work, so make sure it has one of the Spring annotations ( @Service
/ @Component
) when you package scan it ( e.g. <context:component-scan base-package="org.example"/>
).
Upvotes: 2
Reputation: 16035
Both answer provided here offer good points (using injection to get the proxied beans and using transaction-annotations), however, I'm pretty sure that for the annotation-driven transactions to work (@Transactional), you need to add the following to your xml-configuration:
<tx:annotation-driven transaction-manager="transactionManager"/>
Also make sure to add the tx-namespace in your beans-tag:
<beans
<!-- SNIP, other namespaces -->
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
<!-- SNIP, other locations -->
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
Upvotes: 3
Reputation: 15229
I think that in your setup the Transactions are managed by JTA, so you can't explicitly start/stop them (i.e. em.getTransaction().begin() will not work). Try telling Spring that you want a certain method to be part of a (JTA managed) transaction via annotation , like:
@Transactional
public void populateWithTestData() {
//...
}
Upvotes: 2