Spring数据访问和数据访问层与业务或服务层之间的交互
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring数据访问和数据访问层与业务或服务层之间的交互相关的知识,希望对你有一定的参考价值。
2. DAO 支持
Spring 中的数据访问对象 (DAO) 支持旨在使其易于使用 以一致的方式访问数据访问技术(如 JDBC、Hibernate 或 JPA)。这 让您相当轻松地在上述持久性技术之间切换, 它还允许您编码,而不必担心捕获异常 特定于每种技术。
2.1. 一致的异常层次结构
Spring 提供了从特定于技术的异常的便捷转换,例如到它自己的异常类层次结构,它有 根异常。这些异常包装原始异常,因此永远不会 您可能会丢失有关可能出错的任何信息的任何风险。SQLException
DataAccessException
除了 JDBC 异常之外,Spring 还可以包装特定于 JPA 和 Hibernate 的异常, 将它们转换为一组集中的运行时异常。这使您可以处理大多数 仅在适当的层中出现不可恢复的持久性异常,而没有 DAO 中烦人的样板捕获和抛出块和异常声明。 (不过,您仍然可以在需要的任何位置捕获和处理异常。如上所述, JDBC 异常(包括特定于数据库的方言)也会转换为相同的 层次结构,这意味着您可以在一致的 JDBC 中执行一些操作 编程模型。
前面的讨论适用于 Spring 支持中的各种模板类 适用于各种 ORM 框架。如果使用基于侦听器的类,则应用程序必须 关心处理和本身,最好由 分别委托给理论方法。这些方法转换异常 与异常层次结构中的异常兼容的异常。未经检查,它们也可能被扔掉 (不过,在例外方面牺牲了通用的DAO抽象)。HibernateExceptions
PersistenceExceptions
convertHibernateAccessException(..)
convertJpaAccessException(..)
SessionFactoryUtils
org.springframework.dao
PersistenceExceptions
下图显示了 Spring 提供的异常层次结构。 (请注意,图中详述的类层次结构仅显示整个层次结构的子集。DataAccessException
2.2. 用于配置 DAO 或存储库类的注释
保证数据访问对象 (DAO) 或存储库提供的最佳方法 例外翻译是使用注释。此注释还 让组件扫描支持查找和配置您的 DAO 和存储库 而不必为它们提供 XML 配置条目。以下示例显示 如何使用注释:@Repository
@Repository
@Repository
public class SomeMovieFinder implements MovieFinder
// ...
注释。 |
任何 DAO 或存储库实现都需要访问持久性资源, 取决于所使用的持久性技术。例如,基于 JDBC 的存储库 需要访问 JDBC,基于 JPA 的存储库需要访问 。实现此目的的最简单方法是具有此资源依赖关系 通过使用其中一个,,oran注释注入。以下示例适用于 JPA 存储库:DataSource
EntityManager
@Autowired
@Inject
@Resource
@PersistenceContext
@Repository
public class JpaMovieFinder implements MovieFinder
@PersistenceContext
private EntityManager entityManager;
// ...
如果您使用经典的 Hibernate API,则可以注入,如下所示 示例显示:SessionFactory
@Repository
public class HibernateMovieFinder implements MovieFinder
private SessionFactory sessionFactory;
@Autowired
public void setSessionFactory(SessionFactory sessionFactory)
this.sessionFactory = sessionFactory;
// ...
我们在这里展示的最后一个示例是典型的 JDBC 支持。您可以注入到初始化方法或构造函数中,您将在其中使用 这。以下示例自动连线 a:DataSource
JdbcTemplate
SimpleJdbcCall
DataSource
DataSource
@Repository
public class JdbcMovieFinder implements MovieFinder
private JdbcTemplate jdbcTemplate;
@Autowired
public void init(DataSource dataSource)
this.jdbcTemplate = new JdbcTemplate(dataSource);
// ...
3. 使用 JDBC 访问数据
Spring Framework JDBC 抽象提供的价值也许最好地表现为 下表中概述的操作顺序。该表显示了哪些操作 Spring 照顾和哪些行动是你的责任。
表 4.春季JDBC - 谁做什么?
行动 | 春天 | 你 |
定义连接参数。 | X | |
打开连接。 | X | |
指定 SQL 语句。 | X | |
声明参数并提供参数值 | X | |
准备并运行语句。 | X | |
设置循环以循环访问结果(如果有)。 | X | |
完成每次迭代的工作。 | X | |
处理任何异常。 | X | |
处理事务。 | X | |
关闭连接、语句和结果集。 | X |
Spring 框架负责所有可以使 JDBC 成为 繁琐的 API。
3.1. 选择 JDBC 数据库访问方法
您可以在多种方法中进行选择,以构成 JDBC 数据库访问的基础。 除了三种风格之外,newand方法还优化了数据库元数据,而RDBMS对象样式采用了 更面向对象的方法类似于JDO查询设计。开始使用后 其中一种方法,您仍然可以混合和匹配以包含来自 不同的方法。所有方法都需要符合 JDBC 2.0 的驱动程序,并且某些 高级功能需要 JDBC 3.0 驱动程序。JdbcTemplate
SimpleJdbcInsert
SimpleJdbcCall
-
JdbcTemplate
是经典和最流行的春季JDBC方法。这 “最低级别”方法和所有其他方法都在幕后使用 Jdbc 模板。 -
NamedParameterJdbcTemplate
包装 ATO 提供命名参数 而不是传统的 JDBC 占位符。这种方法提供更好的 文档和 SQL 语句有多个参数时的易用性。JdbcTemplate
?
-
SimpleJdbcInsert
并优化数据库元数据以限制数量 必要的配置。此方法简化了编码,因此您需要 仅提供表或过程的名称,并提供参数匹配的映射 列名称。仅当数据库提供足够的元数据时,此操作才有效。如果 数据库不提供此元数据,您必须提供显式 参数的配置。SimpleJdbcCall
- RDBMS 对象 — 包括、和— 要求您在初始化期间创建可重用和线程安全的对象 数据访问层。此方法以 JDO 查询为模型,其中您可以定义您的 查询字符串,声明参数,然后编译查询。一旦你这样做了,,,方法就可以被称为多个 具有各种参数值的时间。
MappingSqlQuery
SqlUpdate
StoredProcedure
execute(…)
update(…)
findObject(…)
3.2. 软件包层次结构
Spring 框架的 JDBC 抽象框架由四个不同的包组成:
-
core
:包包含类及其 各种回调接口,以及各种相关类。名为 theandclasses 的子包包含 theandclass。另一个名为 的子包包含类和相关的支持类。请参阅使用 JDBC 核心类控制基本的 JDBC 处理和错误处理、JDBC 批处理操作和使用SimpleJdbc类简化 JDBC 操作。org.springframework.jdbc.core
JdbcTemplate
org.springframework.jdbc.core.simple
SimpleJdbcInsert
SimpleJdbcCall
org.springframework.jdbc.core.namedparam
NamedParameterJdbcTemplate
-
datasource
:该软件包包含一个易于访问的实用程序类和各种可用于的简单实现 在 Jakarta EE 容器之外测试和运行未修改的 JDBC 代码。一个子包 named提供对创建的支持 使用 Java 数据库引擎(如 HSQL、H2 和 Derby)嵌入数据库。请参阅控制数据库连接和嵌入式数据库支持。org.springframework.jdbc.datasource
DataSource
DataSource
org.springfamework.jdbc.datasource.embedded
-
object
:包包含表示 RDBMS 的类 查询、更新和存储过程作为线程安全、可重用的对象。请参阅将 JDBC 操作建模为 Java 对象。此方法由 JDO 建模,尽管对象由查询返回 自然与数据库断开连接。这种更高级别的 JDBC 抽象 取决于包中的较低级别抽象。org.springframework.jdbc.object
org.springframework.jdbc.core
-
support
:包提供翻译 功能和一些实用程序类。JDBC 处理期间引发的异常是 转换为包中定义的异常。这意味着 使用 Spring JDBC 抽象层的代码不需要实现 JDBC 或 特定于 RDBMS 的错误处理。所有翻译的异常均未选中,这为您提供了 捕获异常的选项,您可以从中恢复,同时让其他 异常将传播到调用方。请参见使用SQLExceptionTranslator。org.springframework.jdbc.support
SQLException
org.springframework.dao
3.3. 使用 JDBC 核心类来控制基本的 JDBC 处理和错误处理
本节介绍如何使用 JDBC 核心类来控制基本的 JDBC 处理。 包括错误处理。它包括以下主题:
- 使用Jdbc 模板
- 使用NamedParameterJdbcTemplate
- 使用SQLExceptionTranslator
- 运行语句
- 运行查询
- 更新数据库
- 检索自动生成的密钥
3.3.1. 使用JdbcTemplate
JdbcTemplate
是 JDBC 核心包中的中心类。它处理 创建和释放资源,这有助于避免常见错误,例如 忘记关闭连接。它执行核心 JDBC 的基本任务 工作流(例如语句创建和执行),保留应用程序代码以提供 SQL 并提取结果。班级:JdbcTemplate
- 运行 SQL 查询
- 更新语句和存储过程调用
- 执行迭代覆盖实例和返回参数值的提取。
ResultSet
- 捕获 JDBC 异常并将其转换为通用的、信息量更大的异常 包中定义的层次结构。(请参阅一致的异常层次结构。
org.springframework.dao
当你使用for你的代码时,你只需要实现回调 接口,为它们提供明确定义的协定。给定由类提供,回调接口创建一个准备好的 语句,提供 SQL 和任何必要的参数。接口也是如此,它创建可调用的语句。接口从 a 的每一行中提取值。JdbcTemplate
Connection
JdbcTemplate
PreparedStatementCreator
CallableStatementCreator
RowCallbackHandler
ResultSet
您可以通过直接实例化在 DAO 实现中使用 或者您可以在Spring IoC容器中配置它并将其提供给 DAO 作为豆子参考。JdbcTemplate
DataSource
此类发出的所有 SQL 都记录在类别下的级别 对应于模板实例的完全限定类名(通常,但如果使用 TheClass 的自定义子类,则可能会有所不同)。DEBUG
JdbcTemplate
JdbcTemplate
以下各节提供了一些用法示例。这些例子 不是公开的所有功能的详尽列表。 请参阅随之而来的javadoc了解这一点。JdbcTemplate
JdbcTemplate
查询 (SELECT
)
以下查询获取关系中的行数:
int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);
以下查询使用绑定变量
int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject(
"select count(*) from t_actor where first_name = ?", Integer.class, "Joe");
以下查询查找:String
String lastName = this.jdbcTemplate.queryForObject(
"select last_name from t_actor where id = ?",
String.class, 1212L);
以下查询查找并填充单个域对象:
Actor actor = jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?",
(resultSet, rowNum) ->
Actor newActor = new Actor();
newActor.setFirstName(resultSet.getString("first_name"));
newActor.setLastName(resultSet.getString("last_name"));
return newActor;
,
1212L);
以下查询查找并填充域对象列表:
List<Actor> actors = this.jdbcTemplate.query(
"select first_name, last_name from t_actor",
(resultSet, rowNum) ->
Actor actor = new Actor();
actor.setFirstName(resultSet.getString("first_name"));
actor.setLastName(resultSet.getString("last_name"));
return actor;
);
如果最后两个代码片段实际上存在于同一个应用程序中,它将使 删除 twolambda 表达式中存在的重复项,以及 将它们提取到单个字段中,然后可以根据需要由 DAO 方法引用。 例如,最好按如下方式编写前面的代码片段:RowMapper
private final RowMapper<Actor> actorRowMapper = (resultSet, rowNum) ->
Actor actor = new Actor();
actor.setFirstName(resultSet.getString("first_name"));
actor.setLastName(resultSet.getString("last_name"));
return actor;
;
public List<Actor> findAllActors()
return this.jdbcTemplate.query("select first_name, last_name from t_actor", actorRowMapper);
更新(,和)与INSERT
UPDATE
DELETE
JdbcTemplate
可以使用该方法执行插入、更新和删除操作。 参数值通常作为变量参数提供,或者作为对象数组提供。update(..)
下面的示例插入一个新条目:
this.jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling");
以下示例更新现有条目:
this.jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?",
"Banjo", 5276L);
下面的示例删除一个条目:
this.jdbcTemplate.update(
"delete from t_actor where id = ?",
Long.valueOf(actorId));
其他操作JdbcTemplate
您可以使用该方法运行任意 SQL。因此, 方法通常用于 DDL 语句。它被大量超载的变体 回调接口、绑定变量数组等。以下示例创建一个 桌子:execute(..)
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
下面的示例调用存储过程:
this.jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
Long.valueOf(unionId));
稍后将介绍更复杂的存储过程支持。
JdbcTemplate
最佳实践
一旦配置,类的实例是线程安全的。这是 很重要,因为这意味着您可以配置 aand 的单个实例,然后将此共享引用安全地注入多个 DAO(或存储库)。 Theis 有状态的,因为它保持对 a 的引用,但是 此状态不是会话状态。JdbcTemplate
JdbcTemplate
JdbcTemplate
DataSource
使用类(以及关联的NamedParameterJdbcTemplate类)时的常见做法是 在 Spring 配置文件中配置,然后进行依赖注入 将豆子共享到您的 DAO 类中。泰斯创建于 二传手。这会导致类似于以下内容的 DAO:JdbcTemplate
DataSource
DataSource
JdbcTemplate
DataSource
public class JdbcCorporateEventDao implements CorporateEventDao
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource)
this.jdbcTemplate = new JdbcTemplate(dataSource);
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
用注释类。 |
注释方法。 |
创建一个新的。 |
以下示例显示了相应的 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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="$jdbc.driverClassName"/>
<property name="url" value="$jdbc.url"/>
<property name="username" value="$jdbc.username"/>
<property name="password" value="$jdbc.password"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
</beans>
显式配置的替代方法是使用组件扫描和注释 支持依赖注入。在这种情况下,您可以使用(这使其成为组件扫描的候选者)对类进行注释并注释这些内容。 方法。以下示例演示如何执行此操作:@Repository
DataSource
@Autowired
@Repository
public class JdbcCorporateEventDao implements CorporateEventDao
private JdbcTemplate jdbcTemplate;
@Autowired
public void setDataSource(DataSource dataSource)
this.jdbcTemplate = new JdbcTemplate(dataSource);
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
用注释类。 |
构造函数注入的。 |
创建一个新的。 |
以下示例显示了相应的 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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- Scans within the base package of the application for @Component classes to configure as beans -->
<context:component-scan base-package="org.springframework.docs.test" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="$jdbc.driverClassName"/>
<property name="url" value="$jdbc.url"/>
<property name="username" value="$jdbc.username"/>
<property name="password" value="$jdbc.password"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
</beans>
如果你使用 Spring 的类和你的各种 JDBC 支持的 DAO 类 从它扩展,你的子类从类继承amethod。您可以选择是否从此类继承。该课程仅为方便起见而提供。JdbcDaoSupport
setDataSource(..)
JdbcDaoSupport
JdbcDaoSupport
无论您选择使用上述哪种模板初始化样式(或 not),很少需要为每个类创建一个新的实例 您想要运行 SQL 的时间。配置后,实例是线程安全的。 如果应用程序访问多个 数据库,您可能需要多个实例,这需要多个实例,随后需要多个不同的实例 已配置实例。JdbcTemplate
JdbcTemplate
JdbcTemplate
DataSources
JdbcTemplate
3.3.2. 使用NamedParameterJdbcTemplate
该类增加了对 JDBC 语句编程的支持 通过使用命名参数,而不是仅使用经典参数对 JDBC 语句进行编程 占位符 () 参数。该类包装和委托给包装以完成其大部分工作。这 部分仅介绍类中那些不同的区域 从自身 — 即通过使用命名对 JDBC 语句进行编程 参数。以下示例演示如何使用:NamedParameterJdbcTemplate
?
NamedParameterJdbcTemplate
JdbcTemplate
JdbcTemplate
NamedParameterJdbcTemplate
JdbcTemplate
NamedParameterJdbcTemplate
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource)
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
public int countOfActorsByFirstName(String firstName)
String sql = "select count(*) from T_ACTOR where first_name = :first_name";
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
请注意,在分配给变量的值和插入到变量(类型)中的相应值中使用了命名参数表示法。sql
namedParameters
MapSqlParameterSource
或者,您可以使用基于样式将命名参数及其相应的值传递给实例。其余的 由 TheAnd Class实现的方法遵循类似的模式,此处不涉及。NamedParameterJdbcTemplate
Map
NamedParameterJdbcOperations
NamedParameterJdbcTemplate
以下示例显示了基于样式的用法:Map
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource)
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
public int countOfActorsByFirstName(String firstName)
String sql = "select count(*) from T_ACTOR where first_name = :first_name";
Map<String, String> namedParameters = Collections.singletonMap("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
一个与(并且存在于相同的 Java包)是接口。你已经看过一个例子 此接口在前面的代码段之一 (Theclass) 中的实现。Anis 命名参数的来源 值为 a。该类是一个 简单的实现,即围绕 a 的适配器,其中键 是参数名称,值是参数值。NamedParameterJdbcTemplate
SqlParameterSource
MapSqlParameterSource
SqlParameterSource
NamedParameterJdbcTemplate
MapSqlParameterSource
java.util.Map
另一个实现是类。此类包装一个任意 JavaBean(即,一个类的实例 坚持 JavaBean 约定),并使用包装的 JavaBean 的属性作为源代码 的命名参数值。SqlParameterSource
BeanPropertySqlParameterSource
以下示例显示了一个典型的 JavaBean:
public class Actor
private Long id;
private String firstName;
private String lastName;
public String getFirstName()
return this.firstName;
public String getLastName()
return this.lastName;
public Long getId()
return this.id;
// setters omitted...
以下示例使用 ato 返回 前面示例中所示的类的成员:NamedParameterJdbcTemplate
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource)
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
public int countOfActors(Actor exampleActor)
// notice how the named parameters match the properties of the above Actor class
String sql = "select count(*) from T_ACTOR where first_name = :firstName and last_name = :lastName";
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
请记住,该类包装了一个经典模板。如果您需要访问包装实例才能访问 仅在类中存在的功能,您可以使用该方法访问包装的接口。NamedParameterJdbcTemplate
JdbcTemplate
JdbcTemplate
JdbcTemplate
getJdbcOperations()
JdbcTemplate
JdbcOperations
另请参阅JdbcTemplate最佳实践,了解有关在应用程序上下文中使用类的指南。NamedParameterJdbcTemplate
3.3.3. 使用SQLExceptionTranslator
SQLExceptionTranslator
是由可以翻译的类实现的接口 在斯普林斯之间, 这在数据访问策略方面是不可知的。实现可以是通用的(对于 例如,对 JDBC 使用 SQLState 代码)或专有代码(例如,使用 Oracle 错误) 代码),以提高精度。SQLException
org.springframework.dao.DataAccessException
SQLErrorCodeSQLExceptionTranslator
是默认使用的实现。此实现使用特定的供应商代码。它更多 比实施精确。错误代码翻译基于 代码保存在调用的 JavaBean 类型类中。此类已创建并 由 an 填充,它(顾名思义)是 创建基于命名的配置文件的内容。此文件填充有供应商代码,并基于取自。实际代码 使用您正在使用的数据库。SQLExceptionTranslator
SQLState
SQLErrorCodes
SQLErrorCodesFactory
SQLErrorCodes
sql-error-codes.xml
DatabaseProductName
DatabaseMetaData
按以下顺序应用匹配规则:SQLErrorCodeSQLExceptionTranslator
- 由子类实现的任何自定义翻译。通常,使用所提供的混凝土,因此此规则不适用。它 仅当您实际提供了子类实现时才适用。
SQLErrorCodeSQLExceptionTranslator
- 提供的接口的任何自定义实现 作为类的属性。
SQLExceptionTranslator
customSqlExceptionTranslator
SQLErrorCodes
- 搜索类的实例列表(为类的属性提供)以查找匹配项。
CustomSQLErrorCodesTranslation
customTranslations