-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 14 posts ] 
Author Message
 Post subject: double local Transaction ( audit trial)
PostPosted: Sat Nov 28, 2015 3:39 am 
Newbie

Joined: Fri Feb 25, 2011 1:37 am
Posts: 11
My Requirement is that I have transaction which inserts some data in table then I need to audit this transaction .I assume that inserting the details of transaction like records inserted in DB is right way to do audit. Now suppose while doing my first transaction due to network outage my transaction could not complete now how will I audit this.

How can I make sure that if my transaction one fails the other does not ? is it good to combine these two transactions.


Top
 Profile  
 
 Post subject: Re: double local Transaction ( audit trial)
PostPosted: Sat Nov 28, 2015 1:32 pm 
Regular
Regular

Joined: Mon Oct 19, 2015 7:49 am
Posts: 61
Location: ChengDu China
Hi,

How to resolve this problem is not the goal of Hibernate, but the goal of EJB or Spring(or COM+ of Microsoft programming on Visual C++ or .NET)

In order to make the code to be simple, I choose to use spring to tell you how to do it perfectly(you can also use EJB3).
[In this example, I use JPA interface of Hibernate, not the raw interface of Hibernate. so that the code can be changed to support EJB3 very easily]

(1) Declare DAO for business data[Data Accessing Layer]
Code:
@org.springframework.streotype.Repository
public class BusinessDataRepositoryImpl implements BusinessDataRepository {

     @javax.persistence.PersistenceContext
     private EntityManager em;

     @Override
     public BusinessData getBusinessDataBy(long id) {
         return this.em.find(BusinessData.class, id);
     }

     @Override
     public void mergeBusinessData(BusinessData businessData) {
         this.em.merge(buisnessData);
     }
}


(2) Declare the DAO for audit data[Data Accessing Layer]
Code:
@org.springframework.streotype.Repository
public class AuditDataRepositoryImpl implements AuditDataRepository {

     @javax.persistence.PersistenceContext
     private EntityManager em;

     @Override
     public void mergeAuditData(AuditData auditData) {
         this.em.merge(auditData);
     }
}


(3) Declare a component of business logic layer[Business Logic Layer]
Code:
@org.springframework.streotype.Service
public class BusinessDataServiceImpl implements BusinessDataService {

    @javax.ejb.Resource //DAO dependency
    private BusinessDataRepository businessDataRepository;

    @javax.ejb.Resource //DAO dependency
    private AuditDataRepository auditDataRepository;

    @javax.ejb.Resource //Another business logic service depenency
    private AuthroizationService authorizationService;

    @org.springfreamwork.transaction.Transactional //Share one transaction.
    @Override
    public void changeBusinessData(BusinessData businessData) {
        BuisnessData oldBusinessData = this.businessDataRepository.getBusinesssDataById(businessData.getId());
        if (oldBusinessData == null) {
             throw new IllegalArgumentException(
                 "The business data whose data is \"" +
                 businessData.getId() +
                 " is not existing");
        }

        if (oldBusinessData.getSatate() != businessData.getState()) {
             User currentUser = this.authorizationService.getCurrentUser();
             AuditData auditData = new AuditData();
             auditData.setBusinessData(businessData);
             auditData.setOperator(currentUser);
             auditData.setTime(new Date());
             if (businessData.getState() == BusinessDataState.APPROVED) {
                 aduitData.setAction("approve");
             } else if (businessData.getState() == BusinessDataState.REJECTED) {
                 aduitData.setAction("reject");
             }
             this.auditDataRepository.merge(auditData); //Add audit data
        }
        this.businessDataRepository.merge(businessData); //Change business data
    }
}


(1) For code aspect, BusinessData and AuditData have different DAOs, the code is seperated.
(2) For runtime aspect, the operation for BusinessData and AuditData share one connection and one transaction.
This solution is very perfect, isn't it?

[
Spring: Both Local transaction and distributed transaction
EJB: Distributed transaction only
]


