У концепции ADF есть интересное свойство: обязанность по отслеживанию незакоммиченных данных полностью ложится на программиста. Сложно сказать, как с этим планировалось справляться силами недоучившихся студентов, на которых рассчитано декларативное программирование в ADF. Да и не об этом речь. Речь пойдет о том, как уйти со страницы, которая создала новую запись в VO, связанном c EO, не зафиксировать эту запись, и потом благополучно вернуться обратно.
Что сразу приходит в голову:
Рабочее решение заключается в создании бина уровня приложения, унаследованного от PagePhaseListener, где мы будем анализировать страницу на ранней фазе жизненного цикла и отслеживать грязные данные. Создаем бин и прописываем его в adf-settings.xml:
Сам класс:
Код 9 - id фазы инициализаци Restore View. Интересно проследить жизненный цикл страницы-источника (откуда переходим) после изменения данных:
В целевой странице (куда переходим) грязные данные присутствуют во всем цикле:
Что сразу приходит в голову:
@PostConstruct public void initBean() { AppModuleImpl mod = (AppModuleImpl)ADFUtils.getBindingApplicationModule(); DBTransaction conn = mod.getDBTransaction(); if (conn.isDirty()) conn.rollback(); ...Так нехорошо, потому что initBean() будет вызываться при каждой активности на странице и транзакция будет теряться тогда, когда это не нужно. К тому же транзакция будет откатываться на фазе prepareRender, что тоже идеологически неверно. То есть контекст уже проинициализирован, загружена модель данных, проверены валидации, всё готово к прорисовке страницы - и тут мы сообщаем системе, что данные нужно изменить. Не знаю, что именно будет происходить глубоко внутри приложения, но думаю, что ничего хорошего.
Рабочее решение заключается в создании бина уровня приложения, унаследованного от PagePhaseListener, где мы будем анализировать страницу на ранней фазе жизненного цикла и отслеживать грязные данные. Создаем бин и прописываем его в adf-settings.xml:
Сам класс:
import java.util.ArrayList; import java.util.Map; import javax.faces.context.FacesContext; import javax.servlet.http.HttpServletRequest; import oracle.adf.controller.v2.lifecycle.PagePhaseEvent; import oracle.adf.controller.v2.lifecycle.PagePhaseListener; import oracle.adf.share.ADFContext; import oracle.jbo.server.DBTransaction; import *.ADFUtils; import *.AppModuleImpl; public class PageListenerBean implements PagePhaseListener{ // если conn.isDirty() и переходим на из url из списка, то rollback private static String[] deniedUrls = {"/myapp/faces/task-flow-definition/tree"}; @Override public void beforePhase(PagePhaseEvent pagePhaseEvent) { try{ if (pagePhaseEvent.getPhaseId() == 9){ AppModuleImpl mod = (AppModuleImpl)ADFUtils.getBindingApplicationModule(); DBTransaction conn = mod.getDBTransaction(); if (isPageChanged()){ if (changedToDisallowed()){ if (conn.isDirty()){ conn.rollback(); System.out.println("= rolled back!"); } } } } }catch(Exception e){ ; } } @Override public void afterPhase(PagePhaseEvent pagePhaseEvent) { ; } private void setApplicationScopeParameter(String val){ ADFContext adfCtx = ADFContext.getCurrent(); MapПри каждом срабатывании проверяется соответствие текущего URL переменной #{applicationScope.currentURL}. Если страница отличается, находится в "стоп-листе" и данные грязные, тогда откатываем транзакцию. Стоп-лист введен во избежание непредвиденных потерь транзакции при единой фиксации нескольких страниц (такое может быть в приложении).applicationScope = adfCtx.getApplicationScope(); applicationScope.put("currentURL", val); } private String getApplicationScopeParameter(){ ADFContext adfCtx = ADFContext.getCurrent(); MapapplicationScope = adfCtx.getApplicationScope(); return (String)applicationScope.get("currentURL"); } private boolean isPageChanged(){ try{ if (!getCurrentUrl().equals(getApplicationScopeParameter())){ setApplicationScopeParameter(getCurrentUrl()); return true; } }catch(NullPointerException e){ System.out.println("--! PageListenerBean: can't get current url"); } return false; } private boolean changedToDisallowed(){ for(String url : this.deniedUrls) if (url.equals(getCurrentUrl())) return true; return false; } private String getCurrentUrl(){ FacesContext ctx = FacesContext.getCurrentInstance(); HttpServletRequest servletRequest = (HttpServletRequest) ctx.getExternalContext().getRequest(); return servletRequest.getRequestURI(); } }
Код 9 - id фазы инициализаци Restore View. Интересно проследить жизненный цикл страницы-источника (откуда переходим) после изменения данных:
В целевой странице (куда переходим) грязные данные присутствуют во всем цикле: