正确使用数据库连接的正常步骤:
Connection con=null;
PreparedStatement preStmt=null;
try {
con = ConnectionUtil.getConnection();
con.setAutoCommit(false);
StringBuffer sql = new StringBuffer("insert into lecture_check(lecture_id,student_id,attend_time,check_status) ")
.append("values(?,?,?,?)");
preStmt = con.prepareStatement(sql.toString());
preStmt.setInt(1,lecture_id);
preStmt.setInt(2,student_id);
preStmt.setString(3,df.format(new Date()));
preStmt.setInt(4,1);
preStmt.executeUpdate();
//preStmt注意关闭,疑问:为什么这里要关闭,下面不是要继续使用吗?答:connection.prepareStatement(sql)只是创建一 个 PreparedStatement 对象,调用2次,会创建2个对象和Connection一样,如果不调用close方法,见问题1,如果是查询操作,涉及resultset ,也需要调用close(),原因同理。
preStmt.close();
//更新讲座剩余座位数
preStmt = con.prepareStatement("update lecture_lecture set rest_seat=rest_seat-1 where id=?");
preStmt.setInt(1,lecture_id);
preStmt.executeUpdate();
con.commit();
} catch (SQLException e) {
try{
if(con!=null&&(!con.isClosed())){
con.rollback();//注意回滚
}
}catch(Exception f){
f.printStackTrace();
}
e.printStackTrace();
}finally{
ConnectionUtil.closeConnection(con, preStmt);//注意关闭
}
更新,插入,删除使用executeUpdate(),查询使用executeQuery(),未知操作使用execute()
查询操作关闭顺序:resultset preparedstatement connection
问题1:是否一定需要按照顺序将resultset preparedstatement connection都关闭?
答:1.使用第三方数据库连接池时,获取到Connection之后,使用完成,调用的关闭方法(close()) ,并没有将Connection关闭,只是放回到连接池中,如果调用的这个方法,而没有手动关闭resultset,PreparedStatement ,则这个PreparedStatement并没有关闭,这样会使得程序内存急速增长,java的内存回收机制可能跟不上速度,最终造成答Out of memory Error。
2.如果使用JDK 7就不用了,它们都实现了java.lang.AutoCloseable
问题2:connection是否可以创建多个preparedstatement ?
答:connection对象可以创建任意多个preparedstatement 对象,而不需要你重新获取连接,并且一般一个线程只使用一个connection,一方面可以控制事务,另一方面可以降低数据库连接消耗,当然有例外的是如果需要开启嵌套的新事务,则需要获取新的connection(类似于spring的传播性:挂起当前事务,并新建事务)。
问题3:preparedstatement是否可以创建任意多个resultset对象,同时循环使用?
答:preparedstatement可以创建任意多个resultset结果集对象,但是每次只有一个结果集生效,并且之前的结果集已失效,要关闭,避免造成内存溢出。原因如下:
Connection con=null;
con=ConnectionUtil.getConnection();
PreparedStatement preStmt=null;
ResultSet rs=null;
ResultSet rs1=null;
preStmt = con.prepareStatement(sql.toString());
rs = preStmt.executeQuery();
preStmt = con.prepareStatement(sql1.toString());
rs1 = preStmt.executeQuery();
以上程序同一个preStmt变量,但其实con.prepareStatement(sql.toString()),con.prepareStatement(sql1.toString())分别创建了两个PreparedStatement对象,这里给名对象1,对象2,当创建对象2赋值给preStmt时,原先创建的对象1已经没有和任何变量绑定了,处于等待系统资源回收状态,那么rs结果集还能有效吗?很明显,它也是处于等待系统资源回收的状态了。跟使用con,preStmt,rs是无关的,所以不要纠结于是否能够重新利用,每次ConnectionUtil.getConnection(),con.prepareStatement(sql.toString()),preStmt.executeQuery()都会创建新的对象,关键在于有没有变量与它绑定。
那么使用createStatement创建的Statement对象是否就可以,因为没有创建新的Statement,答案是不可以的,一个Statement对象同时只能有一个结果集在活动.这是宽容性的,就是说即使没有调用ResultSet的close()方法,只要打开第二个结果集就隐含着对上一个结果集的关闭。
比如以下程序就是错误的:
Connection conn = null;
Statement stmt = null;
conn = .......;
stmt = conm.createStatement(xxxxxx);
ResultSet rs = stmt.executeQuery(sql1);
while(rs.next()){
str = rs.getString(xxxxx);
ResultSet rs1 = stmt.executeQuery(\"select * from 表 where 字段=str\");
}
当stmt.executeQuery(\"select * from 表 where 字段=str\");赋给rs1时,这时隐含的操作是已经关闭了rs,你还能循环下去吗?所以如果要同时操作多个结果集一定要让它他绑定到不同的Statement对象上,好在一个connection对象可以创建任意多个Statement对象,而不需要你重新获取连接。
问题4:那怎么编写,使得多个结果集对象能同时循环活动?
答:如果你想同时对多个结果集操作,就要创建多个Statement或者prepareStatement对象,如果不需要同时操作,那么可以在一个Statement或prepareStatement对象上顺序操作多个结果集,不过需要注意每次操作完成后关闭prepareStatement和ResultSet。
问题5:如果sql查询语句不变,使用同一个prepareStatement对象多次直接调用executeQuery方法(中间数据会进行更新),会从数据库查询出最新数据吗?
答:会,只要确保是同一个Connection数据库连接,涉及数据库事务,请参考以下链接。事务隔离性级别,数据库锁,spring事务传播性,Spring事务的隔离级别_porkczr的博客-CSDN博客
比如以下程序就是正确的
public static void main(String[] args) throws SQLException {
Connection conn = DBpool.getConnection();
conn.setAutoCommit(false);
PreparedStatement preStmt=null;
ResultSet rs=null;
StringBuffer sql=null;
sql = new StringBuffer("select * from REDUCEDORMITORYFEE r where r.ISSUCCESS='N'");
preStmt = conn.prepareStatement(sql.toString());
rs = preStmt.executeQuery();
if(rs.next()){
System.out.println(rs.getDouble("ARREARS"));
}
rs.close();//这里要对rs进行关闭,但是不能关闭preStmt,因为还要继续使用
sql=new StringBuffer("update REDUCEDORMITORYFEE set ARREARS='2000' where ISSUCCESS='N'");
//这里不能重新赋值给preStmt,要创建新的PreparedStatement变量,preStmt要确保引用的
PreparedStatement preStmt1=conn.prepareStatement(sql.toString());
preStmt1.executeUpdate();
preStmt1.close();
//还是之前的PreparedStatement对象。
//preStmt直接调用executeQuery方法,再次从数据库查询最新数据
rs = preStmt.executeQuery();
if(rs.next()){
System.out.println(rs.getDouble("ARREARS"));
}
rs.close();
preStmt.close();
conn.commit();
}