传智播客 JDBC基础之存储过程与批处理

 

    和事物相关的另外一个比较重要的概念就是隔离级别。怎么理解隔离级别呢?主要从多线程并发读取数据时的正确性角度出发。先来说明一下什么是脏读、不可重复读、和幻读。理解这些概念后对理解隔离级别很有帮助。所谓脏读(dirty reads)是指一个事务读取了另一个未提交的并行事务写的数据。不可重复读(non-repeatable reads)是指一个事务重新读取前面读取过的数据,发现该数据已经被另一个已提交的事务修改过。幻读(phantom read)是指一个事务重新执行一个查询,返回一套符合查询条件的行, 发现这些行因为其他最近提交的事务而发生了改变。
    隔离级别分四种。第一种是读未提交(Read uncommitted),这是最低的级别。如果数据库设置成该级别,则有可能读到他人未提交的数据。出现这种情况的话,当别人最终又没有提交,这将导致此次读取的数据是错误的。该级别同样会导致不可重复读和幻读的现象。第二种隔离级别是读已提交(Read committed),这种级别可以避免脏读。别人未提交的数据是不会读到的。但是不可重复读和幻读还是有可能发生的。第三种隔离级别是可重复读(Repeatable read),这种级别消除了脏读和不可重复读,但是未解决幻读。最高的隔离级别是可串行化(Serializable ),这种级别消除了脏读、不可重复读和幻读。但是,并不是说把隔离级别设置得越高越好。隔离级别越高,数据库的并发性就越弱,性能会大大下降。所以对不同的数据库,应该选择合适的隔离级别。在程序中最好不要改变数据库默认的隔离级别,有些数据库并不一定都是按照上面的分类来的。
    接下来的知识点是用JDBC来调用存储过程。同样,JDBC调用存储过程是很简单的,复杂的是存储过程本身。
      DELIMITER $$

      DROP PROCEDURE IF EXISTS `jdbc`.`addUser` $$
      CREATE PROCEDURE `jdbc`.`addUser` (in pname varchar(45), in birthday date, in money float, out pid int)
      BEGIN
        insert into user(name, birthday, money) values(pname,birthday, money);
        select last_insert_id() into pid;
      END $$

      DELIMITER ;
    上面是一个简单的存储过程,三个输入参数,一个输出参数,插入一条记录并得到该记录的id。要调用存储过程,以前的Statement和PreparedStatement是不可以用的。下面就是调用存储过程的示例代码:
      static void ps() throws SQLException {
          Connection conn = null;
          CallableStatement cs = null;
          ResultSet rs = null;
          try {
              conn = JdbcUtils.getConnection();
              String sql = "{ call addUser(?,?,?,?) } ";
              cs = conn.prepareCall(sql);
              cs.registerOutParameter(4, Types.INTEGER);
              cs.setString(1, "ps name");
              cs.setDate(2, new java.sql.Date(System.currentTimeMillis()));
              cs.setFloat(3, 100f);
              cs.executeUpdate();
              int id = cs.getInt(4);
              System.out.println("id=" + id);
          } finally {
              JdbcUtils.free(rs, cs, conn);
          }
      }
    调用存储过程采用的是CallableStatement,存储过程的sql语句写法比较固定,直接大括号加上call开头,后面跟存储过程的名字。CallableStatement是继承自PreparedStatement,同时也间接继承自Statement。CallableStatement对象要设置输入参数,同时也要注意注册输出参数。
    前期的代码在与数据库进行通信时,都是先建立连接,建立连接花费的成本是最高的,然后发一个SQL语句,执行完后就关闭了连接。还有一个问题是发送的SQL语句都是通过网络传送的,比起本地调用来说,网络传输的成本也是高很多的。如果要插入或者更新一批数据进数据库,还是采用前期的方法,花费的时间会很多,给用户的感受会很慢。但是如果采用批处理,则在两个方便都会节约大部分成本,速度会更快。下面是批处理的示例代码:
      static void createBatch() throws SQLException {
          Connection conn = null;
          PreparedStatement ps = null;
          ResultSet rs = null;
          try {
              conn = JdbcUtils.getConnection();
              String sql = "insert into user(name,birthday, money) values (?, ?, ?) ";
              ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
              for (int i = 0; i < 100; i++) {
                  ps.setString(1, "batch name" + i);
                  ps.setDate(2, new Date(System.currentTimeMillis()));
                  ps.setFloat(3, 100f + i);
 
                  ps.addBatch();
              }
              int[] is = ps.executeBatch();
          } finally {
              JdbcUtils.free(rs, ps, conn);
          }
      }
    如果不用批处理语句打包,则需要调用以前的create方法一百次,完成一百次数据库的连接和网络通讯。用批处理的方式,可以将100次插入打成一个包,然后一次性发给数据库,数据库执行完毕,返回结果。如果在两种方法调用上加上时间,就能够区分两者的速度差距了。但是并不是说用了批处理就一定比不用批处理要快,这根数据的版本、驱动的版本等都有很大关系。所以实际要用的时候,应该在环境上先测试一下两者的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值