1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/redraiment-jactiverecord

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

jActiveRecord — это библиотека объектно-реляционного отображения (ORM), написанная на Java по мотивам ActiveRecord из Ruby on Rails.

Особенности jActiveRecord:

  • нулевая конфигурация: нет файлов конфигурации XML и аннотаций;
  • нулевые зависимости: не требует сторонних библиотек, работает с Java 6 и выше;
  • нулевой SQL: не нужно писать SQL-запросы, в том числе сложные;
  • динамичность: не нужны статические классы для каждой таблицы;
  • упрощённость: по сравнению с ActiveRecord, jActiveRecord упрощает некоторые вещи;
  • поддержка нескольких баз данных;
  • многопоточная безопасность;
  • поддерживает транзакции.

Начало работы

В качестве примера создадим систему зомби-твитов, используя jActiveRecord.

Установка

jActiveRecord поддерживается Maven и доступен в центральном репозитории. Чтобы использовать его, добавьте следующую зависимость в файл pom.xml:

<dependency>
  <groupId>me.zzp</groupId>
  <artifactId>jactiverecord</artifactId>
  <version>2.3</version>
</dependency>

Подключение к базе данных

Вход в jActiveRecord — класс DB. Он создаёт объект базы данных через метод open. Метод open принимает параметры, совместимые с java.sql.DriverManager#getConnection.

Пример создания объекта базы данных SQLite в памяти:

DB sqlite3 = DB.open("jdbc:sqlite::memory:");

Метод open по умолчанию создаёт только одно соединение с базой данных, поэтому он подходит для баз данных в памяти. В реальных проектах рекомендуется использовать пул соединений, например C3P0.

Создание таблиц

Создаём таблицу пользователей (зомби) с двумя полями: name (имя) и graveyard (место захоронения).

Table Zombie = sqlite3.createTable("zombies", "name text", "graveyard text");

Аргументы метода createTable описывают поля таблицы. Они должны быть в формате «имя поля + тип».

createTable автоматически добавляет поле id как первичный ключ. Если ваша база данных не поддерживается jActiveRecord, вы можете реализовать интерфейс Dialect и добавить его в META-INF/services/me.zzp.ar.d.Dialect. jActiveRecord использует ServiceLoader для автоматической загрузки реализаций Dialect.

Также jActiveRecord добавляет два поля created_at и updated_at типа timestamp для хранения времени создания и обновления записи. Таким образом, таблица содержит пять полей: id, name, graveyard, created_at и updated_at.

Добавление записей

Получаем объект таблицы и добавляем записи с помощью метода create. Можно использовать именованные параметры для ясности.

Table Zombie = sqlite3.active("zombies");
Zombie.create("name:", "Ash", "graveyard:", "Glen Haven Memorial Cemetery");
Zombie.create("name", "Bob", "graveyard", "Chapel Hill Cemetery");
Zombie.create("graveyard", "My Fathers Basement", "name", "Jim");

Поскольку в Java нет именованных параметров, имена столбцов могут заканчиваться двоеточием. Это позволяет использовать «name:» вместо «name». Порядок аргументов не имеет значения, поэтому третья запись с именем Jim также будет успешно добавлена.

Запрос данных

jActiveRecord предоставляет следующие методы запроса:

  • find(int id): возвращает запись по указанному id;
  • all(): возвращает все записи, соответствующие условиям;
  • paging(int page, int size): выполняет постраничный запрос на основе all(), начиная с указанной страницы;
  • first(): возвращает первую запись на основе all();
  • last(): возвращает последнюю запись на основе all();
  • where(String condition, Object... args): возвращает записи, удовлетворяющие условию, на основе all(). Условие совместимо с java.sql.PreparedStatement;
  • first(String condition, Object... args): возвращает первую запись, соответствующую условию, на основе where();
  • last(String condition, Object... args): возвращает последнюю запись, соответствующую условию, на основе where();
  • findBy(String key, Object value): возвращает все записи со значением указанного столбца, равным значению;
  • findA(String key, Object value): возвращает первую запись со значением указанного столбца, равным значению, на основе findBy().

Методы first, last и find возвращают одну запись, а остальные методы могут возвращать несколько записей.

Например, чтобы получить запись с id = 3, можно использовать любой из следующих методов:

Zombie.find(3);
Zombie.findA("name", "Jim");
Zombie.first("graveyard like ?", "My Father%");

Записи, возвращаемые базой данных, представлены объектами Record. Для доступа к данным используется метод get. Благодаря дженерикам, данные автоматически преобразуются в соответствующий тип:

Record jim = Zombie.find(3);
int id = jim.get("id");
String name = jim.get("name");
Timestamp createdAt = jim.get("created_at");

Кроме того, Record предоставляет интерфейсы для принудительного преобразования типов, такие как getInt и getStr.

jActiveRecord не использует Bean, так как они не универсальны и требуют создания отдельного класса для каждой таблицы. Кроме того, использование Bean не даёт никаких преимуществ, кроме проверки правильности имён геттеров и сеттеров во время компиляции.

Обновление данных

Чтобы обновить данные, сначала нужно найти нужную запись. Затем можно изменить значения и сохранить изменения.

Для обновления значений используется метод set, а для сохранения изменений — save или update. Также можно вызвать метод update, который выполняет обновление и сохранение за один шаг. Этот метод принимает именованные аргументы, как и create.

Record jim = Zombie.find(3);
jim.set("graveyard", "Benny Hills Memorial").save();
jim.update("graveyard:", "Benny Hills Memorial"); // Same with above

Эти строки кода обновляют место захоронения зомби с id = 3 до «Benny Hills Memorial».

Удаление данных

Для удаления записей используются методы delete и destroy. Первый удаляет запись по её идентификатору, а второй — по объекту Record. Также есть метод purge для удаления всех записей, соответствующих текущим условиям.

