4. 使用 R2DBC 访问数据
R2DBC(“反应式关系数据库连接”)是一个社区驱动的 规范工作,以使用反应模式标准化对 SQL 数据库的访问。
4.1. 软件包层次结构
Spring 框架的 R2DBC 抽象框架由两个不同的包组成:
-
core
:包包含类以及各种相关类。请参阅 使用 R2DBC核心类控制基本 R2DBC 处理和错误处理。org.springframework.r2dbc.core
DatabaseClient
-
connection
:包包含实用程序类 便于访问和各种简单实施 可用于测试和运行未修改的 R2DBC。请参阅控制数据库连接。org.springframework.r2dbc.connection
ConnectionFactory
ConnectionFactory
4.2. 使用 R2DBC 核心类来控制基本的 R2DBC 处理和错误处理
本节介绍如何使用 R2DBC 核心类来控制基本的 R2DBC 处理, 包括错误处理。它包括以下主题:
- 使用数据库客户端
- 执行语句
- 查询 (选择)
- 使用数据库客户端更新(插入、更新和删除)
- 语句筛选器
- 检索自动生成的密钥
4.2.1. 使用DatabaseClient
DatabaseClient
是 R2DBC 核心包中的中心类。它处理 创建和释放资源,这有助于避免常见错误,例如 忘记关闭连接。它执行核心R2DBC的基本任务 工作流(例如语句创建和执行),保留应用程序代码以提供 SQL 并提取结果。班级:DatabaseClient
- 运行 SQL 查询
- 更新语句和存储过程调用
- 执行迭代覆盖实例
Result
- 捕获 R2DBC 异常并将其转换为通用的、信息量更大的异常 包中定义的层次结构。(请参阅一致的异常层次结构。
org.springframework.dao
客户端有一个功能强大、流畅的 API,使用反应式类型进行声明性组合。
当你使用for代码时,你只需要实现接口,给它们一个明确定义的契约。 给定由类提供的 a,acallback 创建一个。映射函数也是如此 提取结果。DatabaseClient
java.util.function
Connection
DatabaseClient
Function
Publisher
Row
您可以通过直接实例化在 DAO 实现中使用 带有引用,或者您可以在Spring IoC容器中配置它 并把它作为豆子参考给DAO。DatabaseClient
ConnectionFactory
创建对象的最简单方法是通过静态工厂方法,如下所示:DatabaseClient
DatabaseClient client = DatabaseClient.create(connectionFactory);
上述方法使用默认设置创建 a。DatabaseClient
您还可以从中获取实例。 您可以通过调用以下方法自定义客户端:Builder
DatabaseClient.builder()
-
….bindMarkers(…)
:提供名为 参数到数据库绑定标记转换。BindMarkersFactory
-
….executeFunction(…)
:设置如何对象获取 跑。ExecuteFunction
Statement
-
….namedParameters(false)
:禁用命名参数扩展。默认启用。
目前支持的数据库有:
- H2
- 玛丽亚数据库
- 微软SQL Server
- MySQL
- 波斯特格雷斯
此类发出的所有 SQL 都记录在类别下的级别 对应于客户端实例的完全限定类名(通常)。此外,每次执行都会在 用于帮助调试的反应序列。DEBUG
DefaultDatabaseClient
以下各节提供了一些用法示例。这些例子 不是公开的所有功能的详尽列表。 请参阅随之而来的javadoc了解这一点。DatabaseClient
DatabaseClient
执行语句
DatabaseClient
提供运行语句的基本功能。 以下示例显示了最小但功能齐全需要包含的内容 创建新表的代码:
Mono<Void> completion = client.sql("CREATE TABLE person (id VARCHAR(255) PRIMARY KEY, name VARCHAR(255), age INTEGER);")
.then();
DatabaseClient
专为方便、流畅的使用而设计。 它公开了中间方法、延续方法和终端方法在 执行规范。上面的示例用于返回一个完成,一旦查询(或查询,如果 SQL 查询包含 多个语句)完成。then()
Publisher
查询 (SELECT
)
SQL 查询可以通过对象或受影响的行数返回值。可以返回更新的行数或行本身, 取决于发出的查询。Row
DatabaseClient
以下查询从表中获取 theandcolumns:id
name
Mono<Map<String, Object>> first = client.sql("SELECT id, name FROM person")
.fetch().first();
以下查询使用绑定变量:
Mono<Map<String, Object>> first = client.sql("SELECT id, name FROM person WHERE first_name = :fn")
.bind("fn", "Joe")
.fetch().first();
您可能已经注意到在示例中使用 above.is 用于指定要使用的数据量的延续运算符。fetch()
fetch()
调用返回结果中的第一行并丢弃剩余的行。 您可以使用以下运算符使用数据:first()
-
first()
返回整个结果的第一行。它的 Kotlin 协程变体 为不可为空的返回值命名,并且如果该值是可选的。awaitSingle()
awaitSingleOrNull()
-
one()
只返回一个结果,如果结果包含更多行,则失败。 使用 Kotlin 协程,对于正好一个值,或者如果该值可能是。awaitOne()
awaitOneOrNull()
null
-
all()
返回结果的所有行。使用 Kotlin 协程时,请使用。flow()
-
rowsUpdated()
返回受影响的行数 (//count)。它的 Kotlin 协程变体被命名。INSERT
UPDATE
DELETE
awaitRowsUpdated()
如果不指定进一步的映射详细信息,查询将返回表格结果 asits 键是不区分大小写的列名,映射到其列值。Map
您可以通过提供 athat get 来控制结果映射 调用 Eachso 它可以返回任意值(奇异值, 集合和地图以及对象)。Function<Row, T>
Row
下面的示例提取列并发出其值:name
Flux<String> names = client.sql("SELECT name FROM person")
.map(row -> row.get("name", String.class))
.all();
怎么样?
null
关系数据库结果可以包含值。 反应式流规范禁止释放值。 该要求要求在提取器功能中进行适当的处理。 虽然可以从 a 获取值,但不得发出值。您必须将任何值包装在对象中(例如,对于单数值),以确保永远不会直接返回 avalue 通过您的提取器函数。
null
null
null
null
Row
null
null
Optional
null
更新(,和)与INSERT
UPDATE
DELETE
DatabaseClient
修改语句的唯一区别是这些语句通常 不返回表格数据,以便用于使用结果。rowsUpdated()
下面的示例演示返回数字的语句 更新的行数:UPDATE
爪哇岛
科特林
Mono<Integer> affectedRows = client.sql("UPDATE person SET first_name = :fn")
.bind("fn", "Joe")
.fetch().rowsUpdated();
将值绑定到查询
典型的应用程序需要参数化 SQL 语句来选择或 根据某些输入更新行。这些是典型的陈述 受接受的从句或陈述的约束 输入参数。参数化语句承担 SQL 注入的风险,如果 参数未正确转义。利用 R2DBC 的 API 消除查询参数的 SQL 注入风险。 您可以使用运算符提供参数化的 SQL 语句 并将参数绑定到实际值。然后运行您的 R2DBC 驱动程序 使用预准备语句和参数替换的语句。SELECT
WHERE
INSERT
UPDATE
DatabaseClient
bind
execute(…)
Statement
参数绑定支持两种绑定策略:
- 按索引,使用从零开始的参数索引。
- 按名称,使用占位符名称。
以下示例显示查询的参数绑定:
db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
.bind("id", "joe")
.bind("name", "Joe")
.bind("age", 34);
R2DBC 本机绑定标记
R2DBC 使用依赖于实际数据库供应商的数据库本机绑定标记。 例如,Postgres 使用索引标记,例如,,. 另一个示例是 SQL Server,它使用前缀为前缀的命名绑定标记。
$1
$2
$n
@
这与 JDBC 不同,JDBC 需要绑定标记。 在 JDBC 中,实际驱动程序将绑定标记转换为数据库本机 标记作为其语句执行的一部分。
?
?
Spring Framework 的 R2DBC 支持允许您使用本机绑定标记或命名绑定。 带有语法的标记。
:name
命名参数支持利用实例扩展命名 查询执行时本机绑定标记的参数,这为您提供了 跨各种数据库供应商的一定程度的查询可移植性。
BindMarkersFactory
查询预处理器将命名参数展开到一系列绑定中 标记,无需根据参数数创建动态查询。 嵌套对象数组被扩展为允许使用(例如)选择列表。Collection
请考虑以下查询:
SELECT id, name, state FROM table WHERE (name, age) IN (('John', 35), ('Ann', 50))
上述查询可以参数化并按如下方式运行:
List<Object[]> tuples = new ArrayList<>();
tuples.add(new Object[] {"John", 35});
tuples.add(new Object[] {"Ann", 50});
client.sql("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)")
.bind("tuples", tuples);
以下示例显示了使用谓词的更简单变体:IN
client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)")
.bind("ages", Arrays.asList(35, 50));
语句筛选器
有时,您需要在实际运行之前微调选项。注册过滤器 () 通过拦截和 修改语句的执行,如以下示例所示:Statement
Statement
StatementFilterFunction
DatabaseClient
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter((s, next) -> next.execute(s.returnGeneratedValues("id")))
.bind("name", …)
.bind("state", …);
DatabaseClient
还公开了简化的重载接受:filter(…)
Function<Statement, Statement>
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter(statement -> s.returnGeneratedValues("id"));
client.sql("SELECT id, name, state FROM table")
.filter(statement -> s.fetchSize(25));
StatementFilterFunction
实现允许过滤对象和过滤对象。Statement
Result
DatabaseClient
最佳实践
一旦配置,类的实例是线程安全的。这是 很重要,因为这意味着您可以配置 aand 的单个实例,然后将此共享引用安全地注入多个 DAO(或存储库)。 Theis 有状态的,因为它保持对 a 的引用, 但此状态不是会话状态。DatabaseClient
DatabaseClient
DatabaseClient
ConnectionFactory
使用类时的一个常见做法是在 Spring 配置文件中配置 a,然后注入依赖项 将豆子共享到您的 DAO 类中。泰斯创建于 二传手。这会导致类似于以下内容的 DAO:DatabaseClient
ConnectionFactory
ConnectionFactory
DatabaseClient
ConnectionFactory
public class R2dbcCorporateEventDao implements CorporateEventDao {
private DatabaseClient databaseClient;
public void setConnectionFactory(ConnectionFactory connectionFactory) {
this.databaseClient = DatabaseClient.create(connectionFactory);
}
// R2DBC-backed implementations of the methods on the CorporateEventDao follow...
}
用注释类。 |
注释方法。 |
创建一个新的。 |
显式配置的替代方法是使用组件扫描和注释 支持依赖注入。在这种情况下,您可以使用(这使其成为组件扫描的候选者)对类进行注释并注释这些内容。 方法。以下示例演示如何执行此操作:@Component
ConnectionFactory
@Autowired
@Component
public class R2dbcCorporateEventDao implements CorporateEventDao {
private DatabaseClient databaseClient;
@Autowired
public void setConnectionFactory(ConnectionFactory connectionFactory) {
this.databaseClient = DatabaseClient.create(connectionFactory);
}
// R2DBC-backed implementations of the methods on the CorporateEventDao follow...
}
用注释类。 |
构造函数注入的。 |
创建一个新的。 |
无论您选择使用上述哪种模板初始化样式(或 not),很少需要为每个类创建一个新的实例 您想要运行 SQL 的时间。配置后,实例是线程安全的。 如果应用程序访问多个 数据库,您可能需要多个实例,这需要多个实例,随后需要多个不同配置的实例。DatabaseClient
DatabaseClient
DatabaseClient
ConnectionFactory
DatabaseClient
4.3. 检索自动生成的密钥
INSERT
语句可能会在表中插入行时生成键 定义自动增量或标识列。完全控制 要生成的列名,只需注册一个 请求为所需列生成的密钥。StatementFilterFunction
Mono<Integer> generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter(statement -> s.returnGeneratedValues("id"))
.map(row -> row.get("id", Integer.class))
.first();
// generatedId emits the generated key once the INSERT statement has finished
4.4. 控制数据库连接
本节涵盖:
- 使用ConnectionFactory
- 使用ConnectionFactoryUtils
- 使用SingleConnectionFactory
- 使用TransactionAwareConnectionFactoryProxy
- 使用R2dbcTransactionManager
4.4.1. 使用ConnectionFactory
Spring 通过 a 获得与数据库的 R2DBC 连接。 A是R2DBC规范的一部分,是一个常见的入口点 对于司机。它允许容器或框架隐藏连接池 以及应用程序代码中的事务管理问题。作为开发人员, 您不需要知道如何连接到数据库的详细信息。那就是 设置管理员的责任。你 最有可能在开发和测试代码时同时担任这两个角色,但您不会 必须知道生产数据源是如何配置的。ConnectionFactory
ConnectionFactory
ConnectionFactory
当你使用Spring的R2DBC层时,你可以配置你自己的一个 由第三方提供的连接池实现。一个流行的 实现是 R2DBC 池 ()。春季实施 分发仅用于测试目的,不提供池化。r2dbc-pool
要配置:ConnectionFactory
- 获取连接,因为您通常获得 R2DBC。
ConnectionFactory
ConnectionFactory
- 提供 R2DBC 网址 (有关正确的值,请参阅驱动程序的文档)。
以下示例演示如何配置:ConnectionFactory
ConnectionFactory factory = ConnectionFactories.get("r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
4.4.2. 使用ConnectionFactoryUtils
Theclass 是一个方便而强大的辅助类 这提供了从中获取连接和关闭连接的方法(如有必要)。ConnectionFactoryUtils
static
ConnectionFactory
例如,它支持订阅者绑定连接。Context
R2dbcTransactionManager
4.4.3. 使用SingleConnectionFactory
Theclass 是接口的实现,它包装一个每次使用后都不会关闭的单曲。SingleConnectionFactory
DelegatingConnectionFactory
Connection
如果任何客户端代码调用池连接的假设(如使用 持久性工具),应将属性设置为。此设置 返回包装物理连接的关闭抑制代理。请注意,您可以 不再将其强制转换为本机或类似对象。close
suppressClose
true
Connection
SingleConnectionFactory
主要是一个测试类,可用于特定要求 例如流水线(如果您的 R2DBC 驱动程序允许此类使用)。 与池化相比,它始终重复使用相同的连接,避免 过度创建物理连接。ConnectionFactory
4.4.4. 使用TransactionAwareConnectionFactoryProxy
TransactionAwareConnectionFactoryProxy
是目标的代理。 代理包装该目标以增加对 Spring 管理事务的感知。ConnectionFactory
ConnectionFactory
有关更多详细信息,请参阅TransactionAwareConnectionFactoryProxyjavadoc。
4.4.5. 使用R2dbcTransactionManager
该类是实现 单个 R2DBC 数据源。它绑定来自指定连接工厂的 R2DBC 连接 到订阅者,可能允许每个订阅者连接一个订阅者连接 连接工厂。R2dbcTransactionManager
ReactiveTransactionManager
Context
需要应用程序代码才能通过 R2DBC 连接而不是 R2DBC 的标准来检索。ConnectionFactoryUtils.getConnection(ConnectionFactory)
ConnectionFactory.create()
所有框架类(例如)都隐式使用此策略。 如果不与此事务管理器一起使用,则查找策略的行为与通用策略完全相同。 因此,它可以在任何情况下使用。DatabaseClient
该类支持应用于连接的自定义隔离级别。R2dbcTransactionManager
5. 对象关系映射 (ORM) 数据访问
本节介绍使用对象关系映射 (ORM) 时的数据访问。
5.1. 使用弹簧的ORM简介
Spring 框架支持与 Java Persistence API (JPA) 和 支持本机休眠资源管理、数据访问对象 (DAO) 实现、 和交易策略。例如,对于Hibernate,有一流的支持 几个方便的 IoC 功能,可解决许多典型的 Hibernate 集成问题。 您可以为 OR(对象关系)映射配置所有支持的功能 工具通过依赖注入。他们可以参与春天的资源和 交易管理,他们遵守Spring的通用交易和DAO 异常层次结构。推荐的集成风格是针对普通 DAO 编写代码 休眠或 JPA API。
Spring 在您创建时为您选择的 ORM 层添加了重要的增强功能 数据访问应用程序。您可以尽可能多地利用集成支持 希望,您应该将此集成工作与构建的成本和风险进行比较 内部类似的基础设施。您可以像使用一样使用大部分ORM支持 库,无论技术如何,因为一切都被设计为一组可重用的 爪哇豆。Spring IoC容器中的ORM有助于配置和部署。因此 本节中的大多数示例都显示了 Spring 容器内的配置。
使用Spring框架创建ORM DAO的好处包括:
- 更易于测试。Spring 的 IoC 方法使交换实现变得容易 以及休眠实例、JDBC 实例、事务管理器和映射对象实现(如果需要)的配置位置。这 反过来,可以更轻松地测试每段与持久性相关的代码 隔离。
SessionFactory
DataSource
- 常见数据访问异常。Spring 可以从您的 ORM 工具中包装异常, 将它们从专有(可能检查的)异常转换为通用运行时层次结构。此功能可让您处理大多数持久性 异常,这些异常不可恢复,仅在适当的层中,没有 烦人的样板捕获、抛出和异常声明。你仍然可以陷阱 并根据需要处理异常。请记住,JDBC 例外(包括 特定于数据库的方言)也转换为相同的层次结构,这意味着您可以 在一致的编程模型中使用 JDBC 执行一些操作。
DataAccessException
- 常规资源管理。Spring 应用程序上下文可以处理位置 以及休眠实例、JPA实例、JDBC实例和其他相关资源的配置。这使得这些 值易于管理和更改。弹簧提供高效、简单和安全的处理 持久性资源。例如,使用 Hibernate 的相关代码通常需要 使用相同的休眠以确保效率和适当的事务处理。 Spring 可以轻松透明地创建并绑定当前线程, 通过暴露通过冬眠的电流。因此,春天 解决了任何本地或JTA的典型Hibernate使用的许多长期问题 事务环境。
SessionFactory
EntityManagerFactory
DataSource
Session
Session
Session
SessionFactory
- 集成事务管理。您可以使用声明式代码包装您的 ORM 代码, 面向方面的编程 (AOP) 样式的方法拦截器,通过注释或显式配置事务 AOP 建议 一个 XML 配置文件。在这两种情况下,事务语义和异常处理 (回滚等)将为您处理。如资源和事务管理中所述, 您还可以交换各种事务管理器,而不会影响您的ORM相关代码。 例如,您可以在本地事务和JTA之间交换,使用相同的完整服务 (如声明性事务)在这两种情况下都可用。此外 JDBC 相关的代码可以与您用于执行 ORM 的代码完全事务集成。 这对于不适合ORM的数据访问(例如批处理和 BLOB 流),但这仍然需要与 ORM 操作共享公共事务。
@Transactional
5.2. 一般 ORM 集成注意事项
本节重点介绍适用于所有ORM技术的注意事项。 休眠部分提供了更多详细信息,并显示了这些功能和 具体上下文中的配置。
Spring 的 ORM 集成的主要目标是清晰的应用程序分层(包含任何数据) 访问和事务技术)和应用程序对象的松散耦合 — 否 对数据访问或事务策略的更多业务服务依赖,不再有 硬编码资源查找,不再有难以替换的单例,不再有自定义服务 登记处。目标是有一种简单而一致的方法来连接应用程序对象,保持 它们尽可能可重用且不受容器依赖关系的影响。所有个人 数据访问功能可以单独使用,但与Spring的集成得很好 应用程序上下文概念,提供基于 XML 的配置和交叉引用 不需要 Spring 感知的普通 JavaBean 实例。在典型的弹簧应用中, 许多重要的对象都是JavaBeans:数据访问模板,数据访问对象, 事务管理器,使用数据访问对象和事务的业务服务 管理器、Web 视图解析程序、使用业务服务的 Web 控制器等。
5.2.1. 资源和事务管理
典型的业务应用程序充斥着重复的资源管理代码。 许多项目试图发明自己的解决方案,有时会牺牲适当的处理方式 为方便编程而出现的故障。弹簧倡导简单的解决方案,以适当的 资源处理,即在 JDBC 的情况下通过模板化和应用 AOP 进行 IoC ORM技术的拦截器。
基础结构提供适当的资源处理和适当的转换 未经检查的基础架构异常层次结构的特定 API 例外。春天 引入了适用于任何数据访问策略的 DAO 异常层次结构。对于直接 JDBC,上一节中提到的类提供了连接处理和到层次结构的正确转换,包括数据库特定SQL错误的转换 代码到有意义的异常类。对于ORM技术,请参阅下一节,了解如何获取相同的异常 翻译优势。JdbcTemplate
SQLException
DataAccessException
当涉及到事务管理时,类与 Spring 挂钩 事务支持,并通过各自的 JTA 和 JDBC 事务支持 春季事务管理器。对于支持的ORM技术,Spring提供了Hibernate。 以及通过Hibernate和JPA事务管理器以及JTA支持的JPA支持。 有关事务支持的详细信息,请参阅事务管理一章。JdbcTemplate
5.2.2. 异常翻译
当你在DAO中使用Hibernate或JPA时,你必须决定如何处理持久性 技术的本机异常类。DAO 抛出 aor 的子类,具体取决于技术。这些异常都是运行时 例外,不必声明或捕获。您可能还必须处理和。这意味着呼叫者只能 将异常视为通常是致命的,除非它们想要依赖于持久性 技术自身的异常结构。抓住具体原因(如乐观 锁定失败)如果不将调用方绑定到实现策略,则是不可能的。 对于强基于 ORM 或 不需要任何特殊的例外处理(或两者兼而有之)。然而,春天让例外 翻译应通过注释透明地应用。以下 示例(一个用于 Java 配置,一个用于 XML 配置)显示了如何执行此操作:HibernateException
PersistenceException
IllegalArgumentException
IllegalStateException
@Repository
@Repository
public class ProductDaoImpl implements ProductDao {
// class body here...
}
<beans>
<!-- Exception translation bean post processor -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean id="myProductDao" class="product.ProductDaoImpl"/>
</beans>
后处理器自动查找所有异常转换器(实现 界面),并建议所有标有注释的 bean,以便发现的翻译人员可以拦截并应用 对引发的异常进行适当的翻译。PersistenceExceptionTranslator
@Repository
总之,您可以基于普通持久技术的 API 实现 DAO,并且 注释,同时仍然受益于 Spring 管理的交易、依赖关系 注入,以及透明异常转换(如果需要)到 Spring 的自定义 异常层次结构。
5.3. 休眠
我们从Hibernate 5在 Spring 环境中的报道开始, 使用它来演示 Spring 集成 OR 映射器的方法。 本节详细介绍了许多问题,并展示了DAO的不同变体 实现和事务划分。这些模式中的大多数可以直接 翻译成所有其他受支持的ORM工具。然后,本章的后面部分 涵盖其他ORM技术并显示简要示例。
5.3.1.在弹簧容器中设置SessionFactory
若要避免将应用程序对象绑定到硬编码的资源查找,可以定义 资源(例如 JDBCor Hibernate)作为 弹簧容器。需要访问资源的应用程序对象接收引用 通过 Bean 引用到此类预定义实例,如 DAO 中所述 下一节中的定义。DataSource
SessionFactory
以下摘自 XML 应用程序上下文定义的摘录演示如何设置 JDBC和它的Hibernateon顶部:DataSource
SessionFactory
<beans>
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>
<bean id="mySessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<property name="mappingResources">
<list>
<value>product.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.HSQLDialect
</value>
</property>
</bean>
</beans>
从本地 Jakarta Commons DBCP 切换到位于 JNDI(通常由应用程序服务器管理)只需 配置,如以下示例所示:BasicDataSource
DataSource
<beans>
<jee:jndi-lookup id="myDataSource" jndi-name="java:comp/env/jdbc/myds"/>
</beans>
您还可以使用 Spring 的 / 访问位于 JNDI 的位置,以检索和公开它。 但是,这在 EJB 上下文之外通常并不常见。SessionFactory
JndiObjectFactoryBean
<jee:jndi-lookup>
5.3.2. 基于普通休眠 API 实现 DAO
Hibernate有一个称为上下文会话的功能,其中Hibernate本身管理 每个事务一个当前。这大致相当于春天的 同步一个休眠事务。相应的 DAO 实现类似于以下示例,基于普通的 Hibernate API:Session
Session
public class ProductDaoImpl implements ProductDao {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Collection loadProductsByCategory(String category) {
return this.sessionFactory.getCurrentSession()
.createQuery("from test.Product product where product.category=?")
.setParameter(0, category)
.list();
}
}
这种风格类似于Hibernate参考文档和示例, 除了在实例变量中保存。我们强烈建议 这种基于实例的设置来自老派类 Hibernate 的 CaveatEmptor 示例应用程序。(通常,除非绝对必要,否则不要将任何资源保留为变量。SessionFactory
static
HibernateUtil
static
前面的 DAO 示例遵循依赖注入模式。它非常适合春季 IoC 容器,就像它根据 Spring 的编码一样。 你也可以在普通的Java中设置这样的DAO(例如,在单元测试中)。为此, 实例化它并使用所需的工厂引用进行调用。作为一个 春豆定义,DAO将类似于以下内容:HibernateTemplate
setSessionFactory(..)
<beans>
<bean id="myProductDao" class="product.ProductDaoImpl">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>
</beans>
这种DAO风格的主要优点是它只依赖于HibernateAPI。无导入 任何春季课程都是必需的。这从非侵入性来看很有吸引力 透视,对于Hibernate开发人员来说可能感觉更自然。
但是,DAO 抛出普通(这是未选中的,因此它没有 被声明或捕获),这意味着调用方只能将异常视为 通常是致命的——除非他们想依赖Hibernate自己的异常层次结构。 如果没有,就不可能捕获特定原因(例如乐观锁定失败) 将调用方绑定到实现策略。这种权衡可能是可以接受的 强烈基于休眠的应用程序,不需要任何特殊异常 治疗,或两者兼而有之。HibernateException
幸运的是,Spring支持Hibernate的方法用于任何Spring交易策略, 返回当前 Spring 管理的事务,即使有。该方法的标准行为保持不变 返回与正在进行的 JTA 事务关联的当前(如果有)。 无论您使用的是 Spring、EJB 容器管理事务 (CMT) 还是 JTA,此行为都适用。LocalSessionFactoryBean
SessionFactory.getCurrentSession()
Session
HibernateTransactionManager
Session
JtaTransactionManager
总之,您可以基于普通的HibernateAPI实现DAO,同时仍然 能够参与Spring管理的交易。
5.3.3. 声明式交易划分
我们建议您使用 Spring 的声明式事务支持,它可以让您 将 Java 代码中的显式事务划分 API 调用替换为 AOP 事务拦截器。您可以在 Spring 中配置此事务拦截器 容器,使用 Java 注释或 XML。此声明性事务功能 让您保持业务服务没有重复的交易划分代码和 专注于添加业务逻辑,这是应用程序的真正价值。
您可以使用注记对服务图层进行注记,并指示 Spring 容器,用于查找这些注释并提供事务语义 这些带注释的方法。以下示例演示如何执行此操作:@Transactional
爪哇岛
科特林
public class ProductServiceImpl implements ProductService {
private ProductDao productDao;
public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}
@Transactional
public void increasePriceOfAllProductsInCategory(final String category) {
List productsToChange = this.productDao.loadProductsByCategory(category);
// ...
}
@Transactional(readOnly = true)
public List<Product> findAllProducts() {
return this.productDao.findAllProducts();
}
}
在容器中,您需要设置实现 (作为 bean)和 aentry,选择在运行时加入处理。以下示例演示如何执行此操作:PlatformTransactionManager
<tx:annotation-driven/>
@Transactional
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- SessionFactory, DataSource, etc. omitted -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:annotation-driven/>
<bean id="myProductService" class="product.SimpleProductService">
<property name="productDao" ref="myProductDao"/>
</bean>
</beans>
5.3.4. 程序化交易划分
您可以在应用程序的更高级别划分事务,在 跨越任意数量的操作的较低级别的数据访问服务。限制也没有 存在于周边业务服务的实现上。它只需要一个弹簧。同样,后者可以来自任何地方,但最好是 作为通过 aMethod 的 Bean 引用。此外,应该通过一种方法设置。以下一对代码段显示 Spring 应用程序上下文中的事务管理器和业务服务定义 以及业务方法实现的示例:PlatformTransactionManager
setTransactionManager(..)
productDAO
setProductDao(..)
<beans>
<bean id="myTxManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>
<bean id="myProductService" class="product.ProductServiceImpl">
<property name="transactionManager" ref="myTxManager"/>
<property name="productDao" ref="myProductDao"/>
</bean>
</beans>
public class ProductServiceImpl implements ProductService {
private TransactionTemplate transactionTemplate;
private ProductDao productDao;
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}
public void increasePriceOfAllProductsInCategory(final String category) {
this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus status) {
List productsToChange = this.productDao.loadProductsByCategory(category);
// do the price increase...
}
});
}
}
弹簧允许抛出任何已检查的应用程序异常 使用回调代码,while 被限制为未选中 回调中的异常。在出现以下情况时触发回滚 未经检查的应用程序异常,或者事务被标记为仅回滚 应用程序(通过设置)。默认情况下,行为方式相同,但允许每个方法配置可配置的回滚策略。TransactionInterceptor
TransactionTemplate
TransactionTemplate
TransactionStatus
TransactionInterceptor
5.3.5. 交易管理策略
两者并委托实际事务 处理到ainstance(可以是a(对于单个休眠)通过使用aunderhood)或a(委托给 容器的 JTA 子系统)用于休眠应用程序。您甚至可以使用自定义实现。从本机休眠事务切换 对 JTA 的管理(例如,当面临某些分布式事务要求时) 应用程序的部署)只是配置问题。您可以替换 Hibernate事务管理器,具有Spring的JTA事务实现。双 事务划分和数据访问代码无需更改即可工作,因为它们 使用通用事务管理 API。TransactionTemplate
TransactionInterceptor
PlatformTransactionManager
HibernateTransactionManager
SessionFactory
ThreadLocal
Session
JtaTransactionManager
PlatformTransactionManager
对于跨多个 Hibernate 会话工厂的分布式事务,您可以将事务策略与多个定义组合在一起。然后,每个 DAO 都会获得一个传递到其相应 Bean 属性中的特定引用。如果所有基础 JDBC 数据 源是事务容器,业务服务可以划分事务 跨越任意数量的 DAO 和任意数量的会话工厂,无需特别考虑,如 只要它用作策略。JtaTransactionManager
LocalSessionFactoryBean
SessionFactory
JtaTransactionManager
两者都允许适当的 使用 Hibernate 进行 JVM 级缓存处理,无需特定于容器的事务管理器 查找或 JCA 连接器(如果不使用 EJB 启动事务)。HibernateTransactionManager
JtaTransactionManager
HibernateTransactionManager
可以将休眠JDBC导出到普通JDBC 特定访问代码。此功能允许高级 使用混合休眠和 JDBC 数据访问进行事务划分,完全无需 JTA,前提是您只能访问一个数据库。自动 将 Hibernate 事务公开为 JDBC 事务,前提是您已经设置了传入的 athrough 类的属性。或者,您可以显式指定应该通过类的属性公开哪些事务。Connection
DataSource
HibernateTransactionManager
SessionFactory
DataSource
dataSource
LocalSessionFactoryBean
DataSource
dataSource
HibernateTransactionManager
5.3.6. 比较容器管理的资源和本地定义的资源
您可以在容器管理的 JNDI 和本地定义的 JNDI 之间切换 一个无需更改任何应用程序代码。是否保留 容器中或应用程序本地的资源定义主要是一个 您使用的交易策略。与 Spring 定义的本地相比,手动注册的 JNDI 不提供任何 好处。通过Hibernate的JCA连接器进行部署可提供 参与 Jakarta EE 服务器的管理基础架构的附加值,但确实如此 除此之外,不会增加实际价值。SessionFactory
SessionFactory
SessionFactory
SessionFactory
Spring 的事务支持不绑定到容器。配置任何策略时 除了 JTA 之外,事务支持也可以在独立或测试环境中工作。 特别是在单数据库事务的典型情况下,Spring 的单资源 本地事务支持是 JTA 的轻量级和强大的替代方案。当您使用 本地 EJB 无状态会话 bean 来驱动事务,您都依赖于 EJB 容器和 JTA 上,即使您只访问单个数据库并且仅使用无状态 会话 Bean,用于通过容器管理的事务提供声明性事务 交易。以编程方式直接使用 JTA 也需要 Jakarta EE 环境。
Spring 驱动的事务也可以与本地定义的 Hibernateas 一起工作,它们使用本地 JDBC,前提是它们访问 单一数据库。因此,您只需要使用 Spring 的 JTA 交易策略,当您 具有分布式事务要求。JCA 连接器需要特定于容器 部署步骤,以及(显然)首先是 JCA 支持。此配置 与使用本地资源部署简单的 Web 应用程序相比,需要更多的工作 定义和弹簧驱动的事务。SessionFactory
DataSource
考虑到所有因素,如果您不使用 EJB,请坚持使用本地设置 和春天的索尔。你得到所有 好处,包括适当的事务 JVM 级缓存和分布式 事务,没有容器部署的不便。JNDI 注册 休眠通过 JCA 连接器仅在 与 EJB 联合使用。SessionFactory
HibernateTransactionManager
JtaTransactionManager
SessionFactory
5.3.7. 休眠时虚假应用程序服务器警告
在一些具有非常严格实现的 JTA 环境中(目前 某些 WebLogic Server 和 WebSphere 版本),当 Hibernate 配置为没有 关于该环境的 JTA 事务管理器、虚假警告或 异常可能会显示在应用程序服务器日志中。这些警告或异常 指示正在访问的连接不再有效或 JDBC 访问不再有效 有效期更长,可能是因为事务不再处于活动状态。举个例子, 以下是WebLogic的一个实际例外:XADataSource
java.sql.SQLException: The transaction is no longer active - status: 'Committed'. No
further JDBC access is allowed within this transaction.
另一个常见问题是 JTA 事务后连接泄漏,休眠 会话(以及潜在的底层 JDBC 连接)未正确关闭。
您可以通过让 Hibernate 知道 JTA 事务管理器来解决这些问题, 它与之同步(与 Spring 一起)。您有两种选择来执行此操作:
- 将你的春豆传递给你的休眠设置。最简单的 way 是对 bean 属性的 bean 引用(请参阅Hibernate 事务设置)。 然后,Spring 将相应的 JTA 策略提供给 Hibernate。
JtaTransactionManager
jtaTransactionManager
LocalSessionFactoryBean
- 您还可以显式配置 Hibernate 的 JTA 相关属性,特别是 “hibernate.transaction.coordinator_class”、“hibernate.connection.handling_mode” 并可能在“hibernateProperties”中“hibernate.transaction.jta.platform” on(有关这些属性的详细信息,请参阅 Hibernate 的手册)。
LocalSessionFactoryBean
本节的其余部分介绍与 和 一起发生的事件的顺序 没有希伯纳特对JTA的意识。PlatformTransactionManager
当休眠未配置任何 JTA 事务管理器感知时, 提交 JTA 事务时会发生以下事件:
- JTA 事务提交。
- Spring'sis 与 JTA 事务同步,所以它是 通过 JTA 事务管理器的回调回调。
JtaTransactionManager
afterCompletion
- 在其他活动中,此同步可以触发 Spring 的回调 休眠,通过休眠的回调(用于清除 休眠缓存),后跟对休眠会话的显式调用, 这会导致休眠尝试连接到 JDBC 连接。
afterTransactionCompletion
close()
close()
- 在某些环境中,此调用会触发警告或 错误,因为应用程序服务器不再认为可用, 因为事务已经提交。
Connection.close()
Connection
当休眠配置为感知 JTA 事务管理器时, 提交 JTA 事务时会发生以下事件:
- JTA 事务已准备好提交。
- Spring'sis 与 JTA 事务同步,因此 事务由 JTA 通过回调回调 事务管理器。
JtaTransactionManager
beforeCompletion
- Spring 知道 Hibernate 本身与 JTA 事务同步, 行为与前面的方案不同。特别是,它与 Hibernate 的事务性资源管理。
- JTA 事务提交。
- 休眠与 JTA 事务同步,因此事务被回调 通过 JTA 事务管理器的回调,并且可以 正确清除其缓存。
afterCompletion
5.4. JPA
春季JPA,在套餐下提供,提供 全面支持Java 持久性 API以类似于与 Hibernate 集成的方式,同时意识到 底层实现,以便提供其他功能。org.springframework.orm.jpa
5.4.1. 在春季环境中设置 JPA 的三个选项
Spring JPA 支持提供了三种设置 JPA 的方法,应用程序使用它来获取实体管理器。EntityManagerFactory
- 使用LocalEntityManagerFactoryBean
- 从 JNDI 获取实体管理器工厂
- 使用LocalContainerEntityManagerFactoryBean
用LocalEntityManagerFactoryBean
只能在简单的部署环境(如独立部署环境)中使用此选项 应用程序和集成测试。
该创建适合 应用程序仅使用 JPA 进行数据访问的简单部署环境。 工厂豆使用 JPA自动检测机制(根据 到 JPA 的 Java SE 引导),并且在大多数情况下,要求您仅指定 持久性单元名称。以下 XML 示例配置了这样的 Bean:LocalEntityManagerFactoryBean
EntityManagerFactory
PersistenceProvider
<beans>
<bean id="myEmf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="myPersistenceUnit"/>
</bean>
</beans>
这种形式的 JPA 部署是最简单和最受限制的。您不能引用 现有的 JDBCbean 定义,不支持全局事务 存在。此外,持久类的编织(字节码转换)是 特定于提供程序,通常需要在启动时指定特定的 JVM 代理。这 选项仅对独立应用程序和测试环境足够,对于 设计了 JPA 规范。DataSource
从 JNDI 获取实体管理器工厂
部署到 Jakarta EE 服务器时,可以使用此选项。查看服务器的文档 关于如何将自定义 JPA 提供程序部署到您的服务器中,允许不同的 提供程序比服务器的默认值。
从JNDI获取(例如在Jakarta EE环境中), 是更改 XML 配置的问题,如以下示例所示:EntityManagerFactory
<beans>
<jee:jndi-lookup id="myEmf" jndi-name="persistence/myPersistenceUnit"/>
</beans>
此操作假定使用标准 Jakarta EE 引导。雅加达EE服务器自动检测 持久性单元(实际上是应用程序 jar 中的文件)和 Jakarta EE 部署描述符中的条目(例如),并为这些持久性单元定义环境命名上下文位置。META-INF/persistence.xml
persistence-unit-ref
web.xml
在这种情况下,整个持久性单元部署,包括编织 (字节码转换)的持久类,由 Jakarta EE 服务器决定。JDBCis通过文件中的JNDI位置定义,事务与服务器的JTA子系统集成。春天 使用获得的,通过以下方式将其传递给应用程序对象 持久性单元的依赖关系注入和管理事务(通常 通过)。DataSource
META-INF/persistence.xml
EntityManager
EntityManagerFactory
JtaTransactionManager
如果在同一应用程序中使用多个持久性单元,则此类 Bean 名称 JNDI 检索的持久性单元应与 应用程序用于引用它们(例如,inandannotations)。@PersistenceUnit
@PersistenceContext
用LocalContainerEntityManagerFactoryBean
您可以在基于 Spring 的应用程序环境中使用此选项来实现完整的 JPA 功能。 这包括 Web 容器(如 Tomcat)、独立应用程序和 具有复杂持久性要求的集成测试。
提供完全控制过度配置,适用于以下环境: 需要细粒度自定义。该 创建基于文件的实例, 提供策略,并指定。因此, 可以使用 JNDI 之外的自定义数据源并控制编织 过程。以下示例显示了 a 的典型 Bean 定义:LocalContainerEntityManagerFactoryBean
EntityManagerFactory
LocalContainerEntityManagerFactoryBean
PersistenceUnitInfo
persistence.xml
dataSourceLookup
loadTimeWeaver
LocalContainerEntityManagerFactoryBean
<beans>
<bean id="myEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="someDataSource"/>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
</property>
</bean>
</beans>
以下示例显示了一个典型文件:persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="myUnit" transaction-type="RESOURCE_LOCAL">
<mapping-file>META-INF/orm.xml</mapping-file>
<exclude-unlisted-classes/>
</persistence-unit>
</persistence>
使用最强大的 JPA 设置 选项,允许在应用程序中进行灵活的本地配置。它支持 链接到现有 JDBC,支持本地和全局事务,以及 等等。但是,它也对运行时环境提出了要求,例如 具有编织功能的类装入器的可用性(如果持久性提供程序需要) 字节码转换。LocalContainerEntityManagerFactoryBean
DataSource
此选项可能与 Jakarta EE 服务器的内置 JPA 功能冲突。在一个 完整的雅加达EE环境,考虑从JNDI获得您的。 或者,指定自定义您的定义(例如, META-INF/my-persistence.xml),并在您的 应用程序 jar 文件。由于 Jakarta EE 服务器仅查找默认文件,因此它会忽略此类自定义持久性单元,因此, 避免与 Spring 驱动的 JPA 设置预先发生冲突。(这适用于树脂 3.1,对于 示例。EntityManagerFactory
persistenceXmlLocation
LocalContainerEntityManagerFactoryBean
META-INF/persistence.xml
什么时候需要装载时间编织?
并非所有 JPA 提供程序都需要 JVM 代理。Hibernate就是一个例子。 如果您的提供商不需要代理,或者您有其他选择,例如 在构建时通过定制编译器或 Ant 任务应用增强功能,则不应使用 加载时间编织者。
接口是 Spring 提供的类,它允许 JPA实例以特定方式插入,具体取决于 环境是 Web 容器或应用程序服务器。通过代理挂钩通常效率不高。代理针对整个虚拟机工作,并且 检查加载的每个类,这在生产中通常是不希望的 服务器环境。LoadTimeWeaver
ClassTransformer
ClassTransformers
Spring 为各种环境提供了许多实现, letting实例仅应用于每个类装入器,而不应用于 对于每个 VM。LoadTimeWeaver
ClassTransformer
请参阅 AOP 章节中的Spring 配置 有关实施及其设置的更多见解,或者 通用或定制到各种平台(如Tomcat,JBoss和WebSphere)。LoadTimeWeaver
如 Spring 配置中所述,您可以配置 上下文范围通过使用注释或 XML 元素。这样的全球织布工被自动拾取 由所有 JPA 实例提供。以下示例 显示了设置加载时间编织器的首选方法,提供自动检测 平台(例如Tomcat的具有编织能力的类加载器或Spring的JVM代理) 以及将织布工自动传播到所有具有织布意识的豆子:LoadTimeWeaver
@EnableLoadTimeWeaving
context:load-time-weaver
LocalContainerEntityManagerFactoryBean
<context:load-time-weaver/>
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
</bean>
但是,如果需要,您可以通过属性手动指定专用编织工,如以下示例所示:loadTimeWeaver
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
</property>
</bean>
无论 LTW 是如何配置的,通过使用这种技术,JPA 应用程序依赖于 检测可以在目标平台(例如 Tomcat)中运行,而无需代理。 当托管应用程序依赖于不同的 JPA 时,这一点尤其重要 实现,因为 JPA 转换器仅在类加载器级别应用,并且 因此,彼此隔离。
处理多个持久性单元
对于依赖于多个持久性单元位置的应用程序(存储在各种 例如,类路径中的 JARS ),Spring 提供了 theto 充当 一个中央存储库,并避免持久性单元发现过程,这可以 贵。默认实现允许指定多个位置。这些位置是 分析并稍后通过持久性单元名称进行检索。(默认情况下,类路径 被搜索文件。以下示例配置 多个地点:PersistenceUnitManager
META-INF/persistence.xml
<bean id="pum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
<property name="persistenceXmlLocations">
<list>
<value>org/springframework/orm/jpa/domain/persistence-multi.xml</value>
<value>classpath:/my/package/**/custom-persistence.xml</value>
<value>classpath*:META-INF/persistence.xml</value>
</list>
</property>
<property name="dataSources">
<map>
<entry key="localDataSource" value-ref="local-db"/>
<entry key="remoteDataSource" value-ref="remote-db"/>
</map>
</property>
<!-- if no datasource is specified, use this one -->
<property name="defaultDataSource" ref="remoteDataSource"/>
</bean>
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitManager" ref="pum"/>
<property name="persistenceUnitName" value="myCustomUnit"/>
</bean>
默认实现允许自定义实例 (在将它们提供给 JPA 提供程序之前)以声明方式(通过其属性,其中 影响所有托管单元)或以编程方式(通过,允许持久性单元选择)。如果指定了 nois,则由 在内部创建和使用。PersistenceUnitInfo
PersistenceUnitPostProcessor
PersistenceUnitManager
LocalContainerEntityManagerFactoryBean
后台引导
LocalContainerEntityManagerFactoryBean
支持后台引导 属性,如以下示例所示:bootstrapExecutor
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="bootstrapExecutor">
<bean class="org.springframework.core.task.SimpleAsyncTaskExecutor"/>
</property>
</bean>
实际的 JPA 提供程序引导将传递给指定的执行器,然后, 并行运行到应用程序引导线程。公开代理可以注入到其他应用程序组件中,甚至能够响应配置检查。但是,一旦实际的JPA提供者 正在被其他组件访问(例如,调用),这些调用 阻止,直到后台引导完成。特别是,当您使用 Spring Data JPA,确保也为其存储库设置延迟引导。EntityManagerFactory
EntityManagerFactoryInfo
createEntityManager
5.4.2. 实现基于 JPA 的 DAO:和EntityManagerFactory
EntityManager
可以针对普通的JPA编写代码,而无需任何Spring依赖项,方法是 使用注射器。Spring可以在字段和方法级别理解theand注释 如果启用了 AIS。以下示例显示了一个简单的 JPA DAO 实现 使用注释:EntityManagerFactory
EntityManager
@PersistenceUnit
@PersistenceContext
PersistenceAnnotationBeanPostProcessor
@PersistenceUnit
public class ProductDaoImpl implements ProductDao {
private EntityManagerFactory emf;
@PersistenceUnit
public void setEntityManagerFactory(EntityManagerFactory emf) {
this.emf = emf;
}
public Collection loadProductsByCategory(String category) {
EntityManager em = this.emf.createEntityManager();
try {
Query query = em.createQuery("from Product as p where p.category = ?1");
query.setParameter(1, category);
return query.getResultList();
}
finally {
if (em != null) {
em.close();
}
}
}
}
前面的DAO不依赖于Spring,仍然非常适合Spring。 应用程序上下文。此外,DAO 利用注释来要求 注入默认值,如以下示例 Bean 定义所示:EntityManagerFactory
<beans>
<!-- bean post-processor for JPA annotations -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id="myProductDao" class="product.ProductDaoImpl"/>
</beans>
作为显式定义 a 的替代方法, 考虑在应用程序中使用 SpringXML 元素 上下文配置。这样做会自动注册所有 Spring 标准 后处理器用于基于注释的配置,包括等等。PersistenceAnnotationBeanPostProcessor
context:annotation-config
CommonAnnotationBeanPostProcessor
请考虑以下示例:
<beans>
<!-- post-processors for all standard config annotations -->
<context:annotation-config/>
<bean id="myProductDao" class="product.ProductDaoImpl"/>
</beans>
这样的DAO的主要问题是它总是创建一个新的通过 工厂。您可以通过请求事务性(也 称为“共享实体管理器”,因为它是实际的共享线程安全代理 事务实体管理器)注入而不是工厂。以下示例演示如何执行此操作:EntityManager
EntityManager
public class ProductDaoImpl implements ProductDao {
@PersistenceContext
private EntityManager em;
public Collection loadProductsByCategory(String category) {
Query query = em.createQuery("from Product as p where p.category = :category");
query.setParameter("category", category);
return query.getResultList();
}
}
注释有一个可选属性调用,默认为。您可以使用此默认值来接收共享代理。另一种选择,,是一个完全 不同的事情。这导致所谓的扩展,这不是 线程安全,因此不得在并发访问的组件中使用,例如 弹簧管理的单胎豆。扩展实例只应在 有状态组件,例如,驻留在会话中,其生命周期不与当前事务绑定,而是完全取决于 应用。@PersistenceContext
type
PersistenceContextType.TRANSACTION
EntityManager
PersistenceContextType.EXTENDED
EntityManager
EntityManager
EntityManager
方法级和现场级进样
您可以在类内的字段或方法上应用指示依赖注入(例如 and)的注释 — 因此 表达式“方法级注入”和“字段级注入”。字段级 注释简洁易用,而方法级注释允许进一步 处理注入的依赖项。在这两种情况下,成员可见性(公共, 受保护或私有)无关紧要。
@PersistenceUnit
@PersistenceContext
类级注释呢?
在Jakarta EE平台上,它们用于依赖声明,而不是资源 注射。
注入是弹簧管理的(知道正在进行的交易)。 即使新的 DAO 实现使用方法级 注射安而不是安,没有变化是 由于注释的使用,在应用程序上下文 XML 中是必需的。EntityManager
EntityManager
EntityManagerFactory
这种DAO风格的主要优点是它只依赖于Java Persistence API。 不需要导入任何 Spring 类。此外,正如对 JPA 注释的理解, 注射由弹簧容器自动应用。这很有吸引力 非侵入性视角,对 JPA 开发人员来说感觉更自然。
5.4.3. 弹簧驱动的 JPA 事务
JPA 的推荐策略是通过 JPA 的本机事务进行本地事务 支持。Spring's提供了许多从本地已知的功能 JDBC 事务(例如特定于事务的隔离级别和资源级别) 针对任何常规 JDBC 连接池的只读优化(无 XA 要求)。JpaTransactionManager
Spring JPA 还允许配置公开 JPA 事务 访问相同内容的 JDBC 访问代码,前提是注册支持检索底层 JDBC。 Spring 为 EclipseLink 和 Hibernate JPA 实现提供了方言。 有关该机制的详细信息,请参阅下一节。JpaTransactionManager
DataSource
JpaDialect
Connection
JpaDialect
5.4.4. 理解和JpaDialect
JpaVendorAdapter
作为高级功能,和子类允许将自定义传递到bean属性中。A实施可以实现以下高级 Spring 支持的功能,通常以特定于供应商的方式:JpaTransactionManager
AbstractEntityManagerFactoryBean
JpaDialect
jpaDialect
JpaDialect
- 应用特定的事务语义(如自定义隔离级别或事务) 超时)
- 检索事务性 JDBC(用于暴露基于 JDBC 的 DAO)
Connection
- 高级翻译到春天
PersistenceExceptions
DataAccessExceptions
这对于特殊事务语义和高级事务语义特别有价值 例外的翻译。默认实现 () 执行 不提供任何特殊功能,如果需要前面列出的功能,则具有 以指定适当的方言。DefaultJpaDialect
请参阅JpaDialect和JpaVendorAdapterjavadoc 有关其操作以及如何在 Spring 的 JPA 支持中使用它们的更多详细信息。
5.4.5. 使用 JTA 事务管理设置 JPA
作为替代方案,Spring还允许多资源 通过 JTA 进行交易协调,无论是在雅加达 EE 环境中还是与 独立的事务协调器,例如 Atomikos。除了选择春天的,你需要走得更远 步骤:JpaTransactionManager
JtaTransactionManager
JpaTransactionManager
- 底层 JDBC 连接池需要支持 XA 并与 您的交易协调员。这在雅加达EE环境中通常很简单, 通过JNDI暴露一种不同的类型。查看您的应用程序服务器 文档了解详细信息。类似地,独立的事务协调器通常 带有特殊的 XA 集成变体。再次检查其文档。
DataSource
DataSource
- 需要为 JTA 配置 JPA设置。这是 特定于提供程序,通常通过指定 ason 的特殊属性。在休眠的情况下,这些属性 甚至是特定于版本的。有关详细信息,请参阅您的 Hibernate 文档。
EntityManagerFactory
jpaProperties
LocalContainerEntityManagerFactoryBean
- Spring'sen强制某些面向Spring的默认值,例如 作为连接释放模式,与休眠自己的默认设置相匹配 Hibernate 5.0 但在 Hibernate 5.1+ 中不再有。对于 JTA 设置,请确保声明 持久性单位事务类型为“JTA”。或者,将Hibernate 5.2的属性设置为恢复Hibernate自己的默认值。 有关相关说明,请参阅休眠时的虚假应用程序服务器警告。
HibernateJpaVendorAdapter
on-close
hibernate.connection.handling_mode
DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT
- 或者,考虑从您的申请中获取 服务器本身(即,通过 JNDI 查找而不是本地声明)。提供的服务器可能需要在服务器配置中定义特殊(使部署 可移植性较低),但针对服务器的 JTA 环境进行了设置。
EntityManagerFactory
LocalContainerEntityManagerFactoryBean
EntityManagerFactory
5.4.6. 用于 JPA 交互的本机休眠设置和本机休眠事务
本机设置的组合允许与其他 JPA 访问代码进行交互。现在休眠实现 JPA 的接口 而休眠句柄本身就是JPA。 Spring 的 JPA 支持设施会自动检测本机休眠会话。LocalSessionFactoryBean
HibernateTransactionManager
@PersistenceContext
SessionFactory
EntityManagerFactory
Session
EntityManager
因此,这种原生的Hibernate设置可以作为标准JPAand组合的替代品。 在许多情况下,允许与(以及)在 INSIDE 之间进行交互 相同的本地事务。这样的设置还提供了更强大的Hibernate集成。 以及更大的配置灵活性,因为它不受 JPA 引导合约的约束。LocalContainerEntityManagerFactoryBean
JpaTransactionManager
SessionFactory.getCurrentSession()
HibernateTemplate
@PersistenceContext EntityManager
在这种情况下,您不需要配置, 由于 Spring 的原生休眠设置提供了更多功能 (例如,自定义 Hibernate 集成器设置、Hibernate 5.3 Bean 容器集成、 以及对只读事务的更强优化)。最后但并非最不重要的一点是,您还可以 通过以下方式表达本机休眠设置, 与样式配置无缝集成(不参与)。HibernateJpaVendorAdapter
LocalSessionFactoryBuilder
@Bean
FactoryBean
6. 使用对象 XML 映射器封送 XML
6.1. 简介
本章描述了 Spring 的对象-XML 映射支持。对象-XML 映射(简称 O-X 映射)是将 XML 文档与 XML 文档相互转换的操作 一个对象。此转换过程也称为 XML 编组或 XML 序列化。本章交替使用这些术语。
在 O-X 映射领域,编组器负责序列化 对象(图形)到 XML。以类似的方式,解组器将 XML 反序列化为 对象图。此 XML 可以采用 DOM 文档、输入或输出的形式 流或 SAX 处理程序。
使用 Spring 满足 O/X 映射需求的一些好处是:
- 易于配置
- 一致的接口
- 一致的异常层次结构
6.1.1. 易于配置
Spring 的豆子工厂可以轻松配置编组器,而无需 构造 JAXB 上下文、JiBX 绑定工厂等。您可以配置编组器 就像应用程序上下文中的任何其他 Bean 一样。此外,基于 XML 命名空间 配置可用于多个编组器,使配置均匀 简单。
6.1.2. 一致的接口
Spring 的 O-X 映射通过两个全局接口运行:Marshaller 和Unmarshaller。这些抽象允许您切换 O-X 映射框架 相对容易,几乎不需要对执行 编组。这种方法还有一个额外的好处,即使可以执行 XML。 使用混合和匹配方法进行编组(例如,使用 JAXB 执行的某些编组) 和一些由XStream)以非侵入性的方式,让您使用每个的力量 科技。
6.1.3. 一致的异常层次结构
Spring 提供了从底层 O-X 映射工具的异常到其 自己的异常层次结构,作为根异常。 这些运行时异常包装原始异常,以便不会丢失任何信息。XmlMappingException
6.2.和Marshaller
Unmarshaller
如简介中所述,编组器序列化对象 到 XML,解组器将 XML 流反序列化为对象。本节介绍 用于此目的的两个 Spring 接口。
6.2.1. 理解Marshaller
Spring 抽象了接口后面的所有编组操作,其主要方法如下:org.springframework.oxm.Marshaller
public interface Marshaller {
/**
* Marshal the object graph with the given root into the provided Result.
*/
void marshal(Object graph, Result result) throws XmlMappingException, IOException;
}
接口有一个主方法,它将给定的对象封送到 鉴于。结果是一个标记界面,基本上 表示 XML 输出抽象。具体的实现包装了各种 XML 表示形式,如下表所示:Marshaller
javax.xml.transform.Result
结果实施 | 包装 XML 表示形式 |
| |
| |
| |
6.2.2. 理解Unmarshaller
与此类似,我们有接口,以下列表显示:Marshaller
org.springframework.oxm.Unmarshaller
public interface Unmarshaller {
/**
* Unmarshal the given provided Source into an object graph.
*/
Object unmarshal(Source source) throws XmlMappingException, IOException;
}
此接口还有一个方法,该方法从给定的(XML 输入抽象)中读取并返回读取的对象。如 with,是一个标记接口,具有三个具体的实现。每 包装不同的 XML 表示形式,如下表所示:javax.xml.transform.Source
Result
Source
源实现 | 包装 XML 表示形式 |
| |
| |
| |
即使有两个单独的编组接口(和),Spring-WS 中的所有实现都在一个类中实现这两个接口。 这意味着您可以连接一个编组器类,并将其同时称为 编组员和作为乌尔的解编组员。Marshaller
Unmarshaller
applicationContext.xml
6.2.3. 理解XmlMappingException
Spring 将异常从底层 O-X 映射工具转换为自己的异常 以根异常为根的层次结构。 这些运行时异常包装原始异常,因此不会丢失任何信息。XmlMappingException
此外,还提供了编组和解组操作之间的区别,即使 底层 O-X 映射工具不会这样做。MarshallingFailureException
UnmarshallingFailureException
O-X 映射异常层次结构如下图所示:
6.3. 使用和Marshaller
Unmarshaller
您可以将 Spring 的 OXM 用于各种情况。在以下示例中,我们 使用它来将 Spring 托管应用程序的设置封送为 XML 文件。在以下示例中,我们 使用一个简单的JavaBean来表示设置:
public class Settings {
private boolean fooEnabled;
public boolean isFooEnabled() {
return fooEnabled;
}
public void setFooEnabled(boolean fooEnabled) {
this.fooEnabled = fooEnabled;
}
}
应用程序类使用此 Bean 来存储其设置。除了一个主方法, 类有两种方法:将设置 Bean 保存到名为的文件,然后再次加载这些设置。以下方法 构造一个 Spring 应用程序上下文并调用以下两个方法:saveSettings()
settings.xml
loadSettings()
main()
public class Application {
private static final String FILE_NAME = "settings.xml";
private Settings settings = new Settings();
private Marshaller marshaller;
private Unmarshaller unmarshaller;
public void setMarshaller(Marshaller marshaller) {
this.marshaller = marshaller;
}
public void setUnmarshaller(Unmarshaller unmarshaller) {
this.unmarshaller = unmarshaller;
}
public void saveSettings() throws IOException {
try (FileOutputStream os = new FileOutputStream(FILE_NAME)) {
this.marshaller.marshal(settings, new StreamResult(os));
}
}
public void loadSettings() throws IOException {
try (FileInputStream is = new FileInputStream(FILE_NAME)) {
this.settings = (Settings) this.unmarshaller.unmarshal(new StreamSource(is));
}
}
public static void main(String[] args) throws IOException {
ApplicationContext appContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
Application application = (Application) appContext.getBean("application");
application.saveSettings();
application.loadSettings();
}
}
这需要同时设置 a和属性。我们 可以使用以下内容来执行此操作:Application
marshaller
unmarshaller
applicationContext.xml
<beans>
<bean id="application" class="Application">
<property name="marshaller" ref="xstreamMarshaller" />
<property name="unmarshaller" ref="xstreamMarshaller" />
</bean>
<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller"/>
</beans>
此应用程序上下文使用 XStream,但我们可以使用任何其他编组器 本章后面介绍的实例。请注意,默认情况下,XStream 不需要 任何进一步的配置,因此 Bean 定义相当简单。还要注意,实现 bothand,所以我们可以在 应用。XStreamMarshaller
Marshaller
Unmarshaller
xstreamMarshaller
marshaller
unmarshaller
此示例应用程序生成以下文件:settings.xml
<?xml version="1.0" encoding="UTF-8"?>
<settings foo-enabled="false"/>
6.4.XML 配置命名空间
您可以使用 OXM 命名空间中的标记更简洁地配置编组器。 要使这些标记可用,您必须首先在 XML 配置文件的前导码。以下示例演示如何执行此操作:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:oxm="http://www.springframework.org/schema/oxm"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/oxm https://www.springframework.org/schema/oxm/spring-oxm.xsd">
引用架构。 |
指定架构位置。 |
该架构使以下元素可用:
- jaxb2-marshaller
- jibx-marshaller
每个标签在其各自的编组部分进行了解释。不过,举个例子, JAXB2 编组器的配置可能类似于以下内容:
<oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/>
6.5. JAXB
JAXB 绑定编译器将 W3C XML 模式转换为一个或多个 Java 类、文件以及可能的一些资源文件。JAXB还提供了一种方法 从带注释的 Java 类生成模式。jaxb.properties
Spring 支持 JAXB 2.0 API 作为 XML 编组策略,遵循Marshaller 和Unmarshaller 中描述的 and接口。 相应的集成类驻留在包中。Marshaller
Unmarshaller
org.springframework.oxm.jaxb
6.5.1. 使用Jaxb2Marshaller
该类实现了两个 Spring 的沙接口。它需要一个上下文路径才能运行。可以通过设置属性来设置上下文路径。上下文路径是以冒号分隔的 Java 包的列表 包含架构派生类的名称。它还提供房产, 这允许您设置编组器支持的类数组。图式 通过为 Bean 指定一个或多个模式资源来执行验证,如以下示例所示:Jaxb2Marshaller
Marshaller
Unmarshaller
contextPath
classesToBeBound
<beans>
<bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>org.springframework.oxm.jaxb.Flight</value>
<value>org.springframework.oxm.jaxb.Flights</value>
</list>
</property>
<property name="schema" value="classpath:org/springframework/oxm/schema.xsd"/>
</bean>
...
</beans>
XML 配置命名空间
元素配置一个, 如以下示例所示:jaxb2-marshaller
org.springframework.oxm.jaxb.Jaxb2Marshaller
<oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/>
或者,您可以使用 child 元素提供要绑定到编组器的类列表:class-to-be-bound
<oxm:jaxb2-marshaller id="marshaller">
<oxm:class-to-be-bound name="org.springframework.ws.samples.airline.schema.Airport"/>
<oxm:class-to-be-bound name="org.springframework.ws.samples.airline.schema.Flight"/>
...
</oxm:jaxb2-marshaller>
下表描述了可用属性:
属性 | 描述 | 必填 |
| 编组器的 ID | 不 |
| JAXB 上下文路径 | 不 |
6.6. 吉布克斯
JiBX框架提供了一个类似于Hibernate为ORM提供的解决方案:一个 绑定定义定义了如何将 Java 对象转换为 Java 对象的规则 在准备绑定并编译类之后,JiBX 绑定编译器 增强类文件并添加代码以处理类的转换实例 从或到 XML。
有关 JiBX 的更多信息,请参阅JiBX 网站 网站。Spring 集成类驻留在包中。org.springframework.oxm.jibx
6.6.1. 使用JibxMarshaller
该类实现了 theand 接口。要操作,它需要要编组的类的名称,您可以 使用属性设置。(可选)可以通过设置属性来设置绑定名称。在下面的示例中,我们绑定类:JibxMarshaller
Marshaller
Unmarshaller
targetClass
bindingName
Flights
<beans>
<bean id="jibxFlightsMarshaller" class="org.springframework.oxm.jibx.JibxMarshaller">
<property name="targetClass">org.springframework.oxm.jibx.Flights</property>
</bean>
...
</beans>
为单个类配置的 AIS。如果要封送多个 类,则必须使用不同的属性值配置多个实例。JibxMarshaller
JibxMarshaller
targetClass
XML 配置命名空间
Thetag 配置 a, 如以下示例所示:jibx-marshaller
org.springframework.oxm.jibx.JibxMarshaller
<oxm:jibx-marshaller id="marshaller" target-class="org.springframework.ws.samples.airline.schema.Flight"/>
下表描述了可用属性:
属性 | 描述 | 必填 |
| 编组器的 ID | 不 |
| 此编组器的目标类 | 是的 |
| 此封送器使用的绑定名称 | 不 |
6.7. XStream
XStream 是一个简单的库,用于将对象序列化为 XML 并再次序列化回来。它没有 需要任何映射并生成干净的 XML。
有关 XStream 的详细信息,请参阅XStream 网站。Spring 集成类驻留在包中。org.springframework.oxm.xstream
6.7.1. 使用XStreamMarshaller
不需要任何配置,可以在 直接应用程序上下文。要进一步自定义 XML,您可以设置别名映射, 它由映射到类的字符串别名组成,如以下示例所示:XStreamMarshaller
<beans>
<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
<property name="aliases">
<props>
<prop key="Flight">org.springframework.oxm.xstream.Flight</prop>
</props>
</property>
</bean>
...
</beans>
7. 附录
7.1.XML 模式
附录的这一部分列出了用于数据访问的 XML 架构,包括:
- tx架构
- jdbc模式
7.1.1. 模式tx
标签处理在Spring的全面支持中配置所有这些豆子 用于交易。这些标签在标题为“事务管理”的章节中介绍。tx
为了完整起见,要使用架构中的元素,您需要具有 Spring XML 配置文件顶部的以下序言。文本中的 以下代码片段引用了正确的架构,以便命名空间中的标记 可供您使用:tx
tx
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- bean definitions here -->
</beans>
声明命名空间的用法。 |
指定位置(与其他架构位置)。 |
7.1.2. 模式jdbc
这些元素允许您快速配置嵌入式数据库或初始化 现有数据源。这些元素分别记录在嵌入式数据库支持和初始化数据源中。jdbc
要使用架构中的元素,您需要在 顶部的 Spring XML 配置文件。以下代码段引用中的文本 正确的架构,以便命名空间中的元素可供您使用:jdbc
jdbc
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jdbc https://www.springframework.org/schema/jdbc/spring-jdbc.xsd">
<!-- bean definitions here -->
</beans>
声明命名空间的用法。 |
指定位置(与其他架构位置)。 |