JDBC接口开发指南

1.简介

JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。

本文介绍如何通过 JDBC 连接瀚高数据库并执行各种数据操作。

2.数据库驱动

JDBC是对数据库操作的接口抽象,而不同数据库厂商的数据库驱动程序则对应JDBC接口实现,通过抽象出JDBC接口,应用程序和实际的数据库驱动即JDBC实现解耦。

在数据库安装目录 interfaces/jdbc 下,获得驱动jar包hgdb-jdbc-xxx.jar。该驱动包与PostgreSQL保持兼容,其中类名、类结构与 PostgreSQL 驱动完全一致,曾经运行于 PostgreSQL 的应用程序可以直接移植到当前系统使用。

3.常用接口
3.1.Driver接口

Driver接口由数据库厂家提供,作为java开发人员,只需要使用Driver接口就可以了。在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序,不同的数据库有不同的装载方法。如:

装载瀚高驱动:Class.forName("org.postgresql.Driver");

3.2.Connection接口

Connection与特定数据库的连接(会话),在连接上下文中执行sql语句并返回结果。

连接数据库:Connection conn = DriverManager.getConnection("url", "user", "password");

参数说明:

参数 描述
url 瀚高数据库连接URL支持的格式如下: • jdbc:postgresql:database • jdbc:postgresql://host/database • jdbc:postgresql://host:port/database • jdbc:postgresql://host:port/database?param1=value1&param2=value2 • jdbc:postgresql://host1:port1,host2:port2/database?param1=value1&param2=value2 说明: • database 为要连接的数据库名称。 • host 为数据库服务器名称或IP地址。缺省情况下,连接服务器为localhost。 • port 为数据库服务器端口。缺省情况下,会尝试连接到5866端口的database。 • param 为参数名称,即数据库连接属性。参数可以配置在URL中,以"?"开始配置,以"="给参数赋值,以"&"作为不同参数的间隔。 • value 为参数值,即数据库连接属性值。连接时需配置connectTimeout、socketTimeout,如果未配置,默认为0,即不会超时。
user 数据库用户名
password 数据库用户密码

常用方法:

createStatement():创建向数据库发送sql的statement对象。

prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。

prepareCall(sql):创建执行存储过程的callableStatement对象。

setAutoCommit(boolean autoCommit):设置事务是否自动提交。

commit() :在链接上提交事务。

rollback() :在此链接上回滚事务。

3.3.Statement接口

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

三种Statement类:

  • Statement:由createStatement创建,用于发送简单的SQL语句(不带参数)。
  • PreparedStatement :继承自Statement接口,由preparedStatement创建,用于发送含有一个或多个参数的SQL语句。PreparedStatement对象比Statement对象的效率更高,并且可以防止SQL注入,所以我们一般都使用PreparedStatement。
  • CallableStatement:继承自PreparedStatement接口,由方法prepareCall创建,用于调用存储过程。

常用Statement方法:

  • execute(String sql):运行语句,返回是否有结果集
  • executeQuery(String sql):运行select语句,返回ResultSet结果集。
  • executeUpdate(String sql):运行insert/update/delete操作,返回更新的行数。
  • addBatch(String sql) :把多条sql语句放到一个批处理中。
  • executeBatch():向数据库发送一批sql语句执行。
3.4.ResultSet接口

ResultSet提供检索不同类型字段的方法,常用的有:

  • getString(int index)、getString(String columnName):获得在数据库里是varchar、char等类型的数据对象。
  • getFloat(int index)、getFloat(String columnName):获得在数据库里是Float类型的数据对象。
  • getDate(int index)、getDate(String columnName):获得在数据库里是Date类型的数据。
  • getBoolean(int index)、getBoolean(String columnName):获得在数据库里是Boolean类型的数据。
  • getObject(int index)、getObject(String columnName):获取在数据库里任意类型的数据。

ResultSet还提供了对结果集进行滚动的方法:

  • next():移动到下一行
  • Previous():移动到前一行
  • absolute(int row):移动到指定行
  • beforeFirst():移动resultSet的最前面。
  • afterLast() :移动到resultSet的最后面。

注意:使用后依次关闭对象及连接:ResultSet → Statement → Connection

4.使用JDBC步骤

加载JDBC驱动程序 → 建立数据库连接Connection → 创建执行SQL的语句Statement → 处理执行结果ResultSet → 释放资源

4.1.注册驱动
bash 复制代码
Class.forName("org.postgresql.Driver");
4.2.建立连接
bash 复制代码
Connection conn = DriverManager.getConnection(url, user, password); 
4.3.创建执行SQL语句的statement

statement

