Spring事務+JDBC小結

數據庫事務

原子性: 多個操作組成最小不可分割單元。
一致性: 操作成功後,數據庫的狀態和業務規則是一致的。
隔離性: 併發數據處理時,相互不干擾。
持久性: 一旦事物提交成功後,數據的操作被記入庫中。

數據庫併發問題

髒讀:讀取不可靠的數據(數據被回滾了),Oracle不會發生髒讀情況。
不可重複讀:同一事務中兩次讀取數據有差異(數據被別的事務修改、刪除)。
幻象讀:A事務讀到B事務提交的新數據,導致A前後讀取不一致。(很像不可重複讀,不同的是幻象第二次數據是增加。採用鎖表防止增加。)
第一次丟失更新:A事務撤銷時,在A事務開始和結束的B事務也抹殺了。無視B的存在。
第二次丟失更新:A事務覆蓋B事務已提交的數據,造成B事務操作丟失。

事務隔離級別

事務隔離級別
隔離級別    髒    讀 不可重複讀    幻象讀 第一類丟失更新 第二類丟失更新
READ UNCOMMITED 允許 允許 允許 不允許 允許
READ COMMITTED 不允許 允許 允許 不允許 允許
REPEATABLE READ 不允許 不允許 允許 不允許 不允許
SERIALIZABLE 不允許 不允許 不允許 不允許 不允許

JDBC+事務

import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

//JDBC的事務
public class JDBC_Demo {
	public static void main(String[]args) throws SQLException {
		Connection conn = null;
		try {
			//獲取數據連接...
			conn = DriverManager.getConnection("");
			//關閉自動提交事務
			conn.setAutoCommit(false);
            //設置事務隔離級別
			conn.setTransactionIsolation(Connection.TRANSACTION_NONE);
			
			Statement stmt = conn.createStatement();
			int rows = stmt.executeUpdate("insert into user u values(1,'大黑')");
            
			//提交事務
			conn.commit();
		} catch (Exception e) {
			// 回滾事務
			conn.rollback();
		}finally {
			//......
		}
	}
}

Spring+事務

Spring用到了ThreadLocal和單例來保證線程安全

數據源配置

<?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:p="http://www.springframework.org/schema/p"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
         http://www.springframework.org/schema/util
         http://www.springframework.org/schema/util/spring-util-4.1.xsd">
         
    <!-- DBCP數據源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/db_database17"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    <!-- C3P0數據源 -->
    <bean id="mySource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"
          p:driverClass="oracle.jdbc.driver.OracleDriver"
          p:jdbcUrl="jdbc:oracle:thin:@localhost:1521:ora9i"
          p:use="admin"
          p:password="123456" />    
</beans>

Spring事務管理器實現類 => DataSourceTransactionManager

DataSourceTransactionManager:可管理JDBC和MyBatis的事務


<?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:p="http://www.springframework.org/schema/p"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
         http://www.springframework.org/schema/util
         http://www.springframework.org/schema/util/spring-util-4.1.xsd">
    
    <!-- DBCP數據源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/db_database17"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    <!-- 引用DBCP數據源,基於DBCP數據源管理事務。 -->
    <bean id="transactionManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
      p:dataSource-ref="dataSource"/>   
</beans>

事務同步管理器

Spring將JDBC的Connection、Hibernate的Session等訪問數據庫的鏈接、會話對象稱爲資源,這些資源同一時刻不線程共享。Spring用ThreadLocal爲每個線程做了獨立的副本,同時維護事務配置的屬性和運行狀態信息。事務同步管理器是Spring事務管理的基石。

事務傳播行爲

說白了就是類與類間的調用。Spring通過事務傳播行爲控制當前事務如何傳播到被調用的方法中。

