译自:An Introduction to Hibernate 6
文中相关链接需要科学上网方可访问,后续有时间再逐个翻译。文章中如果存在任何不准确的地方,欢迎指正。
尚未完成,不断更新中....
系列文章:
目录
2. 配置和引导
我们希望这一节可以很简短。不幸的是,有几种不同的方法可以配置和引导Hibernate,我们将至少需要详细描述其中两种。
获取Hibernate实例的四种基本方式如下表所示:
使用标准的JPA定义的XML,并使用Persistence.createEntityManagerFactory() | 当重要的是在不同JPA实现之间的可移植性时通常选择此选项 |
使用Configuration 类构建一个SessionFactory | 当不需要在不同JPA实现之间进行可移植性时,这个选项更快,提供了一些灵活性并且避免了类型转换 |
使用org.hibernate.boot 中定义的更复杂的API | 主要由框架集成者使用,这个选项超出了本文档的范围 |
让容器负责引导过程并注入SessionFactory 或EntityManagerFactory | 在像WildFly或Quarkus这样的容器环境中使用 |
这里我们将重点放在前两个选项上。
在容器中的Hibernate 实际上,最后一个选项非常受欢迎,因为每个主要的Java应用服务器和微服务框架都内置了对Hibernate的支持。这些容器环境通常还具有自动管理EntityManager或Session的生命周期以及与容器管理的事务的关联的功能。
要了解如何在这样的容器环境中配置Hibernate,您需要参考所选容器的文档。对于Quarkus,这是相关的文档。
如果您在容器环境之外使用Hibernate,您需要:
-
将Hibernate ORM本身以及适当的JDBC驱动程序作为项目的依赖项引入项目中,以及
-
使用有关您的数据库的信息配置Hibernate,通过指定配置属性。
2.1. 将Hibernate包含到项目构建中
首先,在项目中添加以下依赖项:
org.hibernate.orm:hibernate-core:{version}
译者注:当然也可以使用maven
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>{version}</version>
</dependency>
这里的{version}
是您使用的Hibernate版本号。
您还需要为您的数据库添加一个JDBC驱动程序的依赖项。
表2. JDBC驱动程序依赖项
数据库 | 驱动依赖项 |
---|---|
PostgreSQL或CockroachDB | org.postgresql:postgresql:{version} |
MySQL或TiDB | com.mysql:mysql-connector-j:{version} |
MariaDB | org.mariadb.jdbc:mariadb-java-client:{version} |
DB2 | com.ibm.db2:jcc:{version} |
SQL Server | com.microsoft.sqlserver:mssql-jdbc:${version} |
Oracle | com.oracle.database.jdbc:ojdbc11:${version} |
H2 | com.h2database:h2:{version} |
HSQLDB | org.hsqldb:hsqldb:{version} |
这里的{version}
是您的数据库的JDBC驱动程序的最新版本号。
2.2. 可选依赖项
你也可以选择添加以下任意附加功能:
表格 3. 可选依赖项
可选功能 | 依赖项 |
---|---|
SLF4J日志实现 | org.apache.logging.log4j:log4j-core 或 org.slf4j:slf4j-jdk14 |
JDBC连接池(例如Agroal) | org.hibernate.orm:hibernate-agroal 和 io.agroal:agroal-pool |
Hibernate Metamodel Generator(特别是如果你使用JPA标准的Criteria查询API) | org.hibernate.orm:hibernate-jpamodelgen |
查询验证器,用于HQL的编译时检查 | org.hibernate:query-validator |
Hibernate Validator,一个Bean验证的实现 | org.hibernate.validator:hibernate-validator 和 org.glassfish:jakarta.el |
本地二级缓存支持(通过JCache和EHCache) | org.hibernate.orm:hibernate-jcache 和 org.ehcache:ehcache |
本地二级缓存支持(通过JCache和Caffeine) | org.hibernate.orm:hibernate-jcache 和 com.github.ben-manes.caffeine:jcache |
通过Infinispan实现的分布式二级缓存支持 | org.infinispan:infinispan-hibernate-cache-v60 |
用于处理JSON数据类型的JSON序列化库,例如Jackson或Yasson | com.fasterxml.jackson.core:jackson-databind 或 org.eclipse:yasson |
Hibernate Spatial | org.hibernate.orm:hibernate-spatial |
Envers,用于审计历史数据 | org.hibernate.orm:hibernate-envers |
如果你想使用字段级别的延迟加载,还可以将Hibernate字节码增强器添加到你的Gradle构建中。
2.3. 使用JPA XML进行配置
在遵循JPA标准的方法中,我们会提供一个名为persistence.xml
的文件,通常放在持久化归档(即包含我们的实体类的.jar文件或目录)的META-INF
目录下。
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
version="2.0">
<persistence-unit name="org.hibernate.example">
<class>org.hibernate.example.Book</class>
<class>org.hibernate.example.Author</class>
<properties>
<!-- PostgreSQL -->
<property name="jakarta.persistence.jdbc.url"
value="jdbc:postgresql://localhost/example"/>
<!-- Credentials -->
<property name="jakarta.persistence.jdbc.user"
value="gavin"/>
<property name="jakarta.persistence.jdbc.password"
value="hibernate"/>
<!-- Automatic schema export -->
<property name="jakarta.persistence.schema-generation.database.action"
value="drop-and-create"/>
<!-- SQL statement logging -->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.highlight_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
<persistence-unit>
元素定义了一个命名持久化单元,其中包括:
- 一组相关联的实体类型,以及
- 一组默认的配置设置,可以在运行时进行增加或覆盖。
每个 <class>
元素指定了一个实体类的完全限定名。
扫描实体类
在一些容器环境中,例如任何Java EE容器,
<class>
元素是不必要的,因为容器将会扫描归档文件以查找被@Entity
注解标记的类,并自动识别这些类。
每个 <property>
元素指定了一个配置属性及其值。请注意:
- jakarta.persistence 命名空间中的配置属性是JPA规范定义的标准属性。
- hibernate 命名空间中的属性是特定于Hibernate的属性。
我们可以通过调用 Persistence.createEntityManagerFactory() 方法来获得一个 EntityManagerFactory:
EntityManagerFactory entityManagerFactory =
Persistence.createEntityManagerFactory("org.hibernate.example");
如果需要,我们可以覆盖persistence.xml
中指定的配置属性:
EntityManagerFactory entityManagerFactory =
Persistence.createEntityManagerFactory("org.hibernate.example",
Map.of(AvailableSettings.JAKARTA_JDBC_PASSWORD, password));
2.4. 使用Hibernate API进行配置
或者,古老的Configuration
类允许在Java代码中配置Hibernate的实例。
SessionFactory sessionFactory =
new Configuration()
.addAnnotatedClass(Book.class)
.addAnnotatedClass(Author.class)
// PostgreSQL
.setProperty(AvailableSettings.JAKARTA_JDBC_URL, "jdbc:postgresql://localhost/example")
// Credentials
.setProperty(AvailableSettings.JAKARTA_JDBC_USER, user)
.setProperty(AvailableSettings.JAKARTA_JDBC_PASSWORD, password)
// Automatic schema export
.setProperty(AvailableSettings.JAKARTA_HBM2DDL_DATABASE_ACTION,
Action.SPEC_ACTION_DROP_AND_CREATE)
// SQL statement logging
.setProperty(AvailableSettings.SHOW_SQL, TRUE.toString())
.setProperty(AvailableSettings.FORMAT_SQL, TRUE.toString())
.setProperty(AvailableSettings.HIGHLIGHT_SQL, TRUE.toString())
// Create a new SessionFactory
.buildSessionFactory();
Configuration
类从Hibernate的非常早期(1.0版本之前)就一直存在,所以看起来并不特别现代。另一方面,它非常易于使用,并且暴露了一些persistence.xml
不支持的选项。
高级配置选项 实际上,Configuration
类只是org.hibernate.boot
包中定义的更现代、更强大但更复杂的API的一个简单外观。如果你有非常高级的需求,例如,如果你正在编写一个框架或实现一个容器,这个API是非常有用的。你可以在用户指南和org.hibernate.boot
包级别的文档中找到更多信息。
2.5. 使用Hibernate属性文件进行配置
如果我们使用Hibernate配置API,但不想将某些配置属性直接放在Java代码中,我们可以在一个名为hibernate.properties
的文件中指定它们,然后将文件放在根类路径下。
# PostgreSQL
jakarta.persistence.jdbc.url=jdbc:postgresql://localhost/example
# Credentials
jakarta.persistence.jdbc.user=hibernate
jakarta.persistence.jdbc.password=zAh7mY$2MNshzAQ5
# SQL statement logging
hibernate.show_sql=true
hibernate.format_sql=true
hibernate.highlight_sql=true
在这个属性文件中,我们可以将一些配置属性(如数据库URL、用户名、密码等)放置在文件中,而不是硬编码到Java代码中。Hibernate会在类路径下查找这个文件,并使用其中的属性进行配置。
2.6. 基本配置设置
AvailableSettings
类枚举了Hibernate所理解的所有配置属性。
当然,我们不会在本章节中覆盖每一个有用的配置设置。相反,我们将提到你需要开始的那些,以及一些其他重要的设置,尤其是在我们谈到性能调优时。
Hibernate有很多开关和切换。请不要乱用这些设置;它们中的大多数很少需要使用,而且许多只是为了与Hibernate的旧版本保持向后兼容。几乎所有这些设置的默认行为都是我们建议的行为,经过精心选择。
你真正需要开始的属性有以下三个:
表4. JDBC连接设置
配置属性名 | 目的 |
---|---|
jakarta.persistence.jdbc.url | 数据库的JDBC URL |
jakarta.persistence.jdbc.user | 数据库的用户名 |
jakarta.persistence.jdbc.password | 数据库的密码 |
在Hibernate 6中,你不需要指定 hibernate.dialect 。Hibernate SQL方言将自动为你确定。只有在你使用自定义的用户编写的Dialect类时才需要指定此属性。
类似地,在使用支持的数据库之一时,也不需要 hibernate.connection.driver_class 或jakarta.persistence.jdbc.driver
连接池化JDBC连接是一个非常重要的性能优化。你可以使用以下属性设置Hibernate内置连接池的大小:
表5. 内置连接池大小
配置属性名 | 目的 |
---|---|
hibernate.connection.pool_size | 内置连接池的大小 |
默认情况下,Hibernate使用一个简单的内置连接池。这个连接池不适合在生产环境中使用,稍后,当我们讨论性能时,我们将看到如何选择一个更强大的实现。
另外,在容器环境中,你至少需要以下其中一个属性:
表6. 事务管理设置
配置属性名 | 目的 |
---|---|
jakarta.persistence.transactionType | (可选,默认为JTA)确定事务管理是通过JTA还是资源本地事务。如果不使用JTA,指定RESOURCE_LOCAL。 |
jakarta.persistence.jtaDataSource | JTA数据源的JNDI名称 |
jakarta.persistence.nonJtaDataSource | 非JTA数据源的JNDI名称 |
在这种情况下,Hibernate将从一个受容器管理的数据源中获取池化的JDBC数据库连接。
2.7. 自动模式导出
你可以让Hibernate从你在Java代码中指定的映射注解中推断出数据库模式,并在初始化时导出该模式,方法是指定以下一个或多个配置属性:
表7. 模式管理设置
配置属性名 | 目的 |
---|---|
jakarta.persistence.schema-generation.database.action | 如果是drop-and-create,则首先删除模式,然后导出表、序列和约束。<br>如果是create,则导出表、序列和约束,而不尝试首先删除它们。<br>如果是create-drop,则在SessionFactory启动时删除模式并重新创建它,此外,在SessionFactory关闭时删除模式。<br>如果是drop,则在SessionFactory关闭时删除模式。<br>如果是validate,则验证数据库模式而不进行更改。<br>如果是update,则只导出模式中缺少的部分。 |
jakarta.persistence.create-database-schemas | (可选)如果为true,则自动创建模式和目录 |
jakarta.persistence.schema-generation.create-source | (可选)如果是metadata-then-script或script-then-metadata,则在导出表和序列时执行额外的SQL脚本 |
jakarta.persistence.schema-generation.create-script-source | (可选)要执行的SQL DDL脚本的名称 |
jakarta.persistence.sql-load-script-source | (可选)要执行的SQL DML脚本的名称 |
这个特性在测试中非常有用。
预初始化数据库中的测试数据或“参考”数据的最简单方法是将一个SQL插入语句列表放入一个名为
import.sql
的文件中,并使用属性jakarta.persistence.sql-load-script-source
指定该文件的路径。我们已经看到了这种方法的一个例子,它比编写Java代码实例化实体实例并在每个实例上调用persist()
方法更清晰。
正如我们前面提到的,通过编程方式控制模式导出也是有用的。
SchemaManager
API允许对模式导出进行编程控制:
sessionFactory.getSchemaManager().exportMappedObjects(true);
JPA具有更有限和不太符合人体工程学的API:
Persistence.generateSchema("org.hibernate.example", Map.of(JAKARTA_HBM2DDL_DATABASE_ACTION, CREATE))
2.8. 记录生成的SQL语句
要查看发送到数据库的生成SQL,你有两种选择。
一种方法是将属性hibernate.show_sql
设置为true
,Hibernate将直接将SQL记录到控制台。通过启用格式化或突出显示,你可以使输出更易读。这些设置在故障排除生成的SQL语句时非常有帮助。
表8. 将SQL记录到控制台的设置
配置属性名 | 目的 |
---|---|
hibernate.show_sql | 如果为true,则将SQL直接记录到控制台 |
hibernate.format_sql | 如果为true,则以多行、缩进格式记录SQL |
hibernate.highlight_sql | 如果为true,则通过ANSI转义码的语法突出显示记录SQL |
或者,你可以使用你首选的SLF4J日志实现启用类别org.hibernate.SQL
的调试级别日志记录。
例如,如果你使用的是Log4J 2(如上所述的Optional dependencies),在你的log4j2.properties
文件中添加以下行:
# SQL执行
logger.hibernate.name = org.hibernate.SQL
logger.hibernate.level = debug
# JDBC参数绑定
logger.jdbc-bind.name=org.hibernate.orm.jdbc.bind
logger.jdbc-bind.level=trace
# JDBC结果集提取
logger.jdbc-extract.name=org.hibernate.orm.jdbc.extract
logger.jdbc-extract.level=trace
但是使用这种方法我们无法得到漂亮的语法突出显示。
2.9. 最小化重复的映射信息
以下属性非常有用,可以最小化你需要在@Entity注解的@Table和@Column注解中显式指定的信息量,我们将在下面的对象/关系映射中讨论这个问题:
表9. 最小化显式映射信息的设置
配置属性名 | 目的 |
---|---|
hibernate.default_schema | 未明确声明模式的实体的默认模式名称 |
hibernate.default_catalog | 未明确声明目录的实体的默认目录名称 |
hibernate.physical_naming_strategy | 实现你的数据库命名标准的PhysicalNamingStrategy |
hibernate.implicit_naming_strategy | 当在注解中没有指定名称时,指定如何推断关系对象的“逻辑”名称的ImplicitNamingStrategy |
编写你自己的
PhysicalNamingStrategy
和/或ImplicitNamingStrategy
是减少实体类上注解混乱的一种特别好的方式,并且可以实现你的数据库命名约定,因此我们认为你应该在任何非平凡数据模型中这样做。我们将在命名策略中详细介绍它们。
2.10. SQL Server中的国际化字符数据
默认情况下,SQL Server的 char 和 varchar 类型不支持Unicode数据。但是Java字符串可以包含任何Unicode字符。因此,如果你在使用SQL Server,可能需要强制Hibernate使用 nchar 和 nvarchar 列类型。
表10. 使用国际化字符数据的设置
配置属性名 | 目的 |
---|---|
hibernate.use_nationalized_character_data | 使用 nchar 和 nvarchar 而不是 char 和 varchar |
另一方面,如果只有一些列存储国际化数据,请使用@Nationalized注解指示实体的字段,这些字段映射这些列。
或者,你可以配置SQL Server使用启用UTF-8的校对_UTF8。