1. 概念
-
Spring的DAO異常體系建立在執行期異常的基礎上,封裝了源異常
-
JDBC資料訪問流程:
-
準備資源
-
啟動事務
-
在事務中執行具體資料訪問操作
-
提交/回滾事務
-
關閉資源,處理異常
-
-
Spring將相同的資料訪問流程固化到模板類中,把資料訪問中固定和變化的部分分開,同時保證模板類是執行緒安全的。Spring為不同的持久化技術都提供了簡化操作的模板和回撥
-
資料庫事務:原子性,一致性,隔離性和永續性(ACID)
-
5類資料庫併發問題:
-
髒讀: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事務增強的方法不能啟動事務,但是外層方法的事務上下文仍然可以傳播到這些方法中
-
2. Spring中使用JDBC程式設計示例
-
本地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);
}
}