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¶m2=value2 + jdbc:postgresql://host1:port1,host2:port2/database?param1=value1¶m2=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.注册驱动 Class.forName("org.postgresql.Driver" );
4.2.建立连接 Connection conn = DriverManager.getConnection(url, user, password);
4.3.创建执行SQL语句的statement statement
存在sql注入的危险,如果用户传入的id为“5 or 1=1”,那么将删除表中的所有记录。
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指令)
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) ResultSet rs = ps.executeQuery(); While(rs.next()){ rs.getString("col_name" ); rs.getInt(1); //… }
4.5.释放资源 数据库连接(Connection)非常耗资源,尽量晚创建,尽量早的释放 。
都要加try catch 以防前面关闭出错,后面的就不执行了 。
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 连接瀚高数据库的过程如下:
使用 Class.forName() 方法显式地加载驱动类;
利用 DriverManager 类的 getConnection() 方法获取一个 Connection 连接对象。
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 的三种模式及行为:
select 模式(默认)
行为:JDBC 驱动会将所有 {call …} 或 {?= call …} 转换为 SELECT 语句。
适用场景:
仅适用于调用函数(FUNCTION),因为瀚高数据库的函数可以通过 SELECT 调用。
不支持直接调用存储过程(PROCEDURE)。
callIfNoReturn 模式
行为:
如果调用没有返回值(即没有 OUT 参数或返回结果集),JDBC 驱动会使用 CALL 语句。
如果调用有返回值,则仍然使用 SELECT 语句。
适用场景:
适用于同时调用存储过程和函数的混合场景。
存储过程通常不返回值,因此会使用 CALL。
函数可能返回值,因此会使用 SELECT。
call 模式
行为:JDBC 驱动始终使用 CALL 语句,无论是否有返回值。
适用场景:
仅适用于调用 存储过程(PROCEDURE)。
不支持调用函数(FUNCTION),因为瀚高数据库的函数无法通过 CALL 调用。
5.2.创建表 通过 JDBC 连接瀚高数据库并执行 DDL 语句的过程如下:
利用 DriverManager 类的 getConnection() 方法获取一个 Connection 连接对象;
使用连接对象的 createStatement() 方法创建一个 Statement 语句对象;
利用语句对象的 execute() 方法执行 SQL 语句;
释放 Statement 以及 Connection 对象资源。
/** * 创建表 */ 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 连接瀚高数据库并执行插入操作的过程如下:
利用 DriverManager 类的 getConnection() 方法获取一个 Connection 连接对象;
使用连接对象的 createStatement() 方法创建一个 Statement 或者 PreparedStatement 语句对象;
利用语句对象的 execute() 或者 executeBatch() 方法执行 INSERT 语句;
释放 Statement 以及 Connection 对象资源。
/** *插入数据 */ 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 连接瀚高数据库并执行查询语句的过程如下:
利用 DriverManager 类的 getConnection() 方法获取一个 Connection 连接对象;
使用连接对象的 createStatement() 方法创建一个 Statement 或者 PreparedStatement 语句对象;
利用语句对象的 executeQuery() 方法执行 SQL 语句或者存储过程,返回一个 ResultSet 结果集对象;
遍历结果集,获取并处理查询结果;
释放 ResultSet、Statement 以及 Connection 对象资源。
/** *查询数据 */ 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 连接瀚高数据库并执行更新语句的过程如下:
利用 DriverManager 类的 getConnection() 方法获取一个 Connection 连接对象;
使用连接对象的 createStatement() 方法创建一个 Statement 或者 PreparedStatement 语句对象;
利用语句对象的 executeUpdate() 方法执行 UPDATE 语句;
释放 Statement 以及 Connection 对象资源。
/** * 修改数据 */ 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 连接瀚高数据库并执行删除语句的过程如下:
利用 DriverManager 类的 getConnection() 方法获取一个 Connection 连接对象;
使用连接对象的 createStatement() 方法创建一个 Statement 或者 PreparedStatement 语句对象;
利用语句对象的 executeUpdate() 方法执行 DELETE 语句;
释放 Statement 以及 Connection 对象资源。
/** * 删除数据 */ 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() 方法禁用自动提交;然后分别执行插入语句和更新语句,并提交事务;在异常处理中回滚事务并打印错误消息。
/** * 处理事务 */ 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 连接瀚高数据库并执行函数调用的过程如下:
利用 DriverManager 类的 getConnection() 方法获取一个 Connection 连接对象;
使用连接对象的 setAutoCommit() 方法禁用自动提交;
使用连接对象的 prepareCall() 方法创建一个 CallableStatement 语句对象;
利用语句对象的 execute() 方法执行函数调用,返回一个 ResultSet 结果集对象;
遍历结果集,获取并处理查询结果;
释放 ResultSet、CallableStatement 以及 Connection 对象资源。
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;
/** * 调用函数 */ 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 连接瀚高数据库并执行存储过程调用的过程如下:
利用 DriverManager 类的 getConnection() 方法获取一个 Connection 连接对象;
使用连接对象的 setAutoCommit() 方法禁用自动提交;
使用连接对象的 prepareCall() 方法创建一个 CallableStatement 语句对象;
利用语句对象的 execute() 方法执行存储过程调用,返回一个 ResultSet 结果集对象;
遍历结果集,获取并处理查询结果;
释放 ResultSet、CallableStatement 以及 Connection 对象资源。
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;
/** * 调用存储过程 */ 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(); } } } } }