发表于: 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,因为是数据库自己生成的我们无法知晓,所以可通过以下来获取自生成键值。具体可以见遇到的问题板块下的代码。
- int rowcount = stmt.executeUpdate (
- "insert into LocalGeniusList (name) values ('Karen')",
- // 插入行并返回键值
- Statement.RETURN_GENERATED_KEYS);
- ResultSet rs = stmt.getGeneratedKeys ();
- // 得到生成的键值
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批处理的认识和预处理知识的加深。
评论