事务的提交与回滚

一.事务的概念

       事务就是某一组操作,要么都执行,要么都不执行。

       比如你要去银行给朋友转钱,必然存在着从你的账户里扣除一定的金额和向你朋友账户里增加相等的金额这两个操作,这两个操作是不可分割的,无论是哪一个操作的失败,成功的操作也要恢复至最初的状态才能使银行和用户双方都满意,而这样的行为就牵扯到了事务的回滚

事务的特性:
       原子性:一个事务是不可分割的最小单位。
       一致性:一个事务在执行之前和执行之后都必须处于一致性状态(比如说转账,前后两个账户的总金额是不会改变的)。
       隔离性:多个并发事务之间的操作不会互相干扰。

       持久性:提交的事务会使得修改的数据是永久的。

二.事务的实例分析

例子:向员工表中添加一个员工的记录。

1.首先需要一个员工实体:

public class Emp {
	private int empId;
	private String ename;
	private String sex ;
	private Date birthday;
	public int getEmpId() {
		return empId;
	}
	public void setEmpId(int empId) {
		this.empId = empId;
	}
	public String getEname() {
		return ename;
	}
	public void setEname(String ename) {
		this.ename = ename;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public Date getBirthday() {
		return birthday;
	}
	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
	public Emp(int empId, String ename, String sex, Date birthday) {
		super();
		this.empId = empId;
		this.ename = ename;
		this.sex = sex;
		this.birthday = birthday;
	}
	public Emp() {
		super();
	}
	@Override
	public String toString() {
		return "Emp [empId=" + empId + ", ename=" + ename + ", sex=" + sex + ", birthday=" + birthday + "]";
	}
	public Emp(String ename, String sex, Date birthday) {
		super();
		this.ename = ename;
		this.sex = sex;
		this.birthday = birthday;
	}
}

2.还需要封装一些数据库的连接:

public class JDBCUtil {
	Connection conn;
	Statement stm;
	ResultSet rs;
	private static JDBCUtil instance;
	private static Properties pro=new Properties();
	
	static {
		
		try {
			InputStream in=JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
			pro.load(in);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			throw new RuntimeException("读取配置文件失败!");
		}	
	}
	private JDBCUtil() {}
	public static JDBCUtil getInstance() {
		if(instance==null) {
			synchronized (JDBCUtil.class) {
				if(instance==null) {
					instance=new JDBCUtil();
				}
			}
		}
		return instance;
	}
	public Connection getConnection() throws ClassNotFoundException, SQLException {
		Class.forName(pro.getProperty("driver"));
		Connection  conn = DriverManager.getConnection(pro.getProperty("url"),pro.getProperty("username"),pro.getProperty("password"));
		
		return conn;
	}
	public void release(ResultSet rs,Statement st,Connection conn) {
		if(rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally {
				if(st != null) {
					try {
						st.close();
					} catch (SQLException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}finally {
						if(conn != null) {
							try {
								conn.close();
							} catch (SQLException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
					}
				}
			}
		}
	}
}

3.配置文件:(在使用时注意更改为自己的数据库)

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/java?characterEncoding=utf8
username=root
password=root

4.添加员工:

public class TestTransacion {
	Connection conn ;
	Connection conn1 ;
	Connection conn2 ;
	PreparedStatement pstm;
	PreparedStatement pstm1;
	ResultSet rs;
	
        public void addEmp(Emp emp) {
		try {
			conn = JDBCUtil.getInstance().getConnection();
			conn.setAutoCommit(false);//设置事务非自动提交
			String sql = "insert into emp (ename,sex,birthday) values (?,?,?)";
			pstm = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS); //在sql未执行之前,进行预编译
			pstm.setString(1, emp.getEname());
			pstm.setString(2, emp.getSex());
			pstm.setDate(3, new java.sql.Date(emp.getBirthday().getTime()));
			pstm.executeUpdate();
			int id = 0;
			rs = pstm.getGeneratedKeys();//获取由于执行此 Statement 对象而创建的所有自动生成的键
			if(rs.next()) {
				id = rs.getInt(1);
			}
			System.out.println(id);
			conn1 = JDBCUtil.getInstance().getConnection();
			sql = "select * from emp where empid = "+id;
			pstm1 = conn1.prepareStatement(sql);
			rs = pstm1.executeQuery();
			if(rs.next()) {
				System.out.println(rs.getString("ename") +"11");
			}
			conn.commit();
			conn2 = JDBCUtil.getInstance().getConnection();
			sql = "select * from emp where empid = "+id;
			pstm1 = conn2.prepareStatement(sql);
			
			rs = pstm1.executeQuery();
			if(rs.next()) {
				System.out.println(rs.getString("ename")+"22");
			}
			
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			JDBCUtil.getInstance().release(rs, pstm, conn);
			if(pstm1 != null) {
				try {
					pstm1.close();
					conn1.close();
					conn2.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	public static void main(String[] args) {
		TestTransacion ttst=new TestTransacion();
		Emp emp=new Emp("lucy", "女");
		ttst.addEmp(emp);
	}
}

在添加员工时共声明了三个Connection变量,在插入之前先设置事务为非自动提交,之后用到了Statement的方法getGenerateKeys()来获取刚刚插入的数据所自动生成的键值(即empId值)可用于下面的查找,下面共有两个查找语句,中间我们用commit提交了事务,一个是提交前的查找,一个是提交后的查找,当执行完时,我们会发现结果是lucy22,为什么呢?因为提交之前数据库里面还没有东西肯定是查不到的。当然切记需要用到不同的coon,因为如果查找时用的coon和插入时的一样的话,事务没有提交也是可以查到的,因为用到的对象是同一个,里面已经有数据了!!!!

三.事务的回滚

       回滚:假设A给B转账,A的money--,B的money++,两者是一个整体。只有两个操作都成功完成了,事务才会提交,否则都会回滚到原来的状态。

       在emp表里再添加一个salary属性,把员工编号为1的员工的工资更改为1000,并且判断员工编号为3的员工的工资是否大于5000,如果小于5000则加1000,这两个操作为一个事务,要想事务能够正常的提交的条件是员工编号为3的员工的工资小于5000,工资加了1000,此时员工编号为1的员工的工资更改为1000.如果员工编号为3的员工的工资大于5000则中断程序,抛出异常,即操作2没有顺利完成,事务不应提交,而应该回滚到最初的状态。

       当然,不是所有的时候都需要恢复最初的状态的,所以有时候就会用到自己设置回滚点,上述程序中注释的部分就是自己添加了如果事务没有正常提交想要恢复到程序执行到哪个位置的回滚点。

public class TestTx {
	public static void main(String[] args) throws SQLException {
		Connection conn = null;
		PreparedStatement  pstm = null;
		ResultSet rs = null;
		//Savepoint  sp = null;	
		try {
			conn = JDBCUtil.getInstance().getConnection();
			conn.setAutoCommit(false);
			String sql = "update emp set salary = 1000 where empid = 1";
			pstm = conn.prepareStatement(sql);
			pstm.executeUpdate();
			
			//sp = conn.setSavepoint(); //设置回滚点
		
			sql = "select salary from emp where empid = 4";
			pstm = conn.prepareStatement(sql);
			rs = pstm.executeQuery();
			if(rs.next()) {
				double salary = rs.getDouble(1);
				if(salary >5000) {
					System.out.println("薪资大于5000");
					throw new RuntimeException("薪资大于5000");
				}
			}
			sql = "update emp set salary = salary + 1000 where empid = 4";
			pstm = conn.prepareStatement(sql);
			pstm.executeUpdate();
			conn.commit();
			
		} catch(RuntimeException e) {
			/*if(conn != null && sp != null) {
				conn.rollback(sp);
			}
			conn.commit();
			throw e ;*/
			e.printStackTrace();
		}catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			if(conn != null) {
				conn.rollback();
			}
			throw e;
		}finally {
			JDBCUtil.getInstance().release(rs, pstm, conn);
		}
	}
}

                
MySQL中的事务是一组原子操作,这些操作要么全部完成,要么全部不完成。如果事务中的任何一个操作失败,则整个事务将被回滚到最初状态。 事务提交回滚是MySQL中非常重要的操作,可以通过以下方式进行实现: 1. 事务提交:当事务中的一组操作全部成功执行后,可以通过执行COMMIT语句来提交事务,使得这些操作的结果永久保存在数据库中。 2. 事务回滚:当事务中的任何一个操作失败或者发生错误时,可以通过执行ROLLBACK语句来回滚事务,使得数据库恢复到事务开始之前的状态。 在MySQL中,可以通过以下方式进行事务操作: 1. 使用START TRANSACTION语句来开始一个新的事务。 2. 在事务中执行多个操作,例如INSERT、UPDATE、DELETE等。 3. 如果事务中的所有操作都成功执行,使用COMMIT语句来提交事务。 4. 如果事务中的任何一个操作失败或者发生错误,使用ROLLBACK语句来回滚事务。 例如,以下是一个MySQL事务的示例: ``` START TRANSACTION; INSERT INTO users (name, email) VALUES ('John Doe', 'john@example.com'); UPDATE accounts SET balance = balance - 100 WHERE user_id = 1; COMMIT; ``` 在上面的示例中,我们首先使用START TRANSACTION语句开始一个新的事务,然后在事务中执行了两个操作:向users表中插入一条新记录,以及更新accounts表中用户1的余额。最后,如果所有操作都成功执行,我们使用COMMIT语句提交事务。如果在执行这些操作的过程中发生了错误,我们可以使用ROLLBACK语句回滚事务
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值