Oracle ADF. Business Components

от автора

Доброе время суток хабравчане. Моя предыдущая статья была небольшим интро в ADF. И так как по результатам опроса я вижу, что тема оказалась интересна, то я продолжаю писать об ADF.

Теперь после небольшого рассказа о данном фреймворке, можно «ринуться в бой» и рассмотреть фичи ADF более конкретно. Данная статья будет об ADF Business Components. О том как работать с ними декларативно и программно.

Общие понятия

ADF Business Components (далее BC) – это часть фреймворка для работы с БД, предоставляющая визуальную и декларативную разработку. Конфигурация BC хранится в XML файлах, но при желании можно сгенерировать Java классы и добавить и/или переопределить логику.
BC делятся на 5 основных частей:

  • Entity Objects (EO)
  • View Objects (VO)
  • Associations
  • View Links
  • Application Module

EO представляет собой таблицу из БД, и, соответственно, экземпляр EO – это строка из таблицы.
Данный компонент инкапсулирует в себе данные, правила валидации и логику персистентности.

EO associations определяют связи между двумя сущностями (таблицами)

VO – компонент, ответственный за чтение данных из data source, а также включает в себя операции по их обновлению.

View Links – определяют связи между VO (по аналогии с associations)

Application Module – это уровень сервиса, предоставляющий работу с бизнес компонентами (а именно с VO и View Links). Также в него можно добавить дополнительные методы и вкладывать другие Application Modules. В конечном счете Application Module используется, как Data Control.

IDE time

Для тестовых БД данных я буду использовать employees таблицу из oracle HR схемы.

После прохождения визарда «Business Components from tables», в результате получим следующий набор файлов.

Здесь Employees – EO, EmployeesView – VO, EmployeesAppModule – Application Module, EmpManagerFkAssoc – ассоциация между работниками и их менеджерами, EmpManagerFkLink – View link для аналогичной связи, но для VO, Business Components Diagram — диаграмма компонентов.

А на панели Data Controls создастся соответствующий data control.

Взглянем на диаграмму.

Можно увидеть, что EmployeesView создан на основе Employees EO. А в ApplicationModule попали два инстанса EmployeesView. Один отвечает за менеджера, а второй за его работников.

Краткий обзор сгенерированных файлов.

Employees

В сгенерированном EO можно посмотреть какие атрибуты в него входят, отредактировать, добавить свои или удалить.

Также можно управлять правилами валидации данных.

EmployeesView

Из атрибутов VO видно, что они берутся из Employees EO.

Можно посмотреть запрос VO, его переменные и критерии (named where clauses)

EmployeesAppModule

Application Module, как мы видели раньше, содержит два экземпляра EmployeesView

EmpManagerFkAssoc

Связь между сущностями.

EmpManagerFkLink

Одинаковые Source и Destination, говорят о том, что View Link построена на базе Associations.

CRUD


Да-да. CRUD будет реализован полностью декларативным путем, мною не будет написано ни одной строчки кода.

Посмотрим какой будет результат.
Вначале будет показана таблица с сотрудниками.

При нажатии на Create employee, произойдет переход на создание нового сотрудника.

При нажатии на Save новый сотрудник добавится в БД, и мы вернемся к таблице.

При нажатии на Update employee, произойдет переход на редактирование информации сотрудника.

После сохранения изменений, снова возврат к таблице.

Ну и наконец при нажатии на Delete employee, сотрудник испарится.

Ходом операций управляет bounded task flow. О том, как работать с task flows, будет отдельная статья. Пока просто посмотрим на диаграмму.

ViewEmployees, createEmployee и updateEmployee это view activities с JSFF, отвечающие за отображение таблицы и форм.
Для отображения сотрудников в таблице, нужно перенести EmployeeView из Data Control на страницу и выбрать в опциях создания – нужный вид таблицы (в моем случае – это read only)

Для вывода форм на страницах createEmployee и updateEmployee нужно сделать аналогичную операцию, но выбрать из категории Form (в моем случае – это ADF Form)

Все остальные activities – это операции над DataControl’ом. Перенесены эти операции также с Data Controls панели.

CreateInsert создает в итераторе новую строку и переводит курсор итератора на нее.
Delete удаляет строку в итераторе, на которой в данный момент находится курсор.
Чтобы действия CreateInsert и Delete вступили в силу, необходимо вызвать операцию Commit, а для того, чтобы откатить изменения – Rollback.
На этом реализация CRUD’а завершена.

CRUD v2

Теперь рассмотрим случай, когда мы не отказываемся полностью от декларативного пути, но операции хотим вызывать в каком-либо managed bean’e.

Для простоты рассмотрим только реализацию вставки, так как остальные выполняются аналогично.
Первым шагом необходимо создать managed bean.

Код bean’a рассмотрим в самом конце.

Я создам новую страницу и перенесу на нее таблицу, чтобы можно было увидеть новые данные, и форму для добавления нового сотрудника.
В input компонентах проставлю связи value на managed bean. И в конце добавлю кнопку, используя в качестве action listener’a метод managed bean’а.

В итоге разметка jspx выглядит следующим образом:

<af:form id="f1">         <af:table value="#{bindings.EmployeesView1.collectionModel}" var="row"                   rows="#{bindings.EmployeesView1.rangeSize}"                   emptyText="#{bindings.EmployeesView1.viewable ? 'No data to display.' : 'Access Denied.'}"                   fetchSize="#{bindings.EmployeesView1.rangeSize}" rowBandingInterval="0" id="t1">             <af:column sortProperty="#{bindings.EmployeesView1.hints.FirstName.name}" sortable="false"                        headerText="#{bindings.EmployeesView1.hints.FirstName.label}" id="c1">                 <af:outputText value="#{row.FirstName}" id="ot1"/>             </af:column>             <af:column sortProperty="#{bindings.EmployeesView1.hints.LastName.name}" sortable="false"                        headerText="#{bindings.EmployeesView1.hints.LastName.label}" id="c2">                 <af:outputText value="#{row.LastName}" id="ot2"/>              </af:column>              <af:column sortProperty="#{bindings.EmployeesView1.hints.Email.name}" sortable="false"                        headerText="#{bindings.EmployeesView1.hints.Email.label}" id="c3">                 <af:outputText value="#{row.Email}" id="ot3"/>             </af:column>             <af:column sortProperty="#{bindings.EmployeesView1.hints.PhoneNumber.name}" sortable="false"                        headerText="#{bindings.EmployeesView1.hints.PhoneNumber.label}" id="c4">                 <af:outputText value="#{row.PhoneNumber}" id="ot4"/>             </af:column>             <af:column sortProperty="#{bindings.EmployeesView1.hints.JobId.name}" sortable="false"                         headerText="#{bindings.EmployeesView1.hints.JobId.label}" id="c5">                 <af:outputText value="#{row.JobId}" id="ot5"/>             </af:column>         </af:table>         <af:panelFormLayout id="pfl1">             <af:inputText value="#{backingBeanScope.employeeBean.employeeId}" label="#{bindings.EmployeeId.hints.label}"                           required="#{bindings.EmployeeId.hints.mandatory}"                           columns="#{bindings.EmployeeId.hints.displayWidth}"                           maximumLength="#{bindings.EmployeeId.hints.precision}"                           shortDesc="#{bindings.EmployeeId.hints.tooltip}" id="it1">                 <f:validator binding="#{bindings.EmployeeId.validator}"/>                 <af:convertNumber groupingUsed="false" pattern="#{bindings.EmployeeId.format}"/>             </af:inputText>             <af:inputText value="#{backingBeanScope.employeeBean.firstName}" label="#{bindings.FirstName.hints.label}"                           required="#{bindings.FirstName.hints.mandatory}"                           columns="#{bindings.FirstName.hints.displayWidth}"                           maximumLength="#{bindings.FirstName.hints.precision}"                           shortDesc="#{bindings.FirstName.hints.tooltip}" id="it2">                 <f:validator binding="#{bindings.FirstName.validator}"/>             </af:inputText>             <af:inputText value="#{backingBeanScope.employeeBean.lastName}" label="#{bindings.LastName.hints.label}"                           required="#{bindings.LastName.hints.mandatory}"                           columns="#{bindings.LastName.hints.displayWidth}"                           maximumLength="#{bindings.LastName.hints.precision}"                           shortDesc="#{bindings.LastName.hints.tooltip}" id="it3">                  <f:validator binding="#{bindings.LastName.validator}"/>               </af:inputText>               <af:inputText value="#{backingBeanScope.employeeBean.email}" label="#{bindings.Email.hints.label}"                           required="#{bindings.Email.hints.mandatory}"                           columns="#{bindings.Email.hints.displayWidth}"                           maximumLength="#{bindings.Email.hints.precision}"                           shortDesc="#{bindings.Email.hints.tooltip}" id="it4">                 <f:validator binding="#{bindings.Email.validator}"/>               </af:inputText>               <af:inputText value="#{backingBeanScope.employeeBean.phoneNumber}" label="#{bindings.PhoneNumber.hints.label}"                           required="#{bindings.PhoneNumber.hints.mandatory}"                           columns="#{bindings.PhoneNumber.hints.displayWidth}"                           maximumLength="#{bindings.PhoneNumber.hints.precision}"                           shortDesc="#{bindings.PhoneNumber.hints.tooltip}" id="it5">                   <f:validator binding="#{bindings.PhoneNumber.validator}"/>               </af:inputText>               <af:inputDate value="#{backingBeanScope.employeeBean.hireDate}" label="#{bindings.HireDate.hints.label}"                             required="#{bindings.HireDate.hints.mandatory}"                             columns="#{bindings.HireDate.hints.displayWidth}"                             shortDesc="#{bindings.HireDate.hints.tooltip}" id="id1">                   <f:validator binding="#{bindings.HireDate.validator}"/>                   <af:convertDateTime pattern="#{bindings.HireDate.format}"/>               </af:inputDate>               <af:inputText value="#{backingBeanScope.employeeBean.jobId}" label="#{bindings.JobId.hints.label}"                             required="#{bindings.JobId.hints.mandatory}"                             columns="#{bindings.JobId.hints.displayWidth}"                             maximumLength="#{bindings.JobId.hints.precision}"                             shortDesc="#{bindings.JobId.hints.tooltip}" id="it6">                   <f:validator binding="#{bindings.JobId.validator}"/>                </af:inputText>                <af:commandButton text="Create Employee" id="cb1"                                 actionListener="#{backingBeanScope.employeeBean.createEmployee}"/>            </af:panelFormLayout> </af:form> 

А описание страницы будет выглядеть так.

Код managed bean’a (get’ры и set’ры опустим).
Поля:

    private int employeeId;     private String firstName;     private String lastName;     private String email;     private String phoneNumber;     private Timestamp hireDate;     private String jobId;     private BindingContainer bindings; 

Метод вставки, использующийся кнопкой на странице:

public void createEmployee(ActionEvent actionEvent) {         // Получаем binding контейнер         BindingContainer bindings = getBindings();         // Выполняем операцию создания новой строки         OperationBinding createOperation =              bindings.getOperationBinding("CreateInsert");         createOperation.execute();         // Забиваем атрибуты данными         AttributeBinding employeeId =              (AttributeBinding)bindings.getControlBinding("EmployeeId");         employeeId.setInputValue(this.employeeId);         AttributeBinding firstName =              (AttributeBinding)bindings.getControlBinding("FirstName");         firstName.setInputValue(this.firstName);         AttributeBinding lastName =              (AttributeBinding)bindings.getControlBinding("LastName");         lastName.setInputValue(this.lastName);         AttributeBinding phoneNumber =              (AttributeBinding)bindings.getControlBinding("PhoneNumber");         phoneNumber.setInputValue(this.phoneNumber);         AttributeBinding email =              (AttributeBinding)bindings.getControlBinding("Email");         email.setInputValue(this.email);         AttributeBinding hireDate =              (AttributeBinding)bindings.getControlBinding("HireDate");         hireDate.setInputValue(this.hireDate);         AttributeBinding jobId =              (AttributeBinding)bindings.getControlBinding("JobId");         jobId.setInputValue(this.jobId);         // Коммитим и тем самым сохраняем новую строку         OperationBinding commitOperation =              bindings.getOperationBinding("Commit");         commitOperation.execute();     } 

Проверим работу managed bean’a, убедившись, что в таблицу попала новая запись.

CRUD v3

И последний пример, в котором будет только использование кода.
Для начала нужно подготовить классы у Business Components.
Для этого откроем EmployeeView перейдем на вкладку Java и сгенерируем следующие классы.

EmployeesViewImpl необходим для работы с запросами, а EmployeesViewRowImpl будет представлять из себя строку с атрибутами.

Таким же путем сгенерируем класс для Application Module.

Осталось написать новый метод для добавления строки в БД в EmployeesAppModuleImpl, создать TO класс для передачи данных в этот метод, и вызвать данный метод в managed bean’e.

Метод в Application Module:

    public void createEmployee(EmployeeInfo employeeInfo) {         // Получаем ViewObject         EmployeesViewImpl employeeView = getEmployeesView1();         // Готовим новую строку.         EmployeesViewRowImpl employee = (EmployeesViewRowImpl)employeeView.createRow();         employee.setEmployeeId(employeeInfo.getEmployeeId());         employee.setEmail(employeeInfo.getEmail());         employee.setPhoneNumber(employeeInfo.getPhoneNumber());         employee.setFirstName(employeeInfo.getFirstName());         employee.setLastName(employeeInfo.getLastName());         employee.setHireDate(new Timestamp(employeeInfo.getHireDate()));         employee.setJobId(employeeInfo.getJobId());         // Производим операцию вставки.         employeeView.insertRow(employee);         // Коммитим         getDBTransaction().commit();     } 

Новый метод bean’a:

    public void createEmployee2(ActionEvent actionEvent) {         // Получаем application module         String applicationModuleClass = "com.matim.forhabr.model.EmplyeesAppModuleImpl";         String config = "EmplyeesAppModuleLocal";         EmplyeesAppModuleImpl appModule = (EmplyeesAppModuleImpl)             Configuration.createRootApplicationModule(applicationModuleClass, config);         // Забиваем данными TO         EmployeeInfo employeeInfo = new EmployeeInfo();         employeeInfo.setEmail(this.email);         employeeInfo.setEmployeeId(this.employeeId);         employeeInfo.setFirstName(this.firstName);         employeeInfo.setHireDate(this.hireDate);         employeeInfo.setJobId(this.jobId);         employeeInfo.setLastName(this.lastName);         employeeInfo.setPhoneNumber(this.phoneNumber);         // Вызываем метод по созданию нового сотрудника         appModule.createEmployee(employeeInfo);         // Освобождаем ресурсы         Configuration.releaseRootApplicationModule(appModule, false);         // Так как вставка идет на стороне AppModule,          // то только для целей, обновления таблицы         // выполним операцию Execute         BindingContainer bindings = getBindings();         OperationBinding executeIterator =              bindings.getOperationBinding("Execute");         executeIterator.execute();     } 

Проверяем работу:

На этом сегодня все. Много всего еще осталось за бортом по этой теме, поэтому оставлю ссылку на дополнительную информацию здесь.

P.S. Следующая статья будет о task flows.

ссылка на оригинал статьи http://habrahabr.ru/post/173059/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *