Spring的DAO异常体系创建在运行期异常的基础上,封装了源异常java
JDBC数据访问流程:mysql
准备资源spring
启动事务sql
在事务中执行具体数据访问操做数据库
提交/回滚事务apache
关闭资源,处理异常编程
Spring将相同的数据访问流程固化到模板类中,把数据访问中固定和变化的部分分开,同时保证模板类是线程安全的。Spring为不一样的持久化技术都提供了简化操做的模板和回调安全
数据库事务:原子性,一致性,隔离性和持久性(ACID)并发
5类数据库并发问题:app
脏读:A事务读取到B事务还没有提交的数据
不可重复读:A事务中读取到B事务已经提交的==更新==数据,即连续两次读取结果不一样
幻读:A事务读取B事务的==新增==数据
第一类更新丢失:A事务撤销时覆盖了B事务的提交
第二类更新丢失:A事务覆盖B事务已经提交的数据
JDBC默认状况下自动提交,即每条执行的SQL语句都对应一个事务,AutoCommit = TRUE
Spring基于ThreadLocal
解决有状态的Connetion
的并发问题,事务同步管理器org.springframework.transaction.support.TransactionSynchronizationManager
使用ThreadLocal
为不一样事务线程提供独立的资源副本
Spring事务管理基于3个接口:TransactionDefinition
,TransactionStatus
和PlatformTransactionManager
Spring为不一样持久化技术提供了从TransactionSynchronizationManager
获取对应线程绑定资源的工具类,如DataSourceUtils.getConnection(DataSource dataSource)
。模板类在内部经过工具类访问TransactionSynchronizationManager
中的线程绑定资源
Spring经过事务传播行为控制当前的事务如何传播到被嵌套调用的目标服务接口方法中
使用<tx:annotation-driven transaction-manager="txManager">
对标注@Transactional
注解的bean进行加工处理,织入事务管理切面
@Transactional
注解的属性
事务传播行为:propagation
,默认PROPAGATION_REQUIRED
,即若是当前没有事务,就新建一个事务;不然加入到当前事务
事务隔离级别:isolation
,默认ISOLATION_DEFAULT
读写事务属性:readOnly
超时时间:timeout
回滚设置:rollbackFor
,rollbackForClassName
,noRollbackFor
,noRollbackForClassName
在相同线程中进行相互嵌套调用的事务方法工做于相同的事务中;若是在不一样线程中,则工做在独立的事务中
特殊方法:
注解不能被继承,因此业务接口中的@Transactional
注解不会被业务实现类继承;方法处的注解会覆盖类定义处的注解
对于基于接口动态代理的AOP事务,因为接口方法都是public
的,实现类的实现方法必须是public
的,同时不能使用static
修饰符。所以,能够经过接口动态代理实施AOP加强、实现Spring事务的方法只能是public
或public final
的
基于CGLib动态代理实施AOP的时候,因为使用final
、static
、private
的方法不能被子类覆盖,相应的,这些方法不能实施AOP加强,实现事务
不能被Spring进行AOP事务加强的方法不能启动事务,可是外层方法的事务上下文仍然能够传播到这些方法中
本地mysql建表
CREATE TABLE `t_user` ( `user_id` varchar(256) NOT NULL, `user_name` varchar(256) DEFAULT NULL, PRIMARY KEY (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
springDAO.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:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd "> <context:component-scan base-package="com.dao" /> <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="123123" /> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="dataSource" /> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource" /> </beans>
User
package com.data; public class User { private String userId; private String userName; public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } }
BaseDAO
package com.dao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; public class BaseDAO { @Autowired protected JdbcTemplate jdbcTemplate; }
UserDAO
package com.dao; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import com.data.User; import com.mapper.UserRowMapper; @Repository public class UserDAO extends BaseDAO { private static final String SQL_GET_USER = "select * from t_user where " + "user_id = ?;"; private static final String SQL_INSERT_USER = "insert into t_user values(?, ?);"; private static final String SQL_CLEAN_USER = "delete from t_user where 1=1;"; @Transactional public User getUserById(String userId) { return jdbcTemplate.queryForObject(SQL_GET_USER, new Object[]{userId}, new UserRowMapper()); } @Transactional public int insertUser(User user) { return jdbcTemplate.update(SQL_INSERT_USER, user.getUserId(), user.getUserName()); } @Transactional public int cleanUser() { return jdbcTemplate.update(SQL_CLEAN_USER); } }
UserRowMapper
package com.dao; import java.sql.ResultSet; import java.sql.SQLException; import org.springframework.jdbc.core.RowMapper; import com.data.User; public class UserRowMapper implements RowMapper<User>{ public User mapRow(ResultSet rs, int rowNumber) throws SQLException { User user = new User(); user.setUserId(rs.getString("user_id")); user.setUserName(rs.getString("user_name")); return user; } }
BaseTestCase
package com; import org.junit.Assert; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"/springDAO.xml"}) public class BaseTestCase extends Assert { }
TestUserDAO
package com.dao; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import com.BaseTestCase; import com.data.User; public class TestUserDAO extends BaseTestCase{ @Before @After public void clean() { dao.cleanUser(); } @Autowired private UserDAO dao; @Test public void getUserById() { User user = new User(); String id = "id"; String name = "name"; user.setUserId(id); user.setUserName(name); assertEquals(dao.insertUser(user), 1); user = dao.getUserById(id); assertEquals(user.getUserId(), id); assertEquals(user.getUserName(), name); } }