数据库事务总结

Java的数据库开发使用JDBC,它的开发步骤有:1.注册驱动;2.获得链接;3.执行sql;4.释放资源。

数据库的事务,是指逻辑上不可分割的一组操作,要么全成功要么全失败。

MYSQL的事务开启方式

start transaction; --开启事务
commit; --提交事务
rollback; --事务回滚

JDBC设计有事务保存点,可以使回滚操作到这个保存点。

Savepoint savePoint = conn.setSavepoint();

事务所具有的特性:1.原子性;2.一致性;3.隔离性;4.持久性。

  1.强调事务不可分割;

  2.事务执行的时候数据完整性保持一致;

  3.一个事务的执行,不应该受到另外一个事务的影响;

  4.事务执行完毕,数据成为持久化的存在。

由于事务的隔离性,数据库事务产生三种读的问题:脏读,不可重复读,幻读。

  脏读:一个事务读到了另一个事务未提交的数据。

      当事务隔离级别为read uncommitted的时候,一个事务执行到一半并未提交,另一个事务读到了数据,前一个事物回滚,这样第二个事务读到的就是不真实的数据。

  不可重复读:一个事务读取到了另一个事务已经提交的数据(一般执行的事update操作)

      当事务第一次读取的时候,此时它需要进行两次查询,同时另一个事务的更新处于两次查询之间;另一个事务更新完后再查询的时候数据是不相同的。此时他们的隔离级别都是read committed。

  幻读:一个事务读取到另外一个事务已经提交的数据(一般执行的事insert操作)

      MYSQL数据库的幻读是有概率性的,不好演示。

数据库的四种隔离级别

read uncommitted  未提交读,那么脏读、不可重复读、幻读都是有可能发生的
read committed  已提交读,可避免脏读,不能避免不可重复读和幻读
repeatable read  重复读,避免脏读、不可重复读,但是幻读还是有可能产生
serializable  串行读,避免所有异常读的产生

  他们的安全性是递增的,效率性是递减的。

  MYSQL默认的是repeatable read;Orical中的是read committed。

通过命令查看事务的隔离级别:
* select @@tx_isolation;
设置数据库隔离级别:
* set session transaction isolation level 隔离级别;

数据库连接池:由于数据库的链接对象的创建是耗时的,如果使用预先创建好的链接对象并复用,则可节省服务器的内存并提高效率。

  连接池的链接的close方法设计的是把链接归还到连接池中,而不是销毁。

开源数据库连接池:DBCP,C3P0,tomcat内置连接池(jndi技术)。

ThreadLocal:一个线程包,可以把一些对象存放在被线程中,作为本线程的全局对象。它的原理是维护一个map集合,放入的k,v对象是本线程对象和需要保存的全局对象。

class MyThreadLocal<T> {
	private Map<Thread,T> map = new HashMap<Thread,T>();
	public void set(T value) {
		map.put(Thread.currentThread(), value);
	}

	public void remove() {
		map.remove(Thread.currentThread());
	}

	public T get() {
		return map.get(Thread.currentThread());
	}
}

service层与事务:DAO层执行的是对数据源的单个操作,service层比较适合作为事务的存放层。在service层中调用DAO层的多个方法,并且使用同一个链接对象。

实例:使用c3p0实现一个转账事务。service层-dao层-model层

   模型javabean

package JDBCConectionPools;

public class account {
	private int id;
	private String name;
	private float money;
	public account(){

	}
	public account(int id, String name, float money) {
		super();
		this.id = id;
		this.name = name;
		this.money = money;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public float getMoney() {
		return money;
	}
	public void setMoney(float money) {
		this.money = money;
	}

}

  dao层

package JDBCConectionPools;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class accountDAO {
	public List<account> selectAllAccount(Connection conn){
		String sql = "select * from account";
        Statement statement;
        ResultSet rs = null;
        List<account> list =new ArrayList<account>();
		try {
			statement = conn.createStatement();
			rs = statement.executeQuery(sql);
			while(rs.next()){
				int id = rs.getInt("id");
				String name = rs.getString("name");
				float money = rs.getFloat("money");
				account a = new account(id,name,money);
				list.add(a);
				System.out.println("id:"+id+" name:"+name+" money:"+money);
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        return list;
	}
	//转出账户
	public void decAccount(Connection conn,String name,float number){
		String sql = "update account set money = money -? where name = ?";
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try {
			stmt = conn.prepareStatement(sql);
			stmt.setFloat(1, number);
			stmt.setString(2, name);
			stmt.executeUpdate();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	//转入账户
	public void incAccount(Connection conn,String name,float number){
		String sql = "update account set money = money + ? where name = ?";
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try {
			stmt = conn.prepareStatement(sql);
			stmt.setFloat(1, number);
			stmt.setString(2, name);
			stmt.executeUpdate();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	//单个搜索
	public account selectUser(Connection conn,String name){
		String sql = "select * from account where name = ?";
        PreparedStatement statement;
        ResultSet rs = null;
        account a = null;
        try {
			statement = conn.prepareStatement(sql);
			statement.setString(1, name);
			rs = statement.executeQuery();
			if(rs.next())
			{
				a = new account(rs.getInt("id"),rs.getString("name"),rs.getFloat("money"));
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return a;
	}
}

 service层

package JDBCConectionPools;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

import org.junit.Test;

public class accountSerivce {
	//搜索事务
	public List<account> selectAccountService(){
		Connection conn = JDBCUtils.getConnection();
		accountDAO dao = new accountDAO();

		List<account> list = null;
		try {
			JDBCUtils.beginTransaction();
			list = dao.selectAllAccount(conn);
			JDBCUtils.commitTransaction();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			try {
				JDBCUtils.rollbackTransaction();
			} catch (SQLException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}
		return list;
	}
	//赚钱服务
	public void transferAccounts(String from,String to,float number){
		Connection conn = JDBCUtils.getConnection();
		accountDAO dao = new accountDAO();

		try {
			JDBCUtils.beginTransaction();
			dao.decAccount(conn, from, number);
			dao.incAccount(conn, to, number);
			account account = dao.selectUser(conn, from);
			if(account != null)
				if(account.getMoney() < 0)
				{
					System.err.println("余额不足!无法转账!");
					JDBCUtils.rollbackTransaction();
				}
				else {
					JDBCUtils.commitTransaction();
				}
			else
				JDBCUtils.rollbackTransaction();
		} catch (SQLException e) {
			e.printStackTrace();
			try {
				JDBCUtils.rollbackTransaction();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
		}
	}
	@Test
	public void test(){
		accountSerivce service = new accountSerivce();
		service.transferAccounts("美美", "冠希", 10000);
		List<account> list = service.selectAccountService();
		for(int i =0;i<list.size();i++)
			System.out.println(list.get(i).getId()+" "+list.get(i).getName()+" "+list.get(i).getMoney());
	}

}

 工具类

package JDBCConectionPools;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JDBCUtils {
	private static ComboPooledDataSource cpds = null;
	private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
	static {
		// 这里有个优点,写好配置文件,想换数据库,简单
		cpds = new ComboPooledDataSource("mysql");// 这是mysql数据库
	}
    public static Connection getConnection(){
		Connection conn = tl.get();
		try {
			if (conn == null) {
				conn = cpds.getConnection();
				tl.set(conn);
			}
		} catch (SQLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		return conn;

    }
    //事务有关
	public static void beginTransaction() throws SQLException{
		Connection conn = tl.get();
		if(conn == null){
			conn = cpds.getConnection();
			tl.set(conn);
		}
		conn.setAutoCommit(false);
	}
	public static void commitTransaction() throws SQLException{
		Connection conn = tl.get();
		if(conn == null){
			conn = cpds.getConnection();
			tl.set(conn);
		}
		conn.commit();
		tl.remove();
	}

	public static void rollbackTransaction() throws SQLException{
		Connection conn = tl.get();
		if(conn == null){
			conn = cpds.getConnection();
			tl.set(conn);
		}
		conn.rollback();
		tl.remove();
	}
	// 释放资源的方法
	public static void release(ResultSet rs, Statement stmt, Connection conn) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			rs = null;
		}
		if (stmt != null) {
			try {
				stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			stmt = null;
		}
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			conn = null;
		}
	}

	public static void release(ResultSet rs,Statement stmt) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			rs = null;
		}
		if (stmt != null) {
			try {
				stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			stmt = null;
		}
	}

	public static void release(Statement stmt, Connection conn) {
		if (stmt != null) {
			try {
				stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			stmt = null;
		}
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			conn = null;
		}
	}

}

  配置文件c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!-- This is default config! -->
    <default-config>
        <property name="initialPoolSize">10</property>
        <property name="maxIdleTime">30</property>
        <property name="maxPoolSize">100</property>
        <property name="minPoolSize">10</property>
        <property name="maxStatements">200</property>
    </default-config>  

    <!-- This is my config for mysql-->
    <named-config name="mysql">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/MyData?characterEncoding=utf-8</property>
        <property name="user">root</property>
        <property name="password">qwert123</property>
        <property name="initialPoolSize">10</property>
        <property name="maxIdleTime">30</property>
        <property name="maxPoolSize">100</property>
        <property name="minPoolSize">10</property>
        <property name="maxStatements">200</property>
    </named-config>
</c3p0-config>

  遇到两个异常:1.查询适合使用statement不能设置?参数;2.使用中文查询的时候sql命令行可查,jdbc查不到,设置utf-8之后查到了。

  设计方式总结:1.工具包提供连接对象的创建与回收,并且使用ThreadLocal保存链接对象,在事务commit之后从线程中移除链接对象。

         2.service层中只执行dao层的操作,不进行jdbc编程。

         3.dao层不用拿到链接对象,用参数链接对象执行sql。

时间: 2024-11-21 01:38:40

数据库事务总结的相关文章

数据库事务的四大特性和事务隔离级别

Reference: [1] http://www.cnblogs.com/fjdingsd/p/5273008.html [2] http://blog.csdn.net/fg2006/article/details/6937413 数据库事务四大特性 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ 原子性(Atomicity) 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,这和前面两篇博客介绍事务的功能是一样的概念,因此事务的操作如果成功就必须要完全

数据库事务隔离级别

数据库事务的隔离级别有4个,由低到高依次为Read uncommitted.Read committed.Repeatable read.Serializable,这四个级别可以逐个解决脏读.不可重复读.幻读这几类问题. √: 可能出现    ×: 不会出现   脏读 不可重复读 幻读 Read uncommitted √ √ √ Read committed × √ √ Repeatable read × × √ Serializable × × × 注意:我们讨论隔离级别的场景,主要是在多个

数据库-事务和锁

事务 所谓事务是用户定义的一个数据库操作系列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位.例如在关系数据库中,一个事务可以是一条sql语句.一组sql语句或整个程序. 给个栗子: 小IT在网上购物,其付款过程至少包括以下几步数据库操作: 更新客户所购商品的库存信息: 生成订单并且保存到数据库: 更新用户相关信息,例如购物数量等: 正常情况下,操作顺利进行,最终交易成功,那么与交易相关的所有数据库信息也成功更新.但是,如果在这一系列过程中任何一个环节出了差错,例如在更新商品库存

数据库事务的四大特性ACID

本篇讲诉数据库中事务的四大特性(ACID),并且将会详细地说明事务的隔离级别. 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ 原子性(Atomicity) 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,所以事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响.为了实现原子性,需要通过日志:将所有对数据的更新操作都写入日志,如果一个事务中的一部分操作已经成功,但以后的操作,由于断电/系统崩溃/其它的软硬件错误而无法继续,则通过

数据库事务的隔离级别

数据库事务的隔离级别有4个,由低到高依次为Read uncommitted.Read committed.Repeatable read.Serializable,这四个级别可以逐个解决脏读.不可重复读.幻读这几类问题. √: 可能出现    ×: 不会出现   脏读 不可重复读 幻读 Read uncommitted √ √ √ Read committed × √ √ Repeatable read × × √ Serializable × × × 注意:我们讨论隔离级别的场景,主要是在多个

数据库事务测试以及级联更新级联删除

数据库事务 start transaction; #开始事务 insert into gzb(gz)values(5000); insert into gzb(gz)values(6000); insert into gzb(gz)values(7000); insert into gzb(gz)values(8000); /*执行事务,并查看是否添加成功数据*/ commit; # commit数据,查看表中数据是否提交 rollback; #数据库回滚 级联更新,级联删除 主要通过两种方式

数据库事务课上代码

1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:tools="http://schemas.android.com/tools" 4 android:layout_width="match_parent

数据库事务【隔离级别】

为了快速同步数据的需要,我分段执行了两次python脚本,即开启了两个进程同步数据,结果服务器不时报出数据库死锁异常,通过排查代码和数据库日志发现,是由长事务并发引起的.代码中有入账和出账两个方法,里面涉及操作较多,都为其加了事务,抛出异常时可自动回滚,采用数据库(mysql)默认的隔离级别(Repeatable read).提到并发,一般就会想到用同步代码块的方法的处理,但是由于项目是分布式的,共用一个主库,单单在代码加锁是不能保证数据的准确的,那就只能在数据库层面去考虑加锁了.由于数据量暂时

[数据库事务与锁]详解三: 深入分析事务的隔离级别

注明: 本文转载自http://www.hollischuang.com/archives/943 本文详细介绍四种事务隔离级别,并通过举例的方式说明不同的级别能解决什么样的读现象.并且介绍了在关系型数据库中不同的隔离级别的实现原理. 在DBMS中,事务保证了一个操作序列可以全部都执行或者全部都不执行(原子性),从一个状态转变到另外一个状态(一致性).由于事务满足久性.所以一旦事务被提交之后,数据就能够被持久化下来,又因为事务是满足隔离性的,所以,当多个事务同时处理同一个数据的时候,多个事务直接

数据库事务解析

数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行. 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源.通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠.一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性.一致性.隔离性和持久性)属性.事务是数据库运行中的一个逻辑工作单位,由DBMS中的事务管理子系统负责事务的处理. 1.操