В приложении, над которым я работаю, нам нужно начать транзакцию до того, как пользователь войдет на «страницу редактирования» (чтобы заблокировать редактируемые записи БД) и завершить ее, когда он нажмет кнопку или покинет страницу.
Для этой цели я использую компонент EJB @Stateful
, который управляет транзакцией, и компонент CDI @ViewScoped
, который используется на «странице редактирования».
Конечно, пользователь может выполнять множество действий на странице редактирования, которые должны выполняться в рамках одной и той же транзакции. Вот пример кода:
@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";
}
}
Теоретически все выглядит хорошо, но у нас есть несколько вопросов:
- Можем ли мы быть уверены, что все операции вызываются в одной и той же транзакции?
- Что, если пользователь закроет вкладку/браузер/перейдет к другому URL-адресу, набрав адресную строку или время сеанса HTTP истекло. Как мы можем обнаружить это и откатить транзакцию? Сначала мы пытались использовать
@PreDestroy
, но, похоже, он так и не был вызван!
Мы используем технологии Java EE 6: JSF, EJB. Развертывание в веб-профиле Glassfish 3.1.2. Мы используем MyBatis вместо JPA. Спасибо за помощь