Top
 Profile  
 
 Post subject: Re: double local Transaction ( audit trial)
PostPosted: Sun Nov 29, 2015 10:18 am 
Newbie

Joined: Fri Feb 25, 2011 1:37 am
Posts: 11
Thanks a lot for the detailed Reply , I highly appreciate it.

In this code there are 2 db calls , if suppose one db call fails and other success ed what will will happen to transaction


Top
 Profile  
 
 Post subject: Re: double local Transaction ( audit trial)
PostPosted: Mon Nov 30, 2015 2:04 am 
Regular
Regular

Joined: Mon Oct 19, 2015 7:49 am
Posts: 61
Location: ChengDu China
Don't worry, no problem.

The annotation @Transactional in on the method of business logic class means the shared transaction should rollback if any one them is failed and it can only be committed when both of them are success.


Top
 Profile  
 
 Post subject: Re: double local Transaction ( audit trial)
PostPosted: Mon Nov 30, 2015 10:58 am 
Newbie

Joined: Fri Feb 25, 2011 1:37 am
Posts: 11
Thanks for update , the solution i am looking for is if suppose any one DB call fails how will i still persisit the other DB call. I know i can do two transaction one for first DB call and other for another DB call is there any other way.


Top
 Profile  
 
 Post subject: Re: double local Transaction ( audit trial)
PostPosted: Mon Nov 30, 2015 11:36 am 
Regular
Regular

Joined: Mon Oct 19, 2015 7:49 am
Posts: 61
Location: ChengDu China
OK, got your point and I know what you are thinking, now let me show how powerful it is,
Please see the Fake code

[Module A]
Code:
@Transactional
public void complexBusinessLogic()
{
    moudleB.step1();
    moudleB.step2();
    moudleB.step3();
    moudleB.step4();
    ... other code ...
}


[Module B]
Code:
@Transaction(propagation = Propagation.REQUIRED) // or nothing
public void step1() { ... }

@Transaction(propagation = Propagation.REQUIRES_NEW)
public void step2() { ... }

@Transaction(propagation = Propagation.REQUIRED) // or nothing
public void step3() { ... }

@Transaction(propagation = Propagation.REQUIRES_NEW)
public void step4() { ... }


As we see, one complex business has 4 sub steps.
(1) ModuleA.complexBusinessLogic: uses @Transactional, it's necessary, can't be omitted, create an transaction
(2) MoudleB.step1 and ModuleB.step3: uses @Transactional with Propagation.REQUIRED, that means they will share the transaction created by the business logic module
(3) MoudleB.step2 and ModuleB.step4: uses @Transactional with Propagation.REQUIRES_NEW, that means they will ignore the transaction created by the business logic module and create their own transactions.

Finally.
3 Db connections and transactions are created.
One of them is created by business logic module and it's shared by step1 and step3, so db operation of step1 and step3 will be rollback if the complex business throw exception or error. they can only be committed when the business logic is done successfully; the other two of them are created by step2 and step4, so step2 and step4 are out of control, they can still be committed even if the business logic meet some exception or error.

Transaction Propagation Table: (Order by time desc, youngest first, oldest last. Young technology learns from old technology)

(1) Spring: REQUIRED, REQUIRES_NEW, SUPPORTS, NOT_SUPPORTED, MANDATORY, NEVER, NESTED(Requires special database)
link: http://www.byteslounge.com/tutorials/spring-transaction-propagation-tutorial

(2) EJB: REQUIRED, REQUIRES_NEW, SUPPORTS, NOT_SUPPORTED, MANDATORY, NEVER
link: http://www.tutorialspoint.com/ejb/ejb_transactions.htm

(3) COM+(Microsoft Visual C++ or .NET): Required, RequiresNew, Supported, NotSupported, Disabled
link: https://msdn.microsoft.com/en-us/library/windows/desktop/ms687120(v=vs.85).aspx


Top
 Profile  
 
 Post subject: Re: double local Transaction ( audit trial)
PostPosted: Tue Dec 01, 2015 2:37 am 
Newbie

Joined: Fri Feb 25, 2011 1:37 am
Posts: 11
Perfect, Thanks a lot.

I was trying to realize this through code which i have written . When moudleB.step1() throws exception then other methods are not executed . Can you please let me know if doing something wrong.


Top
 Profile  
 
 Post subject: Re: double local Transaction ( audit trial)
PostPosted: Tue Dec 01, 2015 9:27 am 
Regular
Regular

Joined: Mon Oct 19, 2015 7:49 am
Posts: 61
Location: ChengDu China
If step1 throws an exception or error, the transaction shared by both moduleA.complexBusinessLogic and moduleB.step1 will rollback; and the next three steps are not executed so it's unnecessary to discuss them.

Commit when no exceptions and errors; otherwise, rollback. This is the default behavior, but you can change it.

(1) Example1
Code:
@Transactional(noRollbackFor = { AException.class, BException.class })
     No exception(Success) -> commit
     AException -> Commit
     BException -> Commit
     Other Exceptions or Errors -> Rollback


(2) Example2
Code:
@Transactional(noRollbackFor = Throwable.class, rollbackFor = { AException.class, BException.class})
    No Exception(Success) -> Commit
    AException -> Rollback
    BException -> Rollback
    Other Exceptions or Errors -> Commit


Top
 Profile  
 
 Post subject: Re: double local Transaction ( audit trial)
PostPosted: Tue Dec 01, 2015 9:35 am 
Newbie

Joined: Fri Feb 25, 2011 1:37 am
Posts: 11
Thanks for the reply , i am unable to grasp it.

What you are saying is

Quote:
If step1 throws an exception or error, the transaction shared by both moduleA.complexBusinessLogic and moduleB.step1 will rollback; and the next three steps are not executed so it's unnecessary to discuss them.


and this was your earlier quote

Quote:
As we see, one complex business has 4 sub steps.
(1) ModuleA.complexBusinessLogic: uses @Transactional, it's necessary, can't be omitted, create an transaction
(2) MoudleB.step1 and ModuleB.step3: uses @Transactional with Propagation.REQUIRED, that means they will share the transaction created by the business logic module
(3) MoudleB.step2 and ModuleB.step4: uses @Transactional with Propagation.REQUIRES_NEW, that means they will ignore the transaction created by the business logic module and create their own transactions.

Finally.
3 Db connections and transactions are created.
One of them is created by business logic module and it's shared by step1 and step3, so db operation of step1 and step3 will be rollback if the complex business throw exception or error. they can only be committed when the business logic is done successfully; the other two of them are created by step2 and step4, so step2 and step4 are out of control, they can still be committed even if the business logic meet some exception or error.


I am still confused can you please explain in more detail if possible


Top
 Profile  
 
 Post subject: Re: double local Transaction ( audit trial)
PostPosted: Tue Dec 01, 2015 10:21 am 
Regular
Regular

Joined: Mon Oct 19, 2015 7:49 am
Posts: 61
Location: ChengDu China
OK, I know, maybe some 3rd party framework is hard to be understand, now forget them all, I will show you how to develop a tiny framework about this and let you know every thing.

Java language version: 7
Maybe this code has a little errors because it typed them in this forum web page directly, not by IDE

###########################################################
Part I: Tiny framework code
###########################################################
Code:
public interface DbAction { //Database operation without return value
    void call(Session session);
}

public interface DbFunction<T> { //Database operation with return value
    T call(Session session);
}

public enum Propagation {
   REQUIRED,
   REQUIRES_NEW
}

public abstract class DbContext {

   private ThreadLocal<Stack<Session>> sessionStackLocal;
   
   protected DbContext() {
      this.sessionStackLocal = new ThreadLocal<>();
      this.sessionStackLocal.set(new Stack<Session>());
   }
   
   protected abstract Session createSession();
   
