I have code.
#Repository
public class ArticlesDao {
#Autowired
private SessionFactory sessionFactory;
/**
* #param count Specifited how many article get from DB
* #param start Start offset. Default 0
* #return all get article
*/
#Transactional
public List<Article> getLastArticles(Integer count, Integer start) {
if (start == null) {
start = 0;
}
final Session currentSession = sessionFactory.getCurrentSession();
final Criteria criteria = currentSession.createCriteria(Article.class);
criteria.addOrder(Order.desc("publishedDate"));
criteria.setFirstResult(count + start);
criteria.setMaxResults(count);
return criteria.list();
}
}
And Controler
#Autowired
ArticlesDao dao;
#RequestMapping(value = "/")
public ModelAndView getHome(#RequestParam("page") int page) {
dao.getLastArticles("STH args");
}
My question is whether Handler getHome() should be annotated #Transactional?
No, you should not use #Transactional over the controller/ controller method.
#Transactional is better/correct to use in service layer or DAO.
Normally I use #Transactional in the service layer and not in the DAO, since I want the transaction to hold an operation of business value and not an elementary operation.
In controllers, as in your example there is no real code, but just delegation to a service method, where the transaction is started, so you don't need to start another transaction in the Controller.
It is a good point to start your declarative transactions on your service/bussiness layer.
Anyway I think that #Transactional should be used in the service layer, and, at the same time, in the integration layer. You can use
#Transactional(propagation=Propagation.REQUIRES_NEW)
in your service layer, and, at the same time,
#Transactional(propagation=Propagation.REQUIRED)
in your DAO. Your integration layer will be more independent, and more easily testeable in a transactional enviroment.
Only the service layer and not the DAO should know the transaction behaviour because this is part of the business logic.
Further in your case you could make your transaction read only with #Transactional(readOnly = true).
Related
I am kinda confuse how to test my dao layer which contain same method as my service layer. I managed to test my service layer and stuck figuring out how to test my dao layer.
#Repository
public class DaoImpl implements Dao {
#Autowired
private NamedParameterJdbcTemplate template;
#Override
public Set<MyForm> findSomething(String title, String name, ZonedDateTime datetime) {
String sql= "SELECT * from myTable WHERE title= :d_title AND name = :d_name AND mydatetime= :d_datetime";
SqlParameterSource namedParameters = new MapSqlParameterSource()
.addValue("d_title", title)
.addValue("d_name",name)
.addValue("d_datetime",datetime.withZoneSameInstant(ZoneId.of("UTC")).toOffsetDateTime());
List<MyForm> myForm= template.query(sql, namedParameters, new MyRowMapper());
Set<MyForm> setMyForm = new HashSet<MyForm>(myForm);
return setMyForm ;
}
To test your datalayer you can make use of Spring Boots Testslice DataJdbcTest.
#DataJdbcTest
public class DaoImplIT {
#Autowired
private DaoImpl daoImpl;
#Test
void test() {
// prepare your database
// call the method to test
// make assertions about the result
}
}
In this testslice Spring Boot will configure all beans related to data access via JDBC. If you provide an in-memory DB (e.g. H2), it will be used automatically.
The main idea of this type of test is to prepare your database by inserting data and then use your repository beans to query them.
I wrote a blog post about this (okay, it's #DataJpaTest but the main idea is the same.). Maybe it will help you.
I am using spring data rest and Spring JPA. I am having one method which update one database table.
#Autowired InvoiceClient;
#Override
#Transactional
public String doBilling(String x){
//get date from TableOne
Bill bill = billsRepository.getBill(x);
if(bill.isPaid()){
generateInvoice();
}
bill.setPaymentDate(new Date());
return "SUCCESS";
}
generateInvoice is non Transactional method which calls #Transactional method from other service.
public void generateInvoice(){
invoiceClient.generateInvoice();//this is #Transactional, make changes in TableTwo
}
In case of any exception in generateInvoice method whole transaction is rolled back.
Now I want to add one more method which will have list of bill numbers. I call doBilling method in loop to do billing for all the bills.
#Override
#Transactional(readOnly = false, rollbackFor = {Throwable.class}, propagation = Propagation.REQUIRED)
public String doBillingForAll(List<String> tx){
for(String x: tx){
doBilling(x);
}
}
But now in case of any exceptions in doBilling method, all the setPayment methods are getting rolled back but generateInvoice is persisted.
I want to rollback generateInvoice also. How can I do it?
You don't need to define a rollbackFor = {Throwable.class}.
By default all RuntimeException do a rollback when using #Transactional.
It can be that because you are using and intermediate non #Transactional annotated method, the main Transaction is suspended and a nested one is created.
Try to put #Transactional in your public void generateInvoice() then Propagation.REQUIRED should be applied with rollback of your invoices
I'm developing a java web application that uses spring like application container. Now while I'm was using the transaction support to spring, I noticed that the time processing of the my annotated method is doubled. Try to descibe the method in a better way:
#Service
public class MyServiceImpl implements MyService{
#Autowired
UtilService utilService;
#Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void loadContracts(File fileToProcess,UtilDTO dto){
List<MyObject> objects = utilService.readSomethings("xxx","yyy")
//I modify Them
//I save or update them
}
}
#Service
public class UtilServiceImpl implements UtilService{
#PersistenceContext
EntityManager entityManager;
public List<MyObject> readSomethings(String p1,String p2){
String queryString = "from MyObject o where o.param1 = :param1 "
+ " and o.param2 = :param2 ";
Query q = entityManager.createQuery(queryString);
q.setParameter("param1", p1);
q.setParameter("param2", p2);
return q.getResultList();
}
}
For example:
The method readSomething is too late while If I remove the annotation its time processing improves.
Why is there this difference?
The performance is likely due to you creating a new transaction and suspending the existing transactions every time that method is invoked.
From the documentation on Propagation.REQUIRES_NEW, emphasis mine:
Create a new transaction, and suspend the current transaction if one exists. Analogous to the EJB transaction attribute of the same name.
Unless you have specific requirements about creating a new transaction for this method, I would recommend letting it fall through to default behavior - that is, Propagation.REQUIRED.
Below is a quick outline of what I'm trying to do. I want to push a record to two different tables in the database from one method call. If anything fails, I want everything to roll back. So if insertIntoB fails, I want anything that would be committed in insertIntoA to be rolled back.
public class Service {
MyDAO dao;
public void insertRecords(List<Record> records){
for (Record record : records){
insertIntoAAndB(record);
}
}
#Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void insertIntoAAndB(Record record){
insertIntoA(record);
insertIntoB(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoA(Record record){
dao.insertIntoA(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoB(Record record){
dao.insertIntoB(record);
}
public void setMyDAO(final MyDAO dao) {
this.dao = dao;
}
}
Where MyDAO dao is an interface that is mapped to the database using mybatis and is set using Spring injections.
Right now if insertIntoB fails, everything from insertIntoA still gets pushed to the database. How can I correct this behavior?
EDIT:
I modified the class to give a more accurate description of what I'm trying to achieve. If I run insertIntoAAndB directly, the roll back works if there are any issues, but if I call insertIntoAAndB from insertRecords, the roll back doesn't work if any issues arise.
I found the solution!
Apparently Spring can't intercept internal method calls to transactional methods. So I took out the method calling the transactional method, and put it into a separate class, and the rollback works just fine. Below is a rough example of the fix.
public class Foo {
public void insertRecords(List<Record> records){
Service myService = new Service();
for (Record record : records){
myService.insertIntoAAndB(record);
}
}
}
public class Service {
MyDAO dao;
#Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void insertIntoAAndB(Record record){
insertIntoA(record);
insertIntoB(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoA(Record record){
dao.insertIntoA(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoB(Record record){
dao.insertIntoB(record);
}
public void setMyDAO(final MyDAO dao) {
this.dao = dao;
}
}
I think the behavior you encounter is dependent on what ORM / persistence provider and database you're using. I tested your case using hibernate & mysql and all my transactions rolled back alright.
If you do use hibernate enable SQL and transaction logging to see what it's doing:
log4j.logger.org.hibernate.SQL=DEBUG
log4j.logger.org.hibernate.transaction=DEBUG
// for hibernate 4.2.2
// log4j.logger.org.hibernate.engine.transaction=DEBUG
If you're on plain jdbc (using spring JdbcTemplate), you can also debug SQL & transaction on Spring level
log4j.logger.org.springframework.jdbc.core=DEBUG
log4j.logger.org.springframework.transaction=DEBUG
Double check your autocommit settings and database specific peciular (eg: most DDL will be comitted right away, you won't be able to roll it back although spring/hibernate did so)
Just because jdk parses aop annotation not only with the method, also parse annotation with the target class.
For example, you have method A with #transactional, and method B which calls method A but without #transactional, When you invoke the method B with reflection, Spring AOP will check the B method with the target class has any annotations.
So if your calling method in this class is not with the #transactional, it will not parse any other method in this method.
At last, show you the source code:
org.springframework.aop.framework.jdkDynamicAopProxy.class
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
......
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping orfancy proxying.
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
}
I'm using spring 3 with hibernate 3.5.4
1- I want to create an object in transaction and save it to DB ( which passes successfully ).
2- I want to update some fields in that object (same object) and updates in in DB in another transaction (and here is the problem).
The problem is, is saves the object successfully in the first transaction but it doesn't update it in DB in the second one.
here is code example:
public String entry(String str){
Bill b = create(str);
b = update(b);
b = updateAgain(b);
return "DONE";
}
#Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false)
public Bill create(String num){
Bill bill = new Bill();
bill.setBillNumber(num);
baseDao.saveObject(bill);
return bill;
}
#Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false)
public Bill update(Bill bill){
bill.setRetailAmount(152.0);
baseDao.saveObject(bill);
return bill;
}
NOTE: I don't want to put the #transactional annotation on method "entry".
Thanks,
The annotation will not take affect if called on a method within the same class. AOP cannot intercept that through proxy. Move your entry method outside the class.
EDIT: Spring enables the Transactional annotation via annotation-driven AOP with proxies or sub-classing. When enabled with proxies, your proxy is out of the picture in a local method call. This blog post has a good explanation with pictures.