zoukankan      html  css  js  c++  java
  • JDBC

    JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统(DBMS)、通用的SQL数据库存取和操作的公共接口(一组API),定义了用来访问数据库的标准Java类库,使用这个类库可以以一种标准的方法、方便地访问数据库资源

    * JDBC:Java Database Connectivity Java连接数据库技术,一组API  Java可以连接:MySQL、Oracle、SQL Server 、Redis、MangoDB....
    
    * Java为了可以用统一的方式来连接不同的数据库,使得我们JDBC的代码具有可移植性。
    * 如果底层的数据库发生了切换:mysql->oralce,oracle->mysql等
    * 尽量的减少Java代码层面的修改。
    
    * SUN设计JDBC的API时,设计了一组接口和一些类。
    
    * 这些接口由数据库厂商来实现,mysql实现了一套,oracle实现了一套....。因为内部如何来通过sql语句操作这个数据库,只有数据厂商自己是最清楚。
    这里接口,就是一组操作标准,凡是实现了这个接口的数据库,
    就可以由Java程序来连接和操作它,否则就不能来连接和使用它。 * * 换句话说,一个数据库产品想要Java程序来使用它,那么必须实现JDBC这些接口 * 那么这些由数据库厂商来提供的JDBC接口的实现类,构成了“数据库驱动” * JDBC是由SUN公司(现在是Oracle)提供。数据库驱动jar是由各个数据库厂商来提供的。 JDBC的程序开发步骤: 1、要在项目中,引入所使用的数据库的驱动 *1)把mysql-connector-java-5.1.36-bin.jar放到项目的libs文件夹中 *2)把这些类引入到编译路径下classpath * 在jar上,右键->Build Path->Add to Build Path 2把这个数据库的驱动(JDBC接口的实现类)加载到内存中:注册驱动    Class.forName("com.mysql.jdbc.Driver"); 3、获取与数据库的连接 * String url = "jdbc:mysql://localhost:3306/test"; * Connection conn = DriverManager.getConnection(url, "root", "123456");

    * 回忆: * mysql是一个服务器端软件, * 客户端: *1)命令行客户端 * mysql -h 主机IP -P 端口号 -u 用户名 -p * Enter Password:xxx *2)SQLyog等可视化工具 * 依然需要指定ip,端口号,用户名,密码,所连接的数据库 *3)JDBC程序也是一个客户端 * url: jdbc:mysql://localhost:3306/test * jdbc:主协议; mysql:子协议; localhost:主机IP地址; 3306:端口号; test:数据库 * 如果是oracle数据库的url: jdbc:oracle:thin:@localhost:1521:testdb * 如果是sql server数据库的url: jdbc:sqlserver://localhost:1433:DatabaseName=testdb * user:用户名 * password:密码 * 网络编程时学url:协议://主机名:端口号/文件路径 * http://localhost:8080/java1111web/index.html * * 错误的演示: *1)Access denied for user 'root'@'localhost' (using password: YES) * 'root'@'localhost'错误或密码错误 *2)com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure * 检查服务是否开启,网络通信是否正常 *3)java.lang.ClassNotFoundException: com.mysql.jdbc.Driver * 检查驱动是否添加到项目的编译路径下 或者是JDK,tomcat等公共的lib下 * * * * 4、操作数据库,与数据库服务器进行通信 * 给服务器传sql,并介绍结果; 通过Statement,PreparedStatement,ResultSet来操作 * * 5、断开连接 * close() * 说明: *1)当用mysql-connector-java-5.0.8-bin.jar版本的jar时,没有注册驱动的Class.forName(...)的代码报如下错误 * java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/test * * 当用mysql-connector-java-5.1.36-bin.jar版本的jar时,发现没有加Class.forName(...)的代码也可以运行,因为 * mysql-connector-java-5.1.36-bin.jar版本的jar文件夹下有一个services/java.sql.Driver文件 */

    加载驱动,把驱动类(JDBC接口的实现类)加载到内存;

    注册驱动,把驱动类的对象交给DriverManager管理,用于后面创建连接等使用。

    Class.forName()

    因为 Driver 接口的驱动程序类都包含了静态代码块,在这个静态代码块中,会调用 DriverManager.registerDriver() 方法来注册自身的一个实例,所以可以换一种方式来加载驱动。(即只要想办法让驱动类的这段静态代码块执行即可注册驱动类,而要让这段静态代码块执行,只要让该类被类加载器加载即可

    调用 Class 类的静态方法 forName(),向其传递要加载的 JDBC 驱动的类名。

    //通过反射,加载与注册驱动类,解耦合(不直接依赖)Class.forName("com.mysql.jdbc.Driver");
     

    获取数据库链接

    可以通过 DriverManager 类建立到数据库的连接Connection:

    DriverManager 试图从已注册的 JDBC 驱动程序集中选择一个适当的驱动程序。

       public static Connection getConnection(String url)

        public static Connection getConnection(String url,String user, String password)

       public static Connection getConnection(String url,Properties info)其中Properties info通常至少应该包括 "user" 和 "password" 属性

    JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。JDBC URL的标准由三部分组成,各部分间用冒号分隔。

    jdbc:<子协议>:<子名称>

      协议:JDBC URL中的协议总是jdbc

        子协议:子协议用于标识一个数据库驱动程序

       子名称:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息

    例如:

    操作或访问数据库

    数据库连接被用于向数据库服务器发送命令和 SQL 语句,并接受数据库服务器返回的结果。

    其实一个数据库连接就是一个Socket连接。

    在 java.sql 包中有 3 个接口分别定义了对数据库的调用的不同方式:

      Statement:用于执行静态 SQL 语句并返回它所生成结果的对象。

       PrepatedStatement:SQL 语句被预编译并存储在此对象中,然后可以使用此对象多次高效地执行该语句。

       CallableStatement:用于执行 SQL 存储过程

    2、 Statement

    通过调用 Connection 对象的 createStatement() 方法创建该对象,该对象用于执行静态的 SQL 语句,并且返回执行结果。

    Statement 接口中定义了下列方法用于执行 SQL 语句:

      int excuteUpdate(String sql):执行更新操作INSERT、UPDATE、DELETE

      ResultSet excuteQuery(String sql):执行查询操作SELECT

    ResultSet

    通过调用 Statement 对象的 excuteQuery() 方法创建该对象

    ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet 接口由数据库厂商实现

    ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的 next() 方法移动到下一行

    ResultSet 接口的常用方法:

        boolean next()

       getXxx(String columnLabel):columnLabel使用 SQL AS 子句指定的列标签。如果未指定 SQL AS 子句,则标签是列名称

        getXxx(int index) :索引从1开始

    释放资源

    Connection、Statement、ResultSet都是应用程序和数据库服务器的连接资源,使用后一定要关闭,可以在finally中关闭

    Connection的对象调用createStatement( )方法用于执行sql语句

    Statement的增删改查

    * 增删改查
    * 
    * 1、DriverManager:管理数据库驱动,通过它可以获取连接
    * DriverManager.getConnection(...)
    * 2、Connection:代表一个数据库连接对象
    * 可以创建Statement对象
    * 3、Statement:可以执行sql
    * executeUpdate(insert或update或delete或DDL语句)。
    * executeQuery(select语句)
    * 4、 ResultSet:包含了查询的结果
    * 通过遍历结果集来获取查询结果
    * 
    * boolean next():判断是否有下一条记录
    * xx getXxx(第几列)
    * xx getXxx(字段名称或别名)
    @Test
        public void test1() throws Exception{
            Class.forName("com.mysql.jdbc.Driver");
            
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
            //String sql = "select * from t_employee"; //查找
            
            String sql2 = "DELETE FROM t_department WHERE dname = '测试部'"; //删除操作;
            String sql3 = "INSERT INTO t_department (did, dname, description) VALUES (6, '测试部', '负责测试工作')";//添加
            String sql4 = "UPDATE t_department SET dname = '找bug' WHERE dname = '测试部'"; //修改操作
            
            Statement st = conn.createStatement();
         int eu = st.executeUpdate(sql4);//删除,添加,修改 //ResultSet rs = st.executeQuery(sql); //查找 System.out.println(eu); //com.mysql.jdbc.JDBC4ResultSet@7a92922 /*if(rs.next()){ int count = rs.getInt("eid"); //String ename = rs.getString("ename"); //只是获取一个 String ename = rs.getString(2); //等同于rs.getString("ename"); String tel = rs.getString("tel"); System.out.println("姓名:"+ ename + "电话" + tel); }*/ st.close(); conn.close(); }
    Statement的问题:
    1、拼接麻烦
    String sql = "INSERT INTO t_employee(ename,tel,gender,salary,birthday)VALUES('" + name +"','"+tel+"','"+gender+"',"+salary+",'"+birth+"')";
            
     2、SQL的注入
      

    SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令,从而利用系统的 SQL 引擎完成恶意行为的做法。对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement 取代 Statement 就可以了。


    3、处理Blob等二进制类型的数据

    BLOB (binary large object),二进制大对象,BLOB常常是数据库中用来存储二进制文件的字段类型。

    插入BLOB类型的数据必须使用PreparedStatement,因为BLOB类型的数据无法使用字符串拼接写的。

    MySQL的四种BLOB类型(除了在存储的最大信息量上不同外,他们是等同的)

    实际使用中根据需要存入的数据大小定义不同的BLOB类型。
    需要注意的是:如果存储的文件过大,数据库的性能会下降。

     

    3、 PreparedStatement

    可以通过调用 Connection 对象的 preparedStatement(String sql) 方法获取 PreparedStatement 对象

    PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过的 SQL 语句

      PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数. setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值

       ResultSet executeQuery()执行查询,并返回该查询生成的 ResultSet 对象。

        int executeUpdate():执行更新,包括增、删、该

    用法对比:
        Class.forName("com.mysql.jdbc.Driver");
            Connection conn = DriverManager.getConnection("jdbc:mysql://loclhost:3306/test", "root", "123456");
            //使用Statement
            String sql = "insert into student values (null, 'kk', 22)";
            conn.createStatement().executeUpdate(sql); //增、删、改
            ResultSet rs = conn.createStatement().executeQuery(sql); //返回结果是一个ResultSet对象
            if(rs.next()){
                String sname = rs.getString(2); //等同于rs.getString("sname");
                
            }
    
            //使用preparedstatement
            String sql2 = "insert into student values (null, ?, ?)";
            PreparedStatement ps = conn.prepareStatement(sql2);
            ps.setString(1, "kris");
            ps.setInt(2, 22);
            ps.executeUpdate(); //executeUpdate()和executeQuery()用法一样
      @Test
        public void test1() throws Exception{
            
            Scanner input = new Scanner(System.in);
            System.out.println("姓名:");
            String ename = input.next();
    
            System.out.println("电话:");
            String tel = input.next();
    
            System.out.println("薪资:");
            double salary = input.nextDouble();
    
            System.out.println("性别:");
            char gender = input.next().charAt(0);
            
            System.out.println("生日:");
            String birth = input.next();
            
            //1.注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            
            //2.获取连接
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
            
            //3.编写sql语句
            String sql = "insert into t_employee (ename, tel, salary, gender, birthday) values (?,?,?,?,?)";
            //"INSERT INTO t_employee(ename,tel,gender,salary,birthday)VALUES(?,?,?,?,?)";
            //4.创建PreparedStatement对象
            PreparedStatement ps =//5.设置值
            ps.setString(1, ename);
            ps.setString(2, tel);
            ps.setDouble(3, salary);
            ps.setString(4, gender + ""); //ps.setString(5, birthday); //要把String变成date类型
           /* SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date parse = sdf.parse(birth);
            java.sql.Date birthday = new java.sql.Date (parse.getTime());//转成毫秒
            ps.setDate(5, birthday); */

          Date date = new SimpleDateFormat("yyyy-MM-dd").parse(birth);
          //转成Date类型,这里的Date是java.util包里的Date,要把它转成java.sql里的Date
          java.sql.Date birthday = new java.sql.Date(date.getTime()); //long类型
          ps.setDate(5, birthday);

    int len = ps.executeUpdate();
            System.out.println(len);
            
            ps.close();
            conn.close();
            
        }
    
    
      @Test
        public void test2() throws Exception{
            
            Scanner input = new Scanner(System.in);
            System.out.print("姓名:");
            String name = input.nextLine(); //next是遇到空格就停止,所以这里用nextLine();
            Class.forName("com.mysql.jdbc.Driver");
            
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
            String sql = "SELECT eid, ename, tel, gender, salary, birthday FROM t_employee where ename = ?";
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setString(1, name);
            
            ResultSet rs = ps.executeQuery();
            
            while(rs.next()){
                
                int eid = rs.getInt(1);  //不是从0开始的
                String ename = rs.getString(2);
                String tel = rs.getString(3);
                String gender = rs.getString(4);
                double salary = rs.getDouble(5);
                Date birthday = rs.getDate(6);
                System.out.println(eid + "	" + ename + "	" + tel + "	" + gender + "	" + salary + "	" + birthday);
            }
            
            rs.close();
            conn.close();
            
        }
    
    
      @Test //处理Blob类型的数据
        public void test3()throws Exception{
            //1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            
            //2、获取连接
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
            
            //3、编写sql
            FileInputStream fis = new FileInputStream("16.gif");
            String sql = "INSERT INTO user values(?,?,now(),?)";
            
            //4、创建一个Statement对象
            PreparedStatement st = conn.prepareStatement(sql);
            
            st.setString(1, "kris");
            st.setString(2, "1234");
            st.setBlob(3, fis);
    //5、执行sql
            int len = st.executeUpdate();
            
            //6、处理结果
            System.out.println(len>0?"添加成功":"添加失败");
            
            //7、断开连接
            st.close();
            conn.close();
        }
        

     获取自增长的键值

    获取自增长的键值:

    (1)在创建PreparedStatement对象时

    原来:PreparedStatement pst = conn.preparedStatement(sql);

    现在:PreparedStatement pst = conn.prepareStatement(orderInsert,Statement.RETURN_GENERATED_KEYS);

    (2)原来执行更新

    原来:int len = pst.executeUpdate();      

     现在:int len = pst.executeUpdate();

    ResultSet rs = pst.getGeneratedKeys();

      if(rs.next()){

              Object key = rs.getInt(第几列);//获取自增长的键值

    }        

    * 在添加时,如何获取自增长的键值
     * 
     * 1、如何获取PreparedStatement对象
     * PreparedStatement pst = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
    
     * 2、获取自增长的键值
            ResultSet rs = pst.getGeneratedKeys();
            if(rs.next()){
                int did = rs.getInt(1);
                System.out.println("新部门的编号:" + did);
            }

    批处理

    当需要成批插入或者更新记录时。可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率。

    JDBC的批量处理语句包括下面两个方法:

      addBatch():添加需要批量处理的SQL语句或参数

       executeBatch():执行批量处理语句;

    通常我们会遇到两种批量执行SQL语句的情况:

      多条SQL语句的批量处理;

      一个SQL语句的批量传参;

    注意:

    JDBC连接MySQL时,如果要使用批处理功能,请再url中加参数 ?rewriteBatchedStatements=true

    PreparedStatement作批处理插入时使用values(使用value没有效果)

     批处理:
     *1)PreparedStatement的对象.addBatch();先添加到批处理的缓存区
     *2)PreparedStatement的对象.executeBatch();统一处理批处理
      
     * 批处理的功能默认没有打开,我们需要在建立连接时,告知mysql打开这个批处理的功能。
      rewriteBatchedStatements=true
      
       url = "jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true"
      
     * 回忆:
     * http://localhost:8080/java1111/login?username=chai&password=123
      
     * 提示:添加语句时,不要用value用values
    @Test
        public void test1() throws Exception{
            
            Class.forName("com.mysql.jdbc.Driver");
            
            String url = "jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true";
            Connection conn = DriverManager.getConnection(url, "root", "123456");
            
            String sql = "insert into student values (null, ?, ?)";
            
            PreparedStatement ps = conn.prepareStatement(sql);
            
            try {
                for(int i = 1; i < 1000; i++){  //循环设置?的值
                    ps.setString(1, "kk" + i);
                    ps.setInt(2, i);
                    
                    ps.addBatch();// 先添加到批处理的缓存
                }
                
                ps.executeBatch(); //统一处理批处理
                System.out.println("添加成功");
            } catch (Exception e) {
                System.out.println("出错了");
            }
            
            ps.close();
            conn.close();
        }

    事务

    JDBC程序中当一个连接对象被创建时,默认情况下是自动提交事务:每次执行一个 SQL 语句时,如果执行成功,就会向数据库自动提交,而不能回滚。
    JDBC程序中为了让多个 SQL 语句作为一个事务执行:(重点)
            调用 Connection 对象的 setAutoCommit(false); 以取消自动提交事务
            在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务
            在其中某个操作失败或出现异常时,调用 rollback(); 方法回滚事务
            若此时 Connection 没有被关闭, 则需要恢复其自动提交状态 setAutoCommit(true);
    注意:
    如果多个操作,每个操作使用的是自己单独的连接,则无法保证事务。即同一个事务的多个操作必须在同一个连接下
    * JDBC如何实现事务管理。
     * 1、开启事务
     * Connection连接对象.setAutoCommit(false);//开启手动提交模式
     * 
     * 2、执行事务中的Sql
     * 和原来一样
     * 
     * 3、提交或回滚
     * Connection连接对象.commit();
     * Connection连接对象.rollback();
     * 
     * 如果都正确,就提交
     * 如果有问题,就回滚
     * 
     * 补充:因为后面的时候,我们的连接对象可能是从“数据库连接池”中获取,用完之后要放回去,
     * 即这个连接对象是“重复”使用的,就算close(),它也不会断开,而是“放回”连接池。
     * 
     * 4* 在关闭连接之前,恢复连接对象的自动提交模式,Connection连接对象.setAutoCommit(true);
     * Connection连接对象.close()

    JDBC程序中当一个连接对象被创建时,默认情况下是自动提交事务:每次执行一个 SQL 语句时,如果执行成功,就会向数据库自动提交,而不能回滚。

    JDBC程序中为了让多个 SQL 语句作为一个事务执行:(重点)

       调用 Connection 对象的 setAutoCommit(false); 以取消自动提交事务

      在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务

       在其中某个操作失败或出现异常时,调用 rollback(); 方法回滚事务

        若此时 Connection 没有被关闭, 则需要恢复其自动提交状态 setAutoCommit(true);

    注意:

    如果多个操作,每个操作使用的是自己单独的连接,则无法保证事务。即同一个事务的多个操作必须在同一个连接下

      @Test
        public void test1() throws ClassNotFoundException, SQLException{
            
            Class.forName("com.mysql.jdbc.Driver");
            
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
            conn.setAutoCommit(false);
            
            String sql1 = "insert into student values (null, ?, ?)"; //添加
            String sql2 = "update student set sname = ? when sname = ?"; //修改
            
            //创建一个statement对象
            
            PreparedStatement ps1 = null;
            PreparedStatement ps2 = null;
            try {
                ps1 = conn.prepareStatement(sql1);
    
                ps1.setString(1, "kk");
                ps1.setInt(2, 18);
                ps1.executeUpdate();
                
                //修改,可以同时添加同时把它修改了
                ps2 = conn.prepareStatement(sql2);
                ps2.setString(1, "kris");
                ps2.setString(2, "kk");
                ps2.executeUpdate();
                
                conn.commit();
                System.out.println("添加成功");
                System.out.println("修改成功");
            } catch (SQLException e) {
                
                conn.rollback();//如果有异常,就回滚;  有一个sql出错,另外一个就不会插入到数据库中,回滚
                System.out.println("添加失败");
                System.out.println("修改失败");
            }
            
            //断开连接
            ps1.close();
            ps2.close();
            conn.setAutoCommit(true); //需要恢复其自动提交状态 setAutoCommit(true);
            conn.close();
            
        }

    4、数据库连接池

    JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口,该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现

    
     * 数据库连接池:也称为数据源 DataSource
     * 
     * 1、为什么需要数据库连接池?
     *1)如果没有连接池,DB服务器可能会超过最大连接数的极限,会挂掉
     *2)每次建立连接(TCP/IP),它需要“三次握手”来建立连接,比较费事,如果这一次连接SQL比较简单,一下就完成了,那么这次连接的成本就太高了
     *3)甚至有的程序员,可能在编写代码时,用完连接后,忘了关闭了,数据库很快就down了
     * 
     * 为了数据库服务器的性能和安全,我们可以在Java程序与数据库服务器之间建立一个数据库连接池。
     * 
     * 数据库连接池,在一开始先“准备”一下连接(初始化连接数),下次Java程序需要连接时,从这个池中直接获取,因为已经提前连接好了,那么直接拿连接对象,速度非常快,
     * 响应速度更快。
     * 数据库连接池会设置一个“最大连接数”,如果一旦池中的连接数达到“最大连接数”,它会让Java程序等待或等待一段时间异常,这样可以保证数据库服务器不会挂掉,因为
     * Java程序中可以处理 异常。
     * 数据库连接池中的连接是重复使用的,这里要注意一些连接的属性设置,再换回去之前要还原,setAutoCommit(true)等。
     * 
     * 有了数据库连接池,JDBC的程序,修改的“获取连接”的方式而已,其他的没有改。
     * 
     * 原来:
     *1)Class.forName(xxx)
     *2)Connection con = DriverManager.getConnection(...)
     * 
     * 现在:
     * 
     * 
     * 使用数据库连接池的步骤:
     * 1、加入支持的jar
     * (1)把druid-1.1.10.jar加入到libs文件夹中
     * (2)添加到Build Path中
     * 
     * 2、设置数据库连接池的参数
     *1)在代码中setProperty(key,value)
     *2)可以配置文件中配置
     * 
     * 3、建立数据库连接池
     * DataSource ds = DruidDataSourceFactory.createDataSource(pro);
     * 
     * 4、获取连接
     */

    Druid(德鲁伊)数据源

    druid.properties
    url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
    username=root
    password=123456
    driverClassName=com.mysql.jdbc.Driver
    initialSize=10
    maxActive=20
    maxWait=2000
    filters=wall
      public class TestPool {
        public static void main(String[] args) throws Exception{
    
            //为什么必须要在main方法中呢,单独Test不运行
        
            Properties pro = new Properties();
            /*单独放到一个文件中
            pro.setProperty("url", "jdbc:mysql://localhost:3306/test");
            pro.setProperty("username", "root");
            pro.setProperty("password", "123456");
            pro.setProperty("driverClassName", "com.mysql.jdbc.Driver");
            
            pro.setProperty("initialSize", "5");
            pro.setProperty("maxActive", "10");*/(TestPool.class.getClassLoader().getResourceAsStream("druid.properties"));
            DataSource ds =//        System.out.println(conn); //ds.getConnection(); -> com.mysql.jdbc.JDBC4Connection@79b4d0f
            System.out.println(ds);
            
            
            /*for(int i = 1; i <= 15; i++){
                Connection conn = ds.getConnection();
                System.out.println(i + "连接" + conn);
                conn.close();
            }*/
            
            
            for(int i = 1; i <= 15; i++ ){
                new Thread("第" + i + "个"){
                    public void run(){
                        try {
                            Connection conn = ds.getConnection();
                            System.out.println(getName() + "连接" + conn);
                            
                                Thread.sleep(1000);
                                conn.close(); //这里没有conn.close(),相当于没有还回连接池,访问结束后放到连接池
                        } catch (Exception e) {
                            
                            e.printStackTrace();
                        }
                    }
                }.start();
                
            }
            System.out.println(ds);
        }
    }

    根据DruidDataSour自定义一个JDBCTools工具类

    package com.atguigu.pool;
    
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Properties;
    
    import javax.sql.DataSource;
    
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    
    /** JDBC工具类提供的方法:
    * (1)获取连接
    * (2)释放资源
    * (3)初始化信息
    * 
    * 注意:整个项目连接池只要一个就够了*/
    
    
    public class JDBCTools {
    
        private static DataSource ds; //import javax.sql.DataSource
        
        static{
            
            Properties pro = new Properties();
            try {
                pro.load(JDBCTools.class.getClassLoader().getResourceAsStream("druid.properties"));
                ds = DruidDataSourceFactory.createDataSource(pro);
            } catch (Exception e) {
            
                e.printStackTrace();
            }
        }
        //获取连接的信息
        public static DataSource getDs(){
            
            return ds;
        }
        //获取连接的方法 如:com.mysql.jdbc.JDBC4Connection@79b4d0f
        public static Connection getConnection() throws SQLException{
            return ds.getConnection();
        }
        
        //关闭连接的方法,抛出异常
        public static void free(Connection conn) throws SQLException{
            
            if(conn != null){
                conn.close();
            }
        }
        
        //关闭连接方法,不会抛出异常; 带Quietly的就是没有throws异常;  try, catch了
        public static void freeQuietly(Connection conn){
            if(conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        //关闭Statement对象
        //这个方法既可以接收Statement的对象,也可以接收它的子接口PreparedStatement的对象
        public static void freeQuietly(Statement st){
            if(st != null){
                try {
                    st.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            
        }
        //关闭ResuleSet对象
        
        public static void freeQuietly(ResultSet rs){
            if(rs != null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            
        }
        //关闭ResultSet、Statement、Connection对象
        public static void freeQuietly(ResultSet rs, Statement st, Connection conn){
            freeQuietly(rs);
            freeQuietly(st);
            freeQuietly(conn);
        }
    }
    View Code
      public static void main(String[] args) throws SQLException {
            Connection conn = JDBCTools.getConnection(); //获取连接
            
            String sql = "insert into student values (null, ?, ?)";
            
            PreparedStatement ps = conn.prepareStatement(sql);
            
            ps.setString(1, "kris");
            ps.setInt(2, 22);
            int len = ps.executeUpdate();
            System.out.println(len);
            JDBCTools.freeQuietly(null, ps, conn); //关闭操作
            
        }

    5、Apache--JDBC工具类

    * commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,
     * 学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。
     * 使用步骤
     * 1、引入jar
     * (1)把commons-dbutilsxx.jar放到libs中
     * (2)添加到Build Path中
     * 
     * QueryRunner类:
     * 该类封装了SQL的执行,是线程安全的。
    (1)可以实现增、删、改、查、批处理、
    (2)考虑了事务处理需要共用Connection。
    (3)该类最主要的就是简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。
     *
     *1)通用的增、删、改
     * QueryRunner对象.update(sql,参数)
     *2)通用的添加
     * QueryRunner对象.insert(sql,自增长键值的结果处理器,参数)
     *3)通用的查询
     * QueryRunner对象.query(sql,结果集处理器对象,参数)
     *4)通用的批处理
     * QueryRunner对象.batch(sql,参数)
     * QueryRunner对象.insertBatch(sql,自增长键值的结果处理器,参数)
     * 
     * 
     * ResultSetHandler<T> rsh:结果集处理器,是一个接口,它有很多实现类
     * (1)ScalarHandler:单个值的处理器;  例如:查询总记录数,返回自增长键值等
     * (2)BeanListHandler:多个JavaBean对象的处理器
     * (3)BeanHandler:一个JavaBean对象的处理器
     * (4)MapListHandler
     * ....
     * 
     * 回忆:
     * InvocationHandler:代理工作处理器
     * 
     * 
     * 特别说明:
     * 当使用QueryRunner时,如果多个sql语句要考虑事务,那么就需要单独传Connection对象,并且要
     *1)conn.setAutoCommit(false);//设置为手动提交模式
     *2)qr.update/query等方法(conn, ...);
     *3)conn.commit();或conn.rollback();
     *4)conn.setAutoCommit(true);
     *5)关闭连接
     */
      public class TestDBUtils {
    
        @Test //添加操作
        public void test1() throws SQLException{
            QueryRunner qr = new QueryRunner(JDBCTools.getDs()); //获取操作池
            int len = qr.update("insert into student values (null, ?, ?)", "静", 22);
            System.out.println(len);
            
            String sql = "insert into student values (null, ?, ?)";
            Long insert = qr.insert(sql, new ScalarHandler<Long>(), "kk", "33");
            System.out.println(insert);  //返回自增长键值;
        
        }
        
        @Test //查询操作
        public void test2() throws SQLException{
            QueryRunner qr = new QueryRunner(JDBCTools.getDs());
            //"select * from e_employee" 也可以
            String sql = "SELECT eid,ename,tel,gender,salary,commission_pct AS commissionPct,birthday,hiredate,job_id AS jobId,email,`mid`,address,native_place AS nativePlace, did  FROM t_employee";
            System.out.println(sql);
            List<Employee> query = qr.query(sql, new BeanListHandler<Employee>(Employee.class));
            for (Employee employee : query) {
                System.out.println(employee);
            }
            
            
            String sql1 = "SELECT eid,ename,tel,gender,salary,commission_pct AS commissionPct,birthday,hiredate,job_id AS jobId,email,`mid`,address,native_place AS nativePlace, did  FROM t_employee where eid = ?";
            Employee emp = qr.query(sql1, new BeanHandler<Employee>(Employee.class), 2);
            System.out.println(emp);
            
            //假设要计算总页数 = 总记录数 / 每页显示的记录数 ;要考虑是否整除
            //每页显示20行
            String sql2 = "select count(*) from t_employee";
            Long count = qr.query(sql2, new ScalarHandler<Long>());
            if(count % 20 == 0){
                System.out.println("总页数:" + count/20);
                
            }else{
                System.out.println("总页数" + (count/20 + 1));
            }
        }
        
        @Test //QueryRunner,封装到map、List中
        public void test3() throws SQLException{
            QueryRunner qr = new QueryRunner(JDBCTools.getDs());
            String sql = "select did, avg(salary) from t_employee group by did"; 
            List<Map<String, Object>> list = qr.query(sql, new MapListHandler());
            //每一行是一个map,很多行是一个List
            for (Map<String, Object> map : list) {
                Set<Entry<String, Object>> entrySet = map.entrySet();
                for (Entry<String, Object> entry : entrySet) {
                    System.out.println(entry);
                }
            }
        }
        
        @Test //事务
        public void test4() throws SQLException{
            String sql1 = "insert into student values (null, ?, ?)";
            String sql2 = "update student set sname = ? where sname = ?";
            QueryRunner qr = new QueryRunner(JDBCTools.getDs());
            Connection conn = JDBCTools.getConnection();
            
            conn.setAutoCommit(false);
            
            try {
                qr.update(conn, sql1, "kk", 22);
                qr.update(conn, sql2, "kris", "kk");
                
                System.out.println("添加修改成功");
                conn.commit();
            } catch (Exception e) {
                
                conn.rollback();
                System.out.println("失败");
            }
            conn.setAutoCommit(true);
            JDBCTools.free(conn);
            
        }
        
    }

    表与JavaBeam

    通过给列取别名的方式,来告知数据库的列名与其对应实体的属性名

    6、DAO和增删改查通用方法

    DAO:Data Access Object访问数据信息的类和接口,包括了对数据的CRUD(Create、Retrival、Update、Delete),而不包含任何业务相关的信息

    作用:为了实现功能的模块化,更有利于代码的维护和升级。

    通用的增删改查方法

    public class BasicDAO {
        //通用的增删改的方法
        //Object... args:给sql中的?设置值用的,有个实参,就说明有几个?需要设置
        public static int update(String sql, Object...args) throws SQLException{
            Connection conn = JDBCTools.getConnection();
            PreparedStatement ps = conn.prepareStatement(sql);
            
            if(args != null && args.length > 0){
                for (int i = 0; i < args.length; i++) {
                    ps.setObject(i+1, args[i]);    
                }
            }
            int len = ps.executeUpdate();
            JDBCTools.freeQuietly(null, ps, conn);    
            return len;
        }
        
        //通用的查询多个对象的操作
        public static <T> ArrayList<T> query(Class<T> clazz, String sql, Object... args ) throws SQLException, Exception, Exception{
            
            Connection conn = JDBCTools.getConnection();
            
            PreparedStatement ps = conn.prepareStatement(sql);
            if(args != null && args.length > 0){
                for (int i = 0; i < args.length; i++) {
                    ps.setObject(i+1, args[i]);
                }
            }
            ResultSet rs = ps.executeQuery();
            
            ArrayList<T> list = new ArrayList<>();
            //在我们的ResultSet对象中,不仅仅有数据(记录),还有关于“字段名”等信息
            //获取结果集的元数据
            ResultSetMetaData metaData = rs.getMetaData();
            int count = metaData.getColumnCount();  //一共查询了几列;
            while(rs.next()){
                T t = clazz.newInstance(); //一行代表一个对象
                //动态设置属性值t
                for(int i = 0; i < count; i++){ //获取第几列的字段名
                    String fileName = metaData.getColumnLabel(i+1); //获取第几列的字段名
                    Field f = clazz.getDeclaredField(fileName);
                    f.setAccessible(true);
                    f.set(t, rs.getObject(fileName));
                    
                }
                list.add(t);
            }
            
            JDBCTools.freeQuietly(rs, ps, conn);
            return list;
        }
        
    public class TestBaseDAO {
        @Test //添加
        public void test1() throws SQLException{
            String sql = "INSERT INTO student VALUES (null, ?, ?)";
            
            int len = BasicDAO.update(sql, "kris",22);
            
        }
        @Test //修改
        public void test2() throws SQLException{
            String sql = "update student set sname = ? where id = ?";
            BasicDAO.update(sql, "静静", 17);
            System.out.println("修改成功"); 
        }
        
        @Test //删除
        public void test3(){ 
            String sql = "delete from student where id > ? ";
            try {
                BasicDAO.update(sql, 15);
                System.out.println("删除成功");
            } catch (SQLException e) {
                System.out.println("删除失败");
                e.printStackTrace();
            }
        }
        
        @Test //查询操作
        public void test4() throws SQLException, Exception{
            //String sql = "select * from t_department";
            //String sql2 = "select * from t_employee"; 
                //Employee类中的属性名:commissionPct,而我们的数据库中的字段名:commission_pct
            //ArrayList<Department> list = BasicDAO.query(Department.class, sql);
            
            //如果表中的字段名与类的属性名不一致,可以通过取别名的方式来解决
            //如果报java.lang.IllegalArgumentException: Can not set double field com.atguigu.bean.Employee.commissionPct to null value
            //原因是数据库中的所有类型的字段都可能是null值,而Java中的基本数据类型是不能用null值,所以可以把基本数据类型修改为它的包装类
            
            String sql2 = "SELECT eid,ename,tel,gender,salary,commission_pct AS commissionPct,birthday,hiredate,job_id AS jobId,email,`mid`,address,native_place AS nativePlace, did  FROM t_employee";
            ArrayList<Employee> list2 = BasicDAO.query(Employee.class, sql2);
            for (Employee depart: list2) {
                System.out.println(depart);
            }
            
        }
        
    public class BasicDAOImpl {
    
        private QueryRunner qr = new QueryRunner();
    
        //通用的增删改查
        public int update(String sql, Object... params) throws SQLException{
            
            Connection conn = JDBCTools.getConnection();
            int len = 0;
            try {
                len = qr.update(conn, sql, params);
            } catch (SQLException e) {
                
                e.printStackTrace();
            }
            return len;
        }
        
        //获取一个对象
        public <T> T getBean(Class<T> clazz, String sql, Object... params) throws SQLException{
            
            Connection conn = JDBCTools.getConnection();
            
            T t = null;
            try {
                t = qr.query(conn, sql, new BeanHandler<>(clazz), params);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return t;
            
        }
        //获取所有的对象
        public <T> List<T> getBeanList(Class<T> clazz, String sql, Object params) throws SQLException{
            Connection conn = JDBCTools.getConnection();
            List<T> list = null;
            try {
                list = qr.query(conn, sql, new BeanListHandler<T>(clazz), params);
            } catch (SQLException e) {
                e.printStackTrace();
            }
            
            return list;
            
            
        }
        //获取一个单一值的方法,专门执行像select * from...
        public Object getSingleValue(String sql, Object...params) throws SQLException{
            
            Connection conn = JDBCTools.getConnection();
            
            Object value = null;
            try {
                value = qr.query(conn, sql, new ScalarHandler(), params);
            } catch (Exception e) {
                e.printStackTrace();
            }
            
            
            return value;
            
        }
        /** 进行批处理的方法
         * 关于二维数组Object[][] params
         *         二维数组的第一维是sql语句要执行的次数
         *         二维数组的第二维就是每条sql语句中要填充的占位符*/
    
        public void batchUpdate(String sql, Object[][] params) throws SQLException{
            
            Connection conn = JDBCTools.getConnection();
            try {
                qr.batch(conn,sql, params);
            } catch (Exception e) {
                
                e.printStackTrace();
            }
        
        }
        
    View Code
  • 相关阅读:
    Appium+python自动化3-启动淘宝app
    Appium+python自动化2-环境搭建(下)
    Appium+python自动化1-环境搭建(上)
    postman提取返回值
    Android studio(AS)的下载和安装
    monkey的安装和配置
    配置自己的CentOS7服务器
    mac-homebrew安装太慢
    vue.js中祖孙通信之provide与inject
    nginx location 以及 proxy_pass 的几种情况 以/结尾的问题
  • 原文地址:https://www.cnblogs.com/shengyang17/p/10159655.html
Copyright © 2011-2022 走看看