什么是JDBC编程?

全称Java Database Connectivity。简单来说就是使用Java里面提供的一些类和方法,利用程序链接数据库,进行增删改查操作。这个过程就叫做JDBC编程。

MySQL数据库操作

传送门

连接数据库步骤

前置准备

  1. 安装MySQL Connector J 8.0。
  2. 导入mysql-connector-java-8.0.20.jar包(通过驱动里面的API访问MySQL数据库)。

    程序中使用步骤

  3. 注册驱动(指明使用什么驱动来连接数据库)。

  4. 建立连接。
  5. 发起请求。
  6. 处理数据。
  7. 关闭连接

基本使用:

  1. package com.desiki.jdbc;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.ResultSet;
  5. import java.sql.Statement;
  6. public class JDBCDemo {
  7. public static void main(String[] args) {
  8. try {
  9. //1、注册驱动(使用什么驱动来连接数据库)
  10. Class.forName("com.mysql.cj.jdbc.Driver");
  11. //2、建立连接
  12. String url = "jdbc:mysql://localhost:3306/mygamedb?useUnicode=true&characterEncoding=UTF8&useSSL=true&&serverTimezone=GMT";
  13. String user = "root";
  14. String password = "root";
  15. Connection con = DriverManager.getConnection(url, user, password);
  16. //3、发起请求
  17. Statement stmt = con.createStatement();
  18. ResultSet rs = stmt.executeQuery("select * from users");
  19. //4、处理数据
  20. while(rs.next())
  21. {
  22. System.out.println(rs.getInt(1)+","+rs.getString(2)+","+rs.getString(3)+","+rs.getDate(4));
  23. }
  24. } catch (Exception e) {
  25. e.printStackTrace();
  26. } finally {
  27. //5、关闭连接
  28. try {
  29. if (rs != null)
  30. rs.close();
  31. } catch (SQLException e) {
  32. e.printStackTrace();
  33. }
  34. try {
  35. if (stmt != null)
  36. stmt.close();
  37. } catch (SQLException e) {
  38. e.printStackTrace();
  39. }
  40. try {
  41. if (con != null)
  42. con.close();
  43. } catch (SQLException e) {
  44. e.printStackTrace();
  45. }//Connection,Statement,ResultSet不能放在一起,需要把每个单独try catch
  46. }
  47. }
  48. }

注意:JDBC中The server time zone value ‘?й???????’ is unrecognized …… 的错误

  1. java.sql.SQLException: The server time zone value '?й???????' is unrecognized or represents more than one time zone.
  2. You must configure either the server or JDBC driver (via the 'serverTimezone' configuration property) to use a more specifc time zone value
  3. if you want to utilize time zone support.

出现这个的原因是因为 mysql返回的时间总是有问题,比实际时间要早8小时。

解决办法:
在jdbc连接的url后面加上serverTimezone=GMT即可解决问题,如果需要使用gmt+8时区,需要写成GMT%2B8。
例如:

String url=”jdbc:mysql://localhost:3306/student?useSSL=true”;
改为
String uri=”jdbc:mysql://localhost:3306/student?useSSL=true&serverTimezone=GMT”;

提取工具类JDBCUtils

由于每次进行数据库操作时都需要上述几部操作,而且像注册驱动,建立连接,关闭连接这些代码都是重复的,故提取出一个工具类JDBCUtils.java来优化程序。

  1. package com.desiki.jdbc;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.ResultSet;
  5. import java.sql.SQLException;
  6. import java.sql.Statement;
  7. public class JDBCUtils {
  8. private static final String connectionURL="jdbc:mysql://localhost:3306/mygamedb?useUnicode=true&characterEncoding=UTF8&useSSL=true&&serverTimezone=GMT";
  9. private static final String username = "root";
  10. private static final String password = "root";
  11. public static Connection getConnection() {
  12. try {
  13. Class.forName("com.mysql.cj.jdbc.Driver");
  14. return DriverManager.getConnection(connectionURL, username, password);
  15. } catch (Exception e) {
  16. // TODO Auto-generated catch block
  17. e.printStackTrace();
  18. }
  19. return null;
  20. }
  21. public static void close(ResultSet rs,Statement stmt,Connection con) {
  22. closeResultSet(rs);
  23. closeStatement(stmt);
  24. closeConnection(con);
  25. }
  26. public static void close(Statement stmt1,Statement stmt2,Connection con) {
  27. closeStatement(stmt1);
  28. closeStatement(stmt2);
  29. closeConnection(con);
  30. }
  31. private static void closeResultSet(ResultSet rs ) {
  32. try {
  33. if(rs!=null)rs.close();
  34. } catch (SQLException e) {
  35. // TODO Auto-generated catch block
  36. e.printStackTrace();
  37. }
  38. }
  39. private static void closeStatement(Statement stmt) {
  40. try {
  41. if(stmt!=null)
  42. stmt.close();
  43. } catch (SQLException e) {
  44. // TODO Auto-generated catch block
  45. e.printStackTrace();
  46. }
  47. }
  48. private static void closeConnection(Connection con) {
  49. try {
  50. if(con!=null)con.close();
  51. } catch (SQLException e) {
  52. // TODO Auto-generated catch block
  53. e.printStackTrace();
  54. }
  55. }
  56. }

JDBC操作(增删改查)

查询

查询所有数据

  1. public static void selectAll() {
  2. Connection con = null;
  3. Statement stmt = null;
  4. ResultSet rs = null;
  5. try {
  6. con = JDBCUtils.getConnection();
  7. stmt = con.createStatement();
  8. rs = stmt.executeQuery("select * from users");//excuteXXX
  9. //跟iterator遍历器类似
  10. while(rs.next()) {
  11. // 通过列的索引来取数据,索引从1开始
  12. System.out.println(rs.getInt(1)+","+rs.getString(2)+","+rs.getString(3));
  13. // 通过列的列名来取数据
  14. System.out.println(rs.getInt("id")+","+rs.getString("username")+","+rs.getString("password"));
  15. }
  16. } catch (Exception e) {
  17. e.printStackTrace();
  18. } finally {
  19. JDBCUtils.close(rs, stmt, con);
  20. }
  21. }

查询带参数的数据

错误用法:使用Statement,会导致SQL注入。

举例:用户名密码验证。

  1. public static Boolean verticy(String username, String password) {
  2. Connection con = null;
  3. Statement stmt = null;
  4. ResultSet rs = null;
  5. try {
  6. con = JDBCUtils.getConnection();
  7. stmt = con.createStatement();
  8. String sql = "select * from users where username='" + username + "' and password = '" + password + "'";
  9. System.out.println(sql);
  10. rs = stmt.executeQuery(sql);
  11. if (rs.next()) {
  12. return true;
  13. } else {
  14. return false;
  15. }
  16. } catch (Exception e) {
  17. e.printStackTrace();
  18. } finally {
  19. JDBCUtils.close(rs, stmt, con);
  20. }
  21. return false;
  22. }

正常情况,在main方法中测试成功:

  1. public static void main(String[] args) {
  2. System.out.println(verticy("azure", "123"));//用户名密码都存在且正确,输出true,合理
  3. }

但是如果这么输入参数,用户名密码都不正确,同样也验证成功:

  1. public static void main(String[] args) {
  2. System.out.println(verticy("azure123", "1213' or '1' = '1"));//用户名密码不正确,也输出了true
  3. }

这就是SQL注入。
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。

上述代码中验证了两个用户名密码的登录。
第一条因为用户名密码都正确,输出true,验证成功,合情合理。
第二条用户名密码都不正确,居然也验证成功了,这是为什么呢?
原因在于我们组拼的sql语句是有漏洞的,比如上述第二条sql组拼后:
select * from users where username='azure123' and password = '1213' or '1' = '1'
条件1=1 恒为true,所以用户名和密码正不正确无所谓,这个条件一定会执行成功。

正确方法:使用PreparedStatement

语法格式:

  1. String sql = "select * from users where username = ? and password = ?";//?代表参数
  2. PreparedStatement pstmt = conn.prepareStatement(sql);
  3. //设置参数索引,从1开始
  4. pstmt.setString(1,xxx);
  5. pstmt.setString(2,xxx);
  6. ResultSet rs = pstmt.executeQuery();

