Oracle ADF. Business Components

в 0:02, , рубрики: ADF, java, JDeveloper, oracle, метки: , , ,

Доброе время суток читатели. Моя предыдущая статья была небольшим интро в 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», в результате получим следующий набор файлов.
Oracle ADF. Business Components

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

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

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

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

Employees

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

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

EmployeesView

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

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

EmployeesAppModule

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

EmpManagerFkAssoc

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

EmpManagerFkLink

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

CRUD

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

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

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

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

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

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

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

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

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

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

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

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

CRUD v2

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

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

Код 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>

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

Код 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, убедившись, что в таблицу попала новая запись.
Oracle ADF. Business Components

CRUD v3

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

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

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

Осталось написать новый метод для добавления строки в БД в 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();
    }

Проверяем работу:
Oracle ADF. Business Components

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

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

Автор: matim

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js