spring框架学习 - Data Access 补充

接上一篇博客:https://blog.csdn.net/qq_43605444/article/details/122086818?spm=1001.2014.3001.5502

二、DAO 支持

Spring 中的数据访问对象 (DAO) 支持旨在以一致的方式轻松使用数据访问技术(例如 JDBC、Hibernate 或 JPA)。 这使您可以相当轻松地在上述持久性技术之间切换,并且还可以让您编写代码而不必担心捕获特定于每种技术的异常。

1、一致的异常层次结构

Spring 提供了从特定于技术的异常(例如 SQLException)到其自己的异常类层次结构的便捷转换,该类层次结构将 DataAccessException 作为根异常。 这些异常包含了原始异常,因此永远不会有丢失有关可能出错的任何信息的风险。

除了 JDBC 异常之外,Spring 还可以包装 JPA 和 Hibernate 特定的异常,将它们转换为一组重点关注的运行时异常。 这使您可以仅在适当的层中处理大多数不可恢复的持久性异常,而无需在 DAO 中使用烦人的样板捕获和抛出块和异常声明。 (不过,您仍然可以在任何需要的地方捕获和处理异常。)如上所述,JDBC 异常(包括特定于数据库的方言)也转换为相同的层次结构,这意味着您可以在一致的编程模型中使用 JDBC 执行某些操作 。

前面的讨论适用于 Spring 对各种 ORM 框架的支持中的各种模板类。 如果使用基于拦截器的类,应用程序必须自己处理 HibernateExceptions 和 PersistenceExceptions,最好分别委托给 SessionFactoryUtils 的 convertHibernateAccessException(…) 或 convertJpaAccessException(…) 方法。 这些方法将异常转换为与 org.springframework.dao 异常层次结构中的异常兼容的异常。 由于 PersistenceExceptions 是未经检查的,它们也可能被抛出(尽管牺牲了在异常方面的通用 DAO 抽象)。

下图显示了 Spring 提供的异常层次结构。 (请注意,图中详述的类层次结构仅显示了整个 DataAccessException 层次结构的一个子集。)
spring框架学习 - Data Access 补充_第1张图片

2、用于配置 DAO 或 Repository 类的注解

保证您的数据访问对象 (DAO) 或存储库提供异常转换的最佳方法是使用 @Repository 注解。 此注解还允许组件扫描支持查找和配置您的 DAO 和存储库,而无需为它们提供 XML 配置条目。 下面的例子展示了如何使用@Repository 注解:

@Repository 
public class SomeMovieFinder implements MovieFinder {
    // ...
}

任何 DAO 或存储库实现都需要访问持久性资源,具体取决于所使用的持久性技术。 例如,基于 JDBC 的存储库需要访问 JDBC 数据源,基于 JPA 的存储库需要访问 EntityManager。 完成此操作的最简单方法是使用 @Autowired、@Inject、@Resource 或 @PersistenceContext注解之一注入此资源依赖项。 以下示例适用于 JPA 存储库:

@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 支持。 您可以将 DataSource 注入到初始化方法或构造函数中,您可以在其中使用此 DataSource 创建 JdbcTemplate 和其他数据访问支持类(例如 SimpleJdbcCall 等)。 以下示例自动装配数据源:

@Repository
public class JdbcMovieFinder implements MovieFinder {

    private JdbcTemplate jdbcTemplate;

    @Autowired
    public void init(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    // ...
}

有关如何配置应用程序上下文以利用这些注释的详细信息,请参阅每种持久性技术的具体覆盖范围。

三、使用 JDBC 进行数据访问

Spring Framework JDBC 抽象提供的 value 可能最好通过下表中列出的操作序列来展示。 该表显示了 Spring 负责哪些操作以及哪些操作是您的责任。

操作 Spring
定义连接参数。 X
打开连接。 X
指定 SQL 语句。 X
声明参数并提供参数值 X
准备并运行语句。 X
设置循环以迭代结果(如果有)。 X
为每次迭代做工作。 X
处理任何异常。 X
处理事务。 X
关闭连接、语句和结果集。 X

Spring Framework 负责处理使 JDBC 成为如此乏味的 API 的所有低级细节。

1、选择 JDBC 数据库访问方法

您可以从多种方法中进行选择,以形成 JDBC 数据库访问的基础。 除了 JdbcTemplate 的三种风格之外,新的 SimpleJdbcInsert 和 SimpleJdbcCall 方法优化了数据库元数据,RDBMS Object 风格采用了类似于 JDO Query 设计的更加面向对象的方法。 一旦开始使用其中一种方法,您仍然可以混合搭配以包含来自不同方法的功能。 所有方法都需要符合 JDBC 2.0 的驱动程序,一些高级功能需要 JDBC 3.0 驱动程序。

  • JdbcTemplate 是经典且最受欢迎的 Spring JDBC 方法。 这种“最低级”方法和所有其他方法都在幕后使用 JdbcTemplate
  • NamedParameterJdbcTemplate 包装了一个 JdbcTemplate 来提供命名参数而不是传统的 JDBC ? 占位符。 当 SQL 语句有多个参数时,此方法可提供更好的文档和易用性。
  • SimpleJdbcInsert 和 SimpleJdbcCall 优化数据库元数据以限制必要配置的数量。 这种方法简化了编码,因此您只需提供表或过程的名称,并提供与列名称匹配的参数映射。 这仅在数据库提供足够的元数据时才有效。 如果数据库不提供此元数据,则必须提供参数的显式配置。
  • RDBMS 对象(包括 MappingSqlQuery、SqlUpdate 和 StoredProcedure)要求您在数据访问层初始化期间创建可重用和线程安全的对象。 此方法模仿 JDO 查询,在其中定义查询字符串、声明参数并编译查询。 一旦你这样做了,execute(… )、update(… ) 和 findObject(… ) 方法可以用不同的参数值多次调用。

2、包层次结构

Spring Framework 的 JDBC 抽象框架由四个不同的包组成:

  • core:org.springframework.jdbc.core 包包含 JdbcTemplate 类及其各种回调接口,以及各种相关类。 名为 org.springframework.jdbc.core.simple 的子包包含 SimpleJdbcInsert 和 SimpleJdbcCall 类。 另一个名为 org.springframework.jdbc.core.namedparam 的子包包含 NamedParameterJdbcTemplate 类和相关的支持类。 请参阅使用 JDBC 核心类来控制基本 JDBC 处理和错误处理、JDBC 批处理操作和使用 SimpleJdbc 类简化 JDBC 操作。
  • datasource : org.springframework.jdbc.datasource 包包含一个实用程序类,用于轻松访问数据源和各种简单的 datasource 实现,您可以使用它们在 Java EE 容器之外测试和运行未修改的 JDBC 代码。 名为 org.springfamework.jdbc.datasource.embedded 的子包支持使用 Java 数据库引擎(如 HSQL、H2 和 Derby)创建嵌入式数据库。 请参阅控制数据库连接和嵌入式数据库支持。
  • object:org.springframework.jdbc.object 包包含将 RDBMS 查询、更新和存储过程表示为线程安全、可重用对象的类。 请参阅将 JDBC 操作建模为 Java 对象。 这种方法是由 JDO 建模的,尽管查询返回的对象自然而然地与数据库断开了连接。 这种较高级别的 JDBC 抽象依赖于 org.springframework.jdbc.core 包中的较低级别的抽象。
  • support :org.springframework.jdbc.support 包提供 SQLException 转换功能和一些实用程序类。 JDBC 处理过程中抛出的异常被转换为 org.springframework.dao 包中定义的异常。 这意味着使用 Spring JDBC 抽象层的代码不需要实现 JDBC 或 RDBMS 特定的错误处理。 所有已翻译的异常都未经检查,这使您可以选择捕获可以从中恢复的异常,同时让其他异常传播给调用者。 请参阅使用 SQLExceptionTranslator。

3、使用 JDBC 核心类来控制基本的 JDBC 处理和错误处理

本节介绍如何使用 JDBC 核心类来控制基本的 JDBC 处理,包括错误处理。 它包括以下主题:

  • 使用 JdbcTemplate
  • 使用 NamedParameterJdbcTemplate
  • 使用 SQLExceptionTranslator
  • 运行语句
  • 运行查询
  • 更新数据库
  • 检索自动生成的密钥

3.1 使用 JdbcTemplate

JdbcTemplate 是 JDBC 核心包中的中心类。 它处理资源的创建和释放,帮助您避免常见错误,例如忘记关闭连接。 它执行核心 JDBC 工作流的基本任务(如语句创建和执行),留下应用程序代码来提供 SQL 和提取结果。 JdbcTemplate 类:

  • 运行 SQL 查询
  • 更新语句和存储过程调用
  • 对 ResultSet 实例执行迭代并提取返回的参数值。
  • 捕获 JDBC 异常并将它们转换为 org.springframework.dao 包中定义的通用的、信息更丰富的异常层次结构。 (请参阅一致的异常层次结构。)

当你在你的代码中使用 JdbcTemplate 时,你只需要实现回调接口,给它们一个明确定义的契约。 给定 JdbcTemplate 类提供的 Connection,PreparedStatementCreator 回调接口创建一个准备好的语句,提供 SQL 和任何必要的参数。 CallableStatementCreator 接口也是如此,它创建可调用语句。 RowCallbackHandler 接口从 ResultSet 的每一行中提取值。

您可以通过直接实例化 DataSource 引用在 DAO 实现中使用 JdbcTemplate,或者您可以在 Spring IoC 容器中配置它并将其作为 bean 引用提供给 DAO。

DataSource 应始终配置为 Spring IoC 容器中的 bean。 在第一种情况下,bean 直接提供给服务; 在第二种情况下,它被提供给准备好的模板。

该类发出的所有 SQL 都记录在 DEBUG 级别下与模板实例的完全限定类名(通常是 JdbcTemplate,但如果使用 JdbcTemplate 类的自定义子类)对应的类别下。

以下部分提供了 JdbcTemplate 用法的一些示例。 这些示例并不是 JdbcTemplate 公开的所有功能的详尽列表。 请参阅随附的 javadoc。

3.1.1 查询 (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;
        });

如果最后两个代码片段确实存在于同一个应用程序中,那么删除两个 RowMapper lambda 表达式中存在的重复项并将它们提取到一个字段中是有意义的,然后可以根据需要由 DAO 方法引用该字段。 例如,将前面的代码片段编写如下可能会更好:

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

3.1.2 用 JdbcTemplate 更新(INSERT、UPDATE 和 DELETE)

您可以使用 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));

3.1.3 其他 JdbcTemplate 操作

您可以使用 execute(…) 方法来运行任意 SQL。 因此,该方法通常用于 DDL 语句。 它重载了带有回调接口、绑定变量数组等的变体。 以下示例创建一个表:

this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");

以下示例调用存储过程:

this.jdbcTemplate.update(
        "call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
        Long.valueOf(unionId));

稍后将介绍更复杂的存储过程支持。

3.1.4 JdbcTemplate 最佳实践

JdbcTemplate 类的实例在配置后是线程安全的。 这很重要,因为这意味着您可以配置 JdbcTemplate 的单个实例,然后将此共享引用安全地注入多个 DAO(或存储库)。 JdbcTemplate 是有状态的,因为它维护对 DataSource 的引用,但此状态不是会话状态。

使用 JdbcTemplate 类(和关联的 NamedParameterJdbcTemplate 类)时的常见做法是在 Spring 配置文件中配置数据源,然后将共享数据源 bean 依赖注入到 DAO 类中。 JdbcTemplate 是在数据源的 setter 中创建的。 这会导致类似于以下内容的 DAO:

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 配置:


<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 注解该类(这使其成为组件扫描的候选对象)并使用 @Autowired 注解DataSource setter 方法。 以下示例显示了如何执行此操作:

@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 配置:


<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">

    
    <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 的 JdbcDaoSupport 类并且您的各种 JDBC 支持的 DAO 类从它扩展而来,您的子类将从 JdbcDaoSupport 类继承 setDataSource(…) 方法。 您可以选择是否从该类继承。 JdbcDaoSupport 类只是为了方便而提供的。

无论您选择使用(或不使用)上述哪种模板初始化样式,每次要运行 SQL 时都很少需要创建 JdbcTemplate 类的新实例。 配置后, JdbcTemplate 实例是线程安全的。 如果您的应用程序访问多个数据库,您可能需要多个 JdbcTemplate 实例,这需要多个数据源,随后需要多个不同配置的 JdbcTemplate 实例。

3.2 使用 NamedParameterJdbcTemplate

NamedParameterJdbcTemplate 类通过使用命名参数添加了对 JDBC 语句编程的支持,而不是仅使用经典占位符 (’?’) 参数对 JDBC 语句进行编程。 NamedParameterJdbcTemplate 类包装了一个 JdbcTemplate 并委托给包装的 JdbcTemplate 来完成它的大部分工作。 本节仅描述 NamedParameterJdbcTemplate 类中与 JdbcTemplate 本身不同的部分 ——即,使用命名参数编写 JDBC 语句。 以下示例显示了如何使用 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)中的相应值。

或者,您可以使用基于 Map 的样式将命名参数及其对应的值传递给 NamedParameterJdbcTemplate 实例。 由 NamedParameterJdbcOperations 公开并由 NamedParameterJdbcTemplate 类实现的其余方法遵循类似的模式,此处不再赘述。

下面的例子展示了 Map-based 样式的使用:

// 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);
}

NamedParameterJdbcTemplate(存在于同一个 Java 包中)相关的一个很好的特性是 SqlParameterSource 接口。 您已经在前面的代码片段之一(MapSqlParameterSource 类)中看到了此接口的实现示例。 SqlParameterSource 是 NamedParameterJdbcTemplate 的命名参数值的来源。 MapSqlParameterSource 类是一个简单的实现,它是一个围绕 java.util.Map 的适配器,其中键是参数名称,值是参数值。

另一个 SqlParameterSource 实现是 BeanPropertySqlParameterSource 类。 此类包装任意 JavaBean(即,遵守 JavaBean 约定的类的实例)并使用包装的 JavaBean 的属性作为命名参数值的来源。

以下示例显示了一个典型的 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...

}

下面的示例使用 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() 方法通过 JdbcOperations 接口访问封装的 JdbcTemplate。

有关在应用程序上下文中使用 NamedParameterJdbcTemplate 类的指南,另请参阅 JdbcTemplate 最佳实践。

3.3 使用 SQLExceptionTranslator

SQLExceptionTranslator 是一个由类实现的接口,可以在 SQLExceptions 和 Spring 自己的 org.springframework.dao.DataAccessException 之间进行转换,它与数据访问策略无关。 实现可以是通用的(例如,对 JDBC 使用 SQLState 代码)或专有的(例如,使用 Oracle 错误代码)以获得更高的精度。

SQLErrorCodeSQLExceptionTranslator 是默认使用的 SQLExceptionTranslator 的实现。 此实现使用特定的供应商代码。 它比 SQLState 实现更精确。 错误代码转换基于名为 SQLErrorCodes 的 JavaBean 类型类中保存的代码。 此类由 SQLErrorCodesFactory 创建和填充,它(顾名思义)是一个工厂,用于根据名为 sql-error-codes.xml 的配置文件的内容创建 SQLErrorCodes。 该文件由供应商代码填充,并基于从 DatabaseMetaData 中获取的 DatabaseProductName。 使用您正在使用的实际数据库的代码。

SQLErrorCodeSQLExceptionTranslator 按以下顺序应用匹配规则:

  1. 由子类实现的任何自定义转换。 通常,使用提供的具体 SQLErrorCodeSQLExceptionTranslator,因此此规则不适用。 它仅适用于您实际提供了子类实现的情况。
  2. 作为 SQLErrorCodes 类的 customSqlExceptionTranslator 属性提供的 SQLExceptionTranslator 接口的任何自定义实现。
  3. 搜索 CustomSQLErrorCodesTranslation 类(为 SQLErrorCodes 类的 customTranslations 属性提供)的实例列表以查找匹配项。
  4. 应用错误代码匹配。
  5. 使用回退转换器。 SQLExceptionSubclassTranslator 是默认的回退转换器。 如果此转换不可用,则下一个回退转换器是 SQLStateSQLExceptionTranslator。

SQLErrorCodesFactory 默认用于定义错误代码和自定义异常转换。 它们是在类路径中名为 sql-error-codes.xml 的文件中查找的,并且匹配的 SQLErrorCodes 实例根据正在使用的数据库的数据库元数据中的数据库名称进行定位。

您可以扩展 SQLErrorCodeSQLExceptionTranslator,如以下示例所示:

public class CustomSQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator {

    protected DataAccessException customTranslate(String task, String sql, SQLException sqlEx) {
        if (sqlEx.getErrorCode() == -12345) {
            return new DeadlockLoserDataAccessException(task, sqlEx);
        }
        return null;
    }
}

在前面的示例中,特定的错误代码 (-12345) 已被转换,而其他错误则由默认的转换器实现进行转换。 要使用此自定义转换器,您必须通过方法 setExceptionTranslator 将其传递给 JdbcTemplate,并且必须使用此 JdbcTemplate 进行需要此转换器的所有数据访问处理。 以下示例显示了如何使用此自定义转换器:

private JdbcTemplate jdbcTemplate;

public void setDataSource(DataSource dataSource) {

    // create a JdbcTemplate and set data source
    this.jdbcTemplate = new JdbcTemplate();
    this.jdbcTemplate.setDataSource(dataSource);

    // create a custom translator and set the DataSource for the default translation lookup
    CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator();
    tr.setDataSource(dataSource);
    this.jdbcTemplate.setExceptionTranslator(tr);

}

public void updateShippingCharge(long orderId, long pct) {
    // use the prepared JdbcTemplate for this update
    this.jdbcTemplate.update("update orders" +
        " set shipping_charge = shipping_charge * ? / 100" +
        " where id = ?", pct, orderId);
}

自定义转换器被传递一个数据源,以便在 sql-error-codes.xml 中查找错误代码。

3.4 运行语句

运行 SQL 语句只需要很少的代码。 您需要一个 DataSource 和一个 JdbcTemplate,包括随 JdbcTemplate 提供的便利方法。 以下示例显示了创建新表的最小但功能齐全的类需要包含的内容:

import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;

public class ExecuteAStatement {

    private JdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public void doExecute() {
        this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
    }
}

3.5 运行查询

某些查询方法返回单个值。 要从一行中检索计数或特定值,请使用 queryForObject(…)。 后者将返回的 JDBC 类型转换为作为参数传入的 Java 类。 如果类型转换无效,则抛出 InvalidDataAccessApiUsageException。 以下示例包含两种查询方法,一种用于 int,另一种用于查询 String:

import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;

public class RunAQuery {

    private JdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public int getCount() {
        return this.jdbcTemplate.queryForObject("select count(*) from mytable", Integer.class);
    }

    public String getName() {
        return this.jdbcTemplate.queryForObject("select name from mytable", String.class);
    }
}

除了单结果查询方法之外,还有几种方法返回一个列表,其中包含查询返回的每一行的条目。 最通用的方法是 queryForList(…),它返回一个 List,其中每个元素都是一个 Map,其中包含每个列的一个条目,使用列名作为键。 如果您在前面的示例中添加一个方法来检索所有行的列表,则可能如下所示:

private JdbcTemplate jdbcTemplate;

public void setDataSource(DataSource dataSource) {
    this.jdbcTemplate = new JdbcTemplate(dataSource);
}

public List<Map<String, Object>> getList() {
    return this.jdbcTemplate.queryForList("select * from mytable");
}

返回的列表类似于以下内容:

[{name=Bob, id=1}, {name=Mary, id=2}]

3.6 更新数据库

以下示例更新某个主键的列:

import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;

public class ExecuteAnUpdate {

    private JdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public void setName(int id, String name) {
        this.jdbcTemplate.update("update mytable set name = ? where id = ?", name, id);
    }
}

在前面的示例中,SQL 语句具有行参数的占位符。 您可以将参数值作为可变参数传递,或者作为对象数组传递。 因此,您应该在原始包装类中显式地包装原语,或者您应该使用自动装箱。

3.7 获取自动生成的密钥

update() 便捷方法支持检索数据库生成的主键。 这种支持是 JDBC 3.0 标准的一部分。 有关详细信息,请参阅规范的第 13.6 章。 该方法将 PreparedStatementCreator 作为其第一个参数,这就是指定所需插入语句的方式。 另一个参数是 KeyHolder,它包含从更新成功返回时生成的密钥。 没有标准的单一方法来创建适当的 PreparedStatement(这解释了为什么方法签名是这样的)。 以下示例适用于 Oracle,但可能不适用于其他平台:

final String INSERT_SQL = "insert into my_test (name) values(?)";
final String name = "Rob";

KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
    PreparedStatement ps = connection.prepareStatement(INSERT_SQL, new String[] { "id" });
    ps.setString(1, name);
    return ps;
}, keyHolder);

// keyHolder.getKey() now contains the generated key

4、控制数据库连接

4.1 使用 DataSource

Spring 通过 DataSource 获取到数据库的连接。 DataSource 是 JDBC 规范的一部分,是一个通用的连接工厂。 它让容器或框架对应用程序代码隐藏连接池和事务管理问题。 作为开发人员,您无需了解有关如何连接到数据库的详细信息。 这是设置数据源的管理员的责任。 您很可能在开发和测试代码时同时担任这两个角色,但您不必知道生产数据源是如何配置的。

在使用 Spring 的 JDBC 层时,可以从 JNDI 获取数据源,也可以使用第三方提供的连接池实现自行配置。 传统的选择是 Apache Commons DBCP 和带有 bean 样式 DataSource 类的 C3P0; 对于现代 JDBC 连接池,请考虑使用 HikariCP 及其构建器样式的 API。

您应该仅将 DriverManagerDataSource 和 SimpleDriverDataSource 类(包含在 Spring 发行版中)用于测试目的! 当发出多个连接请求时,这些变体不提供池化并且性能不佳。

以下部分使用 Spring 的 DriverManagerDataSource 实现。 稍后将介绍其他几个 DataSource 变体。

要配置 DriverManagerDataSource

  1. 获取与 DriverManagerDataSource 的连接,就像通常获取 JDBC 连接一样。
  2. 指定 JDBC 驱动程序的完全限定类名,以便 DriverManager 可以加载驱动程序类。
  3. 提供因 JDBC 驱动程序而异的 URL。 (有关正确值,请参阅驱动程序的文档。)
  4. 提供用户名和密码以连接到数据库。

以下示例显示了如何在 Java 中配置 DriverManagerDataSource:

DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");

以下示例显示了相应的 XML 配置:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <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"/>

接下来的两个示例显示了 DBCP 和 C3P0 的基本连接和配置。 要了解更多有助于控制池功能的选项,请参阅相应连接池实现的产品文档。

以下示例显示了 DBCP 配置:

<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"/>

以下示例显示了 C3P0 配置:

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="driverClass" value="${jdbc.driverClassName}"/>
    <property name="jdbcUrl" value="${jdbc.url}"/>
    <property name="user" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
bean>

<context:property-placeholder location="jdbc.properties"/>

4.2 使用 DataSourceUtils

DataSourceUtils 类是一个方便且功能强大的帮助器类,它提供静态方法以从 JNDI 获取连接并在必要时关闭连接。 它支持线程绑定连接,例如 DataSourceTransactionManager。

4.3 实施 SmartDataSource

SmartDataSource 接口应该由可以提供到关系数据库的连接的类来实现。 它扩展了 DataSource 接口,让使用它的类查询在给定操作后是否应关闭连接。 当您知道需要重用连接时,这种用法非常有效。

4.4 扩展 AbstractDataSource

AbstractDataSource 是 Spring 的 DataSource 实现的抽象基类。 它实现了所有 DataSource 实现通用的代码。 如果您编写自己的 DataSource 实现,则应该扩展 AbstractDataSource 类。

4.5 使用 SingleConnectionDataSource

SingleConnectionDataSource 类是 SmartDataSource 接口的一个实现,它包装了一个每次使用后都不会关闭的 Connection。 这不支持多线程。

如果任何客户端代码基于池连接的假设调用关闭(如使用持久性工具时),则应将抑制关闭属性设置为 true。 此设置返回一个封装物理连接的关闭抑制代理。 请注意,您不能再将其转换为本机 Oracle Connection 或类似对象。

SingleConnectionDataSource 主要是一个测试类。 结合简单的 JNDI 环境,它通常可以轻松测试应用程序服务器外部的代码。 与 DriverManagerDataSource 相比,它始终重用相同的连接,避免过度创建物理连接。

4.6 使用 DriverManagerDataSource

DriverManagerDataSource 类是标准 DataSource 接口的实现,它通过 bean 属性配置一个普通的 JDBC 驱动程序,并每次返回一个新的 Connection。

此实现对于 Java EE 容器之外的测试和独立环境非常有用,可以作为 Spring IoC 容器中的 DataSource bean 或与简单的 JNDI 环境结合使用。 假设池的 Connection.close() 调用关闭连接,因此任何数据源感知持久性代码都应该工作。 但是,使用 JavaBean 样式的连接池(例如 commons-dbcp)非常简单,即使在测试环境中也是如此,因此使用这种连接池几乎总是比使用 DriverManagerDataSource 更可取。

4.7 使用 TransactionAwareDataSourceProxy

TransactionAwareDataSourceProxy 是目标数据源的代理。 代理包装目标 DataSource 以添加对 Spring 管理的事务的感知。 在这方面,它类似于由 Java EE 服务器提供的事务性 JNDI 数据源。

很少需要使用此类,除非必须调用已经存在的代码并传递标准的 JDBC DataSource 接口实现。 在这种情况下,您仍然可以使用此代码,同时让此代码参与 Spring 托管事务。 通常最好使用更高级别的资源管理抽象来编写自己的新代码,例如 JdbcTemplate 或 DataSourceUtils。

有关更多详细信息,请参阅 TransactionAwareDataSourceProxy javadoc。

4.8 使用 DataSourceTransactionManager

DataSourceTransactionManager 类是单个 JDBC 数据源的 PlatformTransactionManager 实现。 它将来自指定数据源的 JDBC 连接绑定到当前正在执行的线程,可能允许每个数据源有一个线程连接。

应用程序代码需要通过 DataSourceUtils.getConnection(DataSource) 而不是 Java EE 的标准 DataSource.getConnection 来检索 JDBC 连接。 它抛出未经检查的 org.springframework.dao 异常,而不是经过检查的 SQLExceptions。 所有框架类(例如 JdbcTemplate)都隐式地使用此策略。 如果不与此事务管理器一起使用,则查找策略的行为与常见策略完全相同。 因此,它可以在任何情况下使用。

DataSourceTransactionManager 类支持作为适当的 JDBC 语句查询超时应用的自定义隔离级别和超时。 为了支持后者,应用程序代码必须使用 JdbcTemplate 或为每个创建的语句调用 DataSourceUtils.applyTransactionTimeout(…) 方法。

在单一资源的情况下,您可以使用此实现代替 JtaTransactionManager,因为它不需要容器支持 JTA。 如果您坚持所需的连接查找模式,则在两者之间切换只是配置问题。 JTA 不支持自定义隔离级别。

文章参考:https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#dao

你可能感兴趣的:(spring框架,spring,java,maven,intellij-idea,后端)