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();
}

 

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