DAO是用户访问数据的对象,虽然在很多情况下,我们将数据存放在数据库中,但是有时候也会将数据存放到文件或者LDAP中。DAO不仅屏蔽了数据存储的介质的不同,也屏蔽了具体实现技术的不同。
Spring本质上希望以一种统一的方式整合底层的持久化技术:以统一的方式进行调用和事务管理,避免让具体的实现侵入到业务层代码中。由于每个持久化技术都有各自的异常体系,所以Spring提供了统一的异常体系,方便定义出和具体实现技术无关的DAO接口。
1 Spring的统一异常体系
统一的异常体系是整合不同持久化技术的关键,Spring提供了一套和实现技术无关的,面向DAO层语义的异常体系,并通过异常转换器将不同持久化技术的异常转换为Spring的异常。
要详细了解Spring为什么要提供统一的异常体系,可以参考这篇文章:http://my.oschina.net/u/218421/blog/38478
在JDBC和很多的持久层技术中,检查型异常被过度使用,以至于在代码里充斥着大量的try/catch样板式的代码。但是,引发异常的问题往往是不可恢复的,强制捕捉检查型异常除了限制开发人员的自由度以外,并没有提供有价值的东西。因此,Spring的异常体系都是建立在运行期异常的基础上,开发者可以根据需要捕捉感兴趣的异常。
Spring的异常体系都继承自DataAccessException,而DataAccessException又继承于NestedRuntimeException,NestedRuntimeException异常以嵌套的方式封装了源异常。因此,虽然不同持久化技术的特定异常被转换到Spring的DAO异常体系中,但是,原始的异常信息并不会丢失,只要用户愿意,就可以通过getCause()方法获取原始异常信息。
2统一数据访问模板
JDBC数据访问的代码,包含以下操作:准备资源,启动事务,具体数据操作,提交事务,关闭资源。但是,对于我们真正有用的代码就是具体的数据操作,其他的行为都是固定的。因此Spring将这个相同的数据访问流程固化在模板类中,并将数据访问中固定和变化的部分分开,同时保证模板类是线程安全的,以便多个数据访问线程共享同一模板实例。固定的部分已经在模板类中准备好,而变化的部分通过回调接口开放出来,用于定义具体数据访问和结果返回操作。
2.1 JdbcTemplate
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.bbc"/>
<!-- 数据源 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- jdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
p:dataSource-ref="dataSource"
/>
</beans>
随便举一个DAO的例子:
public class LoginLogDao implements ILoginLogDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void insertLoginLog(LoginLog log) {
// TODO Auto-generated method stub
String sql="insert into t_login_log(user_id,ip,login_time) values(?,?,?)";
jdbcTemplate.update(sql, new Object[]{log.getUser_id(),log.getIp(),log.getLogin_time()});
}
}
从上面我们可以看到,如果我们直接使用模板类,一般都需要在DAO中定义一个模板对象,然后在xml中定义数据源和模板。Spring为每一个持久化技术都提供了支持类,支持类已经为我们完成这样的功能。我们只需要提供数据源,支持类会为我们创建好模板,我们只需要编写实际的数据访问逻辑。
public class JdbcCustomerDAO extends JdbcDaoSupport implements CustomerDAO
{
//no need to set datasource here
public void insert(Customer customer){
String sql = "INSERT INTO CUSTOMER " +
"(CUST_ID, NAME, AGE) VALUES (?, ?, ?)";
getJdbcTemplate().update(sql, new Object[] { customer.getCustId(),
customer.getName(),customer.getAge()
});
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="customerDAO" class="com.mkyong.customer.dao.impl.JdbcCustomerDAO">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
我们可以看一下JdbcDaoSupport的源码:
public abstract class JdbcDaoSupport extends DaoSupport {
private JdbcTemplate jdbcTemplate;
/**
* Set the JDBC DataSource to be used by this DAO.
*/
public final void setDataSource(DataSource dataSource) {
if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()) {
this.jdbcTemplate = createJdbcTemplate(dataSource);
initTemplateConfig();
}
}
... ... 略
}
setDataSource()方法是final的,无法重写,类中也没有dataSource属性。因此,我们在通过支持类来编写自己的DAO时,只能通过xml的方式来生成相应的bean,无法通过注解的方式来设置dataSource,因为setDataSource是final的,而且是无法修改的第三方类库。
可以注意到jdbc并没有对象关系映射ORM。下面的hibernate是有ORM的:
2.2 HibernateTemplate
可以参考我的这篇文章:
Spring中使用hibernate