批处理、事务、批处理与事务结合

时间:2021-11-17 23:07:46
批处理(batch)------------>好比快递员【不能一件一件的送快递】
- 批处理指的是一次操作中执行多条SQL语句
- 批处理相比于一次一次执行效率会提高很多
         
- 批处理主要是分两步:
1.将要执行的SQL语句保存
2.执行SQL语句
              
- Statement和PreparedStatement都支持批处理操作,这里我们只需要掌握PreparedStatement的批处理方式:
- 方法:
   void addBatch()
     - 将要执行的SQL先保存起来,先不执行
     - 这个方法在设置完所有的占位符之后调用
   int[] executeBatch()
     - 这个方法用来执行SQL语句,这个方法会将批处理中所有SQL语句执行     
                   - mysql默认批处理是关闭的,所以我们还需要去打开mysql的批处理: rewriteBatchedStatements=true

url="jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true"

我们需要将以上的参数添加到mysql的url地址中          
- 注意:低版本的mysql-jdbc驱动也不支持批处理,一般都是在修改的时候使用批处理,查询的时候不使用!
    案例演示:   1.创建一张新的数据表         2.反复打开数据库客户端,插入语句【相当于每次获取一个connection连接,执行executeUpdate语句】效率不高     3.引出批处理--->执行效率高,资源利用率好!  
public class TestBatch {

@Test
public void test() {
Connection conn = null;
PreparedStatement ps = null;
String sql ="insert into test (name) values(?)";
//Time:368236
//Time:1393
try {
//获取连接
conn = JDBCUtil.getConnection();
ps = conn.prepareStatement(sql);
//设置参数
for(int i = 0; i < 5000 ;i++){
//填充占位符
ps.setString(1,"emp_"+i);
//添加到批处理方法中,调用无参的,有参的是Statement来调用的!
ps.addBatch();
}
long start = System.currentTimeMillis();
//执行批处理
ps.executeBatch();
long end = System.currentTimeMillis();
System.out.println("Time:"+(end-start));
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
JDBCUtil.close(conn);
}
}
}

 


事务:   演示银行转账的功能:1.创建一张表示账号的表             2.向表中插入几个用户         3.在数据库执行sql语句,实现转账               4.从java代码中演示上面的案例:(1)创建Dao类
public class AccountDao {
public void update(Integer id,Integer count){
Connection conn = null;
PreparedStatement ps = null;
try {
String sql="UPDATE t_account SET balance = balance + ? WHERE id = ?";
conn = JDBCUtil.getConnection();
ps = conn.prepareStatement(sql);
ps.setInt(1, count);
ps.setInt(2, id);

ps.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
JDBCUtil.close(conn);
}
}
}

(2).测试该DAO

@Test
public void test() {
//孙悟空向猪八戒转账100元
dao.update(1, -100);
dao.update(2, 100);
}
上面是可以正常执行的 但是如果上面的程序在suwukong减去100元之后,shaheshang加钱之前,出现了异常第二个 update() 就不会执行,导致数据不一致,一个减少了一个没增加
@Test
public void test() {
//孙悟空向猪八戒转账100元
dao.update(1, -100);
int a = 10/0;//异常
dao.update(2, 100);
}

这时候就需要用到 事务!

 

事务的特性(ACID): - 原子性(atomicity)     一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。                   - 一致性(consistency)     事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。 - 隔离性(isolation)     一个事务的执行不能被其他事务干扰。     即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。                        - 持久性(durability)     持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。     接下来的其他操作或故障不应该对其有任何影响。                        操作事务的基本步骤:     1.开启事务          - 开启事务以后,我们只后的所有操作将都会在同一个事务当中     2.操作数据库          - 开启事务以后再去操作数据库,所有操作将不会直接提交到数据库中     3.提交事务          - 将修改应用到数据库     4.回滚事务          - 数据库操作过程中出现异常了,回滚事务,回滚事务以后,数据库变成开启事务之前的状态            提交事务与回滚事务只能执行其中一条 mysql中的事务控制     #开启事务          START TRANSACTION     #回滚事务          ROLLBACK                   #提交事务          COMMIT                   ROLLBACK  要与 START TRANSACTION 连用,单独使用没用 JDBC中的事务主要通过Connection对象来控制的1.开启事务     void setAutoCommit(boolean autoCommit) throws SQLException;          - 设置事务是否自动提交,默认是自动提交          - 设置事务手动提交             conn.setAutoCommit(false);                   2.提交事务
public class TestAccount {

private AccountDao dao = new AccountDao();
@Test
public void test() {
Connection conn = null;
PreparedStatement ps = null;
conn = JDBCUtil.getConnection();
//默认true自动提交,false手动提交
try {
//开启事务需要手动提交
conn.setAutoCommit(false);
//孙悟空向猪八戒转账100元
dao.update(conn,1, -100);
int a = 10/0;//异常
dao.update(conn,2, 100);
//提交事务
conn.commit();
} catch (SQLException e) {
// TODO Auto-generated catch block
try {
//出现异常就回滚,保持数据一致性
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
但是结果依旧是一个减少一个没增加因为两次update调用了两个Connection,本身还有个Connection,一共三个Connection注意:我们在同一个事务中使用的数据库连接(Connection)必须是同一个,否则事务还是不作用!            所以将conn 传给 update()此时原来的AccountDAO中的update方法要改为如下所示: 
public class AccountDao {
public void update(Connection conn,Integer id,Integer count){

PreparedStatement ps = null;
try {
String sql="UPDATE t_account SET balance = balance + ? WHERE id = ?";

ps = conn.prepareStatement(sql);
ps.setInt(1, count);
ps.setInt(2, id);

ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
//此时也不能在这关闭数据库,要在外边统一关闭
JDBCUtil.close(null);
}
}
}

 


事务与批处理结合 一般是将事务与批处理结合起来写,更快
public class TestAccount {

private AccountDao dao = new AccountDao();
@Test
public void test() {
Connection conn = null;
PreparedStatement ps = null;
conn = JDBCUtil.getConnection();
try {
//默认true自动提交,false手动提交
//开启事务需要手动提交
conn.setAutoCommit(false);

//批处理
String sql ="insert into test (name) values(?)";
ps = conn.prepareStatement(sql);
//设置参数
for(int i = 0; i < 5000 ;i++){
//填充占位符
ps.setString(1,"emp_"+i);
//添加到批处理方法中,调用无参的,有参的是Statement来调用的!
ps.addBatch();
}
long start = System.currentTimeMillis();
//执行批处理
ps.executeBatch();
long end = System.currentTimeMillis();
System.out.println("Time:"+(end-start));

//提交事务
conn.commit();
} catch (SQLException e) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}