What do you think abount @Transactional annotation? I used it with spring framework on service class to manage transaction into and intra service call.
It's a very elegant and easy way to manage transactions.
I've started working on a new project and I decided to use Guice as Dependency Injection framework, IMHO more lite and easy than Spring, and Hibernate
but I would like to continue to use the @Transactional annotation on my service class.
I didn't found anything good to use and I decided to use a little of AOP to recreate this functionality.
Let's started with order, I would like that:
1) When I call a method on a service class, the operations in this method belong to a single transaction.
2) If the caller of the method 1) has a transaction, the thansanction must is the parent (caller) transaction.
In practice the "Required" attribute of EBJ's Transactional parameter, see
Required Attribute
This is the first case:
And this the second one:
I'm going to use AOP and Guice to set a Method Interceptor that check is the called method has a transaction, and in case of not, create a new one and link it to thread with ThreadLocal class. I create also an annotation that identify che class and method subject and not subject to this check:
A) The Transactional Annotation:
01 package net.zonablog.persistence.aop;
02
03 import java.lang.annotation.ElementType;
04 import java.lang.annotation.Retention;
05 import java.lang.annotation.RetentionPolicy;
06 import java.lang.annotation.Target;
07
08 @Retention(RetentionPolicy.RUNTIME)
09 @Target({ ElementType.TYPE, ElementType.METHOD })
10 public @interface Transactional {
11
12 }
|
The NoTransactional annotation, that mark the non-transactional method on a class marked as transactional:
01 package net.zonablog.persistence.aop;
02
03 import java.lang.annotation.ElementType;
04 import java.lang.annotation.Retention;
05 import java.lang.annotation.RetentionPolicy;
06 import java.lang.annotation.Target;
07
08 @Retention(RetentionPolicy.RUNTIME)
09 @Target({ ElementType.METHOD })
10 public @interface NoTransactional {
11
12 }
|
The implementation of this interceptor:
01 package net.zonablog.persistence.aop;
02
03 import net.zonablog.service.persistence.BasePersistentService;
04 import net.zonablog.service.persistence.HibernateSessionService;
05
06 import org.aopalliance.intercept.MethodInterceptor;
07 import org.aopalliance.intercept.MethodInvocation;
08 import org.hibernate.Session;
09 import org.hibernate.Transaction;
10 import org.slf4j.Logger;
11 import org.slf4j.LoggerFactory;
12
13 import com.google.inject.Inject;
14
15 public class TransactionInterceptor implements MethodInterceptor {
16
17 private Logger logger = LoggerFactory.getLogger(this.getClass());
18 private ThreadLocal<Session> threadLocal;
19
20 @Inject
21 private HibernateSessionService hibernateSessionService;
22
23 public TransactionInterceptor() {
24 logger.info("Inizializzation of " + this.getClass());
25 threadLocal = new ThreadLocal<Session>();
26 }
27
28 @SuppressWarnings("rawtypes")
29 public Object invoke(MethodInvocation invocation) throws Throwable {
30
31 logger.trace("Start of transactional method");
32
33 // Verifico se vengo chiamato da un'altra applicazione che ha già la
34 // transazione attiva
35 Transaction transaction = null;
36 Session session = threadLocal.get();
37 boolean startNewTransaction = true;
38
39 if (session == null) {
40 logger.trace("Start new transaction");
41 session = hibernateSessionService.openSession();
42 threadLocal.set(session);
43 transaction = session.getTransaction();
44 transaction.begin();
45 } else {
46 logger.trace("Already in transaction");
47 startNewTransaction = false;
48 }
49
50 if (invocation.getThis() instanceof BasePersistentService)
51 ((BasePersistentService) invocation.getThis()).setSession(session);
52
53 try {
54
55 Object result = invocation.proceed();
56
57 if (transaction != null) {
58 logger.trace("Commit transaction");
59 transaction.commit();
60 threadLocal.remove();
61 }
62
63 return result;
64
65 } catch (Exception e) {
66
67 if (transaction != null) {
68 logger.debug("Rollback transaction due [" + e.getMessage() + "]");
69 transaction.rollback();
70 threadLocal.remove();
71 }
72
73 throw e;
74 } finally {
75 logger.trace("Exit transactional method");
76
77 if (startNewTransaction && session != null)
78 session.close();
79 }
80
81 }
82
83 @Override
84 protected void finalize() throws Throwable {
85 threadLocal.remove();
86 super.finalize();
87 }
88
89 }
|
I'm using Jersey with Guice, and I use the @Transactional annotation also on webresources. Es:
01 package net.zonablog.rest.controller;
02
03 import java.util.UUID;
04
05 import javax.mail.internet.AddressException;
06 import javax.mail.internet.InternetAddress;
07 import javax.ws.rs.FormParam;
08 import javax.ws.rs.GET;
09 import javax.ws.rs.POST;
10 import javax.ws.rs.Path;
11 import javax.ws.rs.PathParam;
12 import javax.ws.rs.Produces;
13 import javax.ws.rs.core.MediaType;
14 import javax.ws.rs.core.Response;
15 import javax.ws.rs.core.Response.ResponseBuilder;
16 import javax.ws.rs.core.Response.Status;
17
18 import com.google.inject.Inject;
19 import com.sun.jersey.spi.resource.PerRequest;
20
21 @Transactional
22 @Path("services/register")
23 @PerRequest
24 public class RegisterController extends BaseController {
25
26 @Inject
27 private MailService mailService;
28
29 @Inject
30 private UserService userService;
31
32 @Inject
33 public RegisterController() {
34 }
35
36 @POST
37 @Produces(MediaType.APPLICATION_XML)
38 public Object register(@FormParam("registerDialogFormEmail") String email,
39 @FormParam("registerDialogFormPass") String password, @FormParam("registerDialogFormRPass") String rpassword) {
40
41 ...
42 }
43 }
|
Note that to let Guice istantiate the webresource, in addition to configuration needed by jersey-guice module, the @Inject must present on constructor's class.
Nessun commento:
Posta un commento