首页 > 代码库 > JDBC-自定义数据库工具类(DBService)

JDBC-自定义数据库工具类(DBService)

 写在前面的话:
     (1)使用JDBC,必须要使用对应的jar包,该笔记中使用jar包:mysql-connector-java-5.1 .6-bin.jar
     (2)使用连接池,一定要导入对象的数据库连接池包,该笔记中使用jar包:c3p0-0.9.1.2.jar
               常用连接池:dbcp连接池、c3p0连接池、druid连接池
 
 
第一版:不用数据库连接池:
 
  1 package cn;  2    3 import java.sql.Connection;  4 import java.sql.DriverManager;  5 import java.sql.PreparedStatement;  6 import java.sql.ResultSet;  7 import java.sql.ResultSetMetaData;  8 import java.sql.SQLException;  9 import java.util.ArrayList; 10 import java.util.HashMap; 11 import java.util.List; 12 import java.util.Map; 13   14 public class SqlService { 15   16        // 四大金刚 17       String driver = "com.mysql.jdbc.Driver" ;// 驱动名称 18       String url = "jdbc:mysql:///mao" ;// 连接字符串 19       String username = "root" ;// 用户名 20       String password = "123456" ;// 密码 21   22        // 三剑客 23       Connection con = null ;// 连接对象 24       PreparedStatement pstmt = null ;// 语句对象 25       ResultSet rs = null ;// 结果集对象 26   27        /** 28        * 获得连接对象 29        * 30        * @return 连接对象 31        * @throws ClassNotFoundException 32        * @throws SQLException 33        */ 34        public Connection getConnection() throws ClassNotFoundException, 35                   SQLException { 36             Class. forName( driver); 37              con = DriverManager.getConnection( url , username , password ); 38              return con ; 39       } 40   41        /** 42        * 关闭三剑客 43        * 44        * @throws SQLException 45        */ 46        public void close(ResultSet rs, PreparedStatement pstmt, Connection con) { 47   48              try { 49                    if (rs != null) 50                         rs.close(); 51                    if (pstmt != null) 52                         pstmt.close(); 53                    if (con != null) 54                         con.close(); 55             } catch (SQLException e) { 56                    // TODO: handle exception 57                   e.printStackTrace(); 58             } 59       } 60   61        /** 62        * 执行更新 63        * 64        * @param sql 65        *            传入的预设的 sql语句 66        * @param params 67        *            问号参数列表 68        * @return 影响行数 69        */ 70        public int execUpdate(String sql, Object[] params) { 71              try { 72                    this .getConnection();// 获得连接对象 73                    this .pstmt = this. con .prepareStatement(sql);// 获得预设语句对象 74   75                    if (params != null) { 76                          // 设置参数列表 77                          for (int i = 0; i < params. length; i++) { 78                                // 因为问号参数的索引是从1开始,所以是i+1,将所有值都转为字符串形式,好让setObject成功运行 79                                this .pstmt .setObject(i + 1, params[i] + "" ); 80                         } 81                   } 82   83                    return this .pstmt .executeUpdate(); // 执行更新,并返回影响行数 84   85             } catch (ClassNotFoundException | SQLException e) { 86                    // TODO Auto-generated catch block 87                   e.printStackTrace(); 88             } finally { 89                    this .close( this. rs, this. pstmt , this .con ); 90             } 91              return 0; 92       } 93   94        /** 95        * 执行查询 96        * 97        * @param sql 98        *            传入的预设的 sql语句 99        * @param params100        *            问号参数列表101        * @return 查询后的结果102        */103        public List<Map<String, Object>> execQuery(String sql, Object[] params) {104  105              try {106                    this .getConnection();// 获得连接对象107                    this .pstmt = this. con .prepareStatement(sql);// 获得预设语句对象108  109                    if (params != null) {110                          // 设置参数列表111                          for (int i = 0; i < params. length; i++) {112                                // 因为问号参数的索引是从1开始,所以是i+1,将所有值都转为字符串形式,好让setObject成功运行113                                this .pstmt .setObject(i + 1, params[i] + "" );114                         }115                   }116  117                    // 执行查询118                   ResultSet rs = pstmt .executeQuery();119  120                   List<Map<String, Object>> al = new ArrayList<Map<String, Object>>();121  122                    // 获得结果集元数据(元数据就是描述数据的数据,比如把表的列类型列名等作为数据)123                   ResultSetMetaData rsmd = rs.getMetaData();124  125                    // 获得列的总数126                    int columnCount = rsmd.getColumnCount();127  128                    // 遍历结果集129                    while (rs.next()) {130                         Map<String, Object> hm = new HashMap<String, Object>();131                          for (int i = 0; i < columnCount; i++) {132                                // 根据列索引取得每一列的列名,索引从1开始133                               String columnName = rsmd.getColumnName(i + 1);134                                // 根据列名获得列值135                               Object columnValue =http://www.mamicode.com/ rs.getObject(columnName);136                                // 将列名作为key,列值作为值,放入 hm中,每个 hm相当于一条记录137                               hm.put(columnName, columnValue);138                         }139                          // 将每个 hm添加到al中, al相当于是整个表,每个 hm是里面的一条记录140                         al.add(hm);141                   }142  143                    return al;144  145             } catch (ClassNotFoundException | SQLException e) {146                    // TODO Auto-generated catch block147                   e.printStackTrace();148             } finally {149                    this .close( this. rs, this. pstmt , this .con );150             }151              return null ;152       }153 }

 

代码解释:
一:为什么直接使用 “Class. forName( driver); ”注册驱动
          ①原始注册驱动写法:
   
1   // 注册驱动:告诉 java 去连接哪种数据库2      //DriverManager :驱动管理类,负责管理驱动,创建连接对象3      DriverManager. registerDriver( new com.mysql.jdbc.Driver());
static void
registerDriver ( Driver  driver)
           DriverManager 注册给定驱动程序。
参数:Driver=====来哦子欲你要连接的数据库(如果连接orcale,来自于oracle,如果连接mysql,来自于mysql)
备注:查询 Drive源码
 
 1 public class Driver extends NonRegisteringDriver implements java.sql.Driver { 2      // ~ Static fields/initializers 3      // --------------------------------------------- 4   5      // 6      // Register ourselves with the DriverManager 7      // 8      static { 9         try {10            java.sql.DriverManager. registerDriver( new Driver());11        } catch (SQLException E) {12             throw new RuntimeException( "Can‘t register driver!" );13        }14     }

 

可以看到: java.sql.DriverManager. registerDriver( new Driver());是写在静态代码块中,只要调用Driver这个方法,就会自动运行这段代码,这样会造成“注册两次驱动”
因此,我们可以改成如下版本:
  
          ②改进版
   
1   // 注册驱动:告诉 java去连接哪种数据库2      //DriverManager.registerDriver(new com.mysql.jdbc.Driver());3      new com.mysql.jdbc.Driver();

 

    但是这样写:
      1、静态初始化已经new了一个Driver对象,注册到DriverManager中去,在此再创建一个Driver对象则是完全没有必要的,浪费空间;
      2、不够灵活,如果需要换数据库的话,需要改动代码,此时最好把要变得内容改成字符串的形式,可以由变量读取外部配置文件进行传入(由此可以解释,为什么源代码中要直接写成静态代码块)     
        
           ③最终版   
     
1  // 注册驱动:告诉 java 去连接哪种数据库2       //DriverManager.registerDriver(new com.mysql.jdbc.Driver());3       //new com.mysql.jdbc.Driver();4       Class. forName( "com.mysql.jdbc.Driver");

 

     最终版的好处:
     1、Class.forName()的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段。
     2、既然在静态初始化器中已经进行了注册,所以我们在使用JDBC时只需要Class.forName(XXX.XXX)就可以了
 
二:获得连接对象
 
con = DriverManager.getConnection( url , username , password );
url:连接到某一个具体的数据库
user:数据库的用户名
password:数据库用户名对应的密码
 
url = "jdbc:mysql://localhost:3306/mao"

 

jdbc:     jdbc协议
mysql:     jdbc子协议(表示mysql协议)
 
备注:
     url其他写法一:
url = jdbc:mysql://localhost:3306/mao?userUnicode=true&characterEncoding=utf8//?userUnicode=true&characterEncoding=utf8//加上这个可以解决数据库的乱码问题

 

/*
*1、存数据时:
*数据库在存放项目数据的时候会先用UTF-8格式将数据解码成字节码,然后再将解码后的字节码重新使用GBK编码存*放到数据库中。
*
*2.取数据时:
*     在从数据库中取数据的时候,数据库会先将数据库中的数据按GBK格式解码成字节码,然后再将解码后的字节*码重新按UTF-8格式编码数据,最后再将数据返回给客户端。
*
*/
     url其他写法二:
//如果连接的mysql是在本机,并且mysql的端口是3306,比如:url:"jdbc:mysql://localhost:3306/mao"//那么url可以简写为:url:"jdbc:mysql:///mao"

 

 
三、语句对象
 
   
1   // 创建语句对象Statement2      Statement stmt = con.createStatement();3  4      //创建预设语句对象PreparedStatement5      String sql = "update class set classDesc = ? where id = ?";6      PreparedStatement pstmt = con.prepareStatement(sql);

 

     接下来是重点:
      1:Statement的缺陷:
               ①容易被sql注入
 
sql注入:在页面表单中输入sql的片段。达到串改程序中sql语句。
 
 
正常情况:
 
select * from user where username=‘zhangsan‘ and password = ‘123456‘;
 
 
当sql被注入之后的语句:
 
select * from user where username=‘zhangsan‘ and password = ‘‘ or ‘1‘=‘1‘
                ②效率不高
               Statement每执行一次,sql进行一次编译。即使每次的sql语句格式都一样
     
     2:PreparedStatement的优势
               ①防止sql注入
                    PreparedStatement会对特殊字符进行转译,并通过参数形式设置到语句中,而不再是变量拼凑成sql语句
                 ②预编译功能
                 相同格式的sql语句只被编译一次,后一条格式相同的sql语句运行时,只需改变参数的值即可
 
 
第二版:使用数据库连接池
代码前备注:
     本工具类代码使用c3p0连接池
c3p0-config.xml
 1 <? xml version ="1.0" encoding= "UTF-8" ?> 2 < c3p0-config> 3        <!-- 默认配置,c3p0框架默认加载这段默认配置 --> 4        < default-config> 5              <!-- 配置JDBC 四个基本属性 --> 6              < property name ="driverClass" > com.mysql.jdbc.Driver</ property > 7              < property name ="jdbcUrl" > jdbc:mysql:///数据库名</ property > 8              < property name ="user" > 数据库用户名</ property > 9              < property name ="password" > 数据库密码</ property >10        </ default-config> <!-- This app is massive! -->11 </ c3p0-config>

 

 
 
  1 package cn.service;  2    3 import java.sql.Connection;  4 import java.sql.PreparedStatement;  5 import java.sql.ResultSet;  6 import java.sql.ResultSetMetaData;  7 import java.sql.SQLException;  8 import java.util.ArrayList;  9 import java.util.HashMap; 10 import java.util.List; 11 import java.util.Map; 12   13 import org.junit.Test; 14   15 import com.mchange.v2.c3p0.ComboPooledDataSource; 16 import com.mchange.v2.c3p0.util.TestUtils; 17   18 public class DBService { 19   20     // 三剑客 21     Connection con = null;// 连接对象 22     PreparedStatement pstmt = null;// 语句对象 23     ResultSet rs = null;// 结果集对象 24   25     /** 26      * 获得连接对象 27      * 28      * @return 连接对象 29      * @throws ClassNotFoundException 30      * @throws SQLException 31      */ 32     public Connection getConnection() throws ClassNotFoundException, 33             SQLException { 34   35         // 创建c3p0连接池 36         ComboPooledDataSource ds = new ComboPooledDataSource("itcast"); 37         // 通过连接池对象创建连接 38         con = ds.getConnection(); 39         return con; 40     } 41   42     /** 43      * 关闭三剑客 44      * 45      * @throws SQLException 46      */ 47     public void close(ResultSet rs, PreparedStatement pstmt, Connection con) { 48   49         try { 50             if (rs != null) 51                 rs.close(); 52             if (pstmt != null) 53                 pstmt.close(); 54             if (con != null) 55                 con.close(); 56         } catch (SQLException e) { 57             // TODO: handle exception 58             e.printStackTrace(); 59         } 60     } 61   62     /** 63      * 执行更新 64      * 65      * @param sql 66      *            传入的预设的sql语句 67      * @param params 68      *            问号参数列表 69      * @return 影响行数 70      */ 71     public int execUpdate(String sql, Object[] params) { 72         try { 73             this.getConnection();// 获得连接对象 74             this.pstmt = this.con.prepareStatement(sql);// 获得预设语句对象 75   76             if (params != null) { 77                 // 设置参数列表 78                 for (int i = 0; i < params.length; i++) { 79                     // 因为问号参数的索引是从1开始,所以是i+1,将所有值都转为字符串形式,好让setObject成功运行 80                     this.pstmt.setObject(i + 1, params[i] + ""); 81                 } 82             } 83   84             return this.pstmt.executeUpdate();// 执行更新,并返回影响行数 85   86         } catch (ClassNotFoundException | SQLException e) { 87             // TODO Auto-generated catch block 88             e.printStackTrace(); 89         } finally { 90             this.close(this.rs, this.pstmt, this.con); 91         } 92         return 0; 93     } 94   95     /** 96      * 执行查询 97      * 98      * @param sql 99      *            传入的预设的sql语句100      * @param params101      *            问号参数列表102      * @return 查询后的结果103      */104     public List<Map<String, Object>> execQuery(String sql, Object[] params) {105  106         try {107             this.getConnection();// 获得连接对象108             this.pstmt = this.con.prepareStatement(sql);// 获得预设语句对象109  110             if (params != null) {111                 // 设置参数列表112                 for (int i = 0; i < params.length; i++) {113                     // 因为问号参数的索引是从1开始,所以是i+1,将所有值都转为字符串形式,好让setObject成功运行114                     this.pstmt.setObject(i + 1, params[i] + "");115                 }116             }117  118             // 执行查询119             ResultSet rs = pstmt.executeQuery();120  121             List<Map<String, Object>> al = new ArrayList<Map<String, Object>>();122  123             // 获得结果集元数据(元数据就是描述数据的数据,比如把表的列类型列名等作为数据)124             ResultSetMetaData rsmd = rs.getMetaData();125  126             // 获得列的总数127             int columnCount = rsmd.getColumnCount();128  129             // 遍历结果集130             while (rs.next()) {131                 Map<String, Object> hm = new HashMap<String, Object>();132                 for (int i = 0; i < columnCount; i++) {133                     // 根据列索引取得每一列的列名,索引从1开始134                     String columnName = rsmd.getColumnName(i + 1);135                     // 根据列名获得列值136                     Object columnValue =http://www.mamicode.com/ rs.getObject(columnName);137                     // 将列名作为key,列值作为值,放入hm中,每个hm相当于一条记录138                     hm.put(columnName, columnValue);139                 }140                 // 将每个hm添加到al中,al相当于是整个表,每个hm是里面的一条记录141                 al.add(hm);142             }143  144             return al;145  146         } catch (ClassNotFoundException | SQLException e) {147             // TODO Auto-generated catch block148             e.printStackTrace();149         } finally {150             this.close(this.rs, this.pstmt, this.con);151         }152  153         return null;154     }155  156 }157  

 

 
 
 
     

JDBC-自定义数据库工具类(DBService)