5.JDBC模板與事務處理

一、Spring的JDBC模板的使用
1.使用入門
需要引入的jar包
基本的jar包:
  spring-expression-4.2.4.RELEASE.jar
  spring-core-4.2.4.RELEASE.jar
  spring-context-4.2.4.RELEASE.jar
  spring-beans-4.2.4.RELEASE.jar
  spring-test-4.2.4.RELEASE.jar
  com.springsource.org.apache.log4j-1.2.15.jar
  com.springsource.org.apache.commons.logging-1.1.1.jar
數據庫驅動jar包:
  mysql-connector-java-5.1.7-bin.jar
Spring中JDBC模板的jar包:
  spring-tx-4.2.4.RELEASE.jar
  spring-jdbc-4.2.4.RELEASE.jar

測試代碼

public class jdbcdemo1 {
    @Test
    public void demo1() {
    	//創建連接池
    	DriverManagerDataSource datasource = new DriverManagerDataSource();
    	datasource.setDriverClassName("com.mysql.jdbc.Driver");  //驅動類在導入的jar文件中
    	datasource.setUrl("jdbc:mysql:///jdbc_database");  //jdbc_database爲所創建的數據庫
    	datasource.setUsername("root");
    	datasource.setPassword("2e5y8hxf");
    	
    	//創建jdbc模板
    	JdbcTemplate jdbctemplate = new JdbcTemplate(datasource);
    	jdbctemplate.update("insert into account values(null,'張三丰',10000)");
    }
}

注意:這裏"com.mysql.jdbc.Driver"路徑是在mysql-connector-java-5.1.7-bin.jar包中的com/mysql/jdbc目錄下的Driver.class文件。
終端SQL操作

create database jdbc_database;
use jdbc_database;
mysql> create table account(
     id int primary key auto_increment,
     name varchar(20),
     money double
);

select *from account;

測試結果
在這裏插入圖片描述

2.將連接池和模板交給Spring管理
導入jar包
需要另外導入spring-aop-4.2.4.RELEASE.jar
配置文件代碼

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    
    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/aop 
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 配置SPring內置的連接池 -->
    <bean id = "dataSource"  class = "org.springframework.jdbc.datasource.DriverManagerDataSource"> 
        <property name="driverClassName" value = "com.mysql.jdbc.Driver"/>
        <property name="url" value = "jdbc:mysql:///jdbc_database"/>
        <property name="username" value = "root"/>
        <property name="password" value = "2e5y8hxf"/>        
    </bean>
    
    <!-- 配置Spring的JDBC模板 -->
    <bean id = "jdbctemplate" class = "org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref = "dataSource"/>
    </bean>

</beans>

注意的問題:注意配置文件要建在src文件下,不能建在包下。
測試代碼

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:jdbc_first.xml")
public class jdbcdemo1 {
	@Resource(name="jdbctemplate")
	private JdbcTemplate jdbctemplate;
	
    @Test
    public void demo1() {
         jdbctemplate.update("insert into account values(null,?,?)","張無忌",100000);
    }
}

3.DBCP連接池的配置
導入jar包
com.springsource.org.apache.commons.dbcp-1.2.2.osgi.jar
com.springsource.org.apache.commons.pool-1.5.3.jar
配置文件代碼
將2.中連接池部分配置代碼替換爲DPCP連接池部分代碼即可

    <!-- 配置DPCP連接池 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value = "com.mysql.jdbc.Driver"/>
        <property name="url" value = "jdbc:mysql:///jdbc_database"/>
        <property name="username" value = "root"/>
        <property name="password" value = "2e5y8hxf"/> 
    </bean>

4.C3P0連接池的配置
導入jar包
com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar
配置文件代碼

    <!-- 配置C3P0連接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value = "com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value = "jdbc:mysql:///jdbc_database"/>
        <property name="user" value = "root"/>
        <property name="password" value = "2e5y8hxf"/>        
    </bean>

5.引入外部屬性文件
建立屬性文件:txt文件,包含以下內容
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///jdbc_database
jdbc.username=root
jdbc.password=2e5y8hxf

