Как стать автором
Обновить

Oracle ADF. Business Components

Время на прочтение 10 мин
Количество просмотров 10K
Доброе время суток хабравчане. Моя предыдущая статья была небольшим интро в 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.
Теги:
Хабы:
0
Комментарии 4
Комментарии Комментарии 4

Публикации

Истории

Работа

Java разработчик
359 вакансий

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн