Фасад доступа к данным для Java (DAF4J)
Это единый интерфейс, предназначенный для доступа к данным, который обеспечивает удобный и типобезопасный доступ к данным с использованием DSL.
daf4j-api.jar
и требуемую реализацию DataAccess
.Использование
Способ 1: Загрузите архив с сайта git@osc
и добавьте его в свой проект, а также добавьте зависимости согласно инструкции (сам daf4j-api.jar
не требует зависимостей, но реализация DataAccess
требует, подробности приведены ниже).
Способ 2: Используйте Maven для добавления зависимостей.
<dependencies>
<dependency>
<groupId>net.cassite</groupId>
<artifactId>daf4j-api</artifactId>
<version>0.1.1-RELEASE</version>
</dependency>
</dependencies>
Предварительный просмотр использования
Язык | Стиль |
---|---|
Java | Stream |
query
.from(user)
.stream()
.filter(user.age.$gt(18).and(user.name.$ne("cass")))
.filter(role.name.$eq("admin"))
.sorted(user.id.desc())
.limit(10)
.list();
// Запрос пользователей старше 18 лет, с именем, отличным от «cass», и ролью администратора, отсортированных по идентификатору пользователя в порядке убывания и ограниченных первыми 10 записями.
| Scala | SQL |
query from user where user.age > 18 & user.name <> "cass" & role.name === "admin" param (orderBy(user.id.desc) top 10) list
// Функционал аналогичен предыдущему примеру.
Причины разработки этой библиотеки классов
DAF4J предназначен для упрощения доступа к данным. В трёхслойной архитектуре структура слоя данных во многом зависит от бизнес-логики. Часто бизнес-слой выполняет только некоторые проверки данных и преобразование типов данных, оставляя прямой вызов слою данных, а слой данных иногда может писать некоторые «специализированные» методы для бизнес-слоя.
Я считаю, что зависимость слоя данных от бизнес-уровня обусловлена тем, что условия запросов не могут быть полностью отделены от бизнес-логики.
Даже такие инструменты, как Spring Data JPA или JPA Criteria, имеют некоторые недостатки в обработке условий (например, меньше(поле, значение)
вместо поле.меньше(значение)
), и код немного более громоздкий.
Мне кажется, если JPA поддерживает внедрение значений через методы, то почему бы не изменить подход и не заменить поля на типы, поддерживающие различные условия и выражения, оставив сигнатуры методов неизменными? Это позволило бы использовать существующие фреймворки и облегчило бы вызов данных.
После некоторого проектирования началась разработка.
Здесь представлен предварительный обзор, подробное использование можно найти на странице Wiki. Кроме того, на странице Wiki есть учебное пособие, демонстрирующее использование DAF4J в контексте простой модели RBAC.
Для использования необходимо импортировать daf4j-api.jar
(или использовать Maven).
В этом примере используется реализация JPQLDataAccess
из daf4j-ds-jpa.jar
. Мы создадим класс сущности, внеся небольшие изменения в способ реализации DAF4J:
Я создал генераторы геттеров и сеттеров для IDEA, чтобы упростить генерацию JavaBean для DAF4J. Генераторы кода доступны в корне репозитория GetterGeneratorForIDEA.txt
и SetterGeneratorForIDEA.txt
.
@Entity
class User{
public final XInt id = new XInt(this);
public final XString name = new XString(this);
public final XInt age = new XInt(this);
@Id
public Integer getId(){ return id.get(); }
public String getName(){ return name.get(); }
public Integer getAge(){ return age.get(); }
public void setId(Integer id){ DataUtils.set(this.id, id); }
public void setName(String name){ DataUtils.set(this.name, name); }
public void setAge(Integer age){ DataUtils.set(this.age, age); }
}
Чтобы использовать функции JPA, вам также потребуется получить EntityManager, а затем использовать его для инициализации JPQLDataAccess и Query.
Query query=new Query(new JPQLDataAccess(entityManager));
Затем вы можете начать использовать:
User user=new User();
// Список всех пользователей старше 18 лет
query
.from(user)
.where(user.age.$gt(18))
.list();
// Вывести список идентификаторов и имён пользователей в виде списка Map<String,Object>, отсортированного по возрасту в порядке убывания
query
.from(user)
.where(user.age.$gt(18))
.param(
new QueryParameter().orderBy(user.age.desc())
)
.select(
new Focus()
.focus(user.id) // Псевдоним для типа краткого имени. Поле
.focus(user.name, "user_name")
);
// Вычислить среднее значение возраста всех пользователей
query
.from(user)
.where(null)
.avg(user.age);
// Увеличить возраст всех пользователей на 1
query
.from(user)
.where(null)
.update(
user.age.as(user.age.add(1))
);
// Удалить всех пользователей
query
.from(user)
.where(null)
.remove();
Приведённый выше запрос является стилем SQL, но иногда он не работает с реляционными базами данных или не нравится синтаксис, подобный SQL, поэтому здесь также предлагается стиль потока запросов.
// Список всех пользователей старше 18 лет
query
.from(user).stream()
.filter(user.age.$gt(18))
.list();
// Вывести список идентификаторов и имён пользователей в виде списка Map<String,Object>, отсортированного по возрасту в порядке убывания
query
.from(user).stream()
.filter(user.age.$gt(18))
.sorted(user.age.desc())
.map(
new Focus()
.focus(user.id)
.focus(user.name, "user_name")
)
.list();
// Вычислить средний возраст всех пользователей
query
.from(user).stream()
.mapToInt(user.age)
.average();
Все эти запросы имеют проверку типов.
На данный момент разработана реализация, ориентированная на стандарт JPA, а также абстрактная реализация для ресурсов.
Реализация для JPA тестируется в среде тестирования с использованием Hibernate-EntityManager-4.0 и проходит тесты в HSQLDB и MySQL. Если вы загрузили исходный код, вы можете запустить тестовые примеры напрямую, HSQLDB будет работать в памяти.
Зависит от:
daf4j-api:0.1.1-RELEASE
slf4j-api:1.7.12
hibernate-jpa-2.0-api:1.0.1.Final
Конечно, для вывода журнала требуется дополнительная система журналов, такая как slf4j-simple/log4j+slf4j-log4j и т. д., и JPA также требует добавления зависимостей. Вы можете добавить зависимости самостоятельно или использовать Maven.
<dependencies>
<dependency>
<groupId>net.cassite</groupId>
<artifactId>daf4j-ds-jpa</artifactId>
<version>0.1.1-RELEASE</version>
``` ###JPQLDataAccess/JPQLDataSource
JPA — это часть JavaEE, стандарт для сохранения данных. Поэтому он был реализован. JPA предоставляет два способа запросов: JPQL и Criteria, но способ Criteria имеет большие ограничения, поэтому используется способ JPQL.
В реляционных базах данных можно выполнять join, group by, having, однако в daf4j-api этих элементов нет. Это связано с моими предыдущими размышлениями об упрощении SQL [1].
1. В SQL, если появляется агрегатная функция, то поля, которые появляются в этой функции, должны быть указаны в предложении group by.
2. Если агрегатная функция является частью условия, она должна быть помещена в предложение having.
То есть в большинстве случаев group by и having могут быть выведены.
При использовании JPA отношения соединения уже определены в сущности. Даже при написании JPQL вы будете писать только так:
```sql
select u from User u join u.roles r ...
Это может быть полностью автоматически сгенерировано.
Однако здесь есть некоторые правила использования.
Например, у пользователя (User) и роли (Role) есть связь «один ко многим». Если вам нужно запросить пользователей на основе ролей, например, всех пользователей с ролью «admin», вы должны написать так:
User user=new User();
Role role=new Role();
user.getRoles().add(role);
query
.from(user)
.stream()
.filter(role.name.$eq("admin"))
.list();
Другими словами, вам необходимо указать поле соединения.
Если это отношение «один к одному», вам нужно заполнить его через setter.
####ResourceDataAccess
В версии 0.1.1 API предоставляет новую реализацию DataAccess. Она абстрагирует все «ресурсы».
Используйте объект Resource для представления ресурса, а интерфейс Source — для представления источника.
API поставляется с реализацией локального файла LocalFileSource. Используйте его следующим образом:
Query query=new Query(new ResourceDataAccess(new LocalFileSource));
Resource r = new Resource();
query
.from(r)
.where(r.location.$like("/Volumes/PROJECTS/openSource/DAF4J"))
.list();
Предложение where поддерживает $eq, $ne, $lt, $gt, $le, $ge, like. Среди них обязательно должно быть $eq или like для location. (like означает поиск из подресурсов.)
Предложение select поддерживает только count.
Предложение update поддерживает set для location и buffer. Для buffer требуется InputStream. Для location требуется строка или concat.
Когда транзакция включена, InputStream будет закрыт при фиксации или откате.
##Scala специальное использование Здесь не буду вдаваться в подробности, код говорит сам за себя:
import entity._
query from entity where age > 18 & id <> 1 param (orderBy(name.desc) top 1) list()
query from entity where age > 18 & id <> 1 param (orderBy(name.desc) top 1) select id ~(name, "a")
query from entity stream() filter age > 18 & id <> 1 sorted name.desc limit 5 list()
query from entity stream() filter age > 18 & id <> 1 sorted name.desc limit 5 map id ~(name, "a") list()
##DataSource DataSource используется для разделения логики в DataAccess, чтобы уменьшить вероятность ошибок кодирования и облегчить модульное тестирование.
Модуль DataSource состоит из следующих основных классов/интерфейсов:
Подробности см. в документации и реализации JPQLDataSource.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )