下表概述的操作序列可能最好地显示了Spring框架JDBC
形象提供的值。该表显示了Spring负责哪些操作以及哪些操作是你须要做的。
Action | Spring | You |
---|---|---|
定义连贯参数 | X | |
关上连贯 | X | |
指定SQL语句。 | X | |
申明参数并提供参数值 | X | |
筹备并运行该语句。 | X | |
设置循环以遍历后果(如果有)。 | X | |
进行每次迭代的工作。 | X | |
解决任何异样。 | X | |
解决异样。 | X | |
敞开连贯,语句和后果集。 | X |
Spring框架负责解决使JDBC
成为如此乏味的API的所有底层细节。
3.1 抉择一种用于JDBC数据库拜访的办法
你能够抉择几种办法来形成JDBC
数据库拜访的根底。除了JdbcTemplate
的三种模式之外,新的SimpleJdbcInsert
和SimpleJdbcCall
办法还优化了数据库元数据,并且RDBMS
Object
款式采纳了一种相似于JDO Query
设计的面向对象的办法。一旦开始应用这些办法之一,你依然能够混合搭配以蕴含来自其余办法的性能。所有办法都须要兼容JDBC 2.0
的驱动程序,而某些高级性能则须要JDBC 3.0
驱动程序。
JdbcTemplate
是经典且最受欢迎的SpringJDBC
办法。这种最低级别的办法和所有其余办法都在幕后应用JdbcTemplate
。NamedParameterJdbcTemplate
包装了一个JdbcTemplate
来提供命名参数,而不是传统的JDBC
?
占位符。当你有多个SQL语句参数时,此办法可提供更好的文档编制和易用性。SimpleJdbcInsert
和SimpleJdbcCall
优化数据库元数据以限度必要的配置量。这种办法简化了编码,因而你仅须要提供表或过程(存储过程)的名称,并提供与列名称匹配的参数映射。仅当数据库提供足够的元数据时,此办法才无效。如果数据库不提供此元数据,则必须提供参数的显式配置。RDBMS
对象(包含MappingSqlQuery
、SqlUpdate
和StoredProcedure
)要求你在数据拜访层初始化期间创立可重用且线程平安的对象。此办法以JDO
查问为模型,其中定义查问字符串、申明参数并编译查问。实现后,能够应用各种参数值屡次调用execute(...)
、update(...)
和findObject(...)
办法。
3.2 包构造
Spring框架的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
和各种简略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
包中定义的异样。这意味着应用SpringJDBC
形象层的代码不须要实现JDBC
或RDBMS
特定的错误处理。所有转换的异样均为uncheck
异样,这使你能够抉择捕捉可从中复原的异样,同时将其余异样传递到调用方。请参见应用SQLExceptionTranslator。
3.3 应用JDBC外围类管制JDBC解决和错误处理
本节介绍如何应用JDBC
外围类管制JDBC
解决,包含错误处理。它包含以下主题:
- 应用
JdbcTemplate
- 应用
NamedParameterJdbcTemplate
- 应用
SQLExceptionTranslator
- 执行语句
- 执行查问
- 更新数据库
- 获取主动生成主键
3.3.1 应用JdbcTemplate
JdbcTemplate
是JDBC
的core
包中的外围类。它解决资源的创立和开释,这有助于你防止常见的谬误,例如遗记敞开连贯。它执行外围JDBC
工作流程的根本工作(例如,语句创立和执行),而使利用程序代码提供SQL并提取后果。JdbcTemplate
类:
- 运行SQL查问
- 更新语句和存储过程调用
- 对
ResultSet
实例执行迭代并提取返回的参数值。 - 捕捉
JDBC
异样并将其转换为org.springframework.dao
包中定义的通用、信息量更大的异样层次结构。 (请参见统一的异样层次结构)
当将JdbcTemplate
用于代码时,只需实现回调接口,即可为它们提供明确定义的约定。给定JdbcTemplate
类提供的Connection
、PreparedStatementCreator
回调接口将创立一条筹备好的语句,提供SQL和任何必要的参数。对于CallableStatementCreator
接口(创立可调用语句)也是如此。 RowCallbackHandler
接口从ResultSet
的每一行提取值。
你能够通过间接实例化DataSource
援用在DAO实现中应用JdbcTemplate
,也能够在Spring IoC容器中对其进行配置,并将其作为Bean援用提供给DAO。
应该始终在Spring IoC容器中将DataSource
配置为Bean。在第一种状况下,bean被间接提供给服务。在第二种状况下,将其提供给筹备好的模板。
此类收回的所有SQL都在DEBUG
级别下记录,该类别对应于模板实例的全限定类名称(通常为JdbcTemplate
,但如果应用JdbcTemplate
类的自定义子类,则可能有所不同)。
以下各节提供了JdbcTemplate
用法的一些示例。这些示例不是JdbcTemplate
裸露的所有性能的详尽列表。请参考附带的javadoc。
查问(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 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);}
应用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));
参考代码:org.liyong.dataaccess.starter.JdbcTemplateTransactionManagerIocContainer
其余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));
稍后将介绍更简单的存储过程反对。
JdbcTemplate最佳做法
一旦配置,JdbcTemplate
类的实例是线程平安的。它很重要,因为这意味着你能够配置JdbcTemplate
的单个实例,而后将该共享援用平安地注入到多个DAO(或存储库)中。JdbcTemplate
是有状态的,因为它保护对DataSource
的援用,然而此状态不是会话状态。
应用JdbcTemplate
类(和关联的NamedParameterJdbcTemplate
类)的常见做法是在Spring配置文件中配置DataSource
,而后将共享的DataSource
bean依赖注入到DAO类中。 JdbcTemplate
在数据源的设置器中创立。这导致相似于以下内容的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配置:
<?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
正文该类(这使其成为组件扫描的候选对象),并应用@Autowired
注解DataSource
setter办法。以下示例显示了如何执行此操作:
@Repository //1public class JdbcCorporateEventDao implements CorporateEventDao { private JdbcTemplate jdbcTemplate; @Autowired //2 public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); //3 } // JDBC-backed implementations of the methods on the CorporateEventDao follow...}
- 用
@Repository
正文类。 - 用
@Autowired
正文DataSource
setter办法。 - 应用
DataSource
创立一个新的JdbcTemplate
。
以下示例显示了相应的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的JdbcDaoSupport
类,并且从中扩大了各种JDBC反对的DAO类,则你的子类将从JdbcDaoSupport
类继承一个setDataSource(..)
办法。你能够抉择是否从此类继承。提供JdbcDaoSupport
类目标只是为了不便。
无论你抉择应用(或不应用)以上哪种模板初始化款式,都无需在每次要运行SQL时都创立JdbcTemplate
类的新实例。配置实现后,JdbcTemplate
实例是线程平安的。如果你的应用程序拜访多个数据库,你可能须要多个JdbcTemplate
实例,这须要多个数据源,而后须要多个不同配置的JdbcTemplate
实例。
参考代码:org.liyong.dataaccess.starter.JdbcTemplateBestTransactionManagerIocContainer
3.3.2 应用NamedParameterJdbcTemplate
与仅应用经典占位符(?
)编程的JDBC语句相同,NamedParameterJdbcTemplate
类减少了应用命名参数对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的格局的应用:
// 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);}
SqlParameterSource
接口是与NamedParameterJdbcTemplate
相干的一个不错的性能(并且存在于同一Java包中)。你曾经在后面的代码片段之一(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
。
另请参阅JdbcTemplate最佳实际,以获取无关在应用程序上下文中应用NamedParameterJdbcTemplate
类的领导。
参考代码:org.liyong.dataaccess.starter.NamedParameterTransactionManagerIocContainer
标记地位
3.3.3 应用SQLExceptionTranslator
SQLExceptionTranslator
是由能够在SQLExceptions
和Spring本人的org.springframework.dao.DataAccessException
之间进行转换的类实现的接口,该类与数据拜访策略无关。为了进步精度,实现能够是通用的(例如,应用SQLState
代码用于JDBC)或专有的(例如,应用Oracle错误代码)。
SQLErrorCodeSQLExceptionTranslator
是默认应用的SQLExceptionTranslator
的实现。此实现应用特定的供应商代码。它比SQLState
实现更准确。错误代码的转换基于一个名为SQLErrorCodes
的JavaBean类型类中的代码。此类由SQLErrorCodesFactory
创立和填充,SQLErrorCodesFactory
是工厂,用于基于名为sql-error-codes.xml
的配置文件的内容创立SQLErrorCodes
。此文件应用供应商代码填充,并基于从DatabaseMetaData
中获取的DatabaseProductName
填充。应用你正在应用的理论数据库的代码。
SQLErrorCodeSQLExceptionTranslator
按以下程序利用匹配规定:
- 子类实现的任何自定义转换。通常,将应用提供的具体
SQLErrorCodeSQLExceptionTranslator
,因而此规定不实用。仅当你的确提供了子类实现时,它才实用。 - 作为
SQLErrorCodes
类的customSqlExceptionTranslator
属性提供的SQLExceptionTranslator
接口的任何自定义实现。 - 搜寻
CustomSQLErrorCodesTranslation
类的实例列表(为SQLErrorCodes
类的customTranslations
属性提供),以查找匹配项。 - 错误代码匹配被利用。
- 应用后备转换器。
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.3.4 运行语句
运行SQL语句须要很少的代码。你须要一个数据源和一个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.3.5 运行查问
一些查询方法返回单个值。要从一行中检索计数或特定值,请应用queryForObject(..)。后者将返回的JDBC Type
转换为作为参数传递的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.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.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
3.4 管制数据库连贯
本节内容包含:
- 应用
DataSource
- 应用
DataSourceUtils
- 实现
SmartDataSource
- 扩张
AbstractDataSource
- 应用
SingleConnectionDataSource
- 应用
DriverManagerDataSource
- 应用
TransactionAwareDataSourceProxy
- 应用
DataSourceTransactionManager
3.4.1 应用DataSource
Spring通过DataSource取得与数据库的连贯。DataSource是JDBC标准的一部分,是通用的连贯工厂。它使容器或框架能够从利用程序代码中暗藏连接池和事务管理问题。作为开发人员,你无需理解无关如何连贯到数据库的详细信息。这是设置数据源的管理员的责任。你很可能在开发和测试代码时同时担当这两个角色,然而你不用晓得如何配置生产数据源。
应用Spring的JDBC层时,你能够从JNDI获取数据源,也能够应用第三方提供的连接池实现来配置本人的数据源。传统的抉择是带有bean款式的DataSource类的Apache Commons DBCP和C3P0。对于古代JDBC连接池,请思考应用具备其生成器款式的API的HikariCP。
你仅应将DriverManagerDataSource和SimpleDriverDataSource类(蕴含在Spring发行版中)用于测试!当收回多个连贯申请时,这些变体不提供缓冲池,并且性能不佳。
以下局部应用Spring的DriverManagerDataSource实现。稍后将介绍其余几种DataSource变体。
要配置DriverManagerDataSource:
- 通常与JDBC连贯一样,取得与DriverManagerDataSource的连贯。
- 指定JDBC驱动程序的规范类名,以便DriverManager能够加载驱动程序类。
- 提供不同JDBC驱动程序的URL。(请参阅驱动程序的文档以取得正确的值。)
- 提供用户名和明码以连贯到数据库。
以下示例显示了如何在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"/>
3.4.2 应用DataSourceUtils
SmartDataSource接口应该由能够提供与关系数据库的连贯的类来实现。它扩大了DataSource接口,以容许应用它的类查问在给定操作后是否应敞开连贯。当你晓得须要重用连贯时,这种用法十分无效。
3.4.4 扩张AbstractDataSource
AbstractDataSource是Spring的DataSource实现的形象基类。它实现了所有DataSource实现通用的代码。如果编写本人的DataSource实现,则应该扩大AbstractDataSource类。
3.4.5 应用SingleConnectionDataSource
SingleConnectionDataSource类是SmartDataSource接口的实现,该接口包装了每次应用后都未敞开的单个Connection。这不是多线程性能。
如果任何客户端代码在假设建设池连贯的状况下调用close(如应用持久性工具时),则应将preventClose属性设置为true。此设置返回一个关闭物理包装的代理。请留神,你不能再将此对象转换为本地Oracle Connection或相似对象。
SingleConnectionDataSource次要是一个测试类。SingleConnectionDataSource次要是一个测试类。例如,它联合简略的JNDI环境,能够在应用服务器内部轻松测试代码。与DriverManagerDataSource相比,它始终重用同一连贯,防止了过多的物理连贯创立。
3.4.6 应用DriverManagerDataSource
DriverManagerDataSource类是规范DataSource接口的实现,该接口通过bean属性配置纯JDBC驱动程序,并每次返回新的Connection。
此实现对于Java EE容器内部的测试和独立环境很有用,能够作为Spring IoC容器中的DataSource bean或与简略的JNDI环境联合应用。假设应用池的Connection.close()调用将敞开连贯,因而任何可辨认DataSource的持久性代码都应起作用。然而,即便在测试环境中,应用JavaBean格调的连接池(例如commons-dbcp)也是如此容易,以至于总是在DriverManagerDataSource上应用这样的连接池。
3.4.7 应用TransactionAwareDataSourceProxy
TransactionAwareDataSourceProxy是指标DataSource的代理。代理包装该指标DataSource以减少对Spring治理的事务的意识。在这方面,它相似于Java EE服务器提供的事务性JNDI数据源。
除非须要调用曾经存在的代码并通过规范的JDBC DataSource接口实现,否则很少须要应用此类。在这种状况下,你依然能够使该代码可用,同时使该代码参加Spring治理的事务。通常,最好应用更高级别的资源管理形象来编写本人的新代码,例如JdbcTemplate或DataSourceUtils。
无关更多详细信息,请参见TransactionAwareDataSourceProxy javadoc。
3.4.8 应用DataSourceTransactionManager
DataSourceTransactionManager类是单个JDBC数据源的PlatformTransactionManager实现。它将JDBC连贯从指定的数据源绑定到以后正在执行的线程,可能容许每个数据源一个线程连贯。
通过DataSourceUtils.getConnection(DataSource)而不是Java EE的规范DataSource.getConnection检索JDBC连贯须要利用程序代码。它抛出未查看的org.springframework.dao异样,而不是通过查看的SQLException。所有框架类(例如JdbcTemplate)都隐式应用此策略。如果不与该事务管理器一起应用,则查找策略的行为与一般策略完全相同。因而,能够在任何状况下应用它。
DataSourceTransactionManager类反对自定义隔离级别和超时,这些隔离级别和超时将利用于适当的JDBC语句查问超时。为了反对后者,利用程序代码必须应用JdbcTemplate或为每个创立的语句调用DataSourceUtils.applyTransactionTimeout(..)办法。
在单资源状况下,能够应用此实现而不是JtaTransactionManager,因为它不须要容器反对JTA。只有遵循所需的连贯查找模式,就能够在两者之间进行切换只是配置问题。JTA不反对自定义隔离级别。
3.5 JDBC批量操作
如果将多个调用批处理到同一条筹备好的语句,则大多数JDBC驱动程序都会进步性能。通过将更新分组成批,能够限度到数据库的往返次数。
3.5.3 应用JdbcTemplate的根本批处理操作
通过实现非凡接口的两个办法BatchPreparedStatementSetter并将该实现作为batchUpdate办法调用中的第二个参数传入,能够实现JdbcTemplate批处理。你能够应用getBatchSize办法提供以后批处理的大小。你能够应用setValues办法设置语句的参数值。此办法称为你在getBatchSize调用中指定的次数。以下示例依据列表中的条目更新t_actor表,并将整个列表用作批处理:
public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public int[] batchUpdate(final List<Actor> actors) { return this.jdbcTemplate.batchUpdate( "update t_actor set first_name = ?, last_name = ? where id = ?", new BatchPreparedStatementSetter() { public void setValues(PreparedStatement ps, int i) throws SQLException { Actor actor = actors.get(i); ps.setString(1, actor.getFirstName()); ps.setString(2, actor.getLastName()); ps.setLong(3, actor.getId().longValue()); } public int getBatchSize() { return actors.size(); } }); } // ... additional methods}
如果解决更新流或从文件读取,则可能具备首选的批处理大小,但最初一批可能没有该数量的条目(译者:意思是最初一批数据可能没有宰割数量大)。在这种状况下,能够应用InterruptibleBatchPreparedStatementSetter接口,该接口可在输出源耗尽后中断批处理(译者:意思是数据源数据耗费完)。isBatchExhausted办法使你能够收回批处理完结的信号。
3.5.2 批处理操作的对象列表
JdbcTemplate和NamedParameterJdbcTemplate都提供了另一种提供批处理更新的形式。无需实现非凡的批处理接口,而是将调用中的所有参数值作为列表提供。框架循环这些值,并应用一个外部语句setter。API会有所不同,具体取决于你是否应用命名参数。对于命名参数,你提供一个SqlParameterSource数组,该批处理的每个成员都有一个条目。你能够应用SqlParameterSourceUtils.createBatch便捷办法创立此数组,传入一个bean款式的对象数组(带有与参数绝对应的getter办法),字符串键Map实例(蕴含对应的参数作为值),或者混合应用。
以下示例显示应用命名参数的批处理更新:
public class JdbcActorDao implements ActorDao { private NamedParameterTemplate namedParameterJdbcTemplate; public void setDataSource(DataSource dataSource) { this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); } public int[] batchUpdate(List<Actor> actors) { return this.namedParameterJdbcTemplate.batchUpdate( "update t_actor set first_name = :firstName, last_name = :lastName where id = :id", SqlParameterSourceUtils.createBatch(actors)); } // ... additional methods}
对于应用经典的SQL语句?
占位符,则传入蕴含更新值的对象数组的列表。该对象数组在SQL语句中的每个占位符必须具备一个条目,并且它们的程序必须与SQL语句中定义的程序雷同。
以下示例与后面的示例雷同,不同之处在于它应用经典的JDBC?
占位符:
public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public int[] batchUpdate(final List<Actor> actors) { List<Object[]> batch = new ArrayList<Object[]>(); for (Actor actor : actors) { Object[] values = new Object[] { actor.getFirstName(), actor.getLastName(), actor.getId()}; batch.add(values); } return this.jdbcTemplate.batchUpdate( "update t_actor set first_name = ?, last_name = ? where id = ?", batch); } // ... additional methods}
咱们后面介绍的所有批处理更新办法都返回一个int数组,其中蕴含每个批处理条目标受影响行数。此计数由JDBC驱动程序报告。如果该计数不可用,则JDBC驱动程序将返回值-2。
在这种状况下,通过在根底PreparedStatement上主动设置值,须要从给定的Java类型派生每个值的对应JDBC类型。只管这通常成果很好,但存在潜在的问题(例如,蕴含Map的空值)。在这种状况下,Spring默认状况下会调用ParameterMetaData.getParameterType,这对于JDBC驱动程序可能会很低廉。如果遇到性能问题,则应应用最新的驱动程序版本,并思考将spring.jdbc.getParameterType.ignore属性设置为true(作为JVM零碎属性或在类门路根目录中的spring.properties文件中)。如对于Oracle 12c(SPR-16139)的报道。或者,你能够思考通过
BatchPreparedStatementSetter
(如前所示),通过为基于“List <Object []>
的调用提供的显式类型数组,通过在服务器上的“registerSqlType
调用来显式指定相应的JDBC类型。自定义“MapSqlParameterSource
实例,或者通过BeanPropertySqlParameterSource
实例从Java申明的属性类型中获取SQL类型,即便对于null
值也是如此。
3.5.3 具备多个批次的批次操作
后面的批处理更新示例解决的批处理太大,以至于你想将它们分解成几个较小的批处理。你能够通过屡次调用batchUpdate办法来应用后面提到的办法来执行此操作,然而当初有一个更不便的办法。除了SQL语句外,此办法还蕴含一个对象汇合,该对象蕴含参数,每个批处理要进行的更新次数以及一个ParameterizedPreparedStatementSetter来设置筹备好的语句的参数值。框架遍历提供的值,并将更新调用分成指定大小的批处理。
以下示例显示了应用100的批量大小的批量更新:
public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public int[][] batchUpdate(final Collection<Actor> actors) { int[][] updateCounts = jdbcTemplate.batchUpdate( "update t_actor set first_name = ?, last_name = ? where id = ?", actors, 100, (PreparedStatement ps, Actor actor) -> { ps.setString(1, actor.getFirstName()); ps.setString(2, actor.getLastName()); ps.setLong(3, actor.getId().longValue()); }); return updateCounts; } // ... additional methods}
此调用的批处理更新办法返回一个int数组,该数组蕴含每个批处理的数组条目以及每个更新受影响的行数的数组。顶层数组的长度批示运行的批处理数量,第二层树脂的长度批示该批处理中的更新数量。 每个批次中的更新数量应该是为所有批次提供的批次大小(最初一个可能更少),这取决于所提供的更新对象的总数。每个更新语句的更新计数是JDBC驱动程序报告的更新计数。如果该计数不可用,则JDBC驱动程序将返回值-2。
Spring外围篇章:
Spring 5 中文解析之外围篇-IoC容器
Spring 5 中文解析外围篇-IoC容器之依赖关系
Spring 5 中文解析外围篇-IoC容器之Bean作用域
Spring 5 中文解析外围篇-IoC容器之自定义Bean性质
Spring 5 中文解析外围篇-IoC容器之BeanDefinition继承与容器拓展点
Spring 5 中文解析外围篇-IoC容器之基于注解的容器配置
Spring 5 中文解析外围篇-IoC容器之类门路扫描和组件治理
Spring 5 中文解析外围篇-IoC容器之JSR330规范注解
Spring 5 中文解析外围篇-IoC容器之基于Java容器配置
Spring 5 中文解析外围篇-IoC容器之Environment形象
Spring 5 中文解析外围篇-IoC容器之ApplicationContext与BeanFactory
Spring 5 中文解析外围篇-IoC容器之Resources
Spring 5 中文解析外围篇-IoC容器之数据校验、数据绑定和类型转换
Spring 5 中文解析外围篇-IoC容器之SpEL表达式
Spring 5 中文解析外围篇-IoC容器之AOP编程(上)")
Spring 5 中文解析外围篇-IoC容器之AOP编程(下)")
Spring 5 中文解析外围篇-IoC容器之Spring AOP API
Spring测试篇章:
Spring 5 中文解析测试篇-Spring测试
Spring 5 中文解析外围篇-集成测试之概要和集成测试注解
Spring 5 中文解析外围篇-集成测试之TestContext(上)")
Spring 5 中文解析外围篇-集成测试之TestContext(中)")
Spring 5 中文解析测试篇-集成测试之TestContext(下)")
Spring 5 中文解析测试篇-Spring MVC测试框架
Spring 5 中文解析测试篇-WebTestClient
Spring存储篇章:
Spring 5 中文解析数据存储篇-Spring框架的事物反对模型的劣势
[Spring 5 中文解析数据存储篇-事务同步和申明式事物治理
](https://mp.weixin.qq.com/s?__...
[Spring 5 中文解析数据存储篇-@Transactional应用
](https://mp.weixin.qq.com/s?__...
Spring 5 中文解析数据存储篇-编程式事物治理
Spring 5 中文解析数据存储篇-DAO反对
残缺电子书地址
作者
集体从事金融行业,就任过易极付、思建科技、某网约车平台等重庆一流技术团队,目前就任于某银行负责对立领取零碎建设。本身对金融行业有强烈的喜好。同时也实际大数据、数据存储、自动化集成和部署、散布式微服务、响应式编程、人工智能等畛域。同时也热衷于技术分享创建公众号和博客站点对常识体系进行分享。关注公众号:青年IT男 获取最新技术文章推送!
博客地址: http://youngitman.tech
CSDN: https://blog.csdn.net/liyong1...
微信公众号:
技术交换群: