Рубрики
Без рубрики

Введение в Ормлит

Краткое и практическое введение в ORMLite на Java.

Автор оригинала: baeldung.

1. Обзор

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

Его основными особенностями являются:

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

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

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

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


    com.j256.ormlite
    ormlite-jdbc
    5.0

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

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

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

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

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

Давайте начнем с определения Библиотеки сущности с именем полем и идентификатором библиотеки полем, которое также является первичным ключом:

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

    @DatabaseField(canBeNull = false)
    private String name;

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

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

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

Свойство, которое будет служить первичным ключом для таблицы, может быть помечено атрибутами id , generatedId или generatedSequence . В нашем примере мы выбираем атрибут generatedId=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. Источник подключения

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

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

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

// work with the connectionSource

connectionSource.close();

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

5. Класс TableUtils

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

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

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

TableUtils.createTableIfNotExists(connectionSource, Library.class);

6. Объекты DAO

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

Dao 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);

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

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

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

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

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

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

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

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

public interface LibraryDao extends Dao {
    public List findByName(String name) throws SQLException;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

7.1. Поля Посторонних Объектов

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

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

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

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

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

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

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

    // standard getters, setters
}

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

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

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

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

Давайте добавим новый объект Library с полем Address и вызовем library Dao , чтобы сохранить оба:

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

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

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

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

7.2. Зарубежные коллекции

Для many стороны отношения мы можем использовать типы ForeignCollection или Icollection с аннотацией @ForeignCollectionField .

Давайте создадим новую сущность Book , подобную приведенным выше, а затем добавим отношение “один ко многим” в класс Library :

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

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

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

    // standard getters, setters
}

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

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

libraryDao.refresh(library);

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

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

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

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

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

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

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

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

8. Конструктор запросов

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

Этот класс содержит методы, соответствующие общим операциям, используемым в SQL-запросе, таким как: select Columns (), where (), groupBy (), | having (), countOf (), distinct (), OrderBy (), join().

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

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

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

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

Полный исходный код примера можно найти на GitHub .