首页 > 代码库 > Spring JDBC

Spring JDBC

为了避免持久化的逻辑分散到应用的各个组件中,将数据访问功能放到一个或多个专注于此项任务的组件中,这样的组件通常称为数据访问对象(DAO)或Repository。

为了避免应用与特定的数据访问策略耦合在一起,编写良好的Repository应该以借口的方式暴漏功能。通过接口来访问Repository可以为这些数据访问接口创建mock实现,提升单元测试的效率。

 

SQLException表示在尝试访问数据库时出现了问题,但这个异常却没有告诉你哪里出错了以及如何进行处理。

可能导致抛出SQLException的常见问题包括:应用程序无法连接数据库;要执行查询存在语法错误;查询中所使用的表/列不存在和试图插入或更新的数据违反了数据库约束。事实上,大多数抛出SQLException的情况是致命的错误。即使对某些SQLException有处理方案,还是要捕获SQLException并查看其属性才能获知问题根源的更多信息。这是因为SQLException被视为处理数据访问所有问题的通用一场。

Spring JDBC提供的数据访问异常体系提供了多个数据访问异常,分别描述了他们抛出时所对应的问题。而且它并没有与特定的持久化方式相关联,这意味着可以使用Spring抛出一致的异常而不用关心所选择的持久化方案。Spring JDBC异常都继承自DataAccessException。DataAccessException是一个非检查型异常,因此不用捕获Srping所抛出的数据访问异常。

 

模板方法将过程中与特定实现相关的部分委托给接口,而这个接口的不同实现定义了过程中的具体行为。

Spring将数据访问过程中固定的和可变的部分明确划分为两个不同的类:模板(template)和回调(callback)。模板管理过程中固定的部分(书屋控制,管理资源以及处理异常),而回调处理自定义的数据访问代码(应用程序相关的数据访问——语句、绑定参数以及整理结果集)。

Spring提供的数据访问模板,分别适用于不同的持久化机制:

jca.cci.core.CciTemplate  JCA CCI连接

jdbc.core.JdbcTemplate  JDBC连接

jdbc.core.namedparam.NamedParameterJdbcTemplate  支持命名参数的JDBC连接

jdbc.core.simple.SimpleJdbcTemplate  通过Java5简化后的JDBC连接

orm.hibernate3.HibernateTemplate  Hibernate 3.x以上的Session

orm.ibatis.SqlMapClientTemplate  iBatis SqlMap客户端

orm.jdo.JdoTemplate  Java数据对象实现

orm.jpa.JpaTempalte  Java持久化API的实体管理器

 

Spring提供了在Spring上下文中配置数据源bean的多种方式,包括:通过JDBC驱动程序定义的数据源;通过JNDI查找的数据源和连接池的数据源。也可以使用Apache Commons DBCP、c3p0和BoneCP这些框架来配置Spring的数据源

使用DBCP配置数据源

  基于java的配置

@Bean
public BasicDataSource dataSource(){
    BasicDataSource dataSource = new BasicDataSource();
    dataSource.setDriverClassName("org.h2.Driver");
    dataSource.setUrl("jdbc:h2:tcp://localhost/...");
    dataSource.setUsernname("username");
    dataSource.setPassword("root");
    dataSource.setInitialSize(5);
    dataSource.setMaxActive(10);
    return dataSource
}

  基于xml的配置

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 
    p:driverClassName="org.h2.Driver"
    p:url="jdbc:h2:tcp://localhost/"
    p:username="username"
    p:password="root"
    p:initialSize="5"
    p:maxActive="10"     />

DBCP BasicDataSource一些常用的池配置属性

initialSize  池启动时创建的连接数量

maxActive  同以时间可以从池中分配的最多连接数,若为0,则表示无限制

madIde  池里不会被释放的最多空闲连接数,若为0,则表示无限制

maxOpenPreparedStatements  在同一时间能够从语句池中分配的预处理语句的最大数量,若为0,则表示无限制

maxWait  在抛出异常之前,池等待连接回收的最大时间,若设置为-1,表示无限等待

minEvictabelIdeTimeMills  连接在池中保持空闲而不被回收的最大时间

minIde  在不创建新连接的情况下,池中保持空闲的最小连接数

poolPreparedStatements  是否对预处理语句进行池管理

 

在Spring中,通过JDBC驱动定义数据源是最简单的配置方式。Spring提供了三个这样的数据源类供选择:

  DriverManagerDataSource:在每个连接请求时都会返回一个新建的连接。与DBCP的BasicDataSource不同,由DriverManagerDataSource提供的连接并没有进行池化管理

  SimpleDriverDataSource:与DriberManagerDataSource的工作方式类似,但是它直接使用JDBC驱动,来解决在特定环境下的类的加载问题,如OSGi容器

  SingleConnectionDataSource:在每个连接强求时都会返回同一个的连接。尽管SingleConnectionDataSource不是严格意义上的连接池数资源,但可以视其为自由一个连接的池

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" 
    p:driverClassName="driverClassName"
    p:url="jdbc:..."
    p:username="username"
    p:passord=""  />
@Bean
public DataSource dataSource(){
    DriverManagerDataSource dataSource =new DriverManagerDataSource();
    dataSource.setDriverClassName("driverClassName");
    dataSource.setUrl("url");
    dataSource.setUsername("username");
    dataSource.setPassword("password");
    return dataSource;
}

 

嵌入式数据库(embedded database)作为应用的一部分运行,而不是应用连接的独立数据库服务器。Spring的jdbc命名空间能够简化嵌入式数据库的配置

<?xml version="1.0" encoding="UTF-8" ?> 
<beans xmlns="http://www.springframework.org/schema/jdbc"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:jdbc="http://www.springframework.org/schema/jdbc"
             xsi:schemaLocation="http:///www.springframework.org/schema/jdbc
                                 http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/beans                    http://www.springframework.org/schema/beans/spring-beans.xsd "
>   <jdbc:embedded-datatbase id="dataSource" type="H2">    <jdbc:script location="" />    <jdbc:script location="" />   </jdbc:embedded-database> </beans>
@Bean
public DataSource dataSource(){
    return new EmbeddedDatabaseBuilder()
        .setType(EmbeddedDatabaseType.H2)
        .addScript("classpath:*.sql")
        .build();
}

 

可以将不同的profile配置在不同的数据源中

package cherry.config;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.jndi.JndiObjectFactoryBean;

@Configuration
public class DataSourceConfiguration {

    @Profile("development")
    @Bean
    public DataSource embeddedDataSource(){
        return new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .addScript("classpath:schema.sql")
                .addScript("classpath:test-data.sql")
                .build();
    }
    
    @Profile("qa")
    @Bean
    public DataSource dataSourceForQA(){
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("driverName");
        dataSource.setUrl("url");
        dataSource.setUsername("username");
        dataSource.setPassword("password");
        dataSource.setInitialSize(5);
        dataSource.setMaxActive(10);
        return dataSource;
    }
    
    @Profile("production")
    @Bean
    public DataSource dataSource(){
        JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
        jndiObjectFactoryBean.setJndiName("jdbc/databaseName");
        jndiObjectFactoryBean.setResourceRef(true);
        jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
        return (DataSource)jndiObjectFactoryBean.getObject();
    }
}

 

Spring JDBC将数据访问的样板代码抽象到模板类之中。Spring为JDBC提供了三个模板类供选择:

  JdbcTemplate: 最基本的Spring JDBC模板,这个模板支持简单的JDBC数据访问功能以及基于索引参数的查询

  NamedParameterJdncTemplate:使用该模板类执行查询时可以将值以命名参数的形式绑定到SQL中,而不是使用简单的索引参数

使用JdbcTemplate只需要为其设置DataSource即可

  @Bean

  public JdbcTemplate jdbcTempalte(DataSource dataSource){

    return new JdbcTemplate(dataSource);

  }

将jdbcTemplate装配到Respository中.JdbcOperations是一个接口,定义了JdbcTemplate所实现的操作

  @Repository

  public class RepositoryImpl implements Respostory{

    private JdbcOperations jdbcOperations;

    @Inject

    public RepositoryImpl(JdbcOperations jdbcOperations){

      this.jdbcOperations = jdbcOperations;

    }

  }

  调用JdbcTemplate.update()方法时会获取连接、创建语句并执行插入SQL;findOne()方法使用了JdbcTemplate的回调;queryForObject()方法从数据库查询,它有三个参数:String(查询数据的sql),rowMapper(从ResultSet中提取数据并构建对象)和可变参数列表(要绑定到查询上的索引参数值)

  命名参数可以赋予SQL中每个参数一个明确的名字,在绑定只到查询语句的时候就通过该名字来引用参数。使用命名参数查询,绑定只的顺序就不重要了。如果查询语句发生了变化导致参数的顺序与之前不一致,也扔不需要修改绑定的代码。

@Bean
public NamedParameterJdbcTemplate jdbcTemplate(DataSource dataSource){
    return new NamedParameterJdbcTemplate(dataSource);
}



public void addSpitter(Spitter spitter){
    private static final String INSER_SPITTER = "insert into spitter(username, password, email) values(:username, :password, :email)";
    Map<String, Object> paramMap = new HashMap<String, Object>();
    paramMap.put("username", spitter.getUsername());
    paramMap.put("password", spittler.getPassword());
    paramMap.put("email", spitter.getEmail());
    jdbcOperations.update(INSER_SPITTER, paramMap);
}

 

Spring JDBC