MySQL---当Java遇上MySQL⑤---单线程与多线程下的事务

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34928644/article/details/82798131

事务transaction

  • 原子性(atomicity):组成事务处理的语句形成了一个逻辑单元,不能只执行其中的一部分。
  • 一致性(consistency):在事务处理执行前后,数据库是一致的(数据库数据完整性约束)
  • 隔离性(isolcation):一个事务处理对另一个事务处理的影响。
  • 持续性(durability):事务处理的效果能够被永久保存下来 。
  • 一个事务只会有一个结果:要么成功、要么失败

MySQL事务:开启、提交、回滚

Start transaction;开始一个事务。

Commit;提交所做的修改。

Rollback;回滚所做的修改。如果在操作时出错,应该从新开始一个事务。

数据库表结构:

CREATE TABLE `tb_user` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(10) DEFAULT NULL,
  `password` VARCHAR(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

没有事务下执行sql语句

演示没有事务下执行 多条SQL语句

从上往下执行,遇到非法的sql语句就会抛出异常,由于出现异常,接下来的sql语句将不会执行。

	@Test
	public void noTransaction() throws Exception {
		Connection con = ConnUtil.getConnection();
		Statement st = con.createStatement();
		//SQL 语句正确
		String sql = "insert into tb_user(username,password) value('Rose','123')";
		st.executeUpdate(sql); //正常执行
		//SQL 语句错误:id是INT型,并非 VARCHAR
		sql = "insert into tb_uservalue('u123','Rose','123')";
		st.executeUpdate(sql); //这里会出现异常
		//SQL 语句正确
		sql = "insert into tb_user(username,password) value('Java','1234')";
		st.executeUpdate(sql); //由于上一句出现异常,这一句就不会执行。
		con.close();
	}

演示没有事务下执行 批量处理

由于是批量处理,java与mysql之间的只有在executeBatch()时才进行通讯,而且把所有sql语句发送过去,当mysql执行出现错误时,会把错误信息封装起来,等这次批量sql语句都执行完毕才把所有语句的执行结果反馈给java。

	@Test
	public void noTransaction2() throws Exception {
		Connection con = ConnUtil.getConnection();
		Statement st = con.createStatement();
		//SQL 语句正确
		String sql = "insert into tb_user(username,password) value('Rose','123')";
		st.addBatch(sql);
		//SQL 语句错误:id是INT型,并非 VARCHAR
		sql = "insert into tb_uservalue('u123','Rose','123')";
		st.addBatch(sql);
		//SQL 语句正确
		sql = "insert into tb_user(username,password) value('Java','1234')";
		st.addBatch(sql);
		/* 运行时下面这一句会出现异常,因为批量处理是只有有一条语句出现问题,就会抛出异常
		 * 但是语法正确的sql语句会照样执行,不同于上面演示!!!
		 * 因为上面演示是 每条sql语句都是要进行一次通讯,在出现异常后,与mysql的连接就断开了,
		 * 而 批量处理 则是一次性把所有sql语句发送过去,即使异常也只是影响下一次的通讯。
		 */
		st.executeBatch();
		con.close();
	}

有事务的情况下执行 sql 语句

演示有事务下执行 多条SQL语句

因为开启了事务,所以当第二条sql语句执行时,mysql反馈过来执行错误的信息,excute()方法就抛出一个异常,这时该异常被捕捉到,进入了catch块中,通过 con.rollback()方法进行事务回滚。

	@Test
	public void transaction(){
		Connection con = ConnUtil.getConnection();
		try {
			con.setAutoCommit( false ); //开启事务
			
			Statement st = con.createStatement();
			//SQL 语句正确
			String sql = "insert into tb_user(username,password) value('Rose','123')";
			st.executeUpdate(sql); //正常执行
			//SQL 语句错误:id是INT型,并非 VARCHAR
//			sql = "insert into tb_uservalue('u123','Rose','123')";
//			st.executeUpdate(sql); //这里会出现异常
			//SQL 语句正确
			sql = "insert into tb_user(username,password) value('Java','1234')";
			st.executeUpdate(sql); 
			
			con.commit(); //提交事务
			System.out.println("事务完成...");
		} catch (SQLException e) {
			e.printStackTrace();
			try {
				con.rollback();
				System.out.println("事务回滚...");
			} catch (SQLException e1) {
				throw new RuntimeException( e1.getMessage(), e1);
			}
		} finally {
			if( con != null ) {
				try {
					//如果后序还有其他业务需要访问数据库的话,应该还原成事务自动提交
					//con.setAutoCommit( true );
					
					//如果没有后序操作应该关闭连接
					con.close();
				} catch (SQLException e) {
					throw new RuntimeException(e.getMessage(), e);
				}
			}
		}
		
	}

演示有事务多线程下执行多条sql语句

由于获取connection的工具是做成单例的形式,所以多线程下不同线程共享同一个connection连接,当一个线程的事务执行完毕后如果出现问题应该就回滚当前事务,而不影响其他线程,但是因为单例就会影响到其他线程的事务。解决方案:见连接池篇

	@Test
	public void demo1(){
		Connection con = ConnUtil.getConnection();
		try {
			con.setAutoCommit( false ); //开启事务
			
			Statement st = con.createStatement();
			
			
			String sql = "insert into tb_user(username,password) value('Rose','123')";
			st.executeUpdate(sql); 
			
			//处理其他业务
			new MyThread(1).start();
			new MyThread(2).start();
			
			con.commit(); //提交事务
			System.out.println("主线程:事务完成...");
		} catch (SQLException e) {
			//e.printStackTrace();
			try {
				con.rollback(); //事务回滚
				System.out.println("主线程:事务回滚...");
			} catch (SQLException e1) {
				throw new RuntimeException( e1.getMessage(), e1);
			}
		} finally {
			if( con != null ) {
				try {
					//con.setAutoCommit( true ); //还原设置
					
					con.close();
				} catch (SQLException e) {
					throw new RuntimeException(e.getMessage(), e);
				}
			}
		}
	}
	
	class MyThread extends Thread{
		int num ;

		public MyThread(int num) {
			this.num = num;
		}

		@Override
		public void run() {

			Connection con = ConnUtil.getConnection();
			try {
				con.setAutoCommit( false ); //开启事务
				
				Statement st = con.createStatement();
				
				String sql = "insert into tb_user(username,password) value('Rose"+num+"','123')";
				st.executeUpdate(sql); 
				
				con.commit(); //提交事务
				System.out.println( "子线程"+num+":事务完成...");
			} catch (SQLException e) {
				//e.printStackTrace();
				try {
					con.rollback(); //事务回滚
					System.out.println("子线程"+num+":事务回滚...");
				} catch (SQLException e1) {
					throw new RuntimeException( e1.getMessage(), e1);
				}
			} finally {
				if( con != null ) {
					try {
						//con.setAutoCommit( true ); //还原设置
						
						con.close();
					} catch (SQLException e) {
						throw new RuntimeException(e.getMessage(), e);
					}
				}
			}
		
		}
		
	}
}

源码

源码链接

猜你喜欢

转载自blog.csdn.net/qq_34928644/article/details/82798131