事務傳播行爲類型   說  明  
PROPAGATION_REQUIRED 如果沒有當前事務,則新建一個事務;如果已存在一個事務,則加入這個事務中。
PROPAGATION_SUPPORTS 支持當前事務,如果當前沒有事務,以非事務方式運行。
PROPAGATION_MANDATORY 使用當前事務。當前沒有事務,拋異常。
PROPAGATION_REQUIRES_NEW 新建事務。如果當前存在事務,則把當前事務掛起。
PROPAGATION_NOT_SUPPORTED 以非事務方式操作。如果當前存在事務,則把事務掛起。
PROPAGATION_NEVER 以非事務方式執行。如果當前存在事務,則拋異常。
PROPAGATION_NESTED 如果當前存在事務,則嵌套事務內運行;當前無事務,則新建事務。

使用XML聲明事務

使用原始的TransactionProxyFactoryBean代理類對業務類進行代理

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class BbtForum {
	
	public interface ForumDao {}

	public interface TopicDao{}
	
	public interface PostDao{}
	
	public ForumDao forumDao;
	
	public TopicDao toipDao;
	
	public PostDao postDao;
	
	public void addTopic() {
		System.out.println("添加");
	}

	public void getForum() {
		System.out.println("查找");
	}
	
}
    <!-- DBCP數據源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/db_database17"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    
    <!-- 引用DBCP數據源,基於DBCP數據源管理事務。 -->
    <bean id="transactionManager" 
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          p:dataSource-ref="dataSource"/>
      
      <!-- 需要實施事務增強的目標Bean -->
     <bean id="bbtForumTarget" class="com.nan.BbtForum"
            p:forumDao-ref="forumDao"
            p:topicDao-ref="topicDao"
            p:postDao-ref="postDao" />
            
    <!--  使用事務代理工廠類爲目標業務Bean提供事務增強 -->
    <bean id="bbtForum" 
          class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
          p:transactionManager-ref="transactionManager"
          p:target-ref="bbtForumTarget">
          <!-- 事務屬性配置 -->
	      <property name="transactionAttributes">
	          <props>	  
	          <!-- 只讀事務  -->    
	             <prop key="get*">PROPAGATION_REQUIRED</prop>
	          </props>
	      </property>
     </bean>

基於AOP/tx命名空間配置事務

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
         http://www.springframework.org/schema/tx
         http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
 
     <!-- DBCP數據源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/db_database17"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    
     <!-- 引用DBCP數據源,基於DBCP數據源管理事務。 -->
    <bean id="txManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
      p:dataSource-ref="dataSource"/>
      
    <!-- 使用切點表達式語言定義目標方法 -->
    <aop:config>
       <!-- 通過AOP定義事務增強切面 -->
        <aop:pointcut  id="serviceMethod" expression="execution(com.jun.service.Forum.*(..))"/>
       <!-- 引用事務增強 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
    </aop:config>
    
     <!-- 事務增強 -->
     <tx:advice id="txAdvice"  transaction-manager ="txManager" >
       <!-- 事務屬性定義 -->
       <tx:attributes>
         <tx:method name="get*" read-only="false" />
         <tx:method name="add*" rollback-for="Exception"/>
         <tx:method name="update*"/>
       </tx:attributes>
     </tx:advice>
</beans>

 使用註解配置聲明事務

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class BbtForum_Demo {
	
	@Transactional(value="topic",readOnly=true)
	public void addTopic() {
		System.out.println("添加");
	}
	
	@Transactional(value="forum")
	public void fetForum() {
		System.out.println("查詢");
	}
}
<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
         http://www.springframework.org/schema/tx
         http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring/context/4.1.xsd">
    <!-- 掃描包 -->
    <context:component-scan base-package="com.nan"/> 
      
    <!-- DBCP數據源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/db_database17"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    
     <!-- 引用DBCP數據源,基於DBCP數據源管理事務。 -->
     <bean id="txManager" 
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
            p:dataSource-ref="dataSource"/>
      
     <!-- 對標註@Transactional註解的Bean進行加工處理,以織入事務管理切面 -->
     <tx:annotation-driven transaction-manager="txManager"/> 
</beans>

通過AspectJ LTW引入事務切面

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
         http://www.springframework.org/schema/tx
         http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring/context/4.1.xsd">
    <!-- DBCP數據源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/db_database17"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    
    <!-- 引用DBCP數據源,基於DBCP數據源管理事務。 -->
    <bean id="transactionManager" 
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          p:dataSource-ref="dataSource"/>    
    
    <!-- 掃描包 -->
    <context:component-scan base-package="com.nan"/>
    
    <tx:annotation-driven/>
</beans>

JDBC

JDBC訪問數據庫的過程

  1. 加載驅動,建立連接。
  2. 創建語句對象。
  3. 執行SQL語句。
  4. 處理結果集。
  5. 關閉連接。

JDBC中一些接口

  • 驅動管理接口DriverManager
  • 連接接口 Connection、DatabasemetaData
  • 語句對象接口 Statement 、PreparedStatement 、CallableStatement
  • 結果集接口 ResultSet 、ResultSetMetaData

Driver接口及驅動類加載

要使用JDBC接口,需要先將對應數據庫的實現部分(驅動)加載進來。 驅動類加載方式(Oracle)裝載驅動類,驅動類通過static塊實現在DriverManager中的“自動註冊”
Class.forName("oracle.jdbc.driver.OracleDriver");

Connection接口

Connection接口負責應用程序對數據庫的連接,在加載驅動之後,使用url、username、password三個參數,創建到具體數據庫的連接。Class.forName("oracle.jdbc.OracleDriver")。
根據url連接參數,找到與之匹配的Driver對象,調用其方法獲取連接
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@192.168.0.26:1521:tarena","name","123456");

Statement接口

//Statement接口用來處理髮送到數據庫的SQL語句對象,通過Connection對象創建。常用三個方法:
Statement stmt=conn.createStatement();
//1.execute方法,如果執行的sql是查詢語句且有結果集則返回true,如果是非查詢語句或者沒有結果集,返回false
boolean flag = stmt.execute(sql);
//2.執行查詢語句,返回結果集
ResultSetrs = stmt.executeQuery(sql);
//3.執行DML語句,返回影響的記錄數
int flag = stmt.executeUpdate(sql);

ResultSet接口

查詢的結果存放在ResultSet對象的一系列行中,指針的最初位置在行首,使用next()方法用來在行間移動,getXXX()方法用來取得字段的內容。ResultSet代表DQL查詢結果,其內部維護了一個讀取數據的遊標,默認情況在,遊標在第一行數據之前,
當調用next()方法時候,遊標會向下移動,並將返回結果集中是否包含數據, 如果包含數據就返回true。
結果集還提供了很好getXXX方法用於獲取結果集遊標指向當前行數據。

Demo

public static void main(String[] args) throws Exception{
	//註冊驅動
	String driver="oracle.jdbc.OracleDriver";
	Class.forName(driver);
	//連接數據庫
	String url="jdbc:oracle:thin:@196.168.201.227:2221:orcl";
	String user="name";
	String pwd="123456";
	Connection conn=DriverManager.getConnection(url, user, pwd);
	//創建Statement
	Statement st=conn.createStatement();
	//執行SQL
	String sql="select id, name from user_";
	ResultSet rs=st.executeQuery(sql);
	//處理結果
	//rs結果集中包含一個遊標,遊標默認在結果集的第一行之前
	//rs.next():移動結果集遊標到下一行,檢查是否有數據, 如果有返回true, 否則false
	while(rs.next()){
	//getXXX(列名): 返回結果集當前行中
	// 指定列名的數據.
	int id = rs.getInt("id");
	String name=rs.getString("name");
	//輸出查詢結果
	System.out.println(id+","+name);
	}
	//關閉連接
	conn.close();
}

 

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