   public <T> T execute(Propagation propagation, final DbFunction<T> handler) {
      final T[] retvalWrapper = (T[])new Object[1];
      this.execute(propagation, new DbAction() {
         @Override
         public void call() {
            retvalWrapper[0] = handler.call();
         }
      });
      return retvalWrapper[0];
   }
      
   public void execute(Propagation propagation, DbAction handler) {
      if (propagation == Propagation.REQUIRES_NEW || this.sessionStackLocal.get().isEmpty()) {
         this.executeByNewSession(handler);
      } else {
         this.executeByExistingSession(this.sessionStackLocal.get().peek(), handler);
      }
   }
   
   private void executeByNewSession(DbAction handler) {
      Session session = this.createSession();
      try {
         this.sessionStackLocal.get().push(session);
         try {
            Transaction tx = session.beginTransaction();
            try {
               handler.call(session);
            } catch (RuntimeException | Error ex) {
               tx.rollback();
               throw ex;
            }
            tx.commit();
         } finally {
            this.sessionStackLocal.get().pop();
         }
      } finally {
         session.close();
      }
   }
   
   private void executeByExistingSession(Session session, DbAction handler) {
      handler.call(session);
   }
}


###########################################################
Part II: Your acutal project code
###########################################################
Code:
public class MyDbContext extends DbContext {

   private static final MyDbContext INSTANCE = new MyDbContext();
   
   private SessionFactory sessionFactory;
   
   private MyDbContext() { //Singleton, private constructor
      this.sessionFactory = ...Use your hibernate.cfg.xml to create SessionFactory...
   }
   
   @Override
   protected Session createSession() {
      return this.sessionFactory.openSession();
   }
   
   public static MyDbContext getInstance() {
      return INSTANCE;
   }
}

public class ModuleA {

   private ModuleB moudleB = new ModuleB();
   
   public void doAll() {
      
      MyDbContext.getInstance().execute(Propagation.REQUIRED, new DbAction() {
         @Override
         public void run(Session session) {
            ModuleA.this.moudleB.subStep1();
            ModuleA.this.moudleB.subStep2();
            ModuleA.this.moudleB.subStep3();
            ModuleA.this.moudleB.subStep4();
         }
      });
   }
}

public class MoudleB {

    public void subStep1() {
      MyDbContext.getInstance().execute(Propagation.REQUIRED, new DbAction() {
         @Override
         public void run(Session session) {
            // Use session to do db operation,
            // don't use it it beginTransaction, commit, rollback
            // don't close it too
            // because everything is manged by MyDbContext
         }
      });
   }
   
   public void subStep2() {
      MyDbContext.getInstance().execute(Propagation.REQUIRES_NEW, new DbAction() {
         @Override
         public void run(Session session) {
            // Use session to do db operation,
            // don't use it it beginTransaction, commit, rollback
            // don't close it too
            // because everything is manged by MyDbContext
         }
      });
   }
   
   public void subStep3() {
      MyDbContext.getInstance().execute(Propagation.REQUIRED, new DbAction() {
         @Override
         public void run(Session session) {
            // Use session to do db operation,
            // don't use it it beginTransaction, commit, rollback
            // don't close it too
            // because everything is manged by MyDbContext
         }
      });
   }
   
   public void subStep4() {
      MyDbContext.getInstance().execute(Propagation.REQUIRES_NEW, new DbAction() {
         @Override
         public void run(Session session) {
            // Use session to do db operation,
            // don't use it it beginTransaction, commit, rollback
            // don't close it too
            // because everything is manged by MyDbContext
         }
      });
   }
}


Last edited by babyfish on Tue Dec 01, 2015 10:37 am, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: double local Transaction ( audit trial)
PostPosted: Tue Dec 01, 2015 10:33 am 
Regular
Regular

Joined: Mon Oct 19, 2015 7:49 am
Posts: 61
Location: ChengDu China
First, DbContext has only one instance so DbContext.sessionStackLocal retains a thread-level global stack about hibernate session. This is very important because the our application is running is multiple threads environment.