Zombie.find(1).destroy();
Zombie.delete(Zombie.find(1)); // Same with above

Обе строки удаляют зомби с id = 1.

Связывание данных

Наконец, переходим к связыванию данных. ORM-библиотеки не только отображают записи в объекты, но и связывают таблицы между собой.

jActiveRecord поддерживает четыре вида связей, аналогичных ActiveRecord: belongsTo, hasOne, hasMany и hasAndBelongsToMany. Каждый метод получает строку-параметр name в качестве имени отношения и возвращает объект ассоциации Association, который имеет три метода:

  • by: определяет имя внешнего ключа, по умолчанию используется name + "_id" в качестве имени внешнего ключа.
  • in: определяет имя связанной таблицы, по умолчанию совпадает с name.
  • through: связывает комбинацию, параметр — это имя другой уже определённой связи. То есть реализуется доступ через несколько таблиц (join нескольких таблиц).

Один ко многим

Возвращаясь к проблеме системы мёртвых аккаунтов в социальных сетях, в предыдущем разделе была создана только одна таблица пользователей, теперь создаётся другая таблица tweets для хранения информации о сообщениях:

Table Tweet = sqlite3.createTable("tweets", "zombie_id int", "content text");

Здесь zombie_id выступает в роли внешнего ключа и связан с id таблицы zombies. Таким образом, у каждого мёртвого аккаунта может быть много связанных сообщений, а каждое сообщение связано только с одним мёртвым аккаунтом. В jActiveRecord используются hasMany и belongsTo для описания таких отношений «один ко многим». hasMany используется на стороне «один», belongsTo — на стороне «многие» (то есть на стороне внешнего ключа).

Zombie.hasMany("tweets").by("zombie_id");
Tweet.belongsTo("zombie").by("zombie_id").in("zombies");

Затем можно получить связанные объекты через имена связей из Record. Например, чтобы получить все сообщения Джима:

Record jim = Zombie.find(3);
Table jimTweets = jim.get("tweets");
for (Record tweet : jimTweets.all()) {
  // ...
}

Или получить информацию о мёртвом аккаунте по сообщению:

Record zombie = Tweet.find(1).get("zombie");

Возможно, вы заметили, что hasMany возвращает несколько записей, поэтому возвращается тип Table; belongsTo всегда возвращает одну запись, поэтому возвращается тип Record. Кроме того, существует особый вид отношений «один ко многим»: hasOne, то есть сторона «многие» имеет и может иметь только одну запись. hasOne используется так же, как hasMany, но возвращает значение типа Record, а не Table.

Связывающая комбинация

Давайте добавим функцию комментариев к системе сообщений:

Table Comment = sqlite3.createTable("comments", "zombie_id int", "tweet_id", "content text");

Одно сообщение может получить много комментариев, а один мёртвый аккаунт может иметь много сообщений. Поэтому мёртвые аккаунты и полученные комментарии связаны комбинацией: мёртвый аккаунт hasMany сообщений hasMany комментариев. jActiveRecord предоставляет through для описания этой комбинации связей.

Zombie.hasMany("tweets").by("zombie_id"); // has defined above
Zombie.hasMany("receive_comments").by("tweet_id").through("tweets");
Zombie.hasMany("send_comments").by("zombie_id").in("comments");

Приведённое выше правило описывает, что Zombie сначала может найти Tweet, а затем, используя Tweet.tweet_id, может найти Comment. Третья строка кода описывает, как Zombie может напрямую получать отправленные комментарии через Comment.zombie_id.

Фактически, through можно использовать для связывания любых типов связей, например, hasAndBelongsToMany зависит от hasOne или belongsTo зависит от другого belongsTo...

Многие ко многим

В RoR существует два метода для многих ко многим связям: has_many through и has_and_belongs_to_many, которые частично перекрываются по функциям. jActiveRecord сохраняет только метод hasAndBelongsToMany для описания многих ко многим связей. Для многих ко многим связей требуется отдельная таблица сопоставления, которая записывает отношения сопоставления. То есть две стороны «многие» не содержат внешних ключей друг друга, а используют третью таблицу для одновременного сохранения их внешних ключей.

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

sqlite3.dropTable("tweets");
Tweet = sqlite3.createTable("tweets", "zombie_id int", "city_id int", "content text");
Table City = sqlite3.createTable("cities", "name text");

Таблица cities содержит информацию обо всех городах, а таблица tweets записывает связь между мёртвыми аккаунтами и городами. Чтобы найти города, где побывал мёртвый аккаунт, Zombie должен сначала подключиться к таблице tweets, а затем использовать её для доступа к городам.

Zombie.hasMany("tweets").by("zombie_id"); // has defined above
Zombie.hasAndBelongsToMany("travelled_cities").by("city_id").in("cities").through("tweets");

Как следует из названия, тип возвращаемого значения для многих ко многим связей всегда Table, а не Record.

Резюме по связям

  • Один к одному: для таблицы с внешним ключом используется belongsTo; для таблицы без внешнего ключа используется hasOne.
  • Один ко многим: для таблицы с внешним ключом используется belongsTo; для таблицы без внешнего ключа используется hasMany.
  • Многие ко многим: обе стороны «многие» используют hasAndBelongsToMany; таблица сопоставления использует belongsTo.

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

Комментарии ( 0 )

Вы можете оставить комментарий после Вход в систему

Введение

Реализация ActiveRecord на Java для RoR. Развернуть Свернуть
MIT
Отмена

Обновления

Пока нет обновлений

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/redraiment-jactiverecord.git
git@api.gitlife.ru:oschina-mirror/redraiment-jactiverecord.git
oschina-mirror
redraiment-jactiverecord
redraiment-jactiverecord
master