Dao

Связи
  • jandcode.core.dao (module)
  • jandcode.core.dbm (module)
  • jandcode.core.dao.Dao (class)
  • jandcode.core.dao.DaoService (class)
  • jandcode.core.dao.DaoInvoker (class)
  • jandcode.core.dao.DaoHolder (class)

Dao - это метод класса, который выполняется в определенном настроенном окружении.

Введение

Пример простейшего dao:

import jandcode.core.dao.*;

public class DaoSimple1 extends BaseDao {

    public String meth1(String param1) throws Exception {
        return "meth1 PARAM1=" + param1;
    }

    public String meth2(String param1) throws Exception {
        return "meth2 PARAM1=" + param1;
    }

    // это метод для внутреннего использования внутри dao
    protected void utilsMethod1() {
    }

}

Для создания dao необходимо создать класс, который унаследован от jandcode.core.dao.BaseDao и реализовать один или несколько публичных методов. Каждый такой метод - dao.

В качестве dao можно использовать и произвольные классы, не унаследованные от BaseDao. В этом случае только методы помеченные аннотацией @DaoMethod будут рассматриваться как dao-методы.

Методы dao могут иметь произвольные параметры и возвращать произвольные результаты. Это в случае, если dao выполняется локально в коде. Если dao предназначен для вызова через внешнего клиента (например из браузера через определенный api), то нужно помнить, что можно использовать только те типы параметров и результата, которые разрешены этим api.

В большинстве случаев безопасно использовать стандартные простые типы java (int, long, String), списки List, мапы Map, даты jandcode.commons.datetime.XDateTime и jandcode.commons.datetime.XDate, Store, StoreRecord.

Выполнение dao:

// берем сервис dao
DaoService daoSvc = getApp().bean(DaoService.class)
// берем исполнителя dao
DaoInvoker daoInv = daoSvc.getDaoInvoker("default")
// создаем экземпляр dao
DaoSimple1 dao = daoInv.createDao(DaoSimple1.class)
// выполняем dao метод
String result = dao.meth1("p1")

DaoService содержит все, что касается dao. DaoInvoker среда исполнения dao.

Метод createDao создает экземпляр proxy-класса для указанного класса dao. Вызовы dao-методов этого proxy-класса транслируются в среду исполнения конкретного DaoInvoker, которая может производить всякие манипуляции и инициализации (логгирование, установка соединения с базой данных, управление транзакциями и т.д.) в процессе исполнения dao, одним из этапов которого будет вызов оригинального метода dao.

Для внутренних нужд создавайте методы protected или private.

Dao-классы не могут использоваться для разделения кода или какого-либо утилитного кода. Для этого предназначены утилитные классы.

Кешировать экземпляры классов нельзя. Создали, выполнили метод, забыли.

Dao для базы данных

Если dao желает иметь доступ к базе данных, то он реализовывается и исполняется в контексте конкретной модели базы данных.

Такой dao необходимо унаследовать от класса jandcode.core.dbm.dao.BaseModelDao и выполнять через DaoInvoker, которой принадлежит модели.

Пример dao для базы данных:

import jandcode.core.dbm.dao.*
import jandcode.core.store.*

class DaoModel1 extends BaseModelDao {

    public Store list() throws Exception {
        return getMdb().loadQuery("select * from tab1 order by id")
    }

    public StoreRecord record(long id) throws Exception {
        return getMdb().loadQueryRecord("select * from tab1 where id=:id", [id: id])
    }

}

В методах dao доступен экземпляр jandcode.core.dbm.mdb.Mdb для выполнения запросов к базе данных и различные утилиты для упрощения типичных задач. Соединенение с базой данных и транзакции поддерживается средой исполнения dao, об этом не нужно беспокоиться в процессе написания dao-методов.

Выполнение dao для модели:

// берем сервис dao для модели
ModelDaoService daoSvc = getModel().bean(ModelDaoService.class)
// берем исполнителя dao, он единственный
DaoInvoker daoInv = daoSvc.getDaoInvoker()
// создаем экземпляр dao
DaoModel1 dao = daoInv.createDao(DaoModel1.class)
// выполняем dao методы
Store lst = dao.list()
StoreRecord rect = dao.record(10)

DaoInvoker

DaoInvoker - это исполнитель dao, который выполняет dao в определенной среде.

Регистрация invoker:

<root>
    <dao>
        <invoker name="invoker1">
        </invoker>
    </dao>
</root>

Исполнители отличаются фильтрами, которые можно настроить конкретному исполнителю. Фильтры влияют на процесс исполнения dao. Настройка фильтров тут.

Каждая модель имеет своего исполнителя с настроенными фильтрами для исполнения в контексте базы данных модели.

Получение исполнителей:

DaoService daoSvc = getApp().bean(DaoService.class)

// глобально зарегистрированный invoker
DaoInvoker invokerGlobal = daoSvc.getDaoInvoker("invoker1")

// invoker модели с именем model1
DaoInvoker invokerModel1 = daoSvc.getDaoInvoker("model:model1")
// что соответствует:
invokerModel1 = getApp().bean(ModelService.class)
        .getModel("model1").bean(ModelDaoService.class).getDaoInvoker()

DaoHolder

DaoHolder - это хранилище, которое сопоставляет произвольное имя конкретному dao. Представлено интерфейсом jandcode.core.dao.DaoHolder, доступно через сервис DaoService.

Простейший пример настройки:

<root>
    <dao>
        <holder name="holder1" invoker="default">
            <item name="dao1/meth1"
                  class="pak1.pak2.DaoSimple1"
                  method="meth1"
                  invoker="default"/>
        </holder>
    </dao>
</root>

В этом примере мы объявляем хранилище dao с именем holder1. Устанавливаем ему исполнителя (invoker) по умолчанию с именем default. В хранилище добавляем имя dao1/meth1, которое привязано к методу meth1 класса pak1.pak2.DaoSimple1. Для исполнения этого dao будет использоваться invoker с именем default, который, кстати, может отличаться от invoker по умолчанию, указанного в атрибуте invoker самого хранилища.

Исполнение dao-метода по имени:

DaoService daoSvc = getApp().bean(DaoService.class)

// получаем DaoHolder по имени
DaoHolder daoHolder = daoSvc.getDaoHolder("holder1")
// исполняем метод dao по имени
String res = (String) daoHolder.invokeDao("dao1/meth1", "param1")

Метод invokeDao первым аргументом принимает имя зарегистрированного метода dao. Остальные аргументы - аргументы зарегистрированного метода. Возвращает результат выполнения dao-метода.

Примеры регистрации dao-методов

Допустим у нас имеются следуюшие dao-классы:

  • pak1.pak2.DictSimple1
  • pak1.pak2.pak3.DictSimple1_Dao

Каждый dao-класс имеет методы meth1 и meth2.

Ниже описаны варианты регистрации dao в хранилище для этих классов.

Метод

<item name="dao1/meth1"
      class="pak1.pak2.DaoSimple1"
      method="meth1"
      invoker="default"/>

Регистрация конкретного метода конкретного класса с явным указанием invoker. Так будут зарегистрированы dao с именами:

  • dao1/meth1

Методы класса

<item name="i" prefix="dao1"
      class="pak1.pak2.DaoSimple1"
      method="*"
      invoker="default"/>

Регистрация указанных методов конкретного класса с явным указанием invoker. Так будут зарегистрированы dao с именами:

  • dao1/meth1
  • dao1/meth2

Класс

<item name="i" prefix="dao1"
      class="pak1.pak2.DaoSimple1"/>

Регистрация всех dao-методов класса. Имя класса используется как дополнительный префикс для имени dao.
Из имени класса удаляется суффикс _dao, Dao или _Dao. Первый символ имени класса делается строчным. Так будут зарегистрированы dao с именами:

  • dao1/dictSimple1/meth1
  • dao1/dictSimple1/meth2

Пакет

<item name="dao1"
      package="pak1.pak2"
      recursive="true"/>

Регистрация всех dao-методов всех классов в указанном пакете. Если класс уже был ранее использован, например был зарегистрирован конкретный метод класса или весь класс, то такие классы пропускаются. Имя item становится общим префиксом для имени dao. Для каждого dao-метода делается префикс, состоящий из имени пакета, вложенного в указанный и имени класса. Из имени класса удаляется суффикс Dao или _Dao. Первый символ имени класса делается строчным. Пакеты будет сканироватся рекурсивно (recursive="true" значение по умолчанию). Так будут зарегистрированы dao с именами:

  • dao1/dictSimple1/meth1
  • dao1/dictSimple1/meth2
  • dao1/pak3/dictSimple1/meth1
  • dao1/pak3/dictSimple1/meth2

Если атрибут recursive будет равен false, то будет сканироватся только указанный пакет, без вложенных. В этом случае будут зарегистрированы такие dao:

  • dao1/dictSimple1/meth1
  • dao1/dictSimple1/meth2

Пакет плоский

<item name="dao1"
      package="pak1.pak2"
      flat="true"/>

Так же как и пакет, только префикс вложенного пакета удаляется.

  • dao1/dictSimple1/meth1
  • dao1/dictSimple1/meth2
  • dao1/dictSimple1/meth1
  • dao1/dictSimple1/meth2

Пакет с пропуском использованных

<item name="dao1"
      class="pak1.pak2.DaoSimple1"
      skipUsed="true"/>

При сканировании пакетов можно пропускать уже использованные классы. Для этого укажите атрибут skipUsed="true".

Префикс

<item name="dao1"
      class="pak1.pak2.DaoSimple1"
      prefix="pfx"/>

Можно указать префикс в атрибуте prefix, тогда он будет использоваться как префикс для имени. Так будут зарегистрированы dao с именами:

  • pfx/meth1
  • pfx/meth2

Явно указанный префикс перекрывает любой автоматически сформированный.

Префикс можно указывать и для пакетов:

<item name="dao1"
      package="pak1.pak2"
      flat="true"
      prefix="pfx1/pfx2"/>

Так будут зарегистрированы dao с именами:

  • pfx1/pfx2/dictSimple1/meth1
  • pfx1/pfx2/dictSimple1/meth2
  • pfx1/pfx2/dictSimple1/meth1
  • pfx1/pfx2/dictSimple1/meth2