Code:
(1) Enter MoudleA.doAll, because stack is empty, so create a session(call it session1) and push it into stack
(2)     Enter MoudleB.subStep1, because propagation is REQUIRED, so execute it on session1 that is peeked from stack
(3)     Leave ModuleB.subStep1, nothing happend
(4)     Enter MoudleB.subStep2, because propagation is REQUIRES_NEW, so create a session(call it session2) and push it into stack
(5)     Leave ModuleB.subStep2, pop session2 from stack and close it
(6)     Enter MoudleB.subStep3, because propagation is REQUIRED, so execute it on session1 that is peeked from stack
(7)     Leave ModuleB.subStep3, nothing happend
(8)     Enter MoudleB.subStep4, because propagation is REQUIRES_NEW, so create a session(call it session3) and push it into stack
(9)     Leave ModuleB.subStep4, pop session3 from stack and close it
(10) Leave ModuleA.doAll, pop session1 from stack and close it


Commit and rollback can only affect one session, one session can be committed when another one is rollback, they don't affect each other

I think you can understand everything by this tiny framework until now.


Top
 Profile  
 
 Post subject: Re: double local Transaction ( audit trial)
PostPosted: Wed Dec 02, 2015 10:02 am 
Newbie

Joined: Fri Feb 25, 2011 1:37 am
Posts: 11
I have understood the concept perfectly . Now thanks a lot.

But my question for rollback to happen in any session of transaction then exception should be thrown .

Code:
@Transactional
public void complexBusinessLogic()
{
  try{
    moudleB.step1();
    moudleB.step2();
    moudleB.step3();
    moudleB.step4();
}catch(Exception e){
}
    ... other code ...
}


Code:
@Transaction(propagation = Propagation.REQUIRED) // or nothing
public void step1() { ... }

@Transaction(propagation = Propagation.REQUIRES_NEW)
public void step2() { ... }

@Transaction(propagation = Propagation.REQUIRED) // or nothing
public void step3() { ... }

@Transaction(propagation = Propagation.REQUIRES_NEW)
public void step4() { ... }


If during the execution of step1 an exception is thrown then my rest of methods in complexBusinessLogic dont execute so i have will have to catch exception in Step1 but i cannot communicate this exception to my caller and he may not come to know about issue occured.

SO how can i make sure that rollback happens and i also let another method continue.


Top
 Profile  
 
 Post subject: Re: double local Transaction ( audit trial)
PostPosted: Wed Dec 02, 2015 11:38 am 
Regular
Regular

Joined: Mon Oct 19, 2015 7:49 am
Posts: 61
Location: ChengDu China
A simple solution:

First, declare a small tool class
Code:
public class ThrowableHolder {

    private Throwable throwable;

    public void set(RuntimeException ex) {
        this.setImpl(ex);
    }

    public void set(Error err) {
        this.setImpl(err);
    }

    public void rethrow() {
        if (this.throwable instanceof RuntimeException) {
            throw (RuntimeException)this.throwable;
        }
        if (this.throwable instanceof Error) {
            throw (Error)this.throwable;
        }
    }

    private void setImpl(Throwable throwable) {
        if (this.throwable == null) { // Only remember the first value
            this.throwable = throwable;
        }   
    }
}

Second, write your complex business
Code:
@Transactional
public void complexBusinessLogic() {

    ExceptionHolder holder = new ExceptionHolder();

    try {
        this.moduleB.step1();
    } catch (AException ex) {
        holder.set(ex);
    }
    try {
        this.moduleB.step2();
    } catch (BException ex) {
        holder.set(ex);
    }
    try {
        this.moduleB.step3();
    } catch (CException ex) {
        holder.set(ex);
    }
    try {
        this.moduleB.step4();
    } catch (DException ex) {
        holder.set(ex);
    }

    holder.rethrow();
}


Top
 Profile  
 
 Post subject: Re: double local Transaction ( audit trial)
PostPosted: Thu Dec 03, 2015 10:12 am 
Newbie

Joined: Fri Feb 25, 2011 1:37 am
Posts: 11
Thanks a lot.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 14 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.