AspectJ Pointcut Expression on domain objects not managed by Spring - java

Question: Can Spring point-cut expressions run on non-managed spring components such as domain object? From my experiments looks like it doesnt, so what is the best way to run pointcut expressions on a regular object?
I created custom annotation name #Encrypt, so that when it is used on top of a field in a domain object, the field is sent to a web service and is automatically encrypted.
I first started with method level annotation, and found that point cut expression doesn't work on Objects not managed by Spring, it has to be a spring bean.
1. Spring Aspect: Checks for custom annotation #Encrypt and prints out.
#Aspect
public class EncryptAspect {
#Around("#annotation(encrypt)")
public Object logAction(ProceedingJoinPoint pjp, Encrypt encrypt)
throws Throwable {
System.out.println("Only encrypt annotation is running!");
return pjp.proceed();
}
}
2. Custom Annotation:
#Documented
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Encrypt
{
// Handled by EncryptFieldAspect
}
3. Domain object using Annotation
public interface CustomerBo {
void addCustomerAround(String name);
}
public class CustomerBoImpl implements CustomerBo {
#Encrypt
public void addCustomerAround(String name){
System.out.println("addCustomerAround() is running, args : " + name);
}
}
4. Invocation
ApplicationContext appContext = new ClassPathXmlApplicationContext("http-outbound-config.xml");
// CustomerBoImpl customer = new CustomerBoImpl(); --> Aspect is not fired if object is created like this.
CustomerBo customer = (CustomerBo) appContext.getBean("customerBo"); // Aspect Works
customer.addCustomerAround("test");

To your first question ("Can Spring point-cut expressions run on non-managed spring components such as domain object?") the answer is no. Spring reference manual has a chapter that exactly explains how Spring AOP works and why it won't work in that case.
The options I see are (in order of how I would most likely approach this issue):
Droping the aspect and encapsulating this invariant in a service or a factory that creates CustomerBo's. It would be best, if CustomerBoImpl was immutable, so that you would not have to worry that it will be decrypted and left in that incorrect state.
If you are using Java Persistence API (JPA) to persist your domain objects, and if it is ok for the encryption to run just before saving them in the database, you may want to use listeners.(Note: the link leads to the documentation of Hibernate, which is one of the implementations of JPA)
The nuclear option - actually switching to using AspectJ which can introduce advice to constructors, field value changes etc.

Related

If Spring can successfully intercept intra class function calls in a #Configuration class, why does it not support it in a regular bean?

