Is there any way to get current transaction id, store/pass it, and check it's status in the another part of application?
For example:
#Service
public class Service {
...
#Transactional(rollbackFor = Exception.class)
public void performAction(Action action) {
// start action
String transactionId = ??? // getting current transaction id
messenger.send(transactionId); // sending transaction id to consumer
// continue action
} <- commit transaction
}
public class Consumer {
...
public void onRecieveMessage(String transactionId) {
TransactionStatus ts = ??? // getting transaction from pool by id
if (ts.isCompleted()) {
// some actions
} else {
// wating or Future<?> or something else...
}
}
}
(Actually, the problem is Consumer.onRecieveMessage executes earlier than action's transaction finishes, and data state is old)
I do not know any way of getting a specific transaction by its ID (hopefully someone does).
What I would do is to implement an instance of TransactionSynchronization that holds a kind of value you want to track:
public class YourTransactionTracker extends TransactionSynchronizationAdapter {
private Value value;
public YourTransactionTracker(Value value) {
this.value = value;
}
#Override
public void afterCompletion(int status) {
//transaction handling your 'value' has been completed with the status 'status'
//do whatever you want here
}
//you can also override other methods like beforeCompletion(), flush(), etc...
//have a look at TransactionSynchronizationAdapter
}
TransactionSynchronizationAdapter is just an implementation of TransactionSynchronization with empty methods.
Then you can hook a callback in the transaction with your TransactionSynchronization instance, that will be called in the different stages of the lifecycle of the transaction.
You can do this in Spring by using the TransactionSynchronizationManager:
TransactionSynchronizationManager.registerSynchronization(
new YourTransactionTracker(theValueYouAreTracking);
Related
I'm using room persistence library to update the database. I'm stuck at a point where I want to fetch the id of recently inserted record.
I know that using long as the return type for the insert method returns the id. But I access this Dao method through a viewmodel.
My DAO method is as follows:
//MyDao
#Insert
long insert(RecordItem record);
This method is accessed from a repository by doing this:
//MyRepository
public class MyRepository {
private MyDao myDao;
public MyRepository(#NonNull Application application) {
MainDatabase mainDatabase = MainDatabase.getInstance(application);
myDao = mainDatabase.myDao();
}
public void insert(RecordItem record) {
MainDatabase.dbWriteExecutor.execute(() -> {
myDao.insert(record);
});
}
}
And the repository method is called from viewmodel as follows:
//MyViewModel
public void insert(RecordItem record) {
repository.insert(record);
}
And finally the viewmodel method as:
//MyActivity
myViewModel.insert(record);
My problem is, I don't know how I can get long returned through a viewmodel method. I tried doing this in repository
//MyRepository
public class MyRepository {
private MyDao myDao;
private long id;
public MyRepository(#NonNull Application application) {
MainDatabase mainDatabase = MainDatabase.getInstance(application);
myDao = mainDatabase.myDao();
}
public long insert(RecordItem record) {
MainDatabase.dbWriteExecutor.execute(() -> {
id = myDao.insert(record);
});
return id;
}
}
and subsequent changes to viewmodel method as well.
However, it returns 0, which I suppose happens since the insert method is executed on a different thread and id is returned as soon as the statement is reached(correct me if I'm wrong).
Thanks in advance.
You can approach following solution for your issue:
Create a Callback interface as below:
public interface DbInsertCallback {
void onInsert(long insertedItemId);
}
Then use this interface on your repository insert(RecordItem record) method like below usage:
public class MyRepository {
// ... Some repo code ...
public void insert(RecordItem record, DbInsertCallback callback) {
MainDatabase.dbWriteExecutor.execute(() -> {
long id = myDao.insert(record);
callback.onInsert(id);
});
}
// ... Rest of repo code ...
}
And also make necessary changes on caller site (I.e. ViewModel & Activity) to provide object of this callback class as parameter as well. To do the implementation of this interface, you can either create object of that interface along with implementation or else pass it contextually like providing this.
You can also use RxJava for this problem, where the insert method will return Single<Long>.
#Insert
Single<long> insert(RecordItem item)
Then when calling insert you call subscribe to get the returning id or use flatMap for any further actions using RxJava.
myDao.insert(record).subscribeWith(new DisposableSingleObserver<long>() {
#Override
public void onSuccess(long id) {
// handle the id
}
#Override
public void onError(Throwable e) {
// handle the error case
}
}
I suggest you to take a look at RxJava further down the line since it makes asynchronous programming much more natural and easier to work with and Room also implements it out of the box.
When using the #EventListener functionality with Spring Data's repositories the behavior is different than when calling the same code procedural.
My persistent objects publish events using the following base class:
public abstract class Aggregate {
#Transient
private transient final Set<Object> events = new LinkedHashSet<>();
protected <T> T registerEvent(T event) {
this.events.add(event);
return event;
}
#DomainEvents
Collection<Object> events() {
return Collections.unmodifiableSet(events);
}
#AfterDomainEventPublication
void clearEvents() {
this.events.clear();
}
}
My event listening class is implemented as follows:
class Service {
#EventListener
public void listener(SomeEvent event) {
someOtherRepository.save(someOtherPersistentObject);
someOtherCode();
}
}
When the listener is triggered and someOtherRepository's save(…) method fails a rollback will be issued. But someOtherCode() is executed regardless of the rollback.
But when I remove all #EventListening functionality and call the listener(…) method directly after the point where the originating repository is responsible for firing the event. Then I get a different behavior. Then someOtherCode() is never executed and the someOtherRepository.save(…) method fails immediately.
The original service responsible for publishing the event looks like this
public OriginatingService {
#Transactional
public void someMethod() {
originatingRepoDifferentFromSomeOtherRepo.save(something);
Why is this happening and is there a way to force the same behavior onto my event listening implementation?
Because writes to the database may be delayed until transaction commit i.e. when the transactional method returns.
Update as below to explicitly trigger an immediate flush:
#EventListener
public void listener(SomeEvent event) {
someOtherRepository.saveAndFlush(someOtherPersistentObject);
someOtherCode();
}
When using #Transactional in a service layer, is there any option to get a reference to the TransactionStatus instance created by the PlatformTransactionManager?
For instance, in the following code :
#Transactional
public void updateCustomer(...) {
// do some business stuff
// can we get here a reference to the TransactionStatus instance ?
}
TransactionStatus status = TransactionAspectSupport.currentTransactionStatus();
The currentTransactionStatus method returns the transaction status of the current method invocation.
If you are interested in the result of a transaction, you could consider the TransactionSynchronizationAdapter which provides a convenient afterCompletion(int status) callback:
class AfterCompletionTransactionHandler
extends TransactionSynchronizationAdapter {
public #Override void afterCompletion(int status) { ... }
}
I am trying to upgrade my spring transaction manager from JtaTransactionManager to HibernateTransactionManager. In JTA TransactionManager we have one method which gives the status of current transaction. Based on the status we are doing some operations. The implementation is as follows :
private void checkTransactionStatus(TransactionStatus status){
if(status instanceof DefaultTransactionStatus) {
DefaultTransactionStatus transactionStatus = (DefaultTransactionStatus) status;
if(transactionStatus.getTransaction() instanceof JtaTransactionObject){
JtaTransactionObject txObject = (JtaTransactionObject) transactionStatus.getTransaction();
int jtaStatus;
try {
jtaStatus = txObject.getUserTransaction().getStatus();
if(jtaStatus==Status.STATUS_MARKED_ROLLBACK){
// logic heare
}
} catch (SystemException e) {}
}
}
}
I want to replace this method with HibernateTransactionManager specific code. I analyzed and found that, HibernateTransactionManager is using HibernateTransactionObject as transaction object. But, unfortunately it's a private inner class that I can't use to get the status. Then I tried to use the parent class JdbcTransactionObjectSupport. But, I don't know how to get the status from this parent class object.
private void checkTransactionStatus(TransactionStatus status){
if(status instanceof DefaultTransactionStatus) {
DefaultTransactionStatus transactionStatus = (DefaultTransactionStatus) status;
if(transactionStatus.getTransaction() instanceof JdbcTransactionObjectSupport){
JdbcTransactionObjectSupport txObject = (JdbcTransactionObjectSupport) transactionStatus.getTransaction();
//how to get the current status ?
}
}
}
Spring has a mechanism for receiving callbacks. You can implement the TransactionSynchronization interface (or easier extend the TransactionSynchronizationAdapter). You probably want to implement the afterCompletion(int) method and put your logic in there.
public class MyTxCallback extends TransactionSynchronizationAdapter {
public void afterCompletion(int status) {
if (status==STATUS_ROLLED_BACK) {
//logic here.
}
}
}
You can then bind that to the transaction by calling the TransactionSynchronizationManager when a transaction is started. Now when the transaction is done the method will be called and you can do your logic (regardless of the underlying transactional resource used).
If you use HibernateTransactionManager you can get the current transaction state from the Hibernate Session:
LocalStatus status = session.getTransaction().getLocalStatus();
and the LocalStatus has the following states:
public enum LocalStatus {
/**
* The local transaction has not yet been begun
*/
NOT_ACTIVE,
/**
* The local transaction has been begun, but not yet completed.
*/
ACTIVE,
/**
* The local transaction has been competed successfully.
*/
COMMITTED,
/**
* The local transaction has been rolled back.
*/
ROLLED_BACK,
/**
* The local transaction attempted to commit, but failed.
*/
FAILED_COMMIT
}
I have a web application that takes input from a user and uses it to generate a report based on the results of calling various external web services.
I want to track the progress of the report generation, being able to see the status, start time and stop time of each step.
I've added the domain objects Job and JobStep:
#Entity
#Table(name="jobs")
#Data
#EqualsAndHashCode(callSuper=false, of={ "id" })
#ToString()
public class Job extends DomainObject {
#NotNull
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="job_id")
private Set<JobStep> steps = new TreeSet<JobStep>();
protected Job() {/*Hibernate requirement*/}
public Job() {
// Create all the steps in the beginning with the default settings:
// status=waiting, date_time both null.
for (JobStep.Type stepType : JobStep.Type.values()) {
JobStep step = new JobStep(stepType);
steps.add(step);
}
}
public Set<JobStep> getSteps() {
return steps;
}
public void startStep(JobStep.Type stepType)
{
for (JobStep step : steps) {
if (step.getType() == stepType) {
step.start();
return;
}
}
}
public void stopStep(JobStep.Type stepType, JobStep.Status status) {
for (JobStep step : steps) {
if (step.getType() == stepType) {
step.stop(status);
return;
}
}
}
}
#Entity
#Table(name="job_steps")
#Data
#EqualsAndHashCode(callSuper=false, of={ "type", "job" })
#ToString
public class JobStep extends DomainObject implements Comparable<JobStep> {
private static final Logger LOG = LoggerFactory.getLogger(JobStep.class);
public enum Type {
TEST_STEP1,
TEST_STEP2,
TEST_STEP3
}
public enum Status {
WAITING,
RUNNING,
FINISHED,
ERROR
}
#NotNull
#Getter
#Enumerated(EnumType.STRING)
private Type type;
#NotNull
#Setter(AccessLevel.NONE)
#Enumerated(EnumType.STRING)
private Status status = Status.WAITING;
#Setter(AccessLevel.NONE)
private DateTime start = null;
#Setter(AccessLevel.NONE)
private DateTime stop = null;
#ManyToOne
private Job job;
protected JobStep() {/*Hibernate requirement */}
public JobStep(Type type) {
this.type = type;
}
public void start() {
assert(status == Status.WAITING);
status = Status.RUNNING;
start = new DateTime();
}
public void stop(Status newStatus) {
assert(newStatus == Status.FINISHED ||
newStatus == Status.ERROR);
assert(status == Status.RUNNING);
status = newStatus;
stop = new DateTime();
}
#Override
public int compareTo(final JobStep o) {
return getType().compareTo(o.getType());
}
}
These are manipulated using the JobService class:
#Service
public class JobService {
private static final Logger LOG = LoggerFactory.getLogger(JobService.class);
#Autowired
private JobDAO jobDao;
#Transactional
public void createJob() {
Job job = new Job();
Long id = jobDao.create(job);
LOG.info("Created job: {}", id);
}
#Transactional
public Job getJob(Long id) {
return jobDao.get(id);
}
#Transactional
public void startJobStep(Job job, JobStep.Type stepType) {
LOG.debug("Starting JobStep '{}' for Job {}", stepType, job.getId());
job.startStep(stepType);
}
#Transactional
public void stopJobStep(Job job, JobStep.Type stepType,
JobStep.Status status) {
LOG.debug("Stopping JobStep '{}' for Job {} with status {}", stepType,
job.getId(), status);
job.stopStep(stepType, status);
}
}
So in a method that starts a step, I can write:
class Foo() {
#Autowired
JobService jobService;
public void methodThatStartsAStep(Job job) {
jobService.startJobStep(job, JobStep.Type.TEST_STEP1);
// Implementation here
}
}
The problem I'm having is finding a way to give the Job instance to the method that requires it in order to record that the step has started.
The obvious solution is to pass the Job as a parameter (as above), but it doesn't always make sense passing a Job - it's only done to record the step (extreme example below):
public int multiplySomeNumbers(Job job, int num1, int num2) {
jobService.startJobStep(job, JobStep.Type.TEST_STEP1);
// Implementation here.
}
I have two thoughts on an ideal solution:
Use an aspect and annotate functions that can cause a change in the job step state. This makes it less coupled, but the aspect would still need to get the job from somewhere;
Store the Job object or id in a global-like scope (e.g. a session or context). I tried using #Scope("session") on my JobService with the intention of storing the Job instance there, but I kept getting java.lang.IllegalStateException: No thread-bound request found. I'm not even sure if this is the right use-case for such a solution.
My questions are:
Is it possible to store the Job or its id somewhere so I don't have to add the Job as a parameter to method?
Is there a way of doing this that I'm not aware of?
re: question 2, I'm going to go out on a limb and take the widest definition of that question possible.
You seem to be reimplementing Spring Batch. Batch has extensive support for defining and executing jobs, persisting job progress, and supporting resumption. It also has contexts for remembering state and moving state from one step to another, chunk-oriented processing, and a generally well thought out and extensive infrastructure, including a bunch of readers and writers for common workflows.
Feel free to ignore this answer, I just wanted to throw the suggestion out there in case it spares you a ton of work.
you can keep it in thread local , you can directly access the Object from thread local / Or you can create custom Spring scope for more info about custom scope http://springindepth.com/book/in-depth-ioc-scope.html . And you can define the Job in custom Scope and inject that into your beans.
EDIT : This will work only if your entire process runs in single thread and your Job steps are static you can follow the process you mentioned. In case if your Jobs are not static ( mean calling the external services / order of external services may be changed based on input) i would implement Chain responsibility and command pattern ( commands as actual process) and Chain as your Job Steps. then you can track / stop / change the steps based on configuration.