Java程序设计(高级及专题)- JDBC

2022-08-04 16:24:43 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

核心API

JDBC接口核心的API

代码语言:javascript复制
            java.sql.*   和  javax.sql.*
        |- Driver接口: 表示java驱动程序接口。所有的具体的数据库厂商要来实现此接口。
            |- connect(url, properties):  连接数据库的方法。
                    url: 连接数据库的URL 
                        URL语法: jdbc协议:数据库子协议://主机:端口/数据库
                        user: 数据库的用户名
                        password: 数据库用户密码
        |- DriverManager类: 驱动管理器类,用于管理所有注册的驱动程序
            |-registerDriver(driver)  : 注册驱动类对象
            |-Connection getConnection(url,user,password);  获取连接对象

        |- Connection接口: 表示java程序和数据库的连接对象。
                |- Statement createStatement() : 创建Statement对象
                |- PreparedStatement prepareStatement(String sql)  创建PreparedStatement对象
                |- CallableStatement prepareCall(String sql) 创建CallableStatement对象

        |- Statement接口: 用于执行静态的sql语句
                |- int executeUpdate(String sql)  : 执行静态的更新sql语句(DDL,DML)
                |- ResultSet executeQuery(String sql)  :执行的静态的查询sql语句(DQL)

            |-PreparedStatement接口:用于执行预编译sql语句
                    |- int executeUpdate() : 执行预编译的更新sql语句(DDL,DML)
                    |-ResultSet executeQuery()  : 执行预编译的查询sql语句(DQL)

                |-CallableStatement接口:用于执行存储过程的sql语句(call xxx)
                        |-ResultSet executeQuery()  : 调用存储过程的方法


        |- ResultSet接口:用于封装查询出来的数据
                |- boolean next() : 将光标移动到下一行
                |-getXX() : 获取列的值

API常用方法

1、Connection 接口

方法名称

功能描述

getMetaData()

该方法用于返回数据库的元数据的 DatabaseMetaData 对象

createStatement

用于创建一个 Statement 对象来将 SQL 语句发送到数据库

preparedStatement(String sql)

用于创建一个 PreparedStatement 对象来将参数化的SQL语句发送到数据库

prepareCall(String sql)

用于创建一个 CallableStatement 对象来调用数据库存储过程

2、Statement 接口

方法名称

功能描述

boolean execute(String sql)

用于执行各种 SQL 语句,该方法返回一个 boolean 类型的值。如果为 true,表示所执行的 SQL 语句具备查询结果,可通过 Statement 的getResultSet() 方法查询结果

int executeUpdate(String sql)

用于执行 SQL 中的 insert、update 和 delete 语句,该方法返回一个 int 类型的值,表示影响数据库中的行数

ResultSet executeQuery(String sql)

用于执行 SQL 中的 select 语句(查询,遍历),该方法返回一个表示查询结果的 ResultSet 对象

execute是executeQuery和executeUpdate的综合. 通常我们没有必要使用execute方法来执行SQL语句,而是使用 executeQuery 或 executeUpdate 更适合。

  • execute、executeUpdate、executeQuery三者的区别: executeUpdate() 这是 PreparedStatement 接口中的方法 executeUpdate(String sql) 这是 PreparedStatement 从父接口 Statement 中继承过来的方法 executeUpdate() 中执行 SQL 语句需要在创建 PerparedStatement 时通过 Connection 的

prepareStatement(String sql) 方法中写出,因为 PerparedStatement 中的 SQL 语句数据库需要进行预编译和缓存,因此要在创建 PerparedStatement 对象时给出 SQL 语句。 而 executeUpdate(String sql) 是 Statement 中的方法,参数中的 SQL 语句只是提交给数据库去执行,并不需要预编译。

3、PreparedStatement 接口

方法名称

功能描述

executeUpdate()

在此 PreparedStatement 对象中执行 SQL 语句,该语句必须是一个 DML 语句,或者无返回内容的 SQL 语句,比如 DDL 语句

executeQuery()

在此 PreparedStatement 对象中执行 SQL 语句,该方法返回的是 ResultSet 对象

setInt(int parameterIndex, int x)

将指定的参数设置为 int 值

setFloat(int parameterIndex, float x)

将指定的参数设置为 Float 值

setString(int parameterIndex, String x)

