zoukankan      html  css  js  c++  java
  • 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 params
    100        *            问号参数列表
    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 = 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 block
    147                   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
               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   // 创建语句对象Statement
    2      Statement stmt = con.createStatement();
    3  
    4      //创建预设语句对象PreparedStatement
    5      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 params
    101      *            问号参数列表
    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 = 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 block
    148             e.printStackTrace();
    149         } finally {
    150             this.close(this.rs, this.pstmt, this.con);
    151         }
    152  
    153         return null;
    154     }
    155  
    156 }
    157  
     
     
     
         
  • 相关阅读:
    关于CSS网页布局id与class命名的问题
    JavaScript弹出层技术探讨文章收藏
    Jquery实现弹窗
    Asp.net中时间格式化的几种方法
    新建商品时,导入商品,随机文件名称的形成
    Linux问题:Crontab 执行shell脚本时相对路径的问题,不能识别。
    linux环境php扩展pdo_mysq安装l错误
    测试php代码的运行时间
    Apache的工作方式
    PHP时间处理
  • 原文地址:https://www.cnblogs.com/CQY1183344265/p/5854429.html
Copyright © 2011-2022 走看看