I have recently noticed that Spring successfully intercepts intra class function calls in a #Configuration class but not in a regular bean.
A call like this
#Repository
public class CustomerDAO {
#Transactional(value=TxType.REQUIRED)
public void saveCustomer() {
// some DB stuff here...
saveCustomer2();
}
#Transactional(value=TxType.REQUIRES_NEW)
public void saveCustomer2() {
// more DB stuff here
}
}
fails to start a new transaction because while the code of saveCustomer() executes in the CustomerDAO proxy, the code of saveCustomer2() gets executed in the unwrapped CustomerDAO class, as I can see by looking at 'this' in the debugger, and so Spring has no chance to intercept the call to saveCustomer2.
However, in the following example, when transactionManager() calls createDataSource() it is correctly intercepted and calls createDataSource() of the proxy, not of the unwrapped class, as evidenced by looking at 'this' in the debugger.
#Configuration
public class PersistenceJPAConfig {
#Bean
public DriverManagerDataSource createDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
//dataSource.set ... DB stuff here
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager( ){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(createDataSource());
return transactionManager;
}
}
So my question is, why can Spring correctly intercept the intra class function calls in the second example, but not in the first. Is it using different types of dynamic proxies?
Edit:
From the answers here and other sources I now understand the following:
#Transactional is implemented using Spring AOP, where the proxy pattern is carried out by wrapping/composition of the user class. The AOP proxy is generic enough so that many Aspects can be chained together, and may be a CGLib proxy or a Java Dynamic Proxy.
In the #Configuration class, Spring also uses CGLib to create an enhanced class which inherits from the user #Configuration class, and overrides the user's #Bean functions with ones that do some extra work before calling the user's/super function such as check if this is the first invocation of the function or not. Is this class a proxy? It depends on the definition. You may say that it is a proxy which uses inheritance from the real object instead of wrapping it using composition.
To sum up, from the answers given here I understand these are two entirely different mechanisms. Why these design choices were made is another, open question.
Is it using different types of dynamic proxies?
Almost exactly
Let's figure out what's the difference between #Configuration classes and AOP proxies answering the following questions:
Why self-invoked #Transactional method has no transactional semantics even though Spring is capable of intercepting self-invoked methods?
How #Configuration and AOP are related?
Why self-invoked #Transactional method has no transactional semantics?
Short answer:
This is how AOP made.
Long answer:
Declarative transaction management relies on AOP (for the majority of Spring applications on Spring AOP)
The Spring Framework’s declarative transaction management is made possible with Spring aspect-oriented programming (AOP)
It is proxy-based (§5.8.1. Understanding AOP Proxies)
Spring AOP is proxy-based.
From the same paragraph SimplePojo.java:
public class SimplePojo implements Pojo {
public void foo() {
// this next method invocation is a direct call on the 'this' reference
this.bar();
}
public void bar() {
// some logic...
}
}
And a snippet proxying it:
public class Main {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory(new SimplePojo());
factory.addInterface(Pojo.class);
factory.addAdvice(new RetryAdvice());
Pojo pojo = (Pojo) factory.getProxy();
// this is a method call on the proxy!
pojo.foo();
}
}
The key thing to understand here is that the client code inside the main(..) method of the Main class has a reference to the proxy.
This means that method calls on that object reference are calls on the proxy.
As a result, the proxy can delegate to all of the interceptors (advice) that are relevant to that particular method call.
However, once the call has finally reached the target object (the SimplePojo, reference in this case), any method calls that it may make on itself, such as this.bar() or this.foo(), are going to be invoked against the this reference, and not the proxy.
This has important implications. It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.
(Key parts are emphasized.)
You may think that aop works as follows:
Imagine we have a Foo class which we want to proxy:
Foo.java:
public class Foo {
public int getInt() {
return 42;
}
}
There is nothing special. Just getInt method returning 42
An interceptor:
Interceptor.java:
public interface Interceptor {
Object invoke(InterceptingFoo interceptingFoo);
}
LogInterceptor.java (for demonstration):
public class LogInterceptor implements Interceptor {
#Override
public Object invoke(InterceptingFoo interceptingFoo) {
System.out.println("log. before");
try {
return interceptingFoo.getInt();
} finally {
System.out.println("log. after");
}
}
}
InvokeTargetInterceptor.java:
public class InvokeTargetInterceptor implements Interceptor {
#Override
public Object invoke(InterceptingFoo interceptingFoo) {
try {
System.out.println("Invoking target");
Object targetRetVal = interceptingFoo.method.invoke(interceptingFoo.target);
System.out.println("Target returned " + targetRetVal);
return targetRetVal;
} catch (Throwable t) {
throw new RuntimeException(t);
} finally {
System.out.println("Invoked target");
}
}
}
Finally InterceptingFoo.java:
public class InterceptingFoo extends Foo {
public Foo target;
public List<Interceptor> interceptors = new ArrayList<>();
public int index = 0;
public Method method;
#Override
public int getInt() {
try {
Interceptor interceptor = interceptors.get(index++);
return (Integer) interceptor.invoke(this);
} finally {
index--;
}
}
}
Wiring everything together:
public static void main(String[] args) throws Throwable {
Foo target = new Foo();
InterceptingFoo interceptingFoo = new InterceptingFoo();
interceptingFoo.method = Foo.class.getDeclaredMethod("getInt");
interceptingFoo.target = target;
interceptingFoo.interceptors.add(new LogInterceptor());
interceptingFoo.interceptors.add(new InvokeTargetInterceptor());
interceptingFoo.getInt();
interceptingFoo.getInt();
}
Will print:
log. before
Invoking target
Target returned 42
Invoked target
log. after
log. before
Invoking target
Target returned 42
Invoked target
log. after
Now let's take a look at ReflectiveMethodInvocation.
Here is a part of its proceed method:
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
++this.currentInterceptorIndex should look familiar now
Here is the target
And there are interceptors
the method
the index
You may try introducing several aspects into your application and see the stack growing at the proceed method when advised method is invoked
Finally everything ends up at MethodProxy.
From its invoke method javadoc:
Invoke the original method, on a different object of the same type.
And as I mentioned previously documentation:
once the call has finally reached the target object any method calls that it may make on itself are going to be invoked against the this reference, and not the proxy
I hope now, more or less, it's clear why.
How #Configuration and AOP are related?
The answer is they are not related.
So Spring here is free to do whatever it wants. Here it is not tied to the proxy AOP semantics.
It enhances such classes using ConfigurationClassEnhancer.
Take a look at:
CALLBACKS
BeanMethodInterceptor
BeanFactoryAwareMethodInterceptor
Returning to the question
If Spring can successfully intercept intra class function calls in a #Configuration class, why does it not support it in a regular bean?
I hope from technical point of view it is clear why.
Now my thoughts from non-technical side:
I think it is not done because Spring AOP is here long enough...
Since Spring Framework 5 the Spring WebFlux framework has been introduced.
Currently Spring Team is working hard towards enhancing reactive programming model
See some notable recent blog posts:
Reactive Transactions with Spring
Spring Data R2DBC 1.0 M2 and Spring Boot starter released
Going Reactive with Spring, Coroutines and Kotlin Flow
More and more features towards less-proxying approach of building Spring applications are introduced. (see this commit for example)
So I think that even though it might be possible to do what you've described it is far from Spring Team's #1 priority for now
Because AOP proxies and #Configuration class serve a different purpose, and are implemented in a significantly different ways (even though both involve using proxies).
Basically, AOP uses composition while #Configuration uses inheritance.
AOP proxies
The way these work is basically that they create proxies that do the relevant advice logic before/after delegating the call to the original (proxied) object. The container registers this proxy instead of the proxied object itself, so all dependencies are set to this proxy and all calls from one bean to another go through this proxy. However, the proxied object itself has no pointer to the proxy (it doesn't know it's proxied, only the proxy has a pointer to the target object). So any calls within that object to other methods don't go through the proxy.
(I'm only adding this here for contrast with #Configuration, since you seem to have correct understanding of this part.)
#Configuration
Now while the objects that you usually apply the AOP proxy to are a standard part of your application, the #Configuration class is different - for one, you probably never intend to create any instances of that class directly yourself. This class truly is just a way to write configuration of the bean container, has no meaning outside Spring and you know that it will be used by Spring in a special way and that it has some special semantics outside of just plain Java code - e.g. that #Bean-annotated methods actually define Spring beans.
Because of this, Spring can do much more radical things to this class without worrying that it will break something in your code (remember, you know that you only provide this class for Spring, and you aren't going to ever create or use its instance directly).
What it actually does is it creates a proxy that's subclass of the #Configuration class. This way, it can intercept invocation of every (non-final non-private) method of the #Configuration class, even within the same object (because the methods are effectively all overriden by the proxy, and Java has all the methods virtual). The proxy does exactly this to redirect any method calls that it recognizes to be (semantically) references to Spring beans to the actual bean instances instead of invoking the superclass method.
read a bit spring source code. I try to answer it.
the point is how spring deal with the #Configurationand #bean.
in the ConfigurationClassPostProcessor which is a BeanFactoryPostProcessor, it will enhance all ConfigurationClasses and creat a Enhancer as a subClass.
this Enhancer register two CALLBACKS(BeanMethodInterceptor,BeanFactoryAwareMethodInterceptor).
you call PersistenceJPAConfig method will go through the CALLBACKS. in BeanMethodInterceptor,it will get bean from spring container.
it may be not clearly. you can see the source code in ConfigurationClassEnhancer.java BeanMethodInterceptor.ConfigurationClassPostProcessor.java enhanceConfigurationClasses
You can't call #Transactional method in same class
It's a limitation of Spring AOP (dynamic objects and cglib).
If you configure Spring to use AspectJ to handle the transactions, your code will work.
The simple and probably best alternative is to refactor your code. For example one class that handles users and one that process each user. Then default transaction handling with Spring AOP will work.
Also #Transactional should be on Service layer and not on #Repository
transactions belong on the Service layer. It's the one that knows about units of work and use cases. It's the right answer if you have several DAOs injected into a Service that need to work together in a single transaction.
So you need to rethink your transaction approach, so your methods can be reuse in a flow including several other DAO operations that are roll-able
Spring uses proxying for method invocation and when you use this... it bypasses that proxy. For #Bean annotations Spring uses reflection to find them.

Intercept private annotated method with AOP

I have a custom annotation:
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface FeatureSwitch {
String featureName();
}
I intercept this with the below aspect and use it to check if a feature is on or off. If the feature is off, then I throw an exception.
Aspect:
#Aspect
public class FeatureSwitchAspect {
private final FeatureSwitchConfigurationApi featureSwitchConfigurationApi;
public FeatureSwitchAspect(final FeatureSwitchConfigurationApi featureSwitchConfigurationApi) {
this.featureSwitchConfigurationApi = featureSwitchConfigurationApi;
}
#Before("#annotation(featureSwitch)")
public void checkFeatureSwitch(final FeatureSwitch featureSwitch) {
final String featureName = featureSwitch.featureName();
Boolean featSwitch = featureSwitchConfigurationApi.isFeatureActive(featureName);
if (!featSwitch) {
throw new FeatureSwitchOffException();
}
}
}
The problem I am having is that the behaviour seems inconsistent. This seems to do as expected when I call a method from a different class, but if I make a call to an annotated private method, no interception occurs. Have I got it configured incorrectly? Any suggestions would be appreciated.
Method calls from within classes will not work with proxy-based AOP.
Since you are using the keyword this (which is a pointer to your original object and not the proxy objects that is wrapping it), you will be calling the wrapped method directly - thus bypassing the code added as a result of your AOP.
You have tagged your question by java and aop, not by spring or spring-aop. So I assume that you are not limited to proxy-based Spring AOP but can use a full-fledged AOP solution like AspectJ (possible even within Spring or application servers). If so, there is a solution:
Use a privileged aspect. Caveat: This is supported in native AspectJ syntax, but not in #AspectJ syntax.

Spring AOP and Spring JPA, Aspect to be executed

I have been writing an aspect that manipulates some of my JPA entities getters. It is supposed to re-format the returned text based on the clients locale. Because not all of my getters should be reformatted I introduced an annotation #ReFormat.
The problem is my aspect is never intercepted when I advise it to an JPA entity but it works fine on non JPA entities (it works when I create my own entity object via a copy constructor).
My annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface ReFormat {
}
My aspect:
#Aspect
public class ReFormatAspect {
#AfterReturning(pointcut = "#annotation(com.path.ReFormat)", returning = "response")
public Object formatter(JoinPoint joinPoint, Object response) {
return response;
}
}
Now this aspect is intercepted successfully within my MVC controllers (or at any other place except spring data) but not for my entities.
#Entity
#Table(name = "place", schema = "db")
public class TestEntity {
#Id
#Column(name = "id")
protected long id;
#Column(name = "about", columnDefinition = "TEXT DEFAULT NULL")
protected String about;
#ReFormat
public String getAbout() {
return this.about;
}
}
I expected a point cut once the getAbout method is called, but it does not work.
Given the facts above I suppose that JPA (Hibernate) is overriding any interceptor may be by CGLib or javassist.
Note: I have this inside my context
<context:annotation-config />
<context:spring-configured />
<aop:aspectj-autoproxy proxy-target-class="true" />
So what is the exact issue, and how do I intercept any method inside an entity?
I understand this should be the view layer work, but still I need to know why :D
Your Entities are not managed by Spring, they are managed by the underlying JPA implementation. Because of this, Spring cannot wrap them in proxies and provide the wanted behavior.
There's no Spring way to solve this. Hibernate might have some interceptor tool for that (that wraps entities as they are created) but I don't know it. Maybe extending EmptyInterceptor.
You can always use AspectJ and wave your aspect in compile time. But this way you will not have an access to spring DI container.
It is possible to use AspectJ aspects together with the Spring DI container. The Spring AOP documentation sais:
Domain objects often fall into this category because they are often created programmatically using the new operator, or by an ORM tool as a result of a database query.
I created a GitHub project where I use your #ReFormat annotation on a method of a bean annotated with #Entity. In order to be able to use DI within your AspectJ aspect you need to use the aspectOf() approach. This is even possible with JavaConfig. So you don't have to use XML configuration at all.

Intercepting Specific Annotations with Spring AOP

I'm looking to see whether or not the following is even possible, as all preliminary searches haven't turned back anything to indicate either way.
I'd like to use Hibernate's Validator annotations to validate bean methods, and I would like to use some AOP framework (Spring, AOP Alliance, AspectJ, etc.) to intercept methods annotated with a subset of the Hibernate Validator annotations (#NotNull, #NotEmpty, #Email, etc.); I then want AOP advice to run when they are encountered.
Is this possible to do? If so, I am having a tough time visualizing how the code would work. Using Spring AOP's MethodInterceptor interface as an example:
First, the bean using Hibernate Validator:
public class SomeBean
{
private String data;
// Hibernate Validator annotation specifying that "data" cannot be an empty
// string.
#NotEmpty
public String getData() { ... } // etc.
}
Then, some code using that bean:
public void someMethod()
{
SomeBean oBean = new SomeBean();
// Validation should fail because we specified that "data" cannot be empty.
oBean.setData("");
}
Next, the AOP advice to be ran when Hibernate Validator-annotated methods are encountered.
public class ValidationInterceptor implements MethodInterceptor
{
public Object invoke(MethodInvocation invocation)
{
// Here's where we would use Hibernate's validator classes.
// My code example here is wrong, but it gets the point across.
Class targetClass = invocation.getClass(); // Should give me SomeBean.class
ClassValidator<targetClass> oValidator= new ClassValidator<targetClass>();
// Here I need to get a reference to the instance of the offending
// SomeBean object whose data has been set to empty...not sure how!
SomeBean oOffendingBean = getTheBadBeanSomehow();
InvalidValue[] badVals = oValidator.getInvalidValues(oOffendingBean);
}
}
So, not only am I choking on what the Spring AOP (pointcut definitions, etc.) configuration would look like to intercept the Hibernate Validator annotations I want, and not only do I not fully grasp how to implement the actual advice (e.g. how to instantiate the offending SomeBean from inside the advice as I mention above in the comments), but I'm not even sure if this solution is possible, Spring or otherwise.
Thanks in advance for some gentle "nudges" in the right direction!
You might be interested in the method validation feature introduced with Hibernate Validator 4.2 which provides support for validating method parameters and return values.
You then might use Seam Validation which integrates this functionality with CDI. If you want to use method validation together with Spring you could have a look this project on GitHub which shows how to integrate the method validation functionality with Spring AOP (disclaimer: I'm the author of this project as well as of Seam Validation).
To make your example working you would have to annote the parameter of the setter method with #NotEmpty like this:
public class SomeBean {
private String data;
#NotEmpty
public String getData() { return data; }
public void setData(#NotEmpty String data) { this.data = data; }
}

Mixing declarative and programmatic transactions with Spring and JPA listeners

I'm using a JPA EntityListener to do some additional audit work and am injecting a Spring-managed AuditService into my AuditEntryListener using #Configurable. The AuditService generates a collection of AuditEntry objects. The AuditService is itself a Singleton scoped bean, and I'd like to gather all the AuditEntry objects under a common key that can then be accessed by the outermost service layer (the one that invoked the persist call which in turn triggered the EntityListener).
I'm looking at using Spring's TransactionSynchronizationManager to set a specific transaction name (using UID() or some other unique strategy) at the beginning of the transaction, and then using that name as a key within the AuditService that will allow me to group all AuditEntry objects created within that transaction.
Is mixing declarative and programmatic transaction management have the potential for trouble? (Though I'm doing nothing more than setting the transaction name). Is there a better way to associate the generated AuditEntry objects with the current transaction? This solution does work for me, but given that the TransactionSynchronizationManager isn't intended for application use, I'd like to make sure that my use of it won't cause some unforseen problems.
Related Question
Finally, a related, but not immediately pertinent question: I know that the documentation for JPA EntityListeners cautions against using the current EntityManager, but if I did want to use it to diff an object against it's persisted self, would I be safe using an #Transactional(propagation=REQUIRES_NEW) annotation around my preUpdate() method?
Prototype Code:
Service Class
#Transactional
public void create(MyEntity e) {
TransactionSynchronizationManager.setCurrentTransactionName(new UID().toString());
this.em.persist(e);
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
#Override
public void afterCommit() {
Set<AuditEntry> entries = auditService.getAuditEntries(TransactionSynchronizationManager.getCurrentTransactionName());
if(entries != null) {
for(AuditEntry entry : entries) {
//do some stuff....
LOG.info(entry.toString());
}
}
}
});
}
JPA EntityListener
#Configurable
public class AuditEntryListener {
#Autowired
private AuditService service;
#PreUpdate
public void preUpdate(Object entity) {
service.auditUpdate(TransactionSynchronizationManager.getCurrentTransactionName(), entity);
}
public void setService(AuditService service) {
this.service = service;
}
public AuditService getService() {
return service;
}
}
AuditService
#Service
public class AuditService {
private Map<String, Set<AuditEntry>> auditEntryMap = new HashMap<String, Set<AuditEntry>>();
public void auditUpdate(String key, Object entity) {
// do some audit work
// add audit entries to map
this.auditEntryMap.get(key).add(ae);
}
}
#Filip
As far as I understand, your requirement is:
Have an unique token generated within each transaction (database
transaction of course)
Keep this unique token easily accessible across all layers
So naturally you're thinking about the TransactionSynchronizationManager provided by Spring as a facility to store the unique token (in this case, an UID)
Be very carefull with this approach, the TransactionSynchronizationManager is the main storage helper to manage all the #Transactional processing for Spring. Under the #Transactional hood, Spring is creating an appropriate EntityManager, an appropriate Synchronization object and attach them to a thread local using TransactionSynchronizationManager.
In your service class code, inside a #Transactional method your are tampering with the Synchronization object, it can end up with undesirable behavior.
I've done an indept analysis of how #Transactional works here, have a look: http://doanduyhai.wordpress.com/2011/11/20/spring-transactional-explained/
Now back to your needs. What you can do is:
Add a Thread local to the AuditService, containing the unique token when entering the #Transactional method and destroy it when exiting the method. Within this method call, you can access the unique token in any layer. Explanation for ThreadLocal usage can be found here: http://doanduyhai.wordpress.com/2011/12/04/threadlocal-explained/
Create a new annotation, let's say #Auditable(uid="AuditScenario1") to annotate methods that need to be audited and use Spring AOP to intercept these method calls and manage the Thread local processing for you
Example:
Modified AuditService
#Service
public class AuditService {
public uidThreadLocal = new ThreadLocal<String>();
...
...
}
Auditable annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#Documented
public #interface Auditable
{
String uid();
}
Usage of #Auditable annotation
#Auditable(uid="AuditScenario1")
#Transactional
public void myMethod()
{
// Something
}
Spring AOP part
#Around("execution(public * *(..)) && #annotation(auditableAnnotation))
public Object manageAuditToken(ProceedingJoinPoint jp, Auditable auditableAnnotation)
{
...
...
AuditService.uidThreadLocal.set(auditableAnnotation.uid())...
...
}
Hope this will help.
You can come up with a solution using the TransactionSynchronizationManager. We register a "TransactionInterceptorEntityListener" with JPA as an entity-listener. What we wanted to achieve is the ability to listen to CRUD events such that we can work with a spring managed "listener" that has a lifecycle tied to the current transaction (i.e., spring-managed but instance per transaction). We sub-class the JPATransactionManager and introduce in the prepareSynchronization() method, a hook to setup a "TransactionInterceptorSynchronizer." We also use the same hook for allow code (in programmatic tx) to associate and retrieve arbitrary objects with the current transaction and also register jobs that run before/after transaction commit.
The overall code is complex, but definitely do-able. If you use JPATemplates for programmatic tx, it is tough to achieve this. So we rolled our own template that simply calls the JPA template after taking care of the interceptor work. We plan to open-source our JPA library (written on top of Spring's classes) soon.
You can see a pattern of adding custom transactions and hooks with Spring managed transactions in the following library for Postgresql

Categories

Resources