将指定参数设置的给定的 Date 值

setDate(int parameterIndex, Date x)

将指定参数设置给定的 Date 值

addBatch()

将一组参数添加到此 PreparedStatement 对象的批处理命令中

setCharacterStream(parameterIndex, reader, length)

将指定的输入流写入数据库的文本字段

setBinaryStream(parameterIndex, x, length)

将二进制的输入流数据写入到二进制的字段中

DML 语句:SELECT、UPDATE、INSERT、DELETE DLL 语句:CREATE DROP ALERT 具体参考: SQL的单表查询 4、ResultSet 接口

方法名称

功能描述

getString(int columnIndex)

用于获取 指定字段的 String 类型的值,参数 columnIndex 代表字段的索引

getString(String columnName)

用于获取指定字段的 String 类型的值,参数 columnIndex 代表字段名称

getInt(int columnIndex)

用于获取指定字段的 int 类型的值,参数 columnIndex 代表字段的索引

getInt(String columnName)

用于获取指定字段的 int 类型的值,参数 columnIndex 代表字段名称

getDate(int columnIndex)

用于获取指定字段的 Date类型的值,参数 columnIndex 代表字段索引

getDate(String columnName)

用于获取指定字段的 Date类型的值,参数 columnIndex 代表字段名称

next()

将游标从当前位置移到下一位置

absolute(int row)

将游标移到此 ResultSet 对象的指定行

afterLast()

将游标移动到此 ResultSet 对象的末尾,即最后一行之后

beforeFirst()

将游标移动到此 ResultSet 对象开头,即第一行之前

previous()

将游标移动到此 ResultSet 对象的上一行

last()

将游标移动到此 Result 对象的最后一行

具体步骤

java代码

代码语言:javascript复制
package com.tool;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

public class DataBase01 { 
   
    /** * JDBC:文件驱动 * 1.引入jar包 ojdbc.jar * 在工程的目录下创建一个lib文件夹用来存放jar包 在ojdbc14.jar这个文件上右键==>build Path==> * add to build Path * 2.编写DB类 a.加载驱动 * b.设置参数url user pwd * c.连接数据库 * d.编写sql语句 * e.编译sql语句 * f.如果存在条件要设置条件后执行sql语句,如果没有条件直接执行sql语句 * g.如果是增删改 那么sql执行结束 如果是查询,遍历结果集 * f.关闭数据库 * @throws IOException * * @throws ClassNotFoundException * @throws SQLException */
    public static void main(String[] args) throws IOException { 
   
        // 创建实例并声明变量
        
        // 连接对象
        Connection con = null;
        // 预处理对象(Statement:执行器)
        PreparedStatement ps = null;
        // 结果集
        ResultSet rs = null;
        
        String driver,url, userName, pwd;
        //44-56行是为了减少硬编码 
        //获取当前类的加载器
        ClassLoader cl = DataBase01.class.getClassLoader();
        //通过该加载器得到一个输入流
        InputStream is = cl.getResourceAsStream("database.properties");
        System.out.println(is);
        //通过输入创建一个Properties对象
        Properties pt = new Properties();
        //从输入流中读取属性列表(键和元素对)
        pt.load(is);
        //获取其中的值
         driver=pt.getProperty("database.driver");
         url=pt.getProperty("database.url");
         userName=pt.getProperty("database.username");
         pwd=pt.getProperty("database.password");
         //打印连接信息
         System.out.println(driver);
         System.out.println(url);
         System.out.println(userName);
         System.out.println(pwd);
        try { 
   
            // 注册驱动
            Class.forName(driver);
            // 获取连接
            con = DriverManager.getConnection(url, userName, pwd);
            // 获得预处理对象
            ps = con.prepareStatement("select * from dvd where d_id in (?,?,?,?,?)");
            // 设置传入字段
            ps.setInt(1, 1);
            ps.setInt(2, 2);
            ps.setInt(3, 3);
            ps.setInt(4, 4);
            ps.setInt(5, 5);
            // ps.executeUpdate();返回执行完成后有多少行数据受到影响 ,在用增删改的时候,这个方法可以判断语句是否执行成功
            //在用增删改的时候,没有结果集,也你需要声明结果集
            rs = ps.executeQuery();// 接受结果集并编译sql语句
            // 处理结果集
            while (rs.next()) { 
   
                System.out.println(rs.getInt("d_id")   "---"   rs.getString("d_name"));
            }
        } catch (Exception e) { 
   
            e.printStackTrace();
        } finally { 
   
            // 关闭数据库、释放资源
            try { 
   
                rs.close();
                ps.close();
                con.close();
            } catch (SQLException e) { 
   
                System.out.println("释放资源失败");
                e.printStackTrace();
            }

        }

    }

}

