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); } }