In application I'm working on we need to start transaction before user enters 'edit page' (to lock DB records currently edited) and end it when he clicks the button or left the page.
For this purpose I use @Stateful
EJB bean which manages the transaction and CDI @ViewScoped
bean which is used on the 'edit page'.
Of course user can take many actions on edit page, which are supposed to be invoked within the same transaction. Here's sample code:
@Stateful
@LocalBean
@TransactionManagement(TransactionManagementType.BEAN)
public class TransactionService {
@Resource
private UserTransaction utx;
@PostConstruct
private void postConstruct() {
System.out.println(this + " postConstruct");
}
@PreDestroy
private void preDestroy() {
System.out.println(this + " preDestroy");
utx.rollback();
}
public void start() {
utx.begin();
//lock db records with select ... for update
}
@Remove
public void commit() {
utx.commit();
}
@Remove
public void rollback() {
utx.rollback();
}
}
@Named
@ViewScoped
public class TestBean implements Serializable {
@Inject
private TransactionService ts;
@Inject
private SqlService sql; //stateless session bean
@PostConstruct
private void postConstruct() {
System.out.println(this + " postConstruct");
ts.start();
}
@PreDestroy
private void preDestroy() {
System.out.println(this + " preDestroy");
ts.rollback();
}
public void methodA() {
//do some db operation
sql.insert();
}
public void methodB() {
//do some db operation
sql.update();
}
public String save() {
ts.commit();
return "otherView";
}
public String cancel() {
ts.rollback();
return "otherView";
}
}
In theory it looks fine, but we have some questions:
- Can we be sure that all operations are invoked in the same transaction?
- What if user closes tab/browser/goes to another URL by typing in address bar or http session times out. How can we detect it and rollback the transaction? At first we tried with
@PreDestroy
but it looks like it's never called!
We use Java EE 6 technologies: JSF, EJB. Deploy on Glassfish 3.1.2 Web Profile. We use MyBatis instead JPA. Thanks for help
IMHO it's very bad approach. you lock your table and wait hoping that user does something. it may take minutes. all other users will have to wait for this one - in most applications it's unacceptable. you shouldn't span transaction between between multiple http requests.
however, if you insist, rollback must not depends on some external events. simply set a transaction timeout