Drop / ignore current transaction and use upcoming transaction if available - java

I have 3 methods defined the following way. methodX and methodY are defined in different classes. methodY and methodZAsync are defined in same class.
#Transactional(propagation = Propagation.REQUIRED)
public void methodX(){
.....
methodY();
methodZAsync(); //
}
#Transactional(propagation = Propagation.REQUIRED)
public void methodY(){
.....
someDatabaseOperations();
.....
}
#Async
public void methodZAsync(){
.....
pollingBasedOnDataOperationsOfMethodY();
.....
}
The problem here is, methodZAsync requires methodY()'s DB operations to be committed before it can start it's work. And this fails because methodZAsync runs in a different thread.
One option is to make methodY's transaction to use #Transactional(propagation = Propagation.REQUIRES_NEW). But since methodY is used in multiple places with a different use cases, I'm not allowed to do that.
I've checked this question but TransactionSynchronization is an interface and Im not sure what to do with rest of the un-implemented methods.
So I thought, instead of making changes in methodY(), If I can somehow make methodX() to tell methodY() to use a new transaction(naive way: close current running transaction), It'll fix the things for me.
Is this doable from methodX() without having to modify methodY()?

You can create a new wrapper method around methodY() that creates a new transaction. Then you can call this new method from methodX() without impacting any other use cases.
#Transactional(propagation = Propagation.REQUIRED)
public void methodX(){
.....
methodYInNewTxn();
methodZAsync(); //
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodYInNewTxn() {
methodY();
}
#Transactional(propagation = Propagation.REQUIRED)
public void methodY(){
.....
someDatabaseOperations();
.....
}

Refer spring boot, there is also
#Transactional(propagation = Propagation.REQUIRES_NEW)
method are there go through. Hope you will get your proper answer

Related

Spring Transactions insert,select in one method

please could me somebody explain how working transaction in method, generally what is going. mapper is iBatis.
#Transactional
public void insert(SameDto same) {
sameDao.insert(same);
webserice.call(same);
}
SameDao.class ->
#Transactional
public void insert(SameDto same) {
mapper.updateToInvalidate(same);
SameDto other = new SameDto(...);
BigDecimal primaryKey= mapper.insert(other);
SameDto equalsSameDto = mapper.getByPrimaryKey(primaryKey);
continue ....
}
I don't understand because Object "equalsSameDto" is null. I could thinks that transaction is not committed yet. But it some legal way how to use it.
Thx for advise.

TransactionAttributeType.REQUIRES_NEW in conjunction with #Observes(during = TransactionPhase.AFTER_SUCCESS)

I recently stumbled into the next piece of Java EE6 code:
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void example(#Observes(during = TransactionPhase.AFTER_SUCCESS) final ExampleEvent exampleEvent) {
Is REQUIRES_NEW really needed?
I mean example method will always be called only after any previous transaction has ended successfully ( because of TransactionPhase.AFTER_SUCCESS).
Or am I missing something?
Your are only observing ExampleEvent, so your example()-Method will not be called by itself (based on the #TransactionAttribute) unless your do something like this:
#Inject
private Event<ExampleEvent> exampleEvent;
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void example(#Observes(during = TransactionPhase.AFTER_SUCCESS) final ExampleEvent exampleEvent) {
ExampleEvent event = new ExampleEvent();
exampleEvent.fire(event);
}
It makes sense to keep the #TransactionAttribute because the previous transaction just finished (AFTER_SUCCESS), so there is no current transaction therefore a new one is needed to be created. It can be possible that this would be done automatically (depending on the AS implementation) even without the annotation, but the result should be the same.

#Transactional multiple update; commit every single query

I have 2 methods in my service
public void updateAll() {
long[] ids = new long[] {1,2,3,4,5,6,7,8,9,10};
for (long id : ids) {
updateId(id);
}
}
public updateId(long id) {
repository.update(id);
}
Let's assume that after the 5th update I have an exception, I would like that the first 4 operations would be committed anyway.
I'm using the #Transactional annotation but if I put the annotation in both methods it doesn't work.
Do I need other parameter?? It might be propagation??
Could you show me how to set this methods?
Thank you!!
Just have:
#Transactional(propagation = Propagation.REQUIRES_NEW)
public updateId(long id) {
}
But, the important bit, call that method from another class.
i.e. move your loop out of this class.
The transactional annotations only kick-in when that public method is called from the outside. Within the same class, calling one transactional method from another will still only use the transaction of the first method.
You need a separate #Transactional on updateId with REQUIRES_NEW.

What makes a transaction a transaction in Spring/Java (specific scenario)?

I've read through the definitions of transactions on this site, as well as some other external sources. But I'm having a hard time grappling with the specific notion of a transaction when writing code.
I have a BuyService class that is transactional. The BuyService class is declared transactional, and its only method is buyWidget(String widgetId). This method calls on the ExampleService class, which has a deleteWidgit(String widgetId) method. It also calls on the InvoiceService class, which uses the writeInvoice(String widgitId) method. Here is the code:
BuyService class:
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
#Transactional
public class BuyService implements BuyServiceInterface
{
private ExampleServiceInterface exampleService;
private InvoiceServiceInterface invoiceService;
public void setExampleService(ExampleServiceInterface exampleService)
{
this.exampleService = exampleService;
}
public void setInvoiceService(InvoiceServiceInterface invoiceService)
{
this.invoiceService = invoiceService;
}
#Override
#Transactional(propagation=Propagation.REQUIRED)
public void buyWidget(Widget widgetId)
{
try
{
Widget purchasedWidget = this.exampleService.getWidgetById(String widgetId);
this.exampleService.deleteWidget(purchasedWidget);
this.invoiceService.writeInvoice(purchasedWidget);
}
catch (WidgetNotFoundException e)
{
System.out.println("Widget with widgetId " + widgetId + " not found.");
}
}
}
I am pretty sure that the buyWidget method constitutes a transaction. It requires the deletion of a widget in a database (in exampleService) and the insertion of data in the purchase database (in invoiceService). But I am confused about terminology after this point. Are the methods deleteWidget and writeInvoice themselves transactions as well?
ExampleService class:
public class ExampleService implements ExampleServiceInterface
{
private ExampleServiceDaoInterface dao;
public void setExampleServiceDao(ExampleServiceDaoInterface dao)
{
this.dao = dao;
}
#Override
public void deleteWidget(Widget oldWidget)
throws WidgetNotFoundException
{
this.dao.delete(oldWidget);
}
#Override
public Widget getWidgetById(String widgetId)
{
return this.dao.getById(widgetId);
}
}
InvoiceService class:
public class InvoiceService implements InvoiceServiceInterface
{
private InvoiceServiceDaoInterface InvoiceServiceDao;
public void setInvoiceServiceDao(InvoiceServiceDaoInterface InvoiceServiceDao)
{
this.InvoiceServiceDao = InvoiceServiceDao;
}
#Override
public void writeInvoice(Widget purchasedWidget)
{
Date purchaseDate = new Date(new java.util.Date().getTime());
String isbn = purchasedWidget.getIsbn();
Purchases newPurchase = new Purchases(purchaseDate, isbn);
this.InvoiceServiceDao.savePurchase(newPurchase);
}
}
Are the two methods called on by buyWidget transactions as well? That is, even if neither of those methods are declared as transactions.
What are some potential pitfalls of not declaring the two child methods as transactions? (Since they apparently appear to be a part of one already).
Are the methods deleteWidget and writeInvoice themselves transactions as well?
They will take part in the buyWidget transaction, but they are not by themselves transactional
Are the two methods called on by buyWidget transactions as well?
The transaction is started before entering the buyWidget method and stopped or rolled back before the method is completed. The two methods will take part in the buyWidget transaction, but are not themselves transactions.
Are the two methods called on by buyWidget transactions as well?
None of the methods are transactions. The annotation #Transactional(propagation=Propagation.REQUIRED) means " Support a current transaction, create a new one if none exists." This transaction will include anything called from the buyWidget() method. Basically, when the method is entered a transaction is started, and when it's exited that transaction will be committed (if everything works out) or rolled back (if there is an Exception thrown, a problem on the DB side, or the transaction is rolled back via Java code).
That is, even if neither of those methods are declared as transactions.
As long as those methods operate on a DB that is aware of JTA, it will work with the existing transaction.
What are some potential pitfalls of not declaring the two child methods as transactions? (Since they apparently appear to be a part of one already).
If those methods are called directly, they will not be part of a transaction. This could result in an inconsistent state of the DB if those methods result in multiple SQL statements (it doesn't look like that, but cannot be definitely ruled out just by looking at the code).

EJB TransactionRequiredException in GlassFish 2.1

In my Stateful bean, I have the following lines:
#Stateful(mappedName = "ejb/RegistrationBean")
#StatefulTimeout(unit = TimeUnit.MINUTES, value = 30)
#TransactionManagement(value=TransactionManagementType.CONTAINER)
public class RegistrationStateful implements RegistrationStatefulRemote {
#PersistenceContext
EntityManager em;
private List<Event> reservedSessions = new ArrayList<Event>();
private boolean madePayment = false;
...
#TransactionAttribute(TransactionAttributeType.REQUIRED)
private void cancelReservation() {
if (reservedSessions.size() != 0) {
Teacher theTeacher;
for (Event session : reservedSessions) {
if ((theTeacher = session.teacher) == null) theTeacher = bestTeacher.teacher;
theTeacher = em.merge(theTeacher) //The exception is thrown here
//Make changes to theTeacher
em.flush(); //The exception is also thrown here
}
//Clear the reservedSessions list
reservedSessions.clear();
}
}
#Remove
public void endRegistration() {}
#PreDestroy
public void destroy() {
//Cancel outstanding reservations if payment has not been made
if (!madePayment) cancelReservation();
}
}
The line em.merge(someEntity) throws the TransactionRequiredException. Could someone please tell me why it happens? I thought with TransactionAttribute.REQUIRED, a transaction will AUTOMATICALLY be created if there isn't an active one. I tried to use em.joinTransaction() but it throws the same Exception. I'm a beginner at this transaction thing. I'd be very grateful if someone could explain this to me.
UPDATE: I'd like to add a bit more information
The Stateful bean actually also has the following function:
#TransactionAttribute(TransactionAttributeType.REQUIRED)
private void reserveSession(List<Event> sessions) throws ReservationException {
//Reserve the sessions
Teacher theTeacher;
for (Event session : sessions) {
if ((theTeacher = session.teacher) == null) theTeacher = bestTeacher.teacher;
theTeacher = em.merge(theTeacher);
//Make changes to theTeacher
em.flush();
}
}
The flow is as following: the user tells me his free time and I reserve some seats for him. After that, I show him his reserved seats and he can choose to make payment or cancel the reservations.
The reserved() function worked perfectly as expected but the cancelReservation() did not.
UPDATE 2: I have fixed the problem last night by commenting out the lines "#TransactionAttribute(TransactionAttributeType.REQUIRED)", "em.merge(theTeacher)" and "em.flush()" in the "cancelReservation()" function. The result is perfect. Would it be safe if I cut off those lines? I was afraid I would get "detached entity" exception when I used "em.merge()" in the first place.
The only thing that springs to mind (if you'll excuse the pun) is that if you're calling cancelReservation() from another method inside the bean, then i'm not sure the transaction annotation will be observed. The annotation ultimately works by summoning an interceptor, and i believe interceptors are only applied to calls between different classes (this is something i should really check).
So, if you have a non-transactional method on the bean which calls a transactional method, then a transaction won't be started when the transactional method is called.
I could be completely wrong about this. I'll go and have a bit of a read of the spec and get back to you.
EDIT: I had a read of the spec, and it reminded me what a disaster zone the J2EE specs are. Horrific. However, the section on transactions does seem to imply that the transaction attributes only apply to calls made to an EJB's business interface. I believe calls from one method to another inside a bean are not considered to go through the business interface, even when the method being called is part of that interface. Therefore, you wouldn't expect them to attract transactions.
Something you could try would be to route them through the interface; there is no nice way of doing this, but you should be able to inject a business-interface self-reference like this:
public class RegistrationStateful implements RegistrationStatefulRemote {
#EJB
private RegistrationStatefulRemote self;
You can then change your #PreDestroy method to look like this:
#PreDestroy
public void destroy() {
self.cancelReservation();
}
And i believe that should count as a normal business interface call, with transactions and so on.
I have never actually tried this, so this could be complete rubbish. If you try it, let me know how it works out!

Categories