數據庫事務
原子性: 多個操作組成最小不可分割單元。
一致性: 操作成功後,數據庫的狀態和業務規則是一致的。
隔離性: 併發數據處理時,相互不干擾。
持久性: 一旦事物提交成功後,數據的操作被記入庫中。
數據庫併發問題
髒讀:讀取不可靠的數據(數據被回滾了),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訪問數據庫的過程
- 加載驅動,建立連接。
- 創建語句對象。
- 執行SQL語句。
- 處理結果集。
- 關閉連接。
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();
}