存在sql注入的危险,如果用户传入的id为"5 or 1=1",那么将删除表中的所有记录。

bash 复制代码
String id = "5";
String sql = "delete from t where id=" + id;
Statement st = conn.createStatement();  
st.executeQuery(sql);  

PreparedStatement

有效的防止sql注入(SQL语句在程序运行前已经进行了预编译,当运行时动态地把参数传给PreprareStatement时,即使参数里有敏感字符如 or '1=1' 数据库会作为一个参数一个字段的属性值来处理而不会作为一个SQL指令)

bash 复制代码
String sql = "insert into user (name,pwd) values(?,?)";
PreparedStatement ps = conn.preparedStatement(sql);
ps.setString(1, "col_value");  //占位符顺序从1开始
ps.setString(2, "123456"); //也可以使用setObject
ps.executeQuery();
4.4.处理执行结果(ResultSet)
bash 复制代码
ResultSet rs = ps.executeQuery();
While(rs.next()){
    rs.getString("col_name");
    rs.getInt(1);
    //...
}
4.5.释放资源

数据库连接(Connection)非常耗资源,尽量晚创建,尽量早的释放 。

都要加try catch 以防前面关闭出错,后面的就不执行了 。

bash 复制代码
try { 
	if (rs != null) {
        rs.close();
    }
} catch (SQLException e) {
    e.printStackTrace();
} finally {
    try {
        if (st != null) {
            st.close();
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
5.示例
5.1.连接数据库

通过 JDBC 连接瀚高数据库的过程如下:

  1. 使用 Class.forName() 方法显式地加载驱动类;
  2. 利用 DriverManager 类的 getConnection() 方法获取一个 Connection 连接对象。
bash 复制代码
private static final String JDBC_DRIVER = "org.postgresql.Driver";
private static final String JDBC_URL = "jdbc:postgresql://192.168.100.101:5866/highgo?escapeSyntaxCallMode=callIfNoReturn";
private static final String JDBC_USER = "highgo";
private static final String JDBC_PASSWORD = "111111";

/**
 * 获取数据库连接
 * @return Connection 数据库连接对象
 * @throws SQLException,ClassNotFoundException
 */
public static Connection getConnection() throws SQLException, ClassNotFoundException {
    //下面一行注册驱动代码可省略。
    Class.forName(JDBC_DRIVER);
    return DriverManager.getConnection(JDBC_URL,JDBC_USER,JDBC_PASSWORD);
}
  • JDBC_DRIVER 指定数据库驱动,从 JDBC 4.0 开始,驱动程序会通过 META-INF/services/java.sql.Driver 文件自动注册,因此不需要显式调用Class.forName();
  • JDBC_URL 中指定了数据库的 IP 地址、端口以及目标数据库;
  • JDBC_USER 和 JDBC_PASSWORD 指定数据库的用户和密码。

JDBC_URL 中 escapeSyntaxCallMode 是一个重要的配置参数,用于控制如何将 JDBC 转义调用语法(如 {call ...} 或 {?= call ...})转换为底层的 SQL 语句。这个参数对存储过程(PROCEDURE)和函数(FUNCTION)的调用行为有直接影响。

escapeSyntaxCallMode 的三种模式及行为:

  1. select 模式(默认)
  • 行为:JDBC 驱动会将所有 {call ...} 或 {?= call ...} 转换为 SELECT 语句。
  • 适用场景:
    • 仅适用于调用函数(FUNCTION),因为瀚高数据库的函数可以通过 SELECT 调用。
    • 不支持直接调用存储过程(PROCEDURE)。
  1. callIfNoReturn 模式
  • 行为:
    • 如果调用没有返回值(即没有 OUT 参数或返回结果集),JDBC 驱动会使用 CALL 语句。
    • 如果调用有返回值,则仍然使用 SELECT 语句。
  • 适用场景:
    • 适用于同时调用存储过程和函数的混合场景。
    • 存储过程通常不返回值,因此会使用 CALL。
    • 函数可能返回值,因此会使用 SELECT。
  1. call 模式
  • 行为:JDBC 驱动始终使用 CALL 语句,无论是否有返回值。
  • 适用场景:
    • 仅适用于调用 存储过程(PROCEDURE)。
    • 不支持调用函数(FUNCTION),因为瀚高数据库的函数无法通过 CALL 调用。

5.2.创建表

通过 JDBC 连接瀚高数据库并执行 DDL 语句的过程如下:

  1. 利用 DriverManager 类的 getConnection() 方法获取一个 Connection 连接对象;
  2. 使用连接对象的 createStatement() 方法创建一个 Statement 语句对象;
  3. 利用语句对象的 execute() 方法执行 SQL 语句;
  4. 释放 Statement 以及 Connection 对象资源。
bash 复制代码
/**
 * 创建表
 */
public static void create(){
    Connection conn = null;
    Statement stmt = null;
    String sql = "create table users (" +
            "id serial primary key," +
            "name varchar(50) not null unique," +
            "create_time timestamp not null" +
            ")";
    try {
        conn = getConnection(); //获取连接
        System.out.println("连接瀚高数据库成功!");
        stmt = conn.createStatement(); //创建statement对象
        stmt.execute(sql); //执行sql
        System.out.println("成功创建 users 表!");
    } catch (Exception e) {
       e.printStackTrace();
    } finally { //释放资源
        try {
            if(stmt != null) stmt.close();
        } catch (Exception e) {
           e.printStackTrace();
        } finally {
            try {
                if(conn != null) conn.close();
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}
5.3.插入数据

通过 JDBC 连接瀚高数据库并执行插入操作的过程如下:

  1. 利用 DriverManager 类的 getConnection() 方法获取一个 Connection 连接对象;
  2. 使用连接对象的 createStatement() 方法创建一个 Statement 或者 PreparedStatement 语句对象;
  3. 利用语句对象的 execute() 或者 executeBatch() 方法执行 INSERT 语句;
  4. 释放 Statement 以及 Connection 对象资源。
bash 复制代码
/**
 *插入数据
 */
public static void insert(){
    Connection conn = null;
    PreparedStatement pstmt = null;
    String sql = "insert into users (name,create_time) values(?,?)";
    try {
        conn = getConnection();//获取连接
        pstmt = conn.prepareStatement(sql); //创建prepareStatement对象
        pstmt.setString(1,"John"); //绑定参数1
        pstmt.setTimestamp(2,new Timestamp(System.currentTimeMillis())); //绑定参数2
        pstmt.addBatch();
        pstmt.setString(1,"Robert"); //绑定参数1
        pstmt.setTimestamp(2,new Timestamp(System.currentTimeMillis())); //绑定参数2
        pstmt.addBatch();

        int[] rowsAffected = pstmt.executeBatch(); //执行sql
        System.out.println("数据写入{" + rowsAffected.length + "}行!");
    } catch (Exception e) {
       e.printStackTrace();
    } finally { //释放资源
        try {
            if(pstmt != null) pstmt.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(conn != null) conn.close();
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}
5.4.查询数据

通过 JDBC 连接瀚高数据库并执行查询语句的过程如下:

  1. 利用 DriverManager 类的 getConnection() 方法获取一个 Connection 连接对象;
  2. 使用连接对象的 createStatement() 方法创建一个 Statement 或者 PreparedStatement 语句对象;
  3. 利用语句对象的 executeQuery() 方法执行 SQL 语句或者存储过程,返回一个 ResultSet 结果集对象;
  4. 遍历结果集,获取并处理查询结果;
  5. 释放 ResultSet、Statement 以及 Connection 对象资源。
bash 复制代码
/**
 *查询数据
 */
private static void select(){
    Connection conn = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    String sql = "select * from users";
    try {
        conn = getConnection(); //获取连接
        pstmt = conn.prepareStatement(sql); //创建prepareStatement对象
        rs = pstmt.executeQuery(); //执行sql
        while(rs.next()){ //处理结果集
            System.out.println(rs.getInt("id") + "\t" +
                                rs.getString("name")  + "\t" +
                                rs.getTimestamp("create_time"));
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally { //释放资源
        try {
            if(rs != null) rs.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(pstmt != null) pstmt.close();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if(conn != null) conn.close();
                } catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}
5.5.修改数据

通过 JDBC 连接瀚高数据库并执行更新语句的过程如下:

  1. 利用 DriverManager 类的 getConnection() 方法获取一个 Connection 连接对象;
  2. 使用连接对象的 createStatement() 方法创建一个 Statement 或者 PreparedStatement 语句对象;
  3. 利用语句对象的 executeUpdate() 方法执行 UPDATE 语句;
  4. 释放 Statement 以及 Connection 对象资源。
bash 复制代码
/**
 * 修改数据
 */
private static void update(){
    Connection conn = null;
    PreparedStatement pstmt = null;
    String sql = "update users set name=? where id=?";
    try {
        conn = getConnection(); //获取连接
        pstmt = conn.prepareStatement(sql);  //创建prepareStatement对象
        pstmt.setString(1,"Tom"); //绑定参数1
        pstmt.setInt(2,1); //绑定参数2
        int rowsAffected = pstmt.executeUpdate(); //执行sql
        System.out.println("更新行数: {"+rowsAffected+"}");
    } catch (Exception e) {
        e.printStackTrace();
    } finally { //释放资源
        try {
            if(pstmt != null) pstmt.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(conn != null) conn.close();
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}
5.6.删除数据

通过 JDBC 连接瀚高数据库并执行删除语句的过程如下:

  1. 利用 DriverManager 类的 getConnection() 方法获取一个 Connection 连接对象;
  2. 使用连接对象的 createStatement() 方法创建一个 Statement 或者 PreparedStatement 语句对象;
  3. 利用语句对象的 executeUpdate() 方法执行 DELETE 语句;
  4. 释放 Statement 以及 Connection 对象资源。
bash 复制代码
/**
 * 删除数据
 */
private static void delete(){
    Connection conn = null;
    PreparedStatement pstmt = null;
    String sql = "delete from users where id=?";
    try {
        conn = getConnection(); //获取连接
        pstmt = conn.prepareStatement(sql); //创建prepareStatement对象
        pstmt.setInt(1,1); //绑定参数1
        int rowsAffected = pstmt.executeUpdate(); //执行sql
        System.out.println("删除行数: {"+rowsAffected+"}");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {//释放资源
        try {
            if(pstmt != null) pstmt.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(conn != null) conn.close();
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}
5.7.处理事务

默认情况下,JDBC 连接瀚高数据库时使用自动提交模式,意味着每个 SQL 语句都会自动执行事务的提交操作。如果我们想要在一个事务中执行多条 SQL 语句,需要禁用连接对象的自动提交属性,并且手动执行 COMMIT 或者 ROLLBACK 操作。

创建连接之后,使用 setAutoCommit() 方法禁用自动提交;然后分别执行插入语句和更新语句,并提交事务;在异常处理中回滚事务并打印错误消息。

bash 复制代码
/**
 * 处理事务
 */
private static void transaction(){
    Connection conn = null;
    PreparedStatement pstmt = null;
    PreparedStatement pstmt2= null;
    String sql = "insert into users (name,create_time) values(?,?)";
    String sql2 = "update users set name = ? where id = ?";
    try {
        conn = getConnection(); //获取连接
        conn.setAutoCommit(false); //设置手动提交
        pstmt = conn.prepareStatement(sql); //创建prepareStatement对象
        pstmt.setString(1,"James"); //绑定参数1
        pstmt.setTimestamp(2,new Timestamp(System.currentTimeMillis())); //绑定参数2
        pstmt.executeUpdate(); //执行sql

        pstmt2 = conn.prepareStatement(sql2); //创建prepareStatement对象
        pstmt2.setString(1,"James"); //绑定参数1
        pstmt2.setInt(2,2); //绑定参数2
        pstmt2.executeUpdate(); //执行sql

        conn.commit(); // 提交事务
        System.out.println("事务提交成功!");
    } catch (Exception e) {
        try {
            conn.rollback(); // 回滚事务
            System.out.println("事务回滚!");
        } catch (SQLException ex) {
            e.printStackTrace();
        }
        e.printStackTrace();
    } finally {
        try {
            if(pstmt != null) pstmt.close();
            if(pstmt2 != null) pstmt2.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(conn != null) {
                    conn.setAutoCommit(true); //设置自动提交
                    conn.close();
                }
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}
5.8.调用函数

通过 JDBC 连接瀚高数据库并执行函数调用的过程如下:

  1. 利用 DriverManager 类的 getConnection() 方法获取一个 Connection 连接对象;
  2. 使用连接对象的 setAutoCommit() 方法禁用自动提交;
  3. 使用连接对象的 prepareCall() 方法创建一个 CallableStatement 语句对象;
  4. 利用语句对象的 execute() 方法执行函数调用,返回一个 ResultSet 结果集对象;
  5. 遍历结果集,获取并处理查询结果;
  6. 释放 ResultSet、CallableStatement 以及 Connection 对象资源。
bash 复制代码
CREATE OR REPLACE FUNCTION get_users_with_cursor_function() returns REFCURSOR AS $$
DECLARE
	ref_cursor REFCURSOR;
BEGIN
    OPEN ref_cursor FOR SELECT id, name, create_time FROM users;
	return ref_cursor;
END;
$$ LANGUAGE plpgsql;
bash 复制代码
/**
 * 调用函数
 */
private static void callFunction(){
    Connection conn = null;
    CallableStatement cstmt = null;
    ResultSet rs = null;
    String callSQL = "{? = call get_users_with_cursor_function()}";
    try {
        conn = getConnection(); //获取连接
        conn.setAutoCommit(false); //设置手动提交
        cstmt = conn.prepareCall(callSQL); //创建prepareCall对象
        cstmt.registerOutParameter(1, Types.REF_CURSOR); //注册输出参数为游标类型
        cstmt.execute(); //执行存储过程
        rs = (ResultSet) cstmt.getObject(1); //获取返回的游标结果集
        // 遍历游标中的数据
        while (rs.next()) {
            System.out.println(rs.getInt("id") + "\t" +
                    rs.getString("name")  + "\t" +
                    rs.getTimestamp("create_time"));
        }
        conn.commit(); //提交事务
    } catch (Exception e) {
        try {
            conn.rollback(); // 回滚事务
            System.out.println("事务回滚!");
        } catch (SQLException ex) {
            e.printStackTrace();
        }
        e.printStackTrace();
    } finally {
        try {
            if(rs != null) rs.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(cstmt != null) cstmt.close();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if(conn != null) {
                        conn.setAutoCommit(true); //设置自动提交
                        conn.close();
                    }
                } catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}
5.9.调用存储过程

通过 JDBC 连接瀚高数据库并执行存储过程调用的过程如下:

  1. 利用 DriverManager 类的 getConnection() 方法获取一个 Connection 连接对象;
  2. 使用连接对象的 setAutoCommit() 方法禁用自动提交;
  3. 使用连接对象的 prepareCall() 方法创建一个 CallableStatement 语句对象;
  4. 利用语句对象的 execute() 方法执行存储过程调用,返回一个 ResultSet 结果集对象;
  5. 遍历结果集,获取并处理查询结果;
  6. 释放 ResultSet、CallableStatement 以及 Connection 对象资源。
bash 复制代码
CREATE OR REPLACE PROCEDURE get_users_with_cursor_procedure(OUT ref_cursor REFCURSOR) AS $$
BEGIN
    OPEN ref_cursor FOR SELECT id, name, create_time FROM users;
END;
$$ LANGUAGE plpgsql;
bash 复制代码
/**
 * 调用存储过程
 */
private static void callProcedure(){
    Connection conn = null;
    CallableStatement cstmt = null;
    ResultSet rs = null;
    String callSQL = "{ CALL get_users_with_cursor_procedure(?) }";
    try {
        conn = getConnection(); //获取连接
        conn.setAutoCommit(false); //设置手动提交
        cstmt = conn.prepareCall(callSQL); //创建prepareCall对象
        cstmt.registerOutParameter(1, Types.REF_CURSOR); //注册输出参数为游标类型
        cstmt.execute(); //执行存储过程
        rs = (ResultSet) cstmt.getObject(1); //获取返回的游标结果集
        // 遍历游标中的数据
        while (rs.next()) {
            System.out.println(rs.getInt("id") + "\t" +
                    rs.getString("name")  + "\t" +
                    rs.getTimestamp("create_time"));
        }
        conn.commit(); //提交事务
    } catch (Exception e) {
        try {
            conn.rollback(); // 回滚事务
            System.out.println("事务回滚!");
        } catch (SQLException ex) {
            e.printStackTrace();
        }
        e.printStackTrace();
    } finally {
        try {
            if(rs != null) rs.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(cstmt != null) cstmt.close();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if(conn != null) {
                        conn.setAutoCommit(true); //设置自动提交
                        conn.close();
                    }
                } catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}
相关推荐
极限实验室6 分钟前
INFINI Labs 产品更新 | INFINI Console 1.29.6 发布 – 优化监控图表异常毛刺等
数据库·产品
先睡9 分钟前
优化MySQL查询
数据库·sql
一头生产的驴13 分钟前
java整合itext pdf实现自定义PDF文件格式导出
java·spring boot·pdf·itextpdf
YuTaoShao20 分钟前
【LeetCode 热题 100】73. 矩阵置零——(解法二)空间复杂度 O(1)
java·算法·leetcode·矩阵
zzywxc78723 分钟前
AI 正在深度重构软件开发的底层逻辑和全生命周期,从技术演进、流程重构和未来趋势三个维度进行系统性分析
java·大数据·开发语言·人工智能·spring
小张是铁粉26 分钟前
oracle的内存架构学习
数据库·学习·oracle·架构
专注API从业者30 分钟前
构建淘宝评论监控系统:API 接口开发与实时数据采集教程
大数据·前端·数据库·oracle
藏在歌词里34 分钟前
数据库-元数据表
数据库
小乌龟不会飞2 小时前
Ubuntu 安装 etcd 与 etcd-cpp-apiv3
数据库·etcd
YuTaoShao3 小时前
【LeetCode 热题 100】56. 合并区间——排序+遍历
java·算法·leetcode·职场和发展