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.
Related
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
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.
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).
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!
If i have a #OneToMany relationship with #Cascade(CascadeType.SAVE_UPDATE) as follows
public class One {
private Integer id;
private List<Many> manyList = new ArrayList<Many>();
#Id
#GeneratedValue
public Integer getId() {
return this.id;
}
#OneToMany
#JoinColumn(name="ONE_ID", updateable=false, nullable=false)
#Cascade(CascadeType.SAVE_UPDATE)
public List<Many> getManyList() {
return this.manyList;
}
}
And Many class
public class Many {
private Integer id;
/**
* required no-arg constructor
*/
public Many() {}
public Many(Integer uniqueId) {
this.id = uniqueId
}
/**
* Without #GeneratedValue annotation
* Hibernate will use assigned Strategy
*/
#Id
public Integer getId() {
return this.id;
}
}
If i have The following scenario
One one = new One();
/**
* generateUniqueId method will Take care of assigning unique id for each Many instance
*/
one.getManyList().add(new Many(generateUniqueId()));
one.getManyList().add(new Many(generateUniqueId()));
one.getManyList().add(new Many(generateUniqueId()));
one.getManyList().add(new Many(generateUniqueId()));
And i call
sessionFactory.getCurrentSession().save(one);
Before going on
According to Transitive persistence Hibernate reference documentation, you can see
If a parent is passed to save(), update() or saveOrUpdate(), all children are passed to saveOrUpdate()
ok. Now Let's see what Java Persistence With Hibernate book Talks about saveOrUpdate method
Hibernate queries the MANY table for the given id, and if it is found, Hibernate updates the row. If it is not found, insertion of a new row is required and done.
Which can be translated according to
INSERT INTO ONE (ID) VALUES (?)
/**
* I have four Many instances added To One instance
* So four select-before-saving
*
* I DO NOT NEED select-before-saving
* Because i know i have a Fresh Transient instance
*/
SELECT * FROM MANY WHERE MANY.ID = ?
SELECT * FROM MANY WHERE MANY.ID = ?
SELECT * FROM MANY WHERE MANY.ID = ?
SELECT * FROM MANY WHERE MANY.ID = ?
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
Any workaround To avoid select-before-saving ??? Yes, You can either
Add a #Version column (Not applied)
Implement isTransient method provided by Hibernate interceptor (The option i have)
So as a way to avoid select-before-saving default behavior when using this kind of cascading, i have improved my code by assigning a Hibernate Interceptor to a Hibernate Session whose Transaction is managed by Spring.
Here goes my repository
Before (Without any Hibernate Interceptor): It works fine!
#Repository
public class SomeEntityRepository extends AbstractRepository<SomeEntity, Integer> {
#Autowired
private SessionFactory sessionFactory;
#Override
public void add(SomeEntity instance) {
sessionFactory.getCurrentSession().save(instance);
}
}
After (With Hibernate Inteceptor): something goes wrong (No SQL query is performed - Neither INSERT Nor SELECT-BEFORE-SAVING)
#Repository
public class SomeEntityRepository extends AbstractRepository<SomeEntity, Integer> {
#Autowired
private SessionFactory sessionFactory;
#Override
public void add(SomeEntity instance) {
sessionFactory.openSession(new EmptyInterceptor() {
/**
* To avoid select-before-saving
*/
#Override
public Boolean isTransient(Object o) {
return true;
}
}).save(instance);
}
}
My question is: Why Spring does not persist my Entity and its relationships when using Hibernate Interceptor and what should i do as workaround to work fine ???
Spring maintains an association between the current session and the current transaction (see SessionFactoryUtils.java.) Since there is already a session associated for the current DAO method call, you have to use this Session, or take the plunge of getting involved with the murky details of associating the new session with the previous transaction context. It's probably possible, but with considerable risk, and is definitely not recommended. In hibernate, if you have a session already open, then it should be used.
Having said that, you may be able to get spring to create a new session for you and associate it with the current transaction context. Use SessionFactoryUtils.getNewSession(SessionFactory, Interceptor). If you use this rather than hibernate's sessionFactory, then this should keep the association with the transaction.
Initially, you can code this up directly in the DAO. When it's tried and tested and hopefully found to be working, you can then take steps to move the spring code out of your DAO, such as using AOP to add around advice to the add() methods that create and clean up new session.
Another alternative is to use a global Interceptor. Even though it's global, you can give it locally controllable behaviour. The TransientInterceptor contains a threadLocal<Boolean>. This is the flag for the current thread to indicate if the interceptor should return true for isTransient. You set it to true at the start of the add() method and clear it at the end. E.g.
class TransientInterceptor extends EntityInterceptor {
ThreadLocal<Boolean> transientFlag = new ThreadLocal<Boolean)();
public boolean isTransient() {
return transientFlag.get()==Boolean.TRUE;
}
static public setTransient(boolean b) {
transientFlag.set(b);
}
}
And then in your DAO:
#Override
public void add(SomeEntity instance) {
try {
TransientInterceptor.set(true);
sessionFactory.getCurrentSession().save(instance);
}
finally {
TransientInterceptor.set(false);
}
}
You can then setup TransientInterceptor as a global interceptor on the SessionFactory (e.g. LocalSessionFactoryBean.) To make this less invasive, you could create an AOP around advice to apply this behaviour to all your DAO add methods, where appropriate.
In the 'after' method you are creating a new session and not flushing it, therefore no update is sent to the database. This has nothing to do with Spring, but is pure Hibernate behavior.
What you probably want is adding an (entity) interceptor to the sessionFactory, probably configured using Spring. You can then just keep your repository's add() method as before.
See http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/orm/hibernate3/LocalSessionFactoryBean.html#setEntityInterceptor%28org.hibernate.Interceptor%29