Conditional Hibernate Interceptor - java

I have a Hibernate Interceptor that is persisting property changes of an entity and is working correctly. I am looking to update its current logic and only intercept when it's being triggered from a particular method, not just when Hibernate views it as being 'dirty'.
I am looking to implement something equivalent to:
#Override
public boolean onFlushDirty(Object entity, Serializableid, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types){
String callingMethod = // determined somehow...
if(callingMethod == MyService.updatePerson) {
// Only persist changes when calling method is updatePerson
}
return false
}
I've looked all around SO and the web, but I have not been able to find anything on the topic.
I've looked into controlling this behavior with AOP, however the interceptor fires blindly
Is anyone aware of being able to trigger an interceptor based on the calling method?

I ended up creating an Abstract class and Interface to support setting the 'isEditable' method I need.
I then wrote a custom annotation that I use within AspectJ to trap all executing code that I want. Within the pointcut, I update the entity, setting the 'isEditable' method.
Now, when my interceptor fires, I do a quick check to see if that entity is editable, if not, I dont persist the changes into the history table.

Related

Hibernate: Trouble using sessionFactory.getTypeHelper().custom(userType)

Hibernate.custom(userType) is gone in Hibernate 5.2.10.Final so I have to use sessionFactory.getTypeHelper().custom(userType). Is there any way to get TypeHelper without sessionFactory? Previously I was using hibernate 3.6.10.Final. I would rather not use sessionFactory but I can't really find a way around it.
The main goal is to take a org.hibernate.usertype.UserType and return a org.hibernate.type.Type.
I have this function in 3.6.10.Final
public Type getHibernateType() {
// Type is class that implements hibernates UserType
return Hibernate.custom(UserType)
}
in 5.2.10.Final I had to change it to something like
public Type getHibernateType() {
return sessionFactory.getTypeHelper().custom(UserType);
}
I don't really want to use sessionFactory if I can help it. So I was wondering if there was another way to get the Type.
Well, technically 5.2 still exposes the functionality via TypeFactory in a static method.
Type type = TypeFactory.custom( UserType.class, null, null );
However, be aware this method is marked #Deprecated and in fact, that entire class has been removed as a part of Hibernate 6.0's type system overhaul.
I would get used to the notion of using the SessionFactory to access this information because that is precisely how we have designed 6.0 to work at present.

Call method post transaction commit

I have a very peculiar requirement where I have to insert records in 2 tables (audit tables) if insertion in one particular table succeeds. Here I am not talking about #PreInsert in Listener because Listeners are always called in the same transaction. I know that can be done manually by simply calling "save" method after the first save succeeds. BUT I wanted to know is there any other way which I can try using Listener be it JPA/EclipseLink/String-data so that future developers of the application are not forced to insert data in audit table manually.
Basically I am looking for #PostCommit type of functionality. Please help me.
I believe you ultimately do want the callback to run within the boundary of your current transaction, you just want it to run after Hibernate has done its things, just like Hibernate Envers works.
To do this, you basically need to register an event action queue callback like the following:
session.getActionQueue().registerProcess(
new BeforeTransactionCompletionProcess() {
#Override
public void doBeforeTransactionCompletion(SessionImplementor session) {
// do whatever you want with the session here.
}
}
);
If you ultimately must run your code outside the transaction, you could do something similar:
session.getActionQueue().registerProcess(
new AfterTransactionCompletionProcess() {
#Override
public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) {
// do whatever you want with the session here.
}
}
);
That should get you going either way.

How can I pass a customized value to a spring aop advice?

If I take a service method named public void delete(int id); as the pointcut, I want to add an after-returning advice on it, but I don't know what kind of object was deleted(however, the servlet which called the service knows the type value), so I was wondering if I can pass a customized value to this after-returning advice when it is activated, like 'user'. I've already checked the related document on Spring's website and I still know nothing. I'd appreciate your answer, THX.
One solution but its required refactoring in Service method
1) Refactoring
public class DeleteRequest {
String type;
Long id;
}
public boolean delete(DeleteRequest request){ // impl}
2) Use Around Advice
2.1) Before proceeding method execution, read passed parameter & get to be deleted object for "XYZ" requirement.
2.2) Capture result of delete method execution
IF its TRUE then DO your stuff
Note: I used this approach for deleted entity notification. So how we can get deleted entity information in after-advice, hence keep it entity information in before phase & use it in after-successful execution.

