1.Spring JDBC
1.1applicationContext.xml內配置好數據庫相關信息
<!-- 指定需要掃描的包(包括子包),使註解生效 -->
<context:component-scan base-package="com.ch5"/>
<!-- 配置數據源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- MySQL數據庫驅動 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!-- 連接數據庫的URL -->
<property name="url" value="jdbc:mysql://localhost:3306/springtest?characterEncoding=utf8"/>
<!-- 連接數據庫的用戶名 -->
<property name="username" value="root"/>
<!-- 連接數據庫的密碼 -->
<property name="password" value="363316495"/>
</bean>
<!-- 配置JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
1.2創建實體類MyUser
public class MyUser {
private Integer uid;
private String uname;
private String usex;
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getUsex() {
return usex;
}
public void setUsex(String usex) {
this.usex = usex;
}
public String toString() {
return "myUser [uid=" + uid +", uname=" + uname + ", usex=" + usex + "]";
}
}
1.3創建數據庫訪問層TestDao
接口類:
package ch5;
import java.util.List;
public interface TestDao {
public int update(String sql, Object[] param);
public List<MyUser> query(String sql, Object[] param);
}
實現類:
@Repository("testDao")
public class TestDaoImpl implements TestDao{
//自動裝配
@Autowired
//使用配置文件中的JDBC(相當於依賴注入了)
private JdbcTemplate jdbcTemplate;
//更新方法
@Override
public int update(String sql, Object[] param) {
return jdbcTemplate.update(sql, param);
}
//查詢方法
@Override
public List<MyUser> query(String sql, Object[] param) {
RowMapper<MyUser> rowMapper = new BeanPropertyRowMapper<MyUser>(MyUser.class);
return jdbcTemplate.query(sql, rowMapper, param);
}
}
1.4測試:
public static void main(String[] args) {
@SuppressWarnings("resource")
ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
//從容器中獲取增強後的目標對象
TestDao td = (TestDao)appCon.getBean("testDao");
//插入的sql
//暫時沒有在這裏發現問題:
//UID爲主鍵,這裏設置爲空,不會出現問題,原因是:mysql數據庫裏面可以勾選主鍵自動增長
String insertSql = "insert into user values(null,?,?)";
//數組param的值與insertSql語句一一對應
//此處我的mysql不支持中文字符,改成了英文字符,不影響
Object param1[] = {"chenheng1", "nan"};
Object param2[] = {"chenheng2", "nv"};
Object param3[] = {"chenheng3", "nan"};
Object param4[] = {"chenheng4", "nv"};
//添加用戶
td.update(insertSql, param1);
td.update(insertSql, param2);
td.update(insertSql, param3);
td.update(insertSql, param4);
//查詢用戶的sql
String selectSql ="select * from user";
List<MyUser> list = td.query(selectSql, null);
for(MyUser mu : list) {
System.out.println(mu);
}
}
}
2.編程式事務管理
(1)基於底層API的編程式事務管理
2.1.1 applicationContext.xml內配置事務管理器
<!-- 爲數據源添加事務管理器 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager" <property name="dataSource" ref="dataSource" />
</bean>
2.1.2 創建數據訪問類
//數據訪問類
@Repository("codeTransaction")
public class CodeTransaction {
@Autowired
// 使用配置文件中的JDBC模板
private JdbcTemplate jdbcTemplate;
//DataSourceTransactionManager是PlatformTransactionManager接口的實現類
@Autowired
private DataSourceTransactionManager txManager;
//定義test方法
public String test() {
// 默認事務定義,例如隔離級別、傳播行爲
TransactionDefinition tf = new DefaultTransactionDefinition();
//開啓事務ts
TransactionStatus ts = txManager.getTransaction(tf);
String message = "ִ執行成功,沒有事務回滾";
try {
// 刪除表中數據
String sql = " delete from user ";
//添加表中數據
String sql1 = " insert into user values(?,?,?) ";
Object param[] = { 1, "topus", "nan" };
// 先刪除數據
jdbcTemplate.update(sql);
//添加一條數據
jdbcTemplate.update(sql1, param);
// 添加相同的一條數據
jdbcTemplate.update(sql1, param);
//提交事務
txManager.commit(ts);
} catch (Exception e) {
// 出現異常,事務回滾
txManager.rollback(ts);
message = "主鍵重複,事務回滾";
e.printStackTrace();
}
//返回的是執行成功與否的消息
return message;
}
}
2.2.3測試
public class TestCodeTransaction {
public static void main(String[] args) {
@SuppressWarnings("resource")
ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
CodeTransaction ct = (CodeTransaction)appCon.getBean("codeTransaction");
String result = ct.test();
System.out.println(result);
}
}
(2)基於TranesactionTemplate的編程式事務管理
2.2.1爲事務管理器添加事務模板
<!-- 爲事務管理器txManager創建transactionTemplate -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="txManager"/>
</bean>
2.2.2 創建數據訪問類
@Repository("transactionTemplateDao")
public class TransactionTemplateDao {
@Autowired
//使用配置文件中的jdbc
private JdbcTemplate jdbcTemplate;
@Autowired
private TransactionTemplate transactionTemplate;
String message = "";
public String test() {
//匿名內部類
transactionTemplate.execute(new TransactionCallback<Object>(){
@Override
public Object doInTransaction(TransactionStatus arg0) {
//刪除
String sql = " delete from user ";
//添加
String sql1 = " insert into user values(?,?,?) ";
Object param[] = {
1,
"zyz",
"nan"
};
try{
//先刪除數據
jdbcTemplate.update(sql);
//添加一條數據
jdbcTemplate.update(sql1, param);
//添加一條重複數據
jdbcTemplate.update(sql1, param);
message = "執行成功沒有事務回滾ִ";
}catch(Exception e){
message = "主鍵重複,事務回滾";
e.printStackTrace();
}
return message;
}
});
return message;
}
}
2.2.3 創建測試類
public class TransactionTemplateTest {
public static void main(String[] args) {
@SuppressWarnings("resource")
ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
TransactionTemplateDao ct = (TransactionTemplateDao)appCon.getBean("transactionTemplateDao");
String result = ct.test();
System.out.println(result);
}
}
3.聲明式事務管理
(基於AOP技術實現的事務管理,其本質是對方法前後進行攔截,然後在目標方法開始之前創建或者加入一個事務,在執行完目標方法之後根據執行情況提交或者回滾事務)
(1)xml
3.1.1創建Dao層
public interface TestDao {
public int save(String sql, Object param[]);
public int delete(String sql, Object param[]);
}
@Repository("TestDao")
public class TestDaoImpl implements TestDao{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int save(String sql, Object[] param) {
return jdbcTemplate.update(sql,param);
}
@Override
public int delete(String sql, Object[] param) {
return jdbcTemplate.update(sql,param);
}
}
3.1.2創建service層
public class TestServiceImpl implements TestService{
@Autowired
private TestDao testDao;
@Override
// 這裏調用Dao層的save和delete方法,參數爲sql和param,等控制層在傳入sql語句
public int save(String sql, Object[] param) {
return testDao.save(sql, param);
}
@Override
public int delete(String sql, Object[] param) {
return testDao.delete(sql, param);
}
}
3.1.3創建controller層
private TestService testService;
public String test() {
// 此處在加入sql語句
String message = "";
String deleteSql ="delete from user";
String saveSql = "insert into user values(?,?,?)";
Object param[] = {1,"topus","nan"};
try{
testService.delete(deleteSql, null);
testService.save(saveSql, param);
//重複插入相同主鍵的數據
testService.save(saveSql, param);
}catch(Exception e){
message = "主鍵重複,回滾";
e.printStackTrace();
}
return message;
}
}
3.1.4創建配置文件
在xml裏面使用<tx:advice>編寫通知聲明事務
使用<aop:config>編寫AOP讓spring自動對目標對象生成代理
<!-- 指定需要掃描的包(包括子包),使註解生效 -->
<context:component-scan base-package="com.statement"/>
<!-- 配置數據源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- MySQL數據庫驅動 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!-- 連接數據庫的URL -->
<property name="url" value="jdbc:mysql://localhost:3306/springtest?characterEncoding=utf8"/>
<!-- 連接數據庫的用戶名 -->
<property name="username" value="root"/>
<!-- 連接數據庫的密碼 -->
<property name="password" value="root"/>
</bean>
<!-- 配置JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 爲數據源添加事務管理器 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 編寫通知聲明事務 -->
<tx:advice id="myAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- *表示任意方法 -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- 編寫AOP,讓Spring自動對目標對象生成代理,需要使用AspectJ的表達式 -->
<aop:config>
<!-- 定義切入點 -->
<aop:pointcut expression="execution(* com.statement.service.*.*())" id="txPointCut"/>
<!-- 切面:將切入點與通知關聯 -->
<aop:advisor advice-ref="myAdvice" pointcut-ref="txPointCut"/>
</aop:config>
</beans>
3.1.6測試
public static void main(String[] args) {
@SuppressWarnings("resource")
ApplicationContext appCon = new ClassPathXmlApplicationContext("/com/statement/xml/XMLstatementapplicationContext.xml");
StatementController ct = (StatementController)appCon.getBean("statementController");
String result = ct.test();
System.out.println(result);
}
(2)基於transactional註解的聲明式事務管理
3.2.1創建配置文件
<!-- 指定需要掃描的包(包括子包),使註解生效 -->
<context:component-scan base-package="com.statement"/>
<!-- 配置數據源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- MySQL數據庫驅動 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!-- 連接數據庫的URL -->
<property name="url" value="jdbc:mysql://localhost:3306/springtest?characterEncoding=utf8"/>
<!-- 連接數據庫的用戶名 -->
<property name="username" value="root"/>
<!-- 連接數據庫的密碼 -->
<property name="password" value="root"/>
</bean>
<!-- 配置JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 爲數據源添加事務管理器 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 爲事務管理器註冊註解驅動 -->
<tx:annotation-driven transaction-manager="txManager" />
</beans>
3.2.2在service層添加註解@Transactional
//加上註解@Transactional,就可以指定這個類需要受Spring的事務管理
//注意@Transactional智能針對pulic屬性範圍內的方法添加
@Transactional
public class TestServiceImpl implements TestService{
@Autowired
private TestDao testDao;
@Override
// 這裏調用Dao層的save和delete方法,參數爲sql和param,等控制層在傳入sql語句
public int save(String sql, Object[] param) {
return testDao.save(sql, param);
}
@Override
public int delete(String sql, Object[] param) {
return testDao.delete(sql, param);
}
}
3.2.3在事務處理中捕獲異常
修改註解
@Transactional(rollbackFor = (Exception.class))
//指定回滾生效的異常類
其實本質上問題在於書內的try catch的異常捕獲寫在的service層,但是在默認情況下,Spring只在(發生未被捕獲的RuntimeException時)纔回滾事務,此時如果想在事務處理中捕獲異常就必須指定回滾生效的異常類.