第五课 Spring的数据源

Spring对DAO的理念

DAO(Data Access Object)是用于访问数据的对象,虽然在大多数情况下将数据保存在数据库中,但这并不是唯一的选择,也可以将数据存储到文件中或LDAP中,DAO不但屏蔽了数据存储的最终介质的不同,也屏蔽了具体的实现技术的不同。
早期,JDBC是访问数据库的主流选择。近几年,数据持久化技术获得了长久的发展,HibernateMyBatis、JPA、JDO成为持久层中争放异彩的实现技术。只要为数据访问定义好DAO接口,并使用具体的技术实现DAO接口的功能,就可以在不同的实现技术间平滑的切换。
业务通过DAO接口访问数据
提供DAO层的抽象可以带来一些好处,首先,可以容易的构造模拟对象,方便单元测试的开展,其次,在使用切面的时候会有更多的选择,既可以使用JDK动态代理又可以使用CGLib动态代理。

统一的异常体系
Spring提供了一套和实现技术无关的、面向DAO层的异常体系,并通过转换器将不同的持久化技术的异常转换成Sping的异常。
很多正统的API或框架中,检查型异常被过多的使用,以致在使用API时,代码里充斥着大量的try/catch中记录异常信息外,并没有做多少实质性的工作。引发异常的问题往往是不可恢复的,如数据库连接失败、SQL语法错误等。强制捕捉的检查型异常除限制开发人员的自由外,并没有提供什么有价值的东西,因此,Spring的异常体系都是建立在运行异常的基础上,开发者可以根据需要捕捉感兴趣的异常。

JDK的很多API之所以难用,一个很大的原因就是检查型异常的泛滥,如JavaMail、EJB、JDBC等。使用这些API一堆堆异常的处理代码喧宾夺主的侵入到业务代码中,破坏了代码的整洁和优雅。
Spring在org.springframework.dao包中提供了一套完备优雅的DAO异常体系。虽然不同的持久化技术的特定的异常被转换到Spring的DAO异常体系中,但原始的异常体系并不会丢失,只要用户愿意,就可以方便的通过getCause()获取原始的异常信息。
Spring通过异常目录树的形式,使开发人员关注某一特定语义的异常变得很容易。在JDBC的SQLException中,用户必须通过异常的getErrorCode()或getSQLState()获取错误代码,然后根据这些代码判断错误原因。这种过于底层的API,不但带来了代码程度上的难度,而且也使代码移植变得困难。因为getErrorCode()方法是数据库相关的。
异常体系结构
异常体系类
异常体系类

JDBC异常转换器
传统的JDBC API在发生几乎所有的数据操作问题时都会抛出相同的SQLException,它将异常的细节信息封装在异常属性中,所以如果希望了解异常的具体原因,则必须分析异常对象信息。
SQLException拥有两个代表异常具体原因的属性:错误码和sql状态码,前者是数据库相关的,可通过getErrorCode()方法返回,其值的类型是int,而后者是一个标准的错误代码,可通过getSQLState()方法返回,是一个String类型的值,由五个字符串组成。
Spring根据错误码和sql状态码信息,将SQLException译成SpringDAO的异常体系所对应的异常。
在org。springframework.jdbc.support包中定义了SQLExceptionTranslator接口,该接口的两个实现类:SQLErrorCodeExceptionTranslator和SQLStateSQLExceptionTranslator分别负责SQLException中的错误码和SQL状态码。

统一的数据访问模板

统一的数据访问模板
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这些类都继承于dao.support.DaoSupport类,DaoSupport类实现了InitializingBean接口,在afterPropertiesSet()接口方法中检查模板对象和数据源是否正确设置,否则将抛出异常。
所有的类都是abstract的,其目的是希望被继承使用,而非直接使用。

数据源

不管采用任何技术都必须拥有数据库连接,在Spring中,数据库连接是通过数据源获得的,在以往的应用中,数据源一般由WEB服务器提供,在Spring中,不但可以通过JNDI获取应用服务器的数据源,也可以直接在Spring容器中配置数据源,此外还可以通过代码的方式创建一个数据源,以便进行无容器依赖的单元测试。

配置一个数据源
Spring在第三方依赖包中包含了两个数据源的实现类包:其一是Apache的DBCP,其二是C3P0。可以在Spring配置文件中,可以在Spring配置文件中利用二者的任何一个配置数据源。
DBCP数据源
DBCP是一个依赖Jakarta commons-pool对象池机制的数据库连接池,所以在类路径下还必须包括commons-pool的类包

<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="123456"
	/>

BasicDataSource提供了一个close方法关闭数据源,所以必须设定destroy-method="close"属性,
以便Spring容器关闭时,数据源能够正常关闭。
假设数据库是Mysql,如果数据源配置不当,则将可能发生经典的“8小时问题”,原因是MYSQL在默认的情况
下如果发现一个连接的空闲时间超过8小时,则将会在数据库端,自动关闭这个连接,而数据源并不知道这个
连接已经被关闭,当它将这个无用的连接返回给某个DAO时,DAO就会报无法获取Connection的异常。

DBCP参数设置说明
DBCP参数设置说明
DBCP参数设置说明
DBCP参数设置说明
DBCP参数设置说明

如果采用DBCP的默认配置,由于testOnBorrow属性默认值为true,数据源在将连接交给DAO之前,会事先检测这个连接是否是好的,如果连接有问题(在数据库端被关闭),则会取一个其它的连接给DAO,所以并不会有"八小时问题",如果每次将连接交给DAO时,都检测连接的有效性,那么在高并发问题时将带来性能问题,因为它需要更多的数据库访问请求。
一种推荐高效的方式是:将testOnBorrow设置为false,testWhileIdle设置为true。再设置好timeBetweenEvictionRunsMillis值,这样,DBCP将通过一个后台线程定时地对空闲连接进行检测,当发现无用的空闲连接,(那些被数据库关闭的连接时),就会将它们清除掉,只要将timeBetweenEvictionRunsMillis的值设置为小于8小时,那些被Mysql关闭的空闲连接就可以被清除出去,从而避免“8小时问题”。
当然MySQL本身可以通过调整,interactive-timeout(以秒为单位)配置参数,更改空闲连接过期时间,所以在设置timeBetweenEvictionRunsMillis值时,必须首先获知MySQL空闲连接最大过期时间。

C3P0数据源
C3P0是一个开放源码的JDBC数据源实现项目,实现了JDBC3和JDBC2的扩展规范说明的Connection和Statement池。

配置一个Oracle数据源:

<bean id="dataSource"
		class="com.mchange.v2.c3p0.ComboPooledDataSource"
		destroy-method="close"
		p:driverClass="oracle.jdbc.driver.OracleDriver"
		p:jdbcUrl="jdbc:oracle:thin:@localhost:1521:ora9i"
		p:user="root"
		p:password="123456" />

ComboPooledDataSource和BasicDataSource一样,都提供了用于关闭数据源的close()方法,这样就可以保证,Spring容器关闭时,数据源能够被成功释放。
C3P0拥有比DBCP更丰富的配置属性,通过这些属性,可以对数据源进行各种有效控制。

  • acquireIncrement:当连接池中连接用完时,C3P0一次性创建新的连接项目。
  • acquireRetryAttempts:定义在从数据库获取新连接失败后,重新尝试获取的次数,默认为30,。
  • acquireRetryDelay:尝试获取连接的间隔时间,单位为毫秒,默认为1000ms。
  • autoCommitOnClose:连接关闭时默认将所有未提交的操作回滚,默认为false。
  • automaticTestTable:C3P0将创建一张名为Test的空表,并使用其自带的查询语句进行测试。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    使用属性文件
    数据源的配置信息有可能经常需要改动,同时可能被其他工程复用,此外用户名/密码等信息比较敏感,可能需要使用特别的安全措施,所以一般将数据源的配置信息独立到一个属性文件中,通过context:property-placeholder引入属性文件,以${XXX}的方式引用属性。
jdbc.properties数据库属性文件:

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/sampledb?serverTimezone=UTC
username=root
password=123456

<!-- 引入jdbc.properties文件 两种方式 -->
	<!-- <context:property-placeholder location="jdbc.properties" /> -->
	<bean 
	class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
		p:location="jdbc.properties"
		p:fileEncoding="utf-8" />

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close"
		p:driverClassName="${driverClassName}"
		p:url="${url}"
		p:username="${username}"
		p:password="${password}" />

在${属性}引用时一定要注意不要有空格,因为空格会也会当做变量来处理。
获取JNDI数据源

如果应用配置在高性能的应用服务器(如WebLogic或WebSphere等)上,则可能更希望使用应用服务器本身提供数据源,应用服务器的数据源使用JNDI开放调用者。Spring为此专门提供了引用JNDI数据源的JndiObjectFactoryBean类。
在这里插入图片描述
通过jndiName指定引用的JNDI名称。
Spring为获取javaEE资源,提供了一个jee命名空间,通过jee命名空间,可以有效的简化JavaEE资源的引用,jee命名空间配置如下:
在这里插入图片描述
Spring的数据源实现
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值