引入屬性文件的兩種方式

    <!-- 第一種方式:通過bean標籤引入(使用較少) -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:jdbc.properties"/>
    </bean>

    <!-- 第二種方式:通過context標籤引入-->     
    <context:property-placeholder location="classpath:jdbc.properties"/> 

    <!-- 修改連接池:配置C3P0連接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value = "${jdbc.driverClass}"/>
        <property name="jdbcUrl" value = "${jdbc.url}"/>
        <property name="user" value = "${jdbc.username}"/>
        <property name="password" value = "${jdbc.password}"/>        
    </bean>

6.進行增刪改查的操作

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:jdbc_first.xml")
public class jdbcdemo1 {
	@Resource(name="jdbctemplate")
	private JdbcTemplate jdbctemplate;
	
    @Test
    //添加一列數據
    public void demo1() {
         jdbctemplate.update("insert into account values(null,?,?)","楊過",100000);
    }
    
    @Test
    //修改id爲3的一列數據
    public void demo2() {
         jdbctemplate.update("update account set name = ? ,money = ? where id = ?","任我行",999999,3);
    }
    
    @Test
    //刪除id爲2的一列數據
    public void demo3() {
         jdbctemplate.update("delete from account where id = ?",2);
    }
    
    @Test
    //查詢id爲1的name數據
    public void demo4() {
         String abc = jdbctemplate.queryForObject("select name from account where id = ?", String.class,1);
         System.out.println(abc);
         //格式:jdbctemplate.queryForObject(sql, rowMapper, args);
    }
    
    @Test
    //對查詢結果進行數值統計
    public void demo5() {
         Long count = jdbctemplate.queryForObject("select count(*) from account", Long.class);
         System.out.println(count);
         //格式:jdbctemplate.queryForObject(sql, requiredType)
    }
    
    @Test
    //將查詢到的結果封裝到一個對象中
    public void demo6() {
         Account account = jdbctemplate.queryForObject("select *from account where id = ?", new MyRowMapper(),8);
         System.out.println(account);
         //格式:jdbctemplate.queryForObject(sql, requiredType)
    }
    
    @Test
    //查詢多條記錄
    public void demo7() {
         List<Account> list = jdbctemplate.query("select *from account", new MyRowMapper());
         for(Account account:list) {
        	 System.out.println(account); 
         }
             

    }
    
    class MyRowMapper implements RowMapper<Account>{

		@Override
		public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
			Account account = new Account();
			account.setId(rs.getInt("id"));
			account.setName(rs.getString("name"));
			account.setMoney(rs.getDouble("money"));
			return account;
		}
    	
    }
}

二、Spring的事務處理
1.事務管理概述
事務管理詳解:轉自:https://www.cnblogs.com/yixianyixian/p/8372832.html
什麼是事務
事務:邏輯上的一組操作,組成這組操作的各個單元,要麼全部成功,要麼全部失敗。
舉例:取錢。
  比如你去ATM機取1000塊錢,大體有兩個步驟:首先輸入密碼金額,銀行卡扣掉1000元錢;然後ATM出1000元錢。這兩個步驟必須是要麼都執行要麼都不執行。如果銀行卡扣除了1000塊但是ATM出錢失敗的話,你將會損失1000元;如果銀行卡扣錢失敗但是ATM卻出了1000塊,那麼銀行將損失1000元。所以,如果一個步驟成功另一個步驟失敗對雙方都不是好事,如果不管哪一個步驟失敗了以後,整個取錢過程都能回滾,也就是完全取消所有操作的話,這對雙方都是極好的。
  事務就是用來解決類似問題的。事務是一系列的動作,它們綜合在一起纔是一個完整的工作單元,這些動作必須全部完成,如果有一個失敗的話,那麼事務就會回滾到最開始的狀態,彷彿什麼都沒發生過一樣。
  在企業級應用程序開發中,事務管理必不可少的技術,用來確保數據的完整性和一致性。

事務的特性
原子性:事務不可分割;
一致性:事務執行前後,數據完整性保持一致;
隔離性:一個事務的執行不應該受到其他事務的干擾;
持久性:一旦事務結束,數據就持續化到數據庫。

如果不考慮隔離性應發的安全性問題
讀問題:
  髒讀:一個事務讀到另一個事務未提交的數據;
  不可重複讀:一個事務讀到另一個事務已經提交的update的數據,導致一個事務中多次查詢結果不一致;
  虛讀、幻讀:一個事務讀到另一個事務已經提交的insert的數據,導致一個事務中多次查詢結果不一致;
  
