1.Spring的数据访问哲学
为了将数据访问层(DAO)与应用程序的其他部分隔离开来,Spring采用的方式之一就是提供统一的异常体系,这个异常体系用在了它支持的所有持久化方案中。
1.1Spring的数据访问异常体系
写JDBC的时候,我们常常需要捕获SQLException,而可能导致SQLException的问题有很多,比如下面这些:
- 应用程序无法连接数据库
- 要执行的查询存在语法错误
- 查询中所使用的表和/或列不存在
- 试图插入或更新的数据违反了数据库约束
而SQLException并不能告诉我们导致抛出异常的原因具体是哪一个。
Spring JDBC提供了很多与平台无关的持久化异常,而且这些异常都继承自DataAccessException。DataAccessException的特殊之处在于他是一个非检查性异常。也就是说,不像SQLException你必须捕获它,DataAccessException可以让你不必捕获Spring所抛出的数据访问异常。这是因为Spring认为触发异常的很多问题是不能在catch代码块中修复的
1.2数据访问模板化
设计模式:模板方法模式
模板方法将过程中与特定实现相关的部分委托给接口,而这个接口的不同实现定义了过程的具体行为。
Spring将数据访问过程中固定的和可变的部分明确划分为两个不同的类:模板(template)和回调(callback)。模板管理过程中固定的部分,而回调处理自定义的数据访问代码。
Spring的模板处理数据访问的固定部分——事物控制、资源管理以及处理异常。同时,应用程序相关的数据访问——语句、绑定参数以及整理结果集——在回调的实现中处理。
Spring提供的数据访问模板,分别适用于不同的持久化机制
模板类(org.springframework.*) | 用途 |
---|---|
jca.cci.core.CciTemplate | JCA CCI连接 |
jdbc.core.JdbcTemplate | JDBC连接 |
jdbc.core.namedparam.NamedParameterJdbcTemplate | 支持命名参数的JDBC连接 |
jdbc.core.simple.SimpleJdbcTemplate | 通过Java5简化后的JDBC连接(Spring3.1中已经废弃) |
orm.hibernate3.HibernateTemplate | Hibernate 3.x以上的Session |
orm.ibatis.SalMapClientTemplate | iBATIS SqlMap客户端 |
orm.jdo.JdoTemplate | Java 数据对象(Java Data Object)实现 |
orm.jpa.JpaTemplate | Java 持久化API的实体管理器 |
2.配置数据源
无论选择Spring的哪种数据访问方式,你都需要配置一个数据源的引用。Spring提供了在Spring上下文中配置数据源bean的多种方式,包括:
- 通过JDBC驱动程序定义的数据源
- 通过JNDI查找的数据源
- 连接池的数据源
2.1使用JNDI数据源
XML配置:
<jee:jndi-loopup id="dataSource"
jndi-name="/jdbc/spitterDS"
resource-ref="true" />
其中jndi-name属性用于指定JNDI中资源的名称。如果只设置了jndi-name属性,那么就会根据指定的名称查找数据源。但是,如果应用程序运行在Java应用服务器中,你需要将resource-ref属性设置为true,这样给定的jndi-name将会自动添加“java:comp/env/”前缀。
Java配置:
@Bean
public JndiObjectFactoryBean dataSource(){
JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
bean.setJndiName("jdbc/SpitterDS");
bean.setResourceRef(true);
bean.setProxyInterface(javax.sql.DataSource.class);
return bean;
}
2.2使用数据源连接池
Spring并没有提供数据源连接池的实现,但是我们有多项可用的方案,包括如下开源的实现:
- Apache Commons DBCP
- c3p0
- BoneCP
以DBCP为例:
XML配置:
<bean id="dataSource" class="org,apache.commons.dbcp.basicDataSource"
p:driverClassName="org.h2.Driver"
P:url="jdbc:h2:tcp://localhost/~/spitter"
p:uasername="sa"
p:initialSize="5"
p:password=""
p:maxActive="10"/>
Java 配置
@Bean
public BasicDataSource dataSource(){
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("org.h2.Driver");
ds.setUrl("jdbc:h2:tcp://localhost/~/spitter");
ds.setUsername("sa");
ds.setPassword("");
ds.setInitialSize(5);
ds.setMaxActive(10);
return ds;
}
BasicDataSource的池配置属性
池配置属性 | 所指定的内容 |
---|---|
initialSize | 池启动时创建的连接数量 |
maxActive | 同一时间可从池中分配的最多连接数。如果设置为0,表示无限制 |
maxIdle | 池里不会被释放的最多空闲连接数。如果设置为0,表示无限制 |
maxOpenPreparedStatments | 在同一时间能够从语句池中分配的预处理语句的最大数量。如果设置为0,表示无限制 |
maxWait | 在抛出异常之前,池等待连接回收的最大时间。如果设置为-1,表示无限等待 |
minEvictableIdleTimeMillis | 连接在池中保持空闲而不被回收的最大时间 |
minIdle | 在不创建新连接的情况下,池中保持空闲的最小连接数 |
poolPreparedStatements | 是否对预处理语句进行池管理 |
2.3基于JDBC驱动的数据源
在Spring中,通过JDBC驱动定义数据源是最简单的配置方式。Spring提供了三个这样的数据源类供选择:
- DriverManagerDataSource:在每个连接请求时都返回一个新建的连接。与DBCP的BasicDataSource不同,由DriverManagerDataSource提供的连接并没有进行池化管理;
- SimpleDriverDataSource:与DriverManagerDataSource的工作方式类似,但是它直接使用JDBC驱动,来解决在特定环境下的类加载问题,这样的环境包括OSGi容器;
- SingleConnectionDataSource:在每个连接请求时都会返回同一个的连接。尽管SingleConnectionDataSource不是严格意义上的连接池数据源,但是你可以将其视为只有一个连接的池。
以上这些数据源的配置与DBCP BasicDataSource的配置类似。例如,如下就是配置DriverManagerDataSource的方法
XML:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="org.h2.Driver"
p:url="jdbc:h2:tcp://localhost/~/spitter"
P:username="sa"
p:password=""/>
Java配置:
@Bean
public DriverManagerDataSource dataSource(){
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("org.h2.Driver");
ds.setUrl("jdbc:h2:tcp://localhost/~/spitter");
ds.setUsername("sa");
ds.setPassword("");
return ds;
}
2.4使用嵌入式的数据源
嵌入式数据库作为应用的一部分运行,而不是应用连接的独立数据库服务器。对于开发和测试来讲,嵌入式数据库是很好的可选方案。这是因为每次重启应用或运行测试的时候,都能够重新填充测试数据。
Java配置:
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpatch:schema.sql")
.addScript("classpatch:test-data.sql")
.build();
}
2.5使用profile选择数据源
Java配置:
//生产环境的数据源
@Profile("production")
@Bean
public JndiObjectFactoryBean dataSource(){
JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
bean.setJndiName("jdbc/SpitterDS");
bean.setResourceRef(true);
bean.setProxyInterface(javax.sql.DataSource.class);
return bean;
}
//QA数据源
@Profile("qa")
@Bean
public BasicDataSource data(){
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("org.h2.Driver");
ds.setUrl("jdbc:h2:tcp://localhost/~/spitter");
ds.setUsername("sa");
ds.setPassword("");
ds.setInitialSize(5);
ds.setMaxActive(10);
return ds;
}
//开发数据源
@Profile("development")
@Bean
public DataSource embeddedDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpatch:schema.sql")
.addScript("classpatch:test-data.sql")
.build();
}
3.在Spring中使用JDBC
使用JDBC模板
Spring的JDBC框架承担了资源管理和异常处理的工作,从而简化了JDBC代码,让我们只需编写从数据库读写数据的必需代码。
Spring为JDBC提供了三个模板类供选择:
- jdbcTemplate:最基本的Spring JDBC模板,这个模板支持简单的JDBC数据库访问功能以及基于索引参数的查询;
- NamedParameterJDBCTemplate:使用该模板类执行查询时可以将值以命名参数的形式绑定到SQL中,而不是使用简单的索引参数;
- SimpleJDBCTemplate:该模板类利用Java5的一些特性如自动装箱、泛型以及可变参数列表来简化JDBC模板的使用。
从Spring3.1开始,SimpleJDBCTemplate已经被废弃,器Java5的特性被转移到了jdbcTemplate中,并且只有在你需要使用命名参数的时候,才需要使用NamedParameterJDBCTemplate。这样的话,对于大多数的JDBC任务来说,jdbcTemplate就是最好的可选方案。
使用jdbcTemplate来插入数据示例:
@Bean
@Autowired
public JdbcTemplate jdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Autowired
private JdbcOperations jdbcTemplate;
public void addSpitter(){
jdbcTemplate.update("insert into spitter (username, password, fullname) values (?,?,?)",
"zhangsan","123456","zhangsanfeng");
}
JdbcOperations 是一个接口,定义了JdbcTemplate 所实现的操作