JdbcTemplate

为了避免业务层模块强依赖于某种类型的数据库,Spring 数据库访问层以接口形式对外提供服务。

使用 Spring JDBC 的异常体系,描述能力强,且平台无关。

1 数据访问模板化

数据访问过程中的固定步骤和变量分为两类:

  • 模板(templates):处理数据访问的固定部分——事务控制、 管理资源以及处理异 常。
  • 回调(callbacks):处理应用程序相关的数据访问——语句、 绑定参数以及整理结果集

针对不同的持久化平台, Spring提供了多个可选的模板。

ORM 持久化技术 模板类(org.springframework.*)
JDBC jdbc.core.JdbcTemplate
JCA CCI jca.cci.core.CciTemplate
Hibernate orm.hibernate5.HibernateTemplate
iBATIS orm.ibatis.SqlMapClientTemplate
JDO orm.jdo.JdoTemplate
JPA orm.jpa.JpaTemplate

2 配置 DataSource

使用 JdbcTemplate 需设置 DataSource,提供了多种配置方式:

  1. JDBC 驱动;
  2. JNDI 查询;
  3. 数据库连接池;

生产环境建议使用从连接池获取连接的数据源;如果有可能,倾向于通过应用服务器的 JNDI 来获取数据源。

2.1 JNDI 数据源

在 Spring 应用部署的服务器中配置数据源,通过 JNDI 获取。数据源配置在应用外部,允许在访问数据库的时再查找数据源,且支持热切换。

public JndiObjectFactoryBean dataSource() {
JndiObjectFactoryBean jndiObjectFB = new JndiObjectFactoryBean();
jndiObjectFB.setJndiName("/jdbc/xxxx");
jndiObjectFB.setResourceRef(true);
jndiObjectFB.setProxyInterface(javax.sql.DataSource.class);
return jndiObjectFB;
}

2.2 数据库连接池

有多项开源数据源连接池实现:Apache Commons DBCP、c3p0、BoneCP

DBCP 为例,BasicDataSource 配置连接池

public BasicDataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("org.h2.Driver");
ds.setUrl("jdbc:h2:tcp://localhost/~/xxxx");
ds.setUsername("sa");
ds.setPassword("");
ds.setInitialSize(5);
return ds;
}

2.3、JDBC 驱动

通过 JDBC 驱动定义数据源是最简单的配置方式。 org.springframework.jdbc.datasource 包中提供了三个数据源类:

  1. DriverManagerDataSource:每次请求连接时都返回新的连接,用过的连接会马上关闭并释放资源;
  2. SimpleDriverDataSource:与 DriverManagerDataSource 类似,但直接使用JDBC驱动,免去了类在特定环境(如 OSGi 容器)中可能遇到的类加载问题。
  3. SingleConnectionDataSource:每次都返回同一个连接对象,可以理解为只有 1 个连接的数据源连接池。

跟之前配置 DBCP 的 BasicDataSource 类似,例如配置 DriverManagerDataSource

@Bean
public DataSource dataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("org.h2.Driver");
ds.setUrl("jdbc:h2:tcp://localhost/~/xxxx");
ds.setUsername("sa");
ds.setPassword("");
return ds;
}

已上三数据源对多线程支持都不好,强烈建议使用数据库连接池。

2.4 选择数据源

借助 Spring 的 bean-profiles 特性,在不同的环境中配置不同的数据源。利用 @Profile 注解,在运行时根据激活的 profile 选择指定的数据源。

@Configuration
public class DataSourceConfiguration {
@Profile("development")
@Bean
public DataSource embeddedDataSource() {
return new EmbeddedDatabaseBuilder()
...
}

@Profile("qa")
@Bean
public BasicDataSource basicDataSource() {
BasicDataSource ds = new BasicDataSource();
...
return ds;
}

@Profile("production")
@Bean
public DataSource dataSource() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
...
return (DataSource)jndiObjectFactoryBean.getObject();
}
}

3 使用 JdbcTemplate

JDBC 直接操作数据库, 处理与数据库访问相关的所有事情, 样板代码繁琐,但很重要。Spring 的 JDBC 框架承担了资源管理和异常处理的样板代码,开发者只需编写读写数据的必需代码。

JdbcTemplate 是最主要的 JDBC 模板,支持简单的 JDBC 数据库访问功能以及基于索引参数的查询;还提供了 NameParameterJdbcTemplate 支持命名参数。

3.1 CRUD

使用 JdbcTemplate 前需设置 DataSource

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
  • 增删改
    • update(final String sql)
String sql = "insert into user (username, password) values (?, ?)";
jdbcTemplate.update(sql, username, password);

当调用 update() 方法时,JdbcTemplate 调用 jdbc 获取一个连接、创建一个 statement,并执行插入语句。内部捕获了可能抛出的 SQLException 异常,然后转为更具体的数据库访问异常,并重新抛出。

    • queryForObject(String sql, RowMapper rowMapper, Object… args)
User user = jdbcOperations.queryForObject(sql,new UserMapper (),param);

public class UserMapper implements RowMapper<User>{
@Override
public User mapRow(ResultSet resultSet, int rows) throws SQLException {
User user = new User();
user.setUsername(resultSet.getString(1));
user.setPassword(resultSet.getString(2));
return user;
}
}

需要实现一个 RowMapper 对象,JdbcTemplate 会调用 mapRow() 方法,从结果集中取出对应属性的值,并构造对象。

Author: iMine
Link: https://imine141.github.io/2020/07/17/Spring/Data/JdbcTemplate/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.