正确的用户名密码验证示例:

  1. public static boolean verticy(String username,String password) {
  2. Connection con = null;
  3. PreparedStatement pstmt = null;
  4. ResultSet rs = null;
  5. try {
  6. con = JDBCUtils.getConnection();
  7. String sql = "select * from users where username = ? and password = ?";
  8. pstmt = con.prepareStatement(sql);
  9. pstmt.setString(1, username);
  10. pstmt.setString(2, password);
  11. rs = pstmt.executeQuery();
  12. if(rs.next())
  13. return true;
  14. else
  15. return false;
  16. } catch (Exception e) {
  17. // TODO Auto-generated catch block
  18. e.printStackTrace();
  19. }finally {
  20. JDBCUtils.close(rs, pstmt, con);
  21. }
  22. return false;
  23. }

这样可以防止SQL注入。一般来说,对于带参数的数据库操作,都是使用PreparedStatement而不是Statement。

分页查询

语法:
limit ?,?

  • 第一个问号:用于指定查询记录的起始行数(从哪一行开始,行数从0开始)。
  • 第二个问号:用于指定查询数据所返回的记录行数(指查询几行)。

公式:
如果要查询 第7页,每页8行:
0-7 第一页
8-15 第二页
16-23 第三页

起始行数:(页数-1)*8
查询行数:自定义,这里是8
所以最终的结果是limit 48,8。

  1. // pageNumber是页数,第几页 pageCount是每页显示多少条数据
  2. public static void selectUserByPage(int pageNumber,int pageCount) {
  3. Connection con = null;
  4. PreparedStatement pstmt = null;
  5. ResultSet rs = null;
  6. try {
  7. con = JDBCUtils.getConnection();
  8. String sql = "select * from users limit ?,?";
  9. stmt = con.prepareStatement(sql);
  10. stmt.setInt(1, (pageNumber-1)*pageCount );
  11. stmt.setInt(2, pageCount);
  12. rs = stmt.executeQuery();
  13. //跟iterator遍历器
  14. while(rs.next()) {
  15. // System.out.println(rs.getInt(1)+","+rs.getString(2)+","+rs.getString(3));
  16. System.out.println(rs.getInt("id")+","+rs.getString("username")+","+rs.getString("password"));
  17. }
  18. } catch (Exception e) {
  19. e.printStackTrace();
  20. } finally {
  21. JDBCUtils.close(rs, pstmt, con);
  22. }
  23. }

插入

  1. public static void insert( String username,String password ) {
  2. Connection con = null;
  3. PreparedStatement stmt = null;
  4. ResultSet rs = null;
  5. try {
  6. con = JDBCUtils.getConnection();
  7. String sql = "insert into users(username,password) values(?,?)";
  8. stmt = con.prepareStatement(sql);
  9. stmt.setString(1, username);
  10. stmt.setString(2, password);
  11. int result =stmt.executeUpdate();// 返回值代表收到影响的行数
  12. } catch (Exception e) {
  13. e.printStackTrace();
  14. } finally {
  15. JDBCUtils.close(rs, stmt, con);
  16. }
  17. }

删除

  1. //一般根据id来删除数据
  2. public static void delete(int id) {
  3. Connection con = null;
  4. PreparedStatement stmt = null;
  5. ResultSet rs = null;
  6. try {
  7. con = JDBCUtils.getConnection();
  8. String sql = "delete from user where id = ?";
  9. stmt = con.prepareStatement(sql);
  10. stmt.setInt(1, id);
  11. int result =stmt.executeUpdate();// 返回值代表收到影响的行数
  12. if(result>0) {
  13. System.out.println("删除成功");
  14. }else {
  15. System.out.println("删除失败");
  16. }
  17. } catch (Exception e) {
  18. e.printStackTrace();
  19. } finally {
  20. JDBCUtils.close(rs, stmt, con);
  21. }
  22. }

修改

正常修改一条或多条记录

  1. //根据id修改密码
  2. public static void update(int id,String newPassword) {
  3. Connection con = null;
  4. PreparedStatement stmt = null;
  5. ResultSet rs = null;
  6. try {
  7. con = JDBCUtils.getConnection();
  8. String sql = "update usesr set password = ? where id = ?";
  9. stmt = con.prepareStatement(sql);
  10. stmt.setString(1, newPassword);
  11. stmt.setInt(2, id);
  12. int result =stmt.executeUpdate();// 返回值代表收到影响的行数
  13. if(result>0) {
  14. System.out.println("修改成功");
  15. }else {
  16. System.out.println("修改失败");
  17. }
  18. } catch (Exception e) {
  19. e.printStackTrace();
  20. } finally {
  21. JDBCUtils.close(rs, stmt, con);
  22. }
  23. }

事务操作

理解事务之前,先讲一个日常生活中最常干的事:转账
从A账户中转1000块给B账户,转账这个过程必须是一个事务。否则,可能会由于各种原因而导致,A账户减少了1000块,但B账户没有相应的增加1000块,或者 B账户增加了1000块,但A账户没有减少1000块。一个步骤成功而另一个步骤失败,这样不是你用户损失就是银行损失。所以,为了保证转账前后的一致性,就必须确保转账操作是一个事务。这样,不管哪一个步骤失败了以后,就可以回滚到转账前的状态,就像这个事务从来没有执行过一样,对双方都有利。
事务就是用来解决类似问题的。事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。
当你需要一次执行多条SQL语句时,可以使用事务。通俗一点说,就是,如果这几条SQL语句全部执行成功,则才对数据库进行一次更新,如果有一条SQL语句执行失败,则这几条SQL语句全部不进行执行。这个时候需要用到事务。参考1

JDBC是Java数据库连接相关的API,所以Java的事务管理也是在通过该API进行的。JDBC的核心是Connection接口,JDBC的事务管理是基于Connection接口来实现的,通过Connection对象进行事务管理。
JDBC对事务的处理规则,必须是基于同一个Connection对象的。

JDBC提供了3个方法来进行事务管理:

  • setAutoCommit() 设置自动提交,方法中需要传入一个boolean类型的参数,true为自动提交,false为手动提交
  • commit() 提交事务
  • rollback() 回滚事务(回滚一般写在catch块中)

JDBC默认的事务处理行为是自动提交。所以JDBC在进行事务管理时,首先要通过Connection对象调用setAutoCommit(false) 方法, 将SQL语句的提交(commit)由驱动程序转交给应用程序负责。并且调用setAutoCommit(false) 方法后,程序必须调用commit或者rollback方法,否则SQL语句不会被提交或回滚。参考2

  1. public static void transferAccounts(String username1,String username2,int money) {
  2. Connection con = null;
  3. PreparedStatement stmt1 = null;
  4. PreparedStatement stmt2 = null;
  5. ResultSet rs = null;
  6. try {
  7. con = JDBCUtils.getConnection();
  8. con.setAutoCommit(false);//设置手动提交事务
  9. //转出
  10. String sql = "update user set balance = balance - ? where username = ?";
  11. stmt1 = con.prepareStatement(sql);
  12. stmt1.setInt(1, money);
  13. stmt1.setString(2, username1);
  14. stmt1.executeUpdate();// 返回值代表收到影响的行数
  15. //转入
  16. sql = "update user set balance = balance + ? where username = ?";
  17. stmt2 = con.prepareStatement(sql);
  18. stmt2.setInt(1, money);
  19. stmt2.setString(2, username2);
  20. stmt2.executeUpdate();// 返回值代表收到影响的行数
  21. con.commit();//提交事务
  22. } catch (Exception e) {
  23. try {
  24. con.rollback();//只要出现异常。就回滚!
  25. } catch (SQLException e1) {
  26. e1.printStackTrace();
  27. }
  28. } finally {
  29. JDBCUtils.close(stmt2, stmt1, con);
  30. }
  31. }

连接池(数据源)

对于现在的JDBCUtils,其实还有些问题。每次数据库操作都需要建立一个新的连接,使用完后再关闭掉。假如有大量的用户使用,会造成性能内存的极大开销,对此,我们可以使用连接池(数据源)。
连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
这篇文档连接池部分写的比较简单,具体可以看这篇JDBC连接池

改良JDBCUtils

给现有的JDBCUtils加入连接池。

  1. package com.desiki.jdbc01;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.ResultSet;
  5. import java.sql.SQLException;
  6. import java.sql.Statement;
  7. import java.util.ArrayList;
  8. public class JDBCUtils {
  9. private static final String connectionURL="jdbc:mysql://localhost:3306/mygamedb?useUnicode=true&characterEncoding=UTF8&useSSL=true&&serverTimezone=GMT";
  10. private static final String username = "root";
  11. private static final String password = "root";
  12. private static ArrayList<Connection> conList = new ArrayList<Connection>();
  13. static {
  14. //设置5个初始连接
  15. for(int i =0;i<5;i++) {
  16. Connection con = createConnection();
  17. conList.add(con);
  18. }
  19. }
  20. public static Connection getConnection() {
  21. if(conList.isEmpty()==false) {
  22. Connection con = conList.get(0);
  23. conList.remove(con);
  24. return con;
  25. }else {
  26. return createConnection();
  27. }
  28. }
  29. private static Connection createConnection() {
  30. try {
  31. Class.forName("com.mysql.cj.jdbc.Driver");
  32. return DriverManager.getConnection(connectionURL, username, password);
  33. } catch (Exception e) {
  34. // TODO Auto-generated catch block
  35. e.printStackTrace();
  36. }
  37. return null;
  38. }
  39. public static void close(ResultSet rs,Statement stmt,Connection con) {
  40. closeResultSet(rs);
  41. closeStatement(stmt);
  42. closeConnection(con);
  43. }
  44. public static void close(Statement stmt1,Statement stmt2,Connection con) {
  45. closeStatement(stmt1);
  46. closeStatement(stmt2);
  47. closeConnection(con);
  48. }
  49. private static void closeResultSet(ResultSet rs ) {
  50. try {
  51. if(rs!=null)rs.close();
  52. } catch (SQLException e) {
  53. // TODO Auto-generated catch block
  54. e.printStackTrace();
  55. }
  56. }
  57. private static void closeStatement(Statement stmt) {
  58. try {
  59. if(stmt!=null)
  60. stmt.close();
  61. } catch (SQLException e) {
  62. // TODO Auto-generated catch block
  63. e.printStackTrace();
  64. }
  65. }
  66. private static void closeConnection(Connection con) {
  67. // try {
  68. // if(con!=null)con.close();
  69. // } catch (SQLException e) {
  70. // // TODO Auto-generated catch block
  71. // e.printStackTrace();
  72. // }
  73. conList.add(con);
  74. }
  75. }

使用方式和之前完全一样。
对于我们自己写的连接池,不是很完善,比较简陋,很多东西没考虑到,一般可以用第三方的数据源。这里介绍两种:DBCB,C3P0。

DBCP

DBCP(DataBase Connection Pool)是Java数据库连接池的一种,由Apache开发,通过数据库连接池,可以让程序自动管理数据库连接的释放和断开。

使用步骤:
下载本体commons-dbcp2-2.7.0-bin.jar包。下载地址: http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi
依赖包commons-pool2-2.8.0-bin.jar包。下载地址:http://commons.apache.org/proper/commons-pool/download_pool.cgi
依赖包commons-logging-1.2-bin.jar包。下载地址:http://commons.apache.org/proper/commons-logging/download_logging.cgi

  1. package com.desiki.jdbc01;
  2. import java.sql.Connection;
  3. import java.sql.ResultSet;
  4. import java.sql.SQLException;
  5. import java.sql.Statement;
  6. import org.apache.commons.dbcp2.BasicDataSource;
  7. public class DBCPDataSource {
  8. private static final String connectionURL="jdbc:mysql://localhost:3306/mygamedb?useUnicode=true&characterEncoding=UTF8&useSSL=true&&serverTimezone=GMT" ;
  9. private static final String username = "root";
  10. private static final String password = "root";
  11. private static BasicDataSource ds;
  12. static {
  13. //初始化dbcp数据源
  14. ds = new BasicDataSource();
  15. ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
  16. ds.setUrl(connectionURL);
  17. ds.setUsername(username);
  18. ds.setPassword(password);
  19. ds.setInitialSize(5);//初始化连接的个数
  20. ds.setMaxTotal(20);//连接的最大个数
  21. ds.setMinIdle(3);//保证连接池中最少有几个空闲的备用连接
  22. }
  23. public static Connection getConnection() {
  24. try {
  25. return ds.getConnection();//通过dbcp得到的链接,不需要归还,直接close就可以,
  26. } catch (SQLException e) {
  27. // TODO Auto-generated catch block
  28. e.printStackTrace();
  29. }
  30. return null;
  31. }
  32. public static void close(ResultSet rs,Statement stmt,Connection con) {
  33. closeResultSet(rs);
  34. closeStatement(stmt);
  35. closeConnection(con);
  36. }
  37. public static void close(Statement stmt1,Statement stmt2,Connection con) {
  38. closeStatement(stmt1);
  39. closeStatement(stmt2);
  40. closeConnection(con);
  41. }
  42. private static void closeResultSet(ResultSet rs ) {
  43. try {
  44. if(rs!=null)rs.close();
  45. } catch (SQLException e) {
  46. // TODO Auto-generated catch block
  47. e.printStackTrace();
  48. }
  49. }
  50. private static void closeStatement(Statement stmt) {
  51. try {
  52. if(stmt!=null)
  53. stmt.close();
  54. } catch (SQLException e) {
  55. // TODO Auto-generated catch block
  56. e.printStackTrace();
  57. }
  58. }
  59. private static void closeConnection(Connection con) {
  60. try {
  61. if(con!=null)con.close();//这里会把链接归还给dbcp连接池,并不是真正的断开链接
  62. } catch (SQLException e) {
  63. // TODO Auto-generated catch block
  64. e.printStackTrace();
  65. }
  66. }
  67. }

C3P0

快速入门:https://www.mchange.com/projects/c3p0/
下载地址:https://sourceforge.net/projects/c3p0/

注意:除去导入本体jar包外还需要导入mchange-commons-java-0.2.19.jar包

  1. package com.desiki.jdbc01;
  2. import java.beans.PropertyVetoException;
  3. import java.sql.Connection;
  4. import java.sql.ResultSet;
  5. import java.sql.SQLException;
  6. import java.sql.Statement;
  7. import com.mchange.v2.c3p0.ComboPooledDataSource;
  8. public class C3P0DataSource {
  9. private static final String connectionURL="jdbc:mysql://localhost:3306/mygamedb?useUnicode=true&characterEncoding=UTF8&useSSL=true&&serverTimezone=GMT" ;
  10. private static final String username = "root";
  11. private static final String password = "root";
  12. private static ComboPooledDataSource ds ;
  13. static {
  14. try {
  15. ds = new ComboPooledDataSource();
  16. ds.setDriverClass("com.mysql.cj.jdbc.Driver");
  17. ds.setJdbcUrl(connectionURL);
  18. ds.setUser(username);
  19. ds.setPassword(password);
  20. ds.setInitialPoolSize(5);
  21. ds.setMaxPoolSize(20);
  22. } catch (PropertyVetoException e) {
  23. // TODO Auto-generated catch block
  24. e.printStackTrace();
  25. }
  26. }
  27. public static Connection getConnection() {
  28. try {
  29. return ds.getConnection();
  30. } catch (SQLException e) {
  31. // TODO Auto-generated catch block
  32. e.printStackTrace();
  33. }
  34. return null;
  35. }
  36. public static void close(ResultSet rs,Statement stmt,Connection con) {
  37. closeResultSet(rs);
  38. closeStatement(stmt);
  39. closeConnection(con);
  40. }
  41. public static void close(Statement stmt1,Statement stmt2,Connection con) {
  42. closeStatement(stmt1);
  43. closeStatement(stmt2);
  44. closeConnection(con);
  45. }
  46. private static void closeResultSet(ResultSet rs ) {
  47. try {
  48. if(rs!=null)rs.close();
  49. } catch (SQLException e) {
  50. // TODO Auto-generated catch block
  51. e.printStackTrace();
  52. }
  53. }
  54. private static void closeStatement(Statement stmt) {
  55. try {
  56. if(stmt!=null)
  57. stmt.close();
  58. } catch (SQLException e) {
  59. // TODO Auto-generated catch block
  60. e.printStackTrace();
  61. }
  62. }
  63. private static void closeConnection(Connection con) {
  64. try {
  65. if(con!=null)con.close();//这里会把链接归还给c3p0连接池,并不是真正的断开链接
  66. } catch (SQLException e) {
  67. // TODO Auto-generated catch block
  68. e.printStackTrace();
  69. }
  70. }
  71. }