Getting single NonNull value using Spring Data

I'd like to have a method in my Repository that returns a single value.
Like this:
TrainingMode findByTrainingNameAndNameEng( String trainingName, String nameEng );
http://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/
Spring Data Docs describe that in this case the method can return null if no entity is found.
I'd like to throw an exception with generic message like No TrainingMode found by %trainingName% and %nameEng% or smth like that.
I can use Optional<TrainingMode> as a return value and then use orElseThrow
Optional<TrainingMode> findByTrainingNameAndNameEng( String trainingName, String nameEng );
repository.findByTrainingNameAndNameEng(name, nameEng).orElseThrow(() -> new RuntimeException(...));
But I should call this method each time when this method is called. It's not clear - DRY priciple is broken.
How to get nonnull single value with orElseThrow using Spring Data?
The DRY principle would be violated if you duplicate null handling throughout the application logic where it is being invoked. If DRY principle is the thing you are worried the most then i can think of:
You can make a "Service" class which would delegate calls to annotated repository and handle null response logic to it, and use that service class instead of calling repositories directly. Drawback would be introducing another layer to your application (which would decouple repositories from your app logic).
There is possibility of adding custom behavior to your data repositories which is described in "3.6.1. Adding custom behavior to single repositories" section of documentation. Sorry for not posting the snippet.
The issue I personally have with second approach is that it pollutes app with interfaces, enforces you to follow a certain naming patterns (never liked 'Impl' suffixes), and might make migrating code a bit more time consuming (when app becomes big it becomes harder to track which interface is responsible for which custom behavior and then people just simply start creating their own behavior which turns out to be duplicate of another).
I found a solution.
First, Spring Data processes getByName and findByName equally. And we can use it: in my case find* can return null (or returns not null Optional, as you wish) and get* should return only value: if null is returned then exception is thrown.
I decided to use AOP for this case.
Here's the aspect:
#Aspect
#Component
public class GetFromRepositoryAspect {
#Around("execution(public !void org.springframework.data.repository.Repository+.get*(..))")
public Object aroundDaoMethod( ProceedingJoinPoint joinpoint ) throws Throwable {
Object result = joinpoint.proceed();
if (null == result) {
throw new FormattedException( "No entity found with arhs %s",
Arrays.toString( joinpoint.getArgs() ) );
}
return result;
}
}
That's all.
You can achieve this by using the Spring nullability annotations. If the method return type is just some Entity and it's not a wrapper type, such as Optional<T>, then org.springframework.dao.EmptyResultDataAccessException will be thrown in case of no results.
Read more about Null Handling of Repository Methods.

Obtaining a Hibernate transaction within a Spring class

I am working on a program that uses Spring and obtains Hibernate transactions transparently using a TransactionInterceptor. This makes it very convenient to say "when this method is invoked from some other class, wrap it in a transaction if it's not already in one."
However, I have a class that needs to attempt a write and must find out immediately whether or not it has succeeded. While I want two methods anyway, I was hoping that there was a way to keep them in the same class without needing to explicitly create an transaction procedurally. In effect, I'd like something like this:
public void methodOne() {
//..do some stuff
try {
transactionalMethod();//won't do what I want
} catch(OptimisticLockingFailure e) {
//..recover
}
}
#Transactional
public void transactionalMethod() {
//...do some stuff to database
}
Unfortunately, as I understand it, this wouldn't work because I'd just be directly calling transactionalMethod. Is there a way to ask Spring to call a local method for me and wrap it in a transaction if needed, or does it have to be in another class that I wire to this one?
Define an interface which the class implements which does the transactionalMethod(); use dependency injection to set the class' value of that to its own implementation; in your bean factory, allow Spring to insert an Around aspect around that interface implementation. That should work for your needs.
If you want the transactionalMethod to be part of it's own transaction and not simply join onto the transaction that is already active you have to set the propagation to REQUIRES_NEW. Like so
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void transactionalMethod() {
//...do some stuff to database
}
You should also check that your transaction manager supports this propagation. the means that transactionalMethos is completely seperate from the other transaction that it was called from and it will commit / rollback completely seperately as well.

Categories