database.properties文件

代码语言:javascript复制
database.driver=com.mysql.jdbc.Driver
database.url=jdbc:mysql://localhost:3306/ahxy?useUnicode=true&characterEncoding=UTF-8
database.username=root
database.password=root

导入jar包: mysql-connector-java-5.1.46.jar

JDBC事务处理:

我们已经知道,事务的概念即:所有的操作要么同时成功,要么同时失败。在MySQL中提供了Commit、Rollback命令进行事务的提交与回滚。实际上在JDBC中也存在事务处理,如果要想进行事务处理的话,则必须按照以下的步骤完成。

JDBC中事务处理的步骤:

1、要取消掉JDBC的自动提交:void setAutoCommit(boolean autoCommit) 2、执行各个SQL语句,加入到批处理之中 3、如果所有语句执行成功,则提交事务 commit();如果出现了错误,则回滚:rollback()

核心代码:

代码语言:javascript复制
conn.setAutoCommit(false); // 取消自动提交
把SQL语句加入批处理
stmt.addBatch(…) ()
stmt.addBatch(…)
//执行批处理操作
stmt.executeBatch();
conn.commit(); // 提交事务

//如果发生错误
conn.rollback();

代码举例: 首先在sql中创建一个空的数据库,现在在java中,使用PreparedStatement插入数据并修改数据。 正常情况下,代码应该这样写:

代码语言:javascript复制
package com.vae.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class JDBCtest { 
   


    //数据库连接地址
    public final static String URL = "jdbc:mysql://localhost:3306/JDBCdb";
    //用户名
    public final static String USERNAME = "root";
    //密码
    public final static String PASSWORD = "smyh";
    //驱动类
    public final static String DRIVER = "com.mysql.jdbc.Driver";
    
    
    public static void main(String[] args) { 
   
        // TODO Auto-generated method stub
        //insert(p);
        //update(p);
        //delete(3);
        insertAndQuery();
    }
    
    
    //方法:使用PreparedStatement插入数据、更新数据
    public static void insertAndQuery(){ 
   
        Connection conn = null;
        try { 
   
            Class.forName(DRIVER);
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            String sql1 = "insert into user(name,pwd)values(?,?)";
            String sql2 = "update user set pwd=? where name=?";
            PreparedStatement ps = conn.prepareStatement(sql1);
            ps.setString(1, "smyhvae");
            ps.setString(2, "007");            
            ps.executeUpdate();
            
            ps = conn.prepareStatement(sql2);
            ps.setString(1, "008");
            ps.setString(2, "smyh");            
            ps.executeUpdate();            
            
            ps.close();
            conn.close();            
            
        } catch (ClassNotFoundException e) { 
   
            e.printStackTrace();
        } catch (SQLException e) { 
   
            e.printStackTrace();
        }
    }
    
}

事务处理: 现在我们把上面的插入操作和修改操作变成一个事务,就要增加一部分代码了。 修改上方的insertAndQuery()方法里面的代码:

代码语言:javascript复制
//方法:使用PreparedStatement插入数据、更新数据
    public static void insertAndQuery(){ 
   
        Connection conn = null;
        try { 
   
            Class.forName(DRIVER);
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            conn.setAutoCommit(false);//设置为手动提交事务
            String sql1 = "insert into user(name,pwd)values(?,?)";
            String sql2 = "update user set pwd=? where name=?";
            PreparedStatement ps = conn.prepareStatement(sql1);
            ps.setString(1, "smyhvae");
            ps.setString(2, "007");            
            ps.executeUpdate();
            
            ps = conn.prepareStatement(sql2);
            ps.setString(1, "008");
            ps.setString(2, "smyh");            
            ps.executeUpdate();            
            conn.commit(); //如果所有sql语句成功,则提交事务
            ps.close();
            conn.close();
            
        } catch (ClassNotFoundException e) { 
   
            e.printStackTrace();
        } catch (SQLException e) { 
   
            e.printStackTrace();
            try { 
   
                conn.rollback();//只要有一个sql语句出现错误,则将事务回滚 
            } catch (SQLException e1) { 
   
                e1.printStackTrace();
            }
        }
        
    }

