«1. Обзор

ORMLite — это облегченная библиотека ORM для приложений Java. Он предоставляет стандартные функции инструмента ORM для наиболее распространенных вариантов использования без дополнительной сложности и дополнительных затрат, связанных с другими платформами ORM.

Его основными функциями являются:

    определение классов сущностей с использованием аннотаций Java расширяемые классы DAO класс QueryBuilder для создания сложных запросов сгенерированные классы для создания и удаления таблиц базы данных поддержка транзакций поддержка отношений сущностей

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

2. Зависимости Maven

Чтобы начать использовать ORMLite, нам нужно добавить зависимость ormlite-jdbc в наш pom.xml:

<dependency>
    <groupId>com.j256.ormlite</groupId>
    <artifactId>ormlite-jdbc</artifactId>
    <version>5.0</version>
</dependency>

По умолчанию это также добавляет зависимость h2. В наших примерах мы будем использовать базу данных H2 в памяти, поэтому нам не нужен еще один драйвер JDBC.

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

3. Определение классов сущностей

Чтобы настроить классы моделей для сохраняемости с помощью ORMLite, мы можем использовать две основные аннотации:

    @DatabaseTable для класса сущностей @DatabaseField для свойств

Начнем путем определения объекта Library с полем имени и полем libraryId, которое также является первичным ключом:

@DatabaseTable(tableName = "libraries")
public class Library {	
 
    @DatabaseField(generatedId = true)
    private long libraryId;

    @DatabaseField(canBeNull = false)
    private String name;

    public Library() {
    }
    
    // standard getters, setters
}

Аннотация @DatabaseTable имеет необязательный атрибут tableName, который указывает имя таблицы, если мы не хотим полагаться на имя класса по умолчанию.

Для каждого поля, которое мы хотим сохранить как столбец в таблице базы данных, мы должны добавить аннотацию @DatabaseField.

Свойство, которое будет служить первичным ключом для таблицы, может быть помечено атрибутами id, generateId или generateSequence. В нашем примере мы выбираем атрибут generateId=true, чтобы первичный ключ автоматически увеличивался.

Также обратите внимание, что класс должен иметь конструктор без аргументов с видимостью по крайней мере в области пакета.

Несколько других знакомых атрибутов, которые мы можем использовать для настройки полей: columnName, dataType, defaultValue, canBeNull, unique.

3.1. Использование аннотаций JPA

В дополнение к аннотациям, специфичным для ORMLite, мы также можем использовать аннотации в стиле JPA для определения наших сущностей.

Эквивалентом объекта Library, который мы определили перед использованием стандартных аннотаций JPA, будет:

@Entity
public class LibraryJPA {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long libraryId;

    @Column
    private String name;
    
    // standard getters, setters
}

Хотя ORMLite распознает эти аннотации, нам все равно нужно добавить зависимость javax.persistence-api для их использования.

Полный список поддерживаемых аннотаций JPA: @Entity, @Id, @Column, @GeneratedValue, @OneToOne, @ManyToOne, @JoinColumn, @Version.

4. ConnectionSource

Для работы с определенными объектами нам необходимо настроить ConnectionSource.

Для этого мы можем использовать класс JdbcConnectionSource, который создает одно соединение, или JdbcPooledConnectionSource, который представляет собой простой объединенный источник соединения:

JdbcPooledConnectionSource connectionSource 
  = new JdbcPooledConnectionSource("jdbc:h2:mem:myDb");

// work with the connectionSource

connectionSource.close();

Другие внешние источники данных с более высокой производительностью также могут использоваться путем их упаковки в объекте DataSourceConnectionSource.

5. Класс TableUtils

На основе ConnectionSource мы можем использовать статические методы класса TableUtils для выполнения операций со схемой базы данных:

    createTable() — для создания таблицы на основе класса сущности определение или объект DatabaseTableConfig createTableIfNotExists() — аналогичен предыдущему методу, за исключением того, что таблица создается только в том случае, если она не существует; это работает только с базами данных, которые его поддерживают dropTable() — для удаления таблицы clearTable() — для удаления данных из таблицы

Давайте посмотрим, как можно использовать TableUtils для создания таблицы для нашего класса Library:

TableUtils.createTableIfNotExists(connectionSource, Library.class);

6. Объекты DAO

ORMLite содержит класс DaoManager, который может создавать для нас объекты DAO с функциональностью CRUD:

Dao<Library, Long> libraryDao 
  = DaoManager.createDao(connectionSource, Library.class);

DaoManager не регенерирует класс для каждого последующего вызова createDao( ), но вместо этого повторно использует его для повышения производительности.

Далее мы можем выполнять операции CRUD над объектами библиотеки:

Library library = new Library();
library.setName("My Library");
libraryDao.create(library);
        
Library result = libraryDao.queryForId(1L);
        
library.setName("My Other Library");
libraryDao.update(library);
        
libraryDao.delete(library);

«

libraryDao.forEach(lib -> {
    System.out.println(lib.getName());
});

«DAO также является итератором, который может перебирать все записи в цикле:

Однако ORMLite закроет базовый оператор SQL, только если цикл дойдет до конца. Исключение или оператор return могут вызвать утечку ресурсов в вашем коде.

try (CloseableWrappedIterable<Library> wrappedIterable 
  = libraryDao.getWrappedIterable()) {
    wrappedIterable.forEach(lib -> {
        System.out.println(lib.getName());
    });
 }

По этой причине документация ORMLite рекомендует использовать итератор напрямую:

Таким образом, мы можем закрыть итератор с помощью блока try-with-resources или блока finally и избежать утечки ресурсов.

6.1. Пользовательский класс DAO

public interface LibraryDao extends Dao<Library, Long> {
    public List<Library> findByName(String name) throws SQLException;
}

Если мы хотим расширить поведение предоставленных объектов DAO, мы можем создать новый интерфейс, который расширяет тип Dao:

public class LibraryDaoImpl extends BaseDaoImpl<Library, Long> 
  implements LibraryDao {
    public LibraryDaoImpl(ConnectionSource connectionSource) throws SQLException {
        super(connectionSource, Library.class);
    }

    @Override
    public List<Library> findByName(String name) throws SQLException {
        return super.queryForEq("name", name);
    }
}

Затем давайте добавим класс, который реализует этот интерфейс и расширяет Класс BaseDaoImpl:

Обратите внимание, что нам нужен конструктор такой формы.

@DatabaseTable(tableName = "libraries", daoClass = LibraryDaoImpl.class)
public class Library { 
    // ...
}

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

LibraryDao customLibraryDao 
  = DaoManager.createDao(connectionSource, Library.class);

Это позволяет нам использовать DaoManager для создания экземпляра нашего пользовательского класса:

Library library = new Library();
library.setName("My Library");

customLibraryDao.create(library);
assertEquals(
  1, customLibraryDao.findByName("My Library").size());

~ ~~ Затем мы можем использовать все методы из стандартного класса DAO, а также наш пользовательский метод:

7. Определение отношений сущностей

ORMLite использует концепцию «чужих» объектов или коллекций для определения отношения между сущностями для постоянства.

Давайте посмотрим, как мы можем определить каждый тип поля.

7.1. Поля внешних объектов

Мы можем создать однонаправленную связь «один к одному» между двумя классами сущностей, используя атрибут external=true в поле, аннотированном @DatabaseField. Поле должно иметь тип, который также сохраняется в базе данных.

@DatabaseTable(tableName="addresses")
public class Address {
    @DatabaseField(generatedId = true)
    private long addressId;

    @DatabaseField(canBeNull = false)
    private String addressLine;
    
    // standard getters, setters 
}

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

@DatabaseTable(tableName = "libraries")
public class Library {      
    //...

    @DatabaseField(foreign=true, foreignAutoCreate = true, 
      foreignAutoRefresh = true)
    private Address address;

    // standard getters, setters
}

Затем мы можем добавить поле типа Address в наш класс Library, который помечен как внешний:

Обратите внимание, что мы Мы также добавили в аннотацию @DatabaseField еще два атрибута: ForeignAutoCreate и ForeignAutoRefresh, оба из которых имеют значение true.

Атрибут foreignAutoCreate=true означает, что когда мы сохраняем объект Library с полем адреса, внешний объект также будет сохранен, при условии, что его id не равен нулю и имеет атрибут generateId=true.

Если мы установим для ForeignAutoCreate значение false, которое является значением по умолчанию, то нам нужно будет явно сохранить внешний объект, прежде чем сохранять объект библиотеки, который на него ссылается.

Точно так же атрибут foreignAutoRefresh=true указывает, что при извлечении объекта Library будет также извлечен связанный с ним сторонний объект. В противном случае нам пришлось бы обновлять его вручную.

Library library = new Library();
library.setName("My Library");
library.setAddress(new Address("Main Street nr 20"));

Dao<Library, Long> libraryDao 
  = DaoManager.createDao(connectionSource, Library.class);
libraryDao.create(library);

Давайте добавим новый объект Library с полем Address и вызовем libraryDao для сохранения обоих:

Dao<Address, Long> addressDao 
  = DaoManager.createDao(connectionSource, Address.class);
assertEquals(1, 
  addressDao.queryForEq("addressLine", "Main Street nr 20")
  .size());

Затем мы можем вызвать addressDao, чтобы убедиться, что Address также был сохранен:

7.2. Внешние коллекции

Для многих сторон отношений мы можем использовать типы ForeignCollection\u003cT\u003e или Collection\u003cT\u003e с аннотацией @ForeignCollectionField.

@DatabaseTable(tableName = "libraries")
public class Library {  
    // ...
    
    @ForeignCollectionField(eager=false)
    private ForeignCollection<Book> books;
    
    // standard getters, setters
}

Давайте создадим новый объект Book, как показано выше, а затем добавим отношение «один ко многим» в класс Library:

@DatabaseTable
public class Book {
    // ...
    @DatabaseField(foreign = true, foreignAutoRefresh = true) 
    private Library library;

    // standard getters, setters
}

В дополнение к этому необходимо добавить поле типа Library в Класс Book:

Library library = new Library();
library.setName("My Library");
libraryDao.create(library);

libraryDao.refresh(library);

library.getBooks().add(new Book("1984"));

В ForeignCollection есть методы add() и remove(), которые работают с записями типа Book:

Здесь мы создали библиотечный объект, затем добавили новый Book в поле books, которое также сохраняет его в базе данных.

Обратите внимание: поскольку наша коллекция помечена как лениво загружаемая (eager=false), нам нужно вызвать метод refresh(), прежде чем мы сможем использовать поле книги.

Book book = new Book("It");
book.setLibrary(library);
bookDao.create(book);

Мы также можем создать связь, установив поле библиотеки в классе Book:

assertEquals(2, bookDao.queryForEq("library_id", library).size());

Чтобы убедиться, что оба объекта Book добавлены в библиотеку, мы можем использовать метод queryForEq() для поиска всех записей Book. с данным library_id:

Здесь library_id — это имя по умолчанию для столбца внешнего ключа, а первичный ключ выводится из объекта библиотеки.

8. QueryBuilder

Каждый DAO можно использовать для получения объекта QueryBuilder, который затем можно использовать для построения более мощных запросов.

«Этот класс содержит методы, соответствующие обычным операциям, используемым в SQL-запросе, например: selectColumns(), where(), groupBy(), Have(), countOf(), Different(), orderBy(), join().

List<Library> libraries = libraryDao.queryBuilder()
  .where()
  .in("libraryId", bookDao.queryBuilder()
    .selectColumns("library_id")
    .groupBy("library_id")
    .having("count(*) > 1"))
  .query();

Давайте рассмотрим пример того, как мы можем найти все записи библиотеки, которые связаны с более чем одной книгой:

9. Заключение

В этой статье мы увидели, как мы можем определить сущности, используя ORMLite, а также основные функции библиотеки, которые мы можем использовать для управления объектами и связанными с ними реляционными базами данных.