Java程序设计(高级及专题)- JDBC「建议收藏」

Java程序设计(高级及专题)- JDBC「建议收藏」Java程序设计(高级及专题)- JDBC

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

在这里插入图片描述

核心API

JDBC接口核心的API

            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代码

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文件

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()

核心代码:

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

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

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

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()方法里面的代码:

//方法:使用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中事务的提交与回滚

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

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 );这样吧下面的语句相当于一个事务,一旦出现错误就会撤销所有操作.

连接池

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

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.net/106657.html原文链接:https://javaforall.net

(0)
上一篇 2022年4月22日 下午3:20
下一篇 2022年4月22日 下午3:40


相关推荐

  • 计算机的发展历史和发展趋势_对未来计算机展望

    计算机的发展历史和发展趋势_对未来计算机展望论计算机发展史及展望(3页)本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦!9.9积分论计算机发展史及展望杨露斯黎炼四川大学,成都双流610207摘要:自从1945年世界上第一台电子计算机诞生以来,计算机技术迅猛发展,CPU的速度越来越快,体积越来越小,价格越来越低。未来光子、一轮趨级计算技术革命。关键词:计算机;发展史;前景展望中图分类号:G4文…

    2022年10月19日
    8
  • excel怎么赋值0和1_excel单元格默认值

    excel怎么赋值0和1_excel单元格默认值getCell 获取列 需要用 createCell获取即可。导出报表时,明明是有数据的可就是报空指针异常。排查后发下报表表格没内容时不能用。

    2022年8月20日
    9
  • Linux visudo 命令

    Linux visudo 命令在类 Unix 操作系统上 visudo 命令编辑 sudo 命令使用的 sudoers 文件 要更改允许哪些用户和组运行 sudo 请运行 visudo 如果运行 sudo 的用户不符合 sudoers 中的身份验证配置 他们将被拒绝以升级的权限运行命令 您不应通过在文本编辑器中打开来直接编辑 sudoers 相反 使用 visudo 对其进行编辑 这将在将更改保存到磁盘之前验证其有效性 描述 visudo 编辑 sudoers 文件 该文件定义了具有管理员权限的用户和组 Visudo 以安全的方式编辑 sudoers

    2026年3月18日
    2
  • OpenClaw配置说明和操作指南

    OpenClaw配置说明和操作指南

    2026年3月13日
    3
  • 深入理解JVM之JVM内存区域与内存分配「建议收藏」

    深入理解JVM之JVM内存区域与内存分配

    2022年2月21日
    98
  • 强大的nginx反向代理异步传输模式(原理)[通俗易懂]

    强大的nginx反向代理异步传输模式(原理)[通俗易懂]sudone.com在nginx的反向代理介绍中,提到了异步传输模式并提到它可以减少后端连接数和压力,这是为何?下面就来讲解下传统的代理(apache/squid)的同步传输和nginx的异步传输的差异。看图:         squid同步传输:浏览器发起请求,而后请求会立刻被转到后台,于是在浏览器和后台之间就建立了一个通道。在请求发起直到请求完成,这条通道都是一直存在的。nginx异步传输:浏…

    2025年11月13日
    8

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号