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.
Related
I am migrating and existing crud application into Axon, and I have some concerns with the following scenario. I have an api given below to create groups,
#PostMapping
public Mono<IdDto> createGroup(#RequestBody #Valid CreateGroupCommand command) {
log.trace("Create GroupResponseInfoDto request {}", command);
return commandGateway.send(command)
.map((o) -> new IdDto((UUID) o));
}
The command looks like,
#Data
public class CreateGroupCommand {
#NotBlank
private String name;
#NotBlank
private String description;
}
and the main requirement here is that the group name must be unique.
Therefore, in the Aggregate I have the following code to check this logic.
#CommandHandler
public GroupAggregate(CreateGroupCommand command, GroupRepository groupRepository, ModelMapper mapper) {
log.trace("Handle create group command {}", command);
groupRepository.findByName(command.getName())
.ifPresent((g) -> {
throw new ApplicationException(UserMsError.GROUP_ALREADY_EXISTS.name());
});
GroupCreatedEvent event = mapper.map(command, GroupCreatedEvent.class);
event.setId(UUID.randomUUID());
AggregateLifecycle.apply(event);
}
And once the validation pareses, the event is persisted by a projector into the db.
#EventSourcingHandler
public void on(GroupCreatedEvent event) {
log.trace("Group create event {}", event);
groupRepository.findByName(event.getName())
.ifPresent((g) -> {
throw new ApplicationException(UserMsError.GROUP_ALREADY_EXISTS.name());
});
Group group = modelMapper.map(event, Group.class);
groupRepository.save(group);
}
The problem now is, there is some lap time between the command execution and the persistance of the event results into group table. If another user creates a group in that time, the command does not fail as the record does not exist in the db. Now, I see in Axon site there is a way to create a temporary table where we put the command execution into some temporary table which we can use for validation purpose, but that requires additional coding and quite extra effort for each such requirement. It also means, if we persist the details on command execution, and for some reason the process fails then the record will exist in our validation table but not on the system. If we try to validate the scenario on event execution, that extra effort might not be required but in this case the problem is I am not able to fail the API call so that the user knows the results. Could you please recommend if there is an alternative approach to validate the input without an intermediate check?
The problem you are looking at is set-based validation. Whenever you're dealing with CQRS, it's the sets that will require extra work to be validated.
Although uncertain, I assume you're talking about the Set-Based Consistency Validation blog? That is, for a reason, the suggested approach to deal with set validation. Note that the implementation used in the blog can be found here.
Added, it quite recently has seen an update that does not include the problem you describe as follows:
It also means, if we persist the details on command execution, and for some reason, the process fails, then the record will exist in our validation table but not on the system.
Axon's transaction logic, supported through the UnitOfWork, will roll back the entire transaction if something fails. This thus anything you'd do inside the UnitOfWork, including updates to another table for validation.
I get that it's some boilerplate code, but it is the predicament of having the uniqueness requirement on a set. What might be something you can look into is forcing the uniqueness through the Aggregate Identifier. Axon's Event Store logic ensures no two events are using the same aggregate identifier. So, if you try to input a new aggregate (hence a new event) for an already existing aggregate identifier, the operation will fail.
This approach is typically not feasible whenever the set-based consistency validation issue is described, though, so I am guessing it won't help you out.
Concluding, I'd take your win from the shared repository on the blog to minimize your personal effort on the matter.
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.
We have an application with three databases. Two of them are only very seldomly updated. We tried JPA to create transactions around it and it worked for the databases, but grails then did not work on different places (gsp related I am told). This was tried quite a while ago (and not by me).
Due to delivery pressure we needed a solution that at least works for us, so I created a new aspect for the methods changing data in multiple databases. I got this to work, it is a fairly simple approach.
In the aspect we request to start a transaction for each data source, by calling getTransaction(TransactionDefinition def) with the propagation set to REQUIRES_NEW. We then proceed and finally rollback or commit depending on the outcome of the call.
However, one test flow failed. This is the scenario where the code requests a rollback by calling TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(). Of the three TransactionStatusses obtained initially, none actually returns isRollbackOnly() with true. However calling TransactionAspectSupport.currentTransationStatus().isRollbackOnly() does return true. So this seems to point to a different transaction status.
I have not been able to figure out how to make this work, other than checking this additional status. I could not find a way to change the currentTransactionStatus to the one of created TransactionStatus. Looking at the TransactionTemplate implementation, I seem to do things correctly (it also just calls getTransaction() on the datasource).
The code calling the decorated method has specified #Transactional(propagation=Propagation.NOT_SUPPORTED), so I expected no currentTransactionStatus, but one is there.
However, if it is not there the proxied code will not be able to request a rollback the standard way, which I want to be able to fix.
So the question is, how to start a transaction correctly from an Aspect so that the currentTransactionStatus is set correctly or how to set the currentTransactionStatus to what I think is the correct one.
Regards,
Wim Veldhuis.
I finally figured it out.
#Transactional leads to a different code path, where eventually TransactionAspectSupport.invokeWithinTransaction is invoked. This method will set up the current transaction correctly.
So in order to make my approach working, I needed to derive from TransactionAspectSupport, do a number of cast operations so I could get to the correct values for the invokeWithinTransaction call, and within the guarded function block use getTransaction(def) to obtain txns for the OTHER databases. I have choose the most important database to be the one used for invoke...
To make it work I had also to provide a TransactionAttributeSource, that returned my default transaction attributes.That one is stored into the TransactionAspectSupport base class during initialization.
#Around("#annotation(framework.db.MultiDbTransactional)")
public Object multiDbTransaction(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
// Get class and method, needed for parent invocation. We need to cast to the actual
// implementation
MethodInvocationProceedingJoinPoint mipJoinPoint = (MethodInvocationProceedingJoinPoint) proceedingJoinPoint;
MethodSignature signature = (MethodSignature) mipJoinPoint.getSignature();
Class<?> clazz = mipJoinPoint.getTarget().getClass();
Method method = signature.getMethod();
return invokeWithinTransaction(method, clazz, new InvocationCallback() {
#Override
public Object proceedWithInvocation() throws Throwable {
// This class will create the other transactions, not of interest here.
MultiDbTxnContext ctx = new MultiDbTxnContext();
ctx.startTransactions();
/*
* We have started the transactions, so do the job. We mimic DEFAULT spring behavior
* regarding exceptions, so runtime exceptions roll back, the rest commits.
*/
try {
Object result = proceedingJoinPoint.proceed();
ctx.finishTransactions();
return result;
} catch (Error | RuntimeException re) {
ctx.rollbackTransactions();
throw re;
} catch (Throwable t) {
ctx.commitTransactions();
throw t;
}
}
});
}
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.
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.