Дочерние item

<item name="lev1">
    <item name="lev2">
        <item name="dao1"
              class="pak1.pak2.DaoSimple1"/>
    </item>
</item>

item могут быть вложенными. Тогда для dao в них будет использоваться в качестве префикса имя item верхнего уровня, включая его префикс.

Так будут зарегистрированы dao с именами:

  • lev1/lev2/dao1/dictSimple1/meth1
  • lev1/lev2/dao1/dictSimple1/meth2

Провайдер элементов

<item name="i" prefix="mydb"
      provider="model" model="mymodel"/>

Если указан атрибут provider, то управление передается соотвествующему провайдеру, который загружает набор dao с префиксом, который известен для этого элемента.

Провайдеру передается конфигурация элемента и он сам решает что и как загружать.

В примере загружаются все dao из персонального хранилища модели mymodel с префиксом mydb.

invoker

<item name="dao1"
      class="pak1.pak2.DaoSimple1"
      invoker="other1"/>

По умолчанию для всех dao в хранилище используется тот invoker, который описан в конфигурации хранилища. Его можно перекрыть для каждого item. Если item порождает несколько dao, то для каждого из них будет установлен указанный invoker.

Можно установить invoker для элементов хранилища по маске с использованием тега rule:

<rule name="rule1" mask="**/test*" invoker="t1"/>
<rule name="rule2" mask="**/*2*/**/*" invoker="x2"/>

Правила срабатывают в обратном порядке: чем позже добавлено правило, тем выше его приоритет.

Если полное имя dao, включая все префиксы, будет совпадать с маской правила, то такому dao будет назначен соотвествующий invoker.

include

<include name="dao-holder-name"/>

В хранилище можно включать другое хранилище, указав его имя в теге include.

Тег include можно использовать только на верхнем уровне.

DaoHolder модели

Модель имеет свое собственное хранилище dao.

// берем сервис dao для модели
ModelDaoService daoSvc = getModel().bean(ModelDaoService.class)
// получаем хранилище dao
DaoHolder modelDaoHolder = daoSvc.getDaoHolder()

Настройка:

<root>
    <dbm>
        <model name="mymodel">
            <dao>
                <holder name="default">
                    <item name="dao1"
                          class="pak1.pak2.DaoSimple1"/>
                    <item name="dao2"
                          package="pak1.pak2"
                          recursive="true"/>
                </holder>
            </dao>
        </model>
    </dbm>
</root>

Элементы из хранилища dao модели можно как есть поместить в другое хранилище, например так:

<root>
    <dao>
        <holder name="holder1" invoker="default">
            ...
            <item name="i" prefix="mydb"
                  provider="model" model="mymodel"/>
        </holder>
    </dao>
</root>

DaoHolderItemProvider

Провайдер элементов для хранилища dao. Для реализации провайдера необходимо реализовать интерфейс jandcode.core.dao.DaoHolderItemProvider и зарегистрировать провайдер:

<root>
    <dao>
        <item-provider name="my1"
                       class="pak1.pak2.My1DaoHolderItemProvider"/>
    </dao>
</root>

Теперь его можно использовать:

<root>
    <dao>
        <holder name="holder1">
            <item name="place1" provider="my1"/>
        </holder>
    </dao>
</root>

Абстрактные классы dao и метод impl()

Класс dao может быть абстрактным. В этом случае у него должен быть метод impl(), который должен вернуть экземпляр объекта, который реализует все абстрактные методы:

import jandcode.core.dao.*

/**
 * Это некий интерфейс для сбора и описания dao-методов
 */
interface IMyDao {
    String method1()
}

/**
 * Это реализатор интерфейса IMyDao
 */
class MyDao_impl implements IMyDao {
    String tag

    MyDao_impl(String tag) {
        this.tag = tag
    }

    String method1() {
        return "m1:${this.tag}"
    }
}

/**
 * Это dao. Этот класс будет использоватся как сборник и dao-методов,
 * но вызов его абстрактных методов будет делегироватся объекту,
 * который вернет метод impl().
 */
abstract class MyDao_dao extends BaseDao implements IMyDao {

    /**
     * Предоставление реализатора методов dao
     */
    public MyDao_impl impl() {
        return new MyDao_impl("t1")
    }

}

Конфигурация module.cfx

dao/invoker

Описание DaoInvoker.

Формат:

<root>
    <dao>
        <invoker name="INVOKER-NAME"
                 class="jandcode.core.dao.DefaultDaoInvoker">
            <filter name="FILTER-NAME"
                    weight="WEIGHT"
                    class="jandcode.core.dao.DaoFilter"/>
        </invoker>
    </dao>
</root>
  • name - имя исполнителя
  • class - класс, указывать не обязательно, только в особых случаях

filter

Описание фильтров для исполнителя.

  • name - имя фильтра
  • weight - вес (int). Чем меньше, тем раньше будет выполнятся
  • class - класс обработчика фильтрации. Должен реализовывать интерфейс jandcode.core.dao.DaoFilter