第八章 Spring 对 Dao的支持

Spring Dao 异常体系

Spring DAO 异常体系类
异常说明
CleanupFailureDataAccessExceptionDAO 操作成功执行,但在释放数据资源时发生异常,如关闭 Connection 时发生异常等
ConcurrencyFailureException表示在进行并发数据操作时发生异常,如乐观锁无法获取、悲观锁无法获取、死锁引发的失败等异常
DataAccessResourceFailureException访问数据资源时失败,如无法获取数据连接,无法获取Hibernate的会话等
DataRetrievalFailureException获取数据失败,如找不到对应主键的数据,使用了错误的列索引等
DataSourceLookupFailureException无法从JNDI中查找到数据源。这个异常时Spring2.0新增的
DataIntegrityViolationException当数据操作违法了数据一致性限制时抛出的异常,如插入重复的主键,引用不存在的外键等
InvalidDataAccessApiUsageException不正确的调用某一持久化技术时抛出的异常,如在Spring JDBC 中查询对象在调用前必须进行编译操作,如果忘记这项操作将会产生该异常。这种异常不是有底层数据资源产生,而是由不正确的使用持久化技术产生的
InvalidDataAccessResourceUsageException在访问数据源时使用了不正确的方法所抛出的异常,如SQL语句错误将抛出该异常
PermissionDeniedDataAccessException数据访问时由于权限不足引发的异常,如仅拥有只读权限的用户试图进行数据更改操作时将抛出该异常
U你categorizedDataAccessException其他未分类的异常都归到该异常类中

Spring 数据访问模板

  • JDBC 数据访问
    • 1、准备资源;
    • 2、启动事务;
    • 3、在事务中执行具体数据访问操作;
    • 4、提交/回滚事务;
    • 5、关闭资源,处理异常;
package com.baobaotao.instrument;

import java.sql.SQLException;

import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;

public class saveCustomer {
	
	public void saveCustomer(Customer customer) throws Exception{
		
		Connection con = null;
		PreparedStatement stmt = null;
		
		try {
			//@1 获取资源
			con = getConnection();
			
			//@2 启动事务
			con.setAutoCommit(false);
			
			//@3 具体数据访问操作和处理
			stmt = con.prepareStatement("insert into CUSTOMERS(ID, NAME) values(?, ?)");
			stmt.setLong(1, customer.getId());
			stmt.setString(2, customer.getName());
			stmt.execute();
			
			//@4 提交事务
			con.commit();
		} catch (SQLException e) {
			try {
				//@5 回滚事务
				con.rollback();
			} catch (SQLException sqlex) {
				sqlex.printStackTrace(System.out);
			}
			throw e;
		}finally {
			try {
				//@6 释放资源
				stmt.close();
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}	
	}
}

配置数据源

DBCP 数据源
  • 使用DBCP 配置 MySQL 数据源的片段:
<bean id="dataSource"
	class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
	p:driverClassName="com.mysql.jdbc.Driver"
	p:url="jdbc:mysql://localhost:3306/sampledb"
	p:username="root"
	p:password="123"/>
  • BasicDataSource 提供了 close()方法关闭数据源,所以必须设定 destroy-method="close"属性,以便Spring 容器关闭时,数据源能够正常关闭。
  • 如果MySQL数据源配置不当,将可能发生经典“8小时问题”。原因时MySQL在默认情况下,如果发现一个连接的空闲时间超过8小时,将会在数据库端自动关闭这个连接。而数据源并不知道这个连接被数据库关闭了,当它将这个无用的连接返回给某个DAO时,DAO就会包无法获取Connection的异常。

事务属性

属性默认值说明
defaultAutoCommittrue连接池创建的连接的默认 auto-commit 状态
defaultReadOnly驱动默认连接池创建的连接的默认的 read-only 状态。如果没有设置则 setReadOnly 方法将不会被调用
defaultTransactionIsolation驱动默认连接池创建的连接默认的 TransactionIsolation 状态,可选值如下: NONE、READ_COMMITTED、READ_UNCOMMITTED、REPEATABLE_READ、SERIALIZABLE

数据源连接数量

属性默认值说明
initialSize0初始化连接:连接池启动时创建的初始化连接数量
maxActive8最大活动连接:连接池在同一时间能够分配的最大活动连接的数量,如果设置为非正数则表示不受限制
maxIdle8最大空闲连接:连接池中允许保持空闲状态的最大连接数量,超过的空闲连接将被释放,如果设置为负数表示不限制
minIdle0最小空闲连接:连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接,如果设置为0则不创建
maxWait无限最大等待时间:当没有可用连接时,连接池等待连接被归还的最大时间(以毫秒计数),超过时间则抛出异常,如果设置为-1表示无限等待

连接健康情况维护和检测

属性默认值说明
validationQuery无默认值SQL 查询语句。在将连接返回给调用者之前,用此SQL验证从连接池取出的连接是否可用。如果指定,则查询必须是一个SQL SELECT 并且必须返回至少一行记录。在MySQL中可用设置为“select 1”,在Oracle中可用设置为“select 1 from dual”
testOnBorrowtrue指明是否在从池中取出连接前进行检验,如果检验失败,则从池中去除该连接并尝试取出另一个新的连接。注意:设置为ture 后如果要生效,validationQuery 参数必须正确设置
testOnReturnfalse指名是否在归还到池中前进行检验。注意:设置为true 后如果要生效,validationQuery参数必须正确设置
testWhileIdlefalse指明连接是否被空闲连接回收器(如果有)进行检验。如果检测失败,则连接将被从池中去除。注意:设置为true 后如果要生效,validationQuery 参数必须正确设置
timeBetweenEvictionRunsMillis-1空闲连接回收器线程运行的周期,以毫秒为单位。如果设置为非正是,则不运行空闲连接回收期线程。注意:启用该参数时,validationQuery 参数必须正确设置
numTestsPerEvictionRun3在每次空闲连接回收器线程(如果有)运行时检测的连接数量
minEvictableIdleTimeMillis1000 * 60 * 30连接在可被空闲连接回收器回收前已经在池中的空闲的时间,以毫秒为单位

缓存语句

属性默认值说明
poolPreparedStatementsfalse开启池的 prepared statement池功能。设置为true时,所有 CallableStatement 和 PreparedStatemen 都会被缓存起来
maxOpenPreparedStatements无限制PreparedStatement 池能够同时分配的打开的 statements 的最大数量,如果设置为0表示不限制

连接泄露回收

属性默认值说明
removeAbandonedfalse标记是否删除泄漏的连接。如果 removeAbandoned 设置为true,那么“存在泄漏嫌疑”的连接可能被池主动清楚。这个机制在(getNumIdle() < 2)and(getNumActive() > getMaxActive() - 3)条件满足时被触发。举例来说:当maxActive=20,活动连接为18,空闲连接为1时可用触发“removeAbandoned”的动作。但是活动连接只有在未被使用的时间超过“removeAbandonedTimeout”时才被回收,默认300秒。如果你的应用需要一个进行长操作的连接,需要考虑将removeAbandonedTimeout 设置更长一些,否则肯发生正常连接被强制清楚的情况
removeAbandonedTimeout300泄漏的连接可用被回收的超时值,单位为秒
logAbandonedfalse标记当 Statement 或连接被泄漏时是否打印程序的 stacktraces 日志。被泄漏的 Statements 和连接的日志添加在每个连接打开或者生成新的 Statement,因为需要生成 stacktrace
  • 如果采用DBCP的默认配置,由于testOnBorrow 属性的默认值为true,数据源在将连接交给DAO前,会事先检测这个连接是否是好的,如果连接有问题(在数据库端被关闭),则会取一个其他的连接给DAO。所以,并不会有“8小时问题”。如果每次将连接交给DAO时都检测连接的有效性,在高并发的应用中将会带来性能的问题,因为它会需要更多的数据库访问请求。
  • 一种推荐的高效方法是:将testOnBorrow设置为false,而将testWhileIdle设置为true,在设置好timeBetweenEvictionRunsMillis值。这样,DBCP将通过一个后台线程定时对空闲连接进行检测,当发现无用的空闲连接(那些被数据库关闭的连接)时,就会将它们清除掉。只要将timeBetweenEvictionRunsMillis的值设置小于8小时,那些被MySQL关闭的空闲连接就可以被清楚出去,避免“8小时问题”。
  • 当然,MySQL本身可以通过调整 interactive-timeout(以秒为单位)配置参数,更改空闲连接的过期时间。所以,在设置timeBetweenEvictionRunsMillis值时,必须首先获知MySQL空闲连接的最大过期时间。
C3P0 数据源
  • 使用C3P0 配置 Oracle 数据源的片段:
<bean id="dataSource"
	class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"
	p:driverClassName="oracle.jdbc.driver.OracleDriver"
	p:jdbcUrl="jdbc:oracle:thin:@localhost:1521:ora9i"
	p:username="root"
	p:password="123"/>
  • C3P0 属性参数
    • acquireIncrement:当连接池中的连接用完时,C3P0一次性创建新连接的数目;
    • acquireRetryAttempts:定义在从数据库获取新连接失败后重复尝试获取的次数,默认为30次;
    • acquireRetryDelay:尝试获取连接的间隔时间,单位为毫秒,默认为1000;
    • autoCommitOnClose:连接关闭时默认将所有未提交的操作回滚,默认为false;
    • automaticTestTable:C3P0将创建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数,那么属性preferredTestQuery将被忽略。用户不能再这张Test表上进行任何操作,它仅为C3P0测试所用,默认为null;
    • breakAfterAcquireFailure:获取连接失败将会引起所有等待获取连接的线程抛出异常。但是数据源任有效保留,并再下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么再尝试获取连接失败后该数据源将申明已断开并永久关闭。默认为false;
    • checkoutTimeout:当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限等待。单位为毫秒,默认为0;
    • connectionTesterClassName:通过实现ConnectionTester或QueryConnectionTester的类来测试连接,类名需要设置为全限定名。默认为 com.mchange.v2.C3P0.impl.DefaultConnectionTester;
    • idleConnectionTestPeriod:隔多少秒检测所有连接池中的空闲连接,默认为0表示不检查;
    • initialPoolSize:初始化时创建的连接数,应在minPoolSize 与 maxPoolSize 直接取值。默认为3;
    • maxPoolSize:连接池中保留的最大连接数。默认为15;
    • maxStatements:JDBC的标准参数,用以控制数据源内部加载的PreparedStatement数量,但由于预缓存的Statement属于单个Connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素,如果 maxStatements 与 maxStatementsPerConnection均为0,则缓存被关闭。默认为0;
    • maxStatementsPerConnection:连接池内单个连接所拥有的最大缓存 Statement树。默认为0;
    • numHelperThreads:C3P0是异步操作的,缓慢的JDBC操作通过帮助进行完成。扩展这些操作可以有效的提升性能,通过多线程实现多个操作同时被执行。默认为3;
    • preferredTestQuety:定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个参数能显著的提高测试速度。测试的表必须在初始数据源的时候就存在。默认为null;
    • propertyCycle:用户修改系统配置参数执行前最多等待的秒数。默认为300;
    • testConnectionOnCheckout:因性能消耗大,请只在需要的时候使用它。如果设为true,那么在每个connection 提交的时候都将校验其有效性。建议使用 idleConnectionTestPeriod 或 automaticTestTable 等方法来提升连接测试的性能。默认为false;
    • testConnectionOnCheckin:如果设为true,那么在取得连接的同时将校验连接的有效性。默认为false。

使用属性文件

  • xml 数据连接对象
<context:property-placeholder location=/WEB-INF/jdbc.properties”/>

<bean id="dataSource"
	class="com.apache.commons.dbcp.BasicDataSource" destroy-method="close"
	p:driverClassName="${jdbc.driverClassName}"
	p:url="${jdbc.url}"
	p:username="${jdbc.username}"
	p:password="${jdbc.password}"/>
  • 在 jdbc.properties 属性文件中定义属性值
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/hm_dream?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=hm123
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值