发表于: 2018-04-08 22:57:46

1 763



day11



今天完成的事情


1.测试statement和preparedstatement

@Test
public void testForRemote() throws Exception {
   try {
       String sql = "INSERT INTO table1(NAME,gender,age) VALUES(?,?,?);";
       Connection con = null;
       Statement stat = null;
       //加载驱动
       Class.forName("com.mysql.jdbc.Driver");
       con = DriverManager.getConnection("jdbc:mysql://39.107.247.104:3306/Jnshu1?useUnicode=true&"+
                       "characterEncoding=utf-8&useSSL=false",
               "root","**********");
       PreparedStatement pstat = con.prepareStatement(sql);
       pstat.setString(1, "陆操");
       pstat.setString(2, "");
       pstat.setString(3, "27");
       //执行预编译语句
       int i = 0;
       while(i<1000){
           pstat.executeUpdate();
           i++;
       }
       pstat.close();
       con.close();
   } catch (ClassNotFoundException e) {
       e.printStackTrace();
   }

以上为使用prepareStatement预编译插入1000条数据到远程mysql,用时100秒


@Test
public void testForRemote2() throws Exception {
   try {
       String sql = "INSERT INTO table1(NAME,gender,age) VALUES('王操','nan','55');";
       Connection con = null;
       Statement stat = null;
       //加载驱动
       Class.forName("com.mysql.jdbc.Driver");
       con = DriverManager.getConnection("jdbc:mysql://39.107.247.104:3306/Jnshu1?useUnicode=true&"+
                       "characterEncoding=utf-8&useSSL=false",
               "root","*********");
       stat = con.createStatement();
       int i = 0;
       while(i<100){
           stat.executeUpdate(sql);
           i++;
       }
       stat.close();
       con.close();

   } catch (ClassNotFoundException e) {
       // TODO Auto-generated catch block
       e.printStackTrace();
   }

以上为Statement语句插1000条,用时50秒

结论:从理论上说,处理大量sql语句的插入应该是preparedstatement占优势的,但是结果不是这样,可能我的代码有地方不规范吧。


2.测试批处理

@Test
public void testForRemote() throws Exception {
try {                                           //语句结尾不能加分号,否则报错,原因见下文
       String sql = "INSERT INTO table1(NAME,gender,age) VALUES(?,?,?);";
       Connection con = null;
       //加载驱动
       Class.forName("com.mysql.jdbc.Driver");
       con = DriverManager.getConnection("jdbc:mysql://39.107.247.104:3306/Jnshu1?useUnicode=true&"+
"characterEncoding=utf-8&useSSL=false&rewriteBatchedStatements=true",
               "root","123456xyz");
       PreparedStatement pstat = con.prepareStatement(sql);
       
      // 模拟数据
     for (int i=0; i<1000000; i++) {
        pstat.setString(1, "空见法师"+i);
        pstat.setString(2, "");
        pstat.setString(3, "108");
        pstat.addBatch();
       
     }
         pstat.executeBatch();
         pstat.clearBatch();

       pstat.close();
       con.close();
   } catch (ClassNotFoundException e) {
       e.printStackTrace();
   }

执行批处理是,发现时间没有显著变快,后来发现URL需要添加一个rewriteBatchedStatements=true,才会去执行批处理,不然还是会一条一条的插数据。但是目前有一个没有解决的问题,就是我只要在URL后加上这个参数就会报莫名其妙的错误,去掉就可以正常运行。所以批处理没有测成。

记得在url 后面添加:rewriteBatchedStatements=true 表示批量插入,如果不添加的话即使使用addbatch() ,executeBatch() 在后台入库的地方还是不会一次请求入库而是多次请求入库。


好的,问题已解决,我每次执行批处理报错,经过在网上的查找发现错误出在我的sql语句多了个分号

String sql = "INSERT INTO table1(NAME,gender,age) VALUES(?,?,?);";

因为在批处理时,它会对你的sql进行进行处理,而多的这个分号,恰恰是它处理后导致sql不再符合语法。这个涉及到相关源码,可以参考这个链接讲的很详细 : https://www.cnblogs.com/applerosa/p/7988765.html

总之,在使用批处理时sql语句结尾不要加分号 ! ! !


解决了这个bug后,测试100万插入数据用时27秒



3. 获取自增长值


我们在插入数据后,如何获取刚刚插入的数据的id呢,这种场景一般用于自增长id,因为是数据库自己生成的我们无法知晓,所以可通过以下来获取自生成键值。具体可以见遇到的问题板块下的代码。

  1. int rowcount = stmt.executeUpdate (  
  2.    "insert into LocalGeniusList (name) values ('Karen')",  
  3. // 插入行并返回键值  
  4. Statement.RETURN_GENERATED_KEYS);  
  5. ResultSet rs = stmt.getGeneratedKeys ();  
  6. // 得到生成的键值 



4.回顾JDBC


4.1 接口:

connection: 连接对象

Statement: 执行命令对象,把SQL语句发送到数据库执行。

ResultSet: (在线式)结果集接口,必须要保持与数据库的连接

4.2 预编译SQL语句

1).避免了频繁的sql拼接(可以使用占位符)

2).防止sql注入

4.3 事务

|-- Connection

void setAutoCommit(boolean autoCommit) ;  设置事务是否自动提交

                                                                          如果设置为false,表示手动提交事务。

void commit();   手动提交事务

void rollback() ;   回滚(出现异常时候,所有已经执行成功的代码需要回退到事务开始前的状态。

Savepoint setSavepoint(String name)


4.4 批处理

比如,要插入100条数据,可以写一个save方法,循环100次,但是会频繁的打开、关闭连接。

那么有没有一直方式可以打开一次连接,发送多条sql语句,最后执行完再关闭连接呢

于是引入了批处理的概念。


应用场景:需要批量执行sql语句,批量保存信息

|-- Statement

批处理相关方法

void addBatch(String sql)     添加批处理

void clearBatch()            清空批处理

int[] executeBatch()         执行批处理



明天计划的事情


继续学习JDBC


遇到的问题


报错 :java.sql.SQLException: Before start of result set 

这个错误网上查到是因为在调用ResultSet的getInt或getString前,一定要rs.next()。

public void testForRemote() throws Exception {
try {
String sql = "INSERT INTO table1(NAME,gender,age) VALUES(?,?,?);";
       Connection con = null;
       //加载驱动
       Class.forName("com.mysql.jdbc.Driver");
       con = DriverManager.getConnection("jdbc:mysql://39.107.247.104:3306/Jnshu1?useUnicode=true&"+
"characterEncoding=utf-8&useSSL=false",
               "root","123456xyz");
       PreparedStatement pstat = con.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
       pstat.setString(1, "小华");
       pstat.setString(2, "");
       pstat.setString(3, "108");
       pstat.executeUpdate();
       ResultSet rs = pstat.getGeneratedKeys();
       // 得到返回的自增长字段
       if (rs.next()) {
             System.out.println(rs.getInt(1));
          }
pstat.close();
       con.close();
   } catch (ClassNotFoundException e) {
e.printStackTrace();
   }

}

原因:ResultSet对象代表SQL语句执行的结果集,维护指向其当前数据行的光标。每调用一次next()方法,光标向下移动一行。最初它位于第一行之前,因此第一次调用next()应把光标置于第一行上,使它成为当前行。随着每次调用next()将导致光标向下移动一行。在ResultSe对象及其t父辈Statement对象关闭之前,光标一直保持有效。所以bug出现的原因在于取出ResultSet对象并对其进行操作时,没有采用.next()方法将ResultSet对象的光标移至指定行,不管Statement对象执行SQL语句是否十分确定能搜出记录,也不可以在没有ResultSet的next()方法之前直接对ResultSet对象进行取值


收获


jdbc批处理的认识和预处理知识的加深。

 



返回列表 返回列表
评论

    分享到