核心代码是第07行、19行、28行。这三行代码就完成了事务处理的操作。两个sql语句中,只要有一个语句出现错误,程序将无法运行,说明事务提交失败,且报错如下:

Java中事务的提交与回滚

简单的来说:当我们到银行给别人转账的时候,你的钱减少和别人的钱增加这两者就是一个事务,一个事务如果里面任何一步出现了差错的话,这个事务就不能执行成功.不然你转账你的钱少了,别人的钱却没有相应的增加,这就世界混乱了哈.这时候可以使用事务的回滚来处理,一旦事务出现差错,不保存上面的操作,这时候你的钱没有少,别人的钱也没有增加,这是一次失败的事务.但是却是避免了世界混乱哈. 例如:

代码语言:javascript复制
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class RollBack_test { 
   
    /** * @param args */
    public static void main(String[] args) { 
   
       Connection conn = null;
       Statement stmt = null;
       try { 
   
           // 动态导入数据库的驱动
           Class.forName("com.mysql.jdbc.Driver");
 
           // 获取数据库链接
           conn = DriverManager.getConnection(
                  "jdbc:mysql://localhost:3306/employee", "root", "wang314159");
          
           // 开启事务
           //不把其设置为true之前都是一个当作一个事务来处理
           conn.setAutoCommit( false );
 
           // 创造SQL语句
           String sql = "INSERT INTO myrollback ( name,age,address,school ) VALUES ( 'test', 22,'大立即','hdu' )";
           
           String sql2 = "INSERT INTO myrollback ( name,age,address,school ) VALUES ( 'test_Name', '33','大立即','hdu' ,'test')";
           // 执行SQL语句
           stmt = conn.createStatement();
           stmt.executeUpdate(sql);
           stmt.executeUpdate(sql2);
           // 提交事务
           conn.commit();
          
           System.out.println( "OK!" );
       } catch (Exception e) { 
   
           e.printStackTrace();
           System.out.println("有错误!");
          
           try { 
   
          // 回滚事务
          //撤销上面对事务的所有操作哈!
              conn.rollback();
           } catch ( Exception e2 ) { 
   }
       } finally { 
   
           // 关闭Statement
           try { 
   
              stmt.close();
           } catch (Exception e) { 
   }
           // 关闭Connection
           try { 
   
              conn.close();
           } catch (Exception e) { 
   }
       }
    }
}
  • 上面就是一个简单的事务回滚的例子,一共有两个sql语句,在执行之前一定要有 conn.setAutoCommit( false );这句话就是设置不自动提交,如果自动提交就是相当于一个sql语句就是一个事务.设置之后对应的数据库中只有四个字段,有意在第二个的sql语句中多给一个字段,这是有错误,但是第一句是没有错误的,那么执行发现出现错误,两条sql语句在数据库中都没有执行.
  • 如果 conn.setAutoCommit( true )那么就是设置自动提交,第一句能够完成提交,数据库也会有相应的记录,但是第二句就会报错哈.
  • 总结:事务回滚之前要 conn.setAutoCommit( false );这样吧下面的语句相当于一个事务,一旦出现错误就会撤销所有操作.

连接池

  • 原因: 传统方式创建和销毁连接都需要消耗系统资源 传统方式创建和销毁连接都需要消耗时间
  • 目的: 为了复用连接,代替传统的频繁占用系统资源和耗费时间的方式 便于管理连接,可以规定最大的连接数(控制应用服务器对数据库的并发访问)
  • 实现的基本思想: 在要使用连接对象之前先创建好规定数量(根据服务器内存的承载能力制定)的连接对象存到放连接池(实现池子的方式一般是用链表结构的集合来实现)中,当应用服务器需要连接对象的时候就从连接池中获取,用完该连接对象时归还连接对象到连接池中。当应用服务器需要连接对象而当前池子中没有连接对象可取时,就让其先等待,如果等待超时还没有回获取到连接对象,就新建一个连接对象给服务器让其使用,用完后销毁该创建的对象。 代码:
代码语言:javascript复制
public class PersonalConnectionPool { 
   

    /** * 用户名 */
    private static String user;
    /** * 密码 */
    private static String password;
    /** * 连接数据库的URL */
    private static String url;


    /** * 连接池 * 规定最大连接数为3 */
    private static LinkedList<Connection> pool;

    /** * 从属性文件中加载数据库驱动,初始化连接池 */
    static{ 
   
        try { 
   
            Properties properties = new Properties();
            pool = new LinkedList<Connection>();
            Class.forName("com.mysql.jdbc.Driver");
            ClassLoader classLoader = PersonalConnectionPool.class.getClassLoader();
            InputStream iStream = classLoader.getResourceAsStream("mysqlCongfig.properties");
            properties.load(iStream);
            user = properties.getProperty("user");
            password = properties.getProperty("password");
            url = properties.getProperty("url");
            //创建三个连接对象(包装类对象)放到池子中
            for (int i = 0; i < 3; i  ) { 
   
                Connection connection = DriverManager.getConnection(url, user, password);
                Connection connectionWrapper = new ConnectionWapper(connection,pool);
                pool.add(connectionWrapper);
            }

        } catch (Exception e) { 
   
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }


    /** * @throws SQLException * @method 向外提供连接对象 */
    public Connection getConnection() throws SQLException { 
   

        Connection connection;
        if(pool.size()>0)
        { 
   
            connection = pool.removeFirst();
        }
        else
        { 
   
            //等待超时,返回一个新创建的对象
            connection = DriverManager.getConnection(url, user, password);
        }
        System.out.println("当前池子中有 " pool.size() " 个对象");
        return connection;
    }
    /** * 归还连接对象 * 直接简化在包装类的close方法中 */
}

池子中用了到了一个包装类,包装了通过DriverManager.getConnection获取到的Connection的实现类对象,该包装也实现了Connection接口,重写了案例中需要的方法。该类结构如下:

代码语言:javascript复制
public class ConnectionWapper implements Connection { 
   

    /** * Connection接口的实现类对象的引用 */
    private Connection connection;
    /** * 存放连接包装对象的池子的引用 * */
    private LinkedList<Connection> pool;
    /** * * @param connection 的实现类对象 */
    public ConnectionWapper(Connection connection, LinkedList<Connection> pool) { 
   
        super();
        this.connection = connection;
        this.pool = pool;
    }
    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException { 
   
        PreparedStatement prepareStatement = connection.prepareStatement(sql);
        return prepareStatement;
    }
    @Override
    public void close() throws SQLException { 
   

        pool.add(this);
        System.out.println("当前池子中有 " pool.size() " 个对象");

    }

    @Override
    ...
}

基于统一,JAVA为数据库连接池提供了公共接口,要求所有项目开发的连接池必须实现DataSource接口,可一统一用一套接口的方法使用不同开发商的数据库连接池。

常用的连接池

dbcp连接池、c3p0连接池、druid连接池。

DBCP

DBCP是一个依赖Jakarta commons-pool对象池机制的数据库连接池.DBCP可以直接的在应用程序中使用,Tomcat的数据源使用的就是DBCP。

c3p0

c3p0是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection和Statement 池的DataSources 对象。

Druid

阿里出品,淘宝和支付宝专用数据库连接池,但它不仅仅是一个数据库连接池,它还包含一个 ProxyDriver,一系列内置的JDBC组件库,一个 SQL Parser。支持所有JDBC兼容的数据库,包括Oracle、MySql、Derby、Postgresql、SQL Server、H2等等。

Druid针对Oracle和MySql做了特别优化,比如Oracle的PS Cache内存占用优化,MySql的ping检 测优化。

Druid提供了MySql、Oracle、Postgresql、SQL-92的 SQL的完整支持,这是一个手写的高性 能SQL Parser,支持Visitor模式,使得分析SQL的抽象语法树很方便。简单SQL语句用时10微秒以内,复 杂SQL 用时30微秒。通过Druid提供的SQL Parser可以在JDBC层拦截SQL做相应处理,比如说分库分表、审计等。Druid防御SQL注入攻击的 WallFilter就是通过Druid的SQL Parser分析语义实现的。

对比

JAVA中DBUtils的QueryRunner类实现增删改查详解

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/106658.html原文链接:https://javaforall.cn

0 人点赞