Статьи

Декларативная ошибка режима SQL

Как мы знаем, определение объекта представления поддерживает три режима SQL. Есть нормальный , декларативный и экспертный режимы. В этом посте я собираюсь сосредоточиться на декларативном режиме SQL. Этот интеллектуальный режим позволяет платформе автоматически создавать запросы SQL во время выполнения в соответствии с атрибутами, запрашиваемыми уровнем представления. Кроме того, в зависимости от запрашиваемых атрибутов фреймворки строят не только предложение Select но весь запрос. Таким образом, предложение From содержит только объекты, которые необходимы для предоставления запрашиваемых атрибутов, и в результате выполнения VO только эти объекты создаются и сохраняются в кэше объектов. Декларативный режим SQL широко рекомендуется, и он стал режимом по умолчанию в JDeveloper 12c. Тем не менее, мы должны быть осторожны с этим подходом, поскольку мы можем столкнуться с небольшой ловушкой, которую я собираюсь выделить в этом посте.

В моем примере есть простой VO, основанный на сущностях сотрудников и отделов:

Снимок экрана 2015-01-31 в 15.44.51
VO настроен на использование декларативного режима SQL.

В проекте ViewController есть пара страниц, содержащих таблицу с сотрудниками (Main) и форму с данными о сотрудниках (Detail):

Снимок экрана 2015-01-31 в 15.49.39
Обе страницы используют один и тот же экземпляр VOmployees VO, поэтому, когда мы перейдем из таблицы в форму сведений, мы увидим сведения о выбранном сотруднике. Это стандартный и очень распространенный подход.

Итак, давайте немного поиграем с этой средой и рассмотрим три простых варианта использования.

Вариант использования № 1. Таблица содержит все атрибуты, которые имеет форма детализации:

Снимок экрана 2015-01-31 в 16.06.18.

Снимок экрана 2015-01-31 в 16.06.34

В этом случае все довольно ясно. VO извлекает все обязательные атрибуты с помощью следующего запроса:

1
2
3
4
5
6
7
8
SELECT Employees.EMPLOYEE_ID,
                Employees.FIRST_NAME, 
                Employees.LAST_NAME,
                Employees.DEPARTMENT_ID,
                Departments.DEPARTMENT_NAME,
                Departments.DEPARTMENT_ID AS DEPARTMENT_ID1
FROM EMPLOYEES Employees, DEPARTMENTS Departments
WHERE Employees.DEPARTMENT_ID = Departments.DEPARTMENT_ID

Итак, когда мы переходим из таблицы в детальную форму, все атрибуты текущей строки VO уже заполнены, и ничего лишнего не происходит.

Случай использования № 2. Таблица не содержит DepartmentName, но содержит атрибут DepartmentId1, который является первичным ключом сущности Departments.

Снимок экрана 2015-01-31 в 16:42.08

Снимок экрана 2015-01-31 в 16:42.25 вечера

В этом случае SQL-запрос выглядит так:

1
2
3
4
5
6
7
SELECT Employees.EMPLOYEE_ID,
                Employees.FIRST_NAME, 
                Employees.LAST_NAME,
                Employees.DEPARTMENT_ID,
                Departments.DEPARTMENT_ID AS DEPARTMENT_ID1
FROM EMPLOYEES Employees, DEPARTMENTS Departments
WHERE Employees.DEPARTMENT_ID = Departments.DEPARTMENT_ID

Таким образом, DepartmentName не был выбран. Тем не менее, объекты отдела были созданы, и они хранятся в кэше объектов. Но эти объекты имеют только один заполненный атрибут DepartmentId. Остальные атрибуты в этих объектах пусты. Когда мы переходим из таблицы в детальную форму, структура сталкивается с проблемой, поскольку атрибут DepartmentName текущей строки VO не заполняется, и это заставляет экземпляр объекта извлекать себя из базы данных. И экземпляр объекта запускает следующий запрос, чтобы заполнить все атрибуты, которые он имеет:

1
2
3
4
5
SELECT DEPARTMENT_ID,
               DEPARTMENT_NAME,
               MANAGER_ID,
               LOCATION_ID
FROM DEPARTMENTS Departments WHERE DEPARTMENT_ID=:ID

Случай использования № 3. Таблица содержит только атрибуты сотрудников.

Снимок экрана 2015-01-31 в 17.08.09

Снимок экрана 2015-01-31 в 17.08.39

В этом случае ВО выполняет следующий запрос:

1
2
3
4
5
SELECT Employees.EMPLOYEE_ID,
               Employees.FIRST_NAME, 
               Employees.LAST_NAME,
               Employees.DEPARTMENT_ID
 FROM EMPLOYEES Employee

Таким образом, в предложении from нет отделов, и в результате выполнения VO не создаются никакие сущности отделов. Когда мы переходим к подробному виду, каркас просто не может заставить экземпляр объекта извлечь DepartmentName из базы данных. Потому что этот экземпляр не существует.

Таким образом, форма сведений содержит пустой атрибут DepartmentName. Кроме того, в 12c мы получим исключение в таком случае:

Снимок экрана 2015-01-31 в 17.06.07

На самом деле это та ловушка, о которой я упоминал в самом начале этого поста. Чтобы устранить проблему в этом случае использования, мы можем добавить атрибут DepartmentId1 в определение привязки дерева таблицы:

01
02
03
04
05
06
07
08
09
10
11
12
13
<tree IterBinding="VEmployeesIterator" id="VEmployees">
  <nodeDefinition
       DefName="com.adfpractice.blog.declarativemodeexample.model.VEmployees"
       Name="VEmployees0">
    <AttrNames>
      <Item Value="EmployeeId"/>
      <Item Value="FirstName"/>
      <Item Value="LastName"/>
      <Item Value="DepartmentId"/>
      <Item Value="DepartmentId1"/>
    </AttrNames>
  </nodeDefinition>
</tree>

Это даст каркас для выполнения запроса, как в сценарии использования № 2.

Второй вариант — установить «Выбрано в запросе» для атрибута DepartmentId1 в определении VO. Это заставит платформу всегда включать атрибут в предложении Select и всегда включать объект Departments в предложении From .

Это оно!

Ссылка: Декларативный SQL Mode Pitfall от нашего партнера по JCG Евгения Федоренко в блоге ADF Practice .