寫問題:
  丟失更新

解決讀問題
設置事務的隔離級別:
  Read uncommitted:未提交讀,任何讀問題解決不了;
  Read committed:已提交讀,解決髒讀,但是不可重複讀,並且虛讀有可能發生;
  Repeatable read:重複讀,解決髒讀和不可重複讀,但是虛讀有可能發生;
  Serializable:解決所有讀問題(效率較低)。

2.Spring的事務管理API
PlatformTransactionManager:平臺事務管理器,這是一個接口,是Spring用於管理事務的真正的對象,其具體實現類如下:
  DataSourceTransactionManager :底層使用JDBC管理事務;
  HibernateTransactionManager :底層使用Hibernate管理事務;

TransactionDefinition:事務定義信息,用於定義事務相關的信息,隔離級別、超時信息、傳播行爲、是否只讀。

TransactionStatus:事務的狀態,用於記錄在事務管理過程中,事務的狀態的對象。

事務管理API之間的關係:Spring進行事務管理的時候,首先,平臺事務管理器根據事務定義信息進行事務的管理,在事務管理過程中產生各種狀態,將這些狀態的信息記錄到事務的狀態的對象中。

3.Spring的事務傳播行爲
事務傳播行爲解決的問題:如果遇到了特別複雜的業務邏輯,有可能出現業務層之間的方法相互調用,事務傳播就是解決業務層方法相互調用的問題。

舉例:如下圖所示,是一個業務層之間方法相互調用的情況
在這裏插入圖片描述
Spring中提供了7中事務傳播行爲:
保證多個操作在同一事務中
PROPAGATION_REQUIRED :默認值,如果X中有事務,使用X中的事務,如果X中沒有事務,則創建一個新的事務,將操作(即X()、c()、d()三個操作)包含進來。
PROPAGATION_SUPPORTS :支持事務,如果X中有事務,則使用X中的事務;如果X中沒有事務,則不使用事務。
PROPAGATION_MANDATORY:如果X中有事務,則使用X中的事務;如果X中沒有事務,則拋出異常。
保證多個操作不再同一個事務中
PROPAGATION_REQUIRES_NEW:如果X中有事務,將X中的事務掛起(暫停),並創建新事務,只包含自身操作(即c、d);如果X中沒有事務,也創建一個新事務,包含自身操作。
PROPAGATION_NOT_SUPPORTED:如果X中有事務,將X中的事務掛起,不使用事務管理。
PROPAGATION_NEVER:如果A中有事務,報異常。
嵌套式事務
PROPAGATION_NESTED:嵌套事務,如果X中有事務,按照X中的事務執行,執行完後,設置一個保存點,執行B中的操作,如果沒有異常,執行通過,如果有異常,可以選擇回滾到最初始位置,也可以回滾到保存點。

4.搭建Spring事務管理環境
步驟
創建Service接口和實現類、
創建DAO接口和實現類、
配置Service和DAO將其交給Spring管理

代碼:
Service和DAO接口和實現類

public interface AccountService {
    public void transfer(String from,String to,double money);
}


public class AccountServiceImpl implements AccountService{
    private AccountDao accountDao;
    
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	/*
	 * from:轉出賬戶
	 * to:轉入賬戶
	 * money:金額
	 * */
	@Override
	public void transfer(String from, String to, double money) {
		accountDao.outMoney(from, money);
		accountDao.inMoney(to, money);
	}
}



public interface AccountDao {
    public void outMoney(String from,double money);
    public void inMoney(String to, double money);
}


public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao{
	/*	這裏繼承了JdbcDaoSupport之後,就不需要使用這部分代碼了,因爲JdbcDaoSupport 裏包含get與set方法
    private JdbcTemplate jdbcTemplate;
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	public JdbcTemplate getJdbcTemplate() {
		return jdbcTemplate;
	}
*/
	@Override
	public void outMoney(String from,double money) {
		this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,from);
		
	}
	@Override
	public void inMoney(String to,double money) {
		this.getJdbcTemplate().update("update account set money = money + ? where name = ?",money,to);
	}

}

配置文件代碼

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    
    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/aop 
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 配置Service -->
    <bean id = "accountService" class = "package2.AccountServiceImpl">
        <property name="accountDao" ref = "accountDao"/>
    </bean>
    
    <!-- 配置DAO -->
    <bean id = "accountDao" class = "package2.AccountDaoImpl">
        <property name="jdbcTemplate" ref = "jdbcTemplate"/>
    </bean>
     
    <context:property-placeholder location="classpath:jdbc.properties"/> 
    
    <!-- 配置C3P0連接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value = "${jdbc.driverClass}"/>
        <property name="jdbcUrl" value = "${jdbc.url}"/>
        <property name="user" value = "${jdbc.username}"/>
        <property name="password" value = "${jdbc.password}"/>        
    </bean>
    
    <!-- 配置Spring的JDBC模板(這一步可以通過一些操作省略) -->
    <bean id = "jdbcTemplate" class = "org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref = "dataSource"/>
    </bean>

</beans>

測試類代碼

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:tx.xml")
public class spring_demo1 {
	@Resource(name = "accountService")
	private AccountService accountService;
	
    @Test
    public void demo1() {
    	accountService.transfer("張三丰", "張無忌", 500);
    }
}

測試結果:張三丰給張無忌轉了500塊錢
在這裏插入圖片描述

5.編程式事務管理(需要寫代碼)
第一步:配置平臺事務管理器

    <!-- 配置平臺事務管理器 -->
    <bean id = "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref = "dataSource"/>
    </bean>

第二步:配置事務管理的模板類

    <!-- 配置事務管理的模板 -->
    <bean id = "transactionTemplate" class = "org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref = "transactionManager"/>
    </bean>

第三步:在業務層注入事務管理的模板

    <!-- 配置Service -->
    <bean id = "accountService" class = "package2.AccountServiceImpl">
        <property name="accountDao" ref = "accountDao"/>
        <!-- 注入事務管理的模板 -->
        <property name="transactionTemplate" ref = "transactionTemplate"/>
    </bean>

同時在AccountServiceImpl類中添加如下代碼:

	//注入事務管理的模板
	private TransactionTemplate transactionTemplate;
	 
	public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
		this.transactionTemplate = transactionTemplate;
	}

第四步:編寫事務管理的代碼
在AccountServiceImpl實現類中修改成如下代碼:

@Override
	public void transfer(String from, String to, double money) {
		transactionTemplate.execute(new TransactionCallbackWithoutResult() {

			@Override
			protected void doInTransactionWithoutResult(TransactionStatus arg0) {
				accountDao.outMoney(from, money);
				int a = 1/0;
				accountDao.inMoney(to, money);
			}	
		});


	}

6.聲明式事務管理(不需要修改代碼)
聲明式事務管理有兩種實現方式:XML方式和註解方式

XML方式:
第一步:搭建事務管理環境(如:4.搭建Spring事務管理環境);
第二步:引入AOP的開發包
spring-aop-4.2.4.RELEASE.jar
spring-aspects-4.2.4.RELEASE.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
org.apache.servicemix.bundles.aopalliance-1.0-1.0.0-rc1.jar
第三步:添加配置文件代碼:在(4.搭建Spring事務管理環境)中的配置文件添加如下代碼,

    <!-- 配置平臺事務管理器 -->
    <bean id = "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref = "dataSource"/>
    </bean>
    
    <!-- 配置事務的增強 -->
    <tx:advice id = "txAdvice" transaction-manager = "transactionManager">
        <tx:attributes>
            <tx:method name="transfer" propagation = "REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    
    <!-- AOP配置 -->
    <aop:config>
        <aop:pointcut expression="execution(* package3.AccountServiceImpl.*(..))" id="pointcut1"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref = "pointcut1"/>
    </aop:config>

註解方式:
第一步:搭建事務管理環境(如:4.搭建Spring事務管理環境);
第二步:引入AOP的開發包
第三步:添加配置文件代碼

    <!-- 配置平臺事務管理器 -->
    <bean id = "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref = "dataSource"/>
    </bean>
    
    <!-- 開啓註解事務 -->
    <tx:annotation-driven transaction-manager = "transactionManager"/>

第四步:在業務層添加註解
在這裏插入圖片描述

結果分析:在進行XML或者註解方式的聲明事務管理後,出現異常,則“扣錢”與“收款”操作都不能執行了,從而實現了事務管理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章