My application is currently using Spring's JdbcTemplate and #Transaction annotation for transaction handling.
I have a method that calls a web service on a single transaction, and I designed it so that exceptions from the web service will rollback all database changes within the transaction.
my question: how can i FLUSH the database changes made before calling my web service?
many thanks
#Autowired
private MyDao dao;
#Transactional
#Override
public void myMethod() {
.....
dao.saveThis(myObjectToSaveIsNotAnIssue);
// I need to FLUSH here in order for my web service to "see" the saved object
callWebservice();
}
My spring config:
<context:component-scan base-package="com.xxx.xxx" />
<context:annotation-config />
<!-- proxy-target-class is set to true to use transactional scope -->
<tx:annotation-driven proxy-target-class="true" transaction-manager="tomcatTransactionManager" />
<bean id="sybaseDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/xxx"/>
<property name="lookupOnStartup" value="true"/>
<property name="proxyInterface" value="javax.sql.DataSource"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="sybaseDataSource"/>
</bean>
<!-- Transaction Manager -->
<bean id="tomcatTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="sybaseDataSource" />
</bean>
JDBC doesn't have any "flushing" concept. The SQL queries are executed when you execute them. Nothing to be flushed is kept in memory.
Your web service won't see anything in database that you hasn't been committed, due to transaction isolation (which is READ_COMMITTED by default). You'll have to set the isolation level to READ_UNCOMMITTED if that's really what you want (in the web service):
#Transactional(isolation = Isolation.READ_UNCOMMITTED)
We can take out callWebservice() method out of transaction in an assembler and then myMethod can called first from assembler, followed by callWebService() method.
And withinCall Webservice method, we can handle the success and failure scenarios. (Eg: update the status)
Related
I'm trying to open multiple transactions​ on a method using #Transactional by Spring framework
And for some reason it's throws java.lang.IllegalStateException : Transaction already active
My method
#Transactional
Public void foo(Entity e){
entityManager.merge(e);
entityManager.getTransaction().begin();
entityManager.getTransaction().commit();
}
Any idea how to open multiple transaction without getting that error?
I think you may be confusing what the Spring #Transactional annotation means. When you use that annotation you're asking Spring to enrich your method invocation with transaction semantics.
You may want to take a look at the TransactionAspectSupport class, more specifically the method invokeWithinTransaction which is the place where all the magic happens for your transactional methods.
You will see there that before your code is invoked, a new transaction is created, then your code is executed, and after that, either a commit or rollback happens. In other words, Spring's aspect controls the transaction for you.
All these magic needs some configuration for it to happen: you need to make sure that Spring finds your #Transactional methods and enrich them. To do so you need to add a piece of configuration:
<tx:annotation-driven/>
And of course you need to define a transaction manager that can be used by your transactional methods. It seems you're using JPA so a JpaTransactionManager makes sense in this case:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="POSTGRESQL"/>
<property name="showSql" value="true"/>
</bean>
</property>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect">
<property name="prepareConnection" value="true"/>
</bean>
</property>
<property name="persistenceUnitName" value="test-api"/>
<property name="packagesToScan">
<list>
<value>com.company.humanresources</value>
</list>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<constructor-arg name="emf" ref="entityManagerFactory"/>
</bean>
Finally, if you want to gain access to the entity manager being used by your current transaction, you can do something like:
#PersistenceUnit(unitName = "test-api")
EntityManagerFactory emf;
#Transactional
public Department addDepartment(String name) {
EntityManager em = EntityManagerFactoryUtils.getTransactionalEntityManager(emf);
Department department = new Department();
department.setName(name);
em.persist(department);
return department;
});
Once more, Spring will control the transaction semantics around your transactional method. You must also take into account that the default Spring behavior only works with public methods.
When using spring and spring data jpa in a normal java application how will jpa know when to commit the data to the database?
I ask this because repository.save() doesn't commit.
Should i manually commit or can spring handle this for me?
My application context xml:
<context:component-scan base-package="...">
</context:component-scan>
<jpa:repositories base-package="..." entity-manager-factory-ref="emf" />
<bean id="emf" class="...spring.MyEMF"></bean>
<!-- Add Transaction support -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf" />
</bean>
<!-- Use #Transaction annotations for managing transactions -->
<tx:annotation-driven transaction-manager="transactionManager" />
I manually init my context with:
new ClassPathXmlApplicationContext("classpath:/data.xml")
No spring can handle transaction for you just before your method repository.save() add #Transactional
#Transactional
public void save(){
//do something
}
and in your configuration class make sure that spring transaction handling is enabled
#EnableTransactionManagement
class myConfig{
}
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:
Begin and Commit transactions are explicit.
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.
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">
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() {
//...
}
Begin and Commit transactions are explicit.
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 :)
Repository Factory here, which I'm not sure if I need...
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.
EDIT to answer comments
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"/> ).
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.
I understand that this is a very long question, but i wanted to ask everything because i'm
stuck with these things for more than 2 weeks and i'm in a situation to solve this within
this week. Please guide me in this matter.
I'm Using EclipseLink jpa version 2, Spring 3, jdk6, MySQL5 and tomcat7.
I have configured the following in each of my DAO classes.
#PersistenceContext
private EntityManager em;
I have the following in my Spring xml:
<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
<property name="url" value="jdbc:mysql://localhost:3306/xxxxx"/>
<property name="username" value="xxxx"/>
<property name="password" value="xxxx"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<property name="jpaDialect" ref="jpaDialect"/>
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="jpaDialect" ref="jpaDialect"/>
</bean>
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter" >
<property name="showSql" value="true"/>
<property name="generateDdl" value="true" />
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect"/>
From Persistence.xml:
<persistence-unit name="xxxxx" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<-- class mappings -->
</persistence-unit>
I've got few confusion about what i have done:
Is the EntityManager injected by Spring? (I understand that #PersistenceContext is a
J2EE annotation, so wondering whether it is injected without Spring's contribution).
As i have already mentioned, i have injected EntityManager in all the DAO classes. Is
this a good practice? or should i make it Singleton by having a separate class like
PersistenceManager, which has EntityManager attribute wired, and have
getEntityManager() method?
As you can see above, i have configured Spring transactions. But when i do CRUD
operations continuously for 2-3 times, application gets stuck and fails with EclipseLink
exception saying unable to get lock, timeout etc. Am i doing anything wrong here or
missing any transaction configurations??
With the above configurations, i can only use #Transactional annotation with default
values which are PROPAGATION_REQUIRED,ISOLATION_DEFAULT. If i change these for any other
values, such as #Transactional(PROPAGATION_REQUIRED,ISOLATION_SERIALIZABLE) etc,
application throws exception as Custom isolation levels are not supported. Again, am
i missing any configurations?
Thanks.
Yes, spring recognizes the #PersistenceContext annotation and injects the entity manager
Spring takes care of that - it injects the same EntityManager instance in all DAOs. In fact, it injects a proxy so that each request uses a different entity manager.
Normally everything should run fine. You need <tx:annotation-driven /> in order to use #Transactional
JPA only supports the default isolation level. You can work this around by customizing the spring jpa dialect, but there's nothing built-in. The way to go is extend XJpaDialect (in your case X=EclipseLink), override the beingTransaction, obtain the Connection (in an eclipse-link specific way), set the desired isolation level (accessible through the transaction definition), and configure this as a property of your LocalContainerEntityManagerFactoryBean:
<property name="jpaDialect">
<bean class="com.foo.util.persistence.EclipseLinkExtendedJpaDialect" />
</property>
I'm developing a web application using Struts2 + Spring, and now I'm trying to add a scheduled task. I'm using Spring's task scheduling to do so. In my applicationContext I have:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
...
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="MYSQL" />
</bean>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
And then I have my DAO that uses this entityManagerFactory:
<bean id="dao" class="data.GenericDAO" />
So this works flawlessly within the web application. But now I have a problem when creating the scheduled task:
<task:scheduled-tasks scheduler="notifier">
<task:scheduled ref="emailService" method="sendMail" fixed-rate="30000" />
</task:scheduled-tasks>
<task:scheduler id="notifier" pool-size="10" />
<bean id="emailService" class="services.emailService" >
<property name="dao" ref="dao" />
</bean>
This executes the method sendMail on my emailService class every 30 seconds. And my emailService has the DAO injected correctly. The thing is that I can fetch objects with my DAO using the findById named queries, but when I try to access any property mapped by Hibernate, such as related collections or entities, I get an "LazyInitializationException: could not initialize proxy - no Session ". I don't know what's wrong, since I believe the scheduled task is being managed by Spring, so it should have no problem using a Spring managed DAO. I must say that I'm using the openSessionInView filter on my struts actions, so maybe I need something similar for this scheduled task.
Any help or suggestion will be appreciated, thanks!
Edit: Finally I found a way to fix this. I changed my regular Dao with one where I can decide when to start and commit the transaction. So before doing anything I start a transaction and then everything works OK. So I still don't know exactly what causes the problem and if someday I'll be able to use my regular DAO, for the moment I'm staying with this solution.
OpenSessionInView won't help you, because you don't have a web context. You need Spring's Declarative Transaction Management.
In most cases, what you need to do is just this XML:
<!-- JPA, not hibernate -->
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="myTxManager" />
<!-- without backing interfaces you probably also need this: -->
<aop:config proxy-target-class="true">
(Annotate your EmailService class as #Transactional to enable this)