Диалект YDB для Hibernate
Введение
Это руководство использования Hibernate с YDB.
Hibernate - это фреймворк объектно-реляционного отображения (ORM) для Java, облегчающий процесс отображения объектно-ориентированных моделей.
Установка диалекта YDB
Примеры для различных систем сборки:
<!-- Set actual versions -->
<dependency>
<groupId>tech.ydb.jdbc</groupId>
<artifactId>ydb-jdbc-driver</artifactId>
<version>${ydb.jdbc.version}</version>
</dependency>
<dependency>
<groupId>tech.ydb.dialects</groupId>
<artifactId>hibernate-ydb-dialect</artifactId>
<version>${hibernate.ydb.dialect.version}</version>
</dependency>
dependencies {
// Set actual versions
implementation "tech.ydb.dialects:hibernate-ydb-dialect:$ydbDialectVersion"
implementation "tech.ydb.jdbc:ydb-jdbc-driver:$ydbJdbcVersion"
}
Если вы используете Hibernate версии 5, вам понадобится <artifactId>hibernate-ydb-dialect-v5</artifactId>
для Maven или implementation "tech.ydb.dialects:hibernate-ydb-dialect-v5:$version"
для Gradle вместо аналогичного пакета без -v5
суффикса.
Конфигурация диалекта
Сконфигурируйте Hibernate для использования диалекта YDB, обновив persistence.xml файл:
<property name="hibernate.dialect">tech.ydb.hibernate.dialect.YdbDialect</property>
Или, если вы используете программную настройку:
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
public static Configuration basedConfiguration() {
return new Configuration()
.setProperty(AvailableSettings.JAKARTA_JDBC_DRIVER, YdbDriver.class.getName())
.setProperty(AvailableSettings.DIALECT, YdbDialect.class.getName());
}
import org.hibernate.cfg.AvailableSettings
import org.hibernate.cfg.Configuration
fun basedConfiguration(): Configuration = Configuration().apply {
setProperty(AvailableSettings.JAKARTA_JDBC_DRIVER, YdbDriver::class.name)
setProperty(AvailableSettings.DIALECT, YdbDialect::class.name)
}
Использование
Используйте этот диалект так же, как и любой другой диалект Hibernate. Сопоставьте классы сущностей с таблицами базы данных и используйте фабрику сессий Hibernate для выполнения операций с базой данных.
Таблица сопоставления Java типов с YDB типами:
Java type | YDB type |
---|---|
bool , Boolean |
Bool |
String , enum с аннотацией @Enumerated(EnumType.STRING) |
Text (синоним Utf8 ) |
java.time.LocalDate |
Date |
java.math.BigDecimal , java.math.BigInteger |
Decimal(22,9) |
double , Double |
Double |
float , Float |
Float |
int , java.lang.Integer |
Int32 |
long , java.lang.Long |
Int64 |
short , java.lang.Short |
Int16 |
byte , java.lang.Byte , enum с аннотацией @Enumerated(EnumType.ORDINAL) |
Int8 |
[]byte |
Bytes (синоним String ) |
java.time.LocalDateTime (timezone будет установлена в UTC ) |
Datetime |
java.time.Instant (timezone будет установлена в UTC ) |
Timestamp |
Диалект YDB поддерживает генерацию схемы базы данных на основе объектов Hibernate.
Например, для класса Group
:
@Getter
@Setter
@Entity
@Table(name = "Groups", indexes = @Index(name = "group_name_index", columnList = "GroupName"))
public class Group {
@Id
@Column(name = "GroupId")
private int id;
@Column(name = "GroupName")
private String name;
@OneToMany(mappedBy = "group")
private List<Student> students;
}
@Entity
@Table(name = "Groups", indexes = [Index(name = "group_name_index", columnList = "GroupName")])
data class Group(
@Id
@Column(name = "GroupId")
val id: Int,
@Column(name = "GroupName")
val name: String,
@OneToMany(mappedBy = "group")
val students: List<Student>
)
Будет сгенерирована следующая таблица Groups
и вторичный индекс group_name_index
к колонке GroupName
:
CREATE TABLE Groups (
GroupId Int32 NOT NULL,
GroupName Text,
PRIMARY KEY (GroupId)
);
ALTER TABLE Groups
ADD INDEX group_name_index GLOBAL
ON (GroupName);
Если эволюционировать сущность Group путем добавления поля deparment
:
@Column
private String department;
@Column
val department: String
Hibernate при старте приложения обновит схему базы данных, если установлен режим update
:
jakarta.persistence.schema-generation.database.action=update
Результат изменения схемы:
ALTER TABLE Groups
ADD COLUMN department Text
Важно
Диалект YDB поддерживает @OneToMany
, @ManyToOne
, @ManyToMany
связи.
Например, сгенерированный SQL скрипт для @OneToMany
:
SELECT
g1_0.GroupId,
g1_0.GroupName
FROM
Groups g1_0
WHERE
g1_0.GroupName='M3439'
SELECT
s1_0.GroupId,
s1_0.StudentId,
s1_0.StudentName
FROM
Students s1_0
WHERE
s1_0.GroupId=?
SELECT
g1_0.GroupId,
g1_0.GroupName,
s1_0.GroupId,
s1_0.StudentId,
s1_0.StudentName
FROM
Groups g1_0
JOIN
Students s1_0
on g1_0.GroupId=s1_0.GroupId
WHERE
g1_0.GroupName='M3439'
Пример с Spring Data JPA
Настройте Spring Data JPA для использования диалекта YDB, обновив свой application.properties
:
spring.jpa.properties.hibernate.dialect=tech.ydb.hibernate.dialect.YdbDialect
spring.datasource.driver-class-name=tech.ydb.jdbc.YdbDriver
spring.datasource.url=jdbc:ydb:<grpc/grpcs>://<host>:<2135/2136>/path/to/database[?saFile=file:~/sa_key.json]
Создадим простую сущность и репозиторий:
@Data
@Entity
@Table(name = "employee")
public class Employee {
@Id
private long id;
@Column(name = "full_name")
private String fullName;
@Column
private String email;
@Column(name = "hire_date")
private LocalDate hireDate;
@Column
private java.math.BigDecimal salary;
@Column(name = "is_active")
private boolean isActive;
@Column
private String department;
@Column
private int age;
}
public interface EmployeeRepository implements CrudRepository<Employee, Long> {}
@Entity
@Table(name = "employee")
data class Employee(
@Id
val id: Long,
@Column(name = "full_name")
val fullName: String,
@Column
val email: String,
@Column(name = "hire_date")
val hireDate: LocalDate,
@Column
val salary: java.math.BigDecimal,
@Column(name = "is_active")
val isActive: Boolean,
@Column
val department: String,
@Column
val age: Int,
)
interface EmployeeRepository : CrudRepository<Employee, Long>
fun EmployeeRepository.findByIdOrNull(id: Long): Employee? = this.findById(id).orElse(null)
Пример использования:
Employee employee = new Employee(
1,
"Example",
"[email protected]",
LocalDate.parse("2023-12-20"),
BigDecimal("500000.000000000"),
true,
"YDB AppTeam",
23
);
/* The following SQL will be executed:
INSERT INTO employee (age,department,email,full_name,hire_date,is_active,limit_date_password,salary,id)
VALUES (?,?,?,?,?,?,?,?,?)
*/
employeeRepository.save(employee);
assertEquals(employee, employeeRepository.findById(employee.getId()).get());
/* The following SQL will be executed:
DELETE FROM employee WHERE id=?
*/
employeeRepository.delete(employee);
assertNull(employeeRepository.findById(employee.getId()).orElse(null));
val employee = Employee(
1,
"Example",
"[email protected]",
LocalDate.parse("2023-12-20"),
BigDecimal("500000.000000000"),
true,
"YDB AppTeam",
23
)
/* The following SQL will be executed:
INSERT INTO employee (age,department,email,full_name,hire_date,is_active,limit_date_password,salary,id)
VALUES (?,?,?,?,?,?,?,?,?)
*/
employeeRepository.save(employee)
assertEquals(employee, employeeRepository.findByIdOrNull(employee.id))
/* The following SQL will be executed:
DELETE FROM employee WHERE id=?
*/
employeeRepository.delete(employee)
assertNull(employeeRepository.findByIdOrNull(employee.id))
Пример простого приложения Spring Data JPA можно найти по ссылке.