JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。
本文介绍如何通过 JDBC 连接瀚高数据库并执行各种数据操作。
JDBC是对数据库操作的接口抽象,而不同数据库厂商的数据库驱动程序则对应JDBC接口实现,通过抽象出JDBC接口,应用程序和实际的数据库驱动即JDBC实现解耦。
在数据库安装目录 interfaces/jdbc 下,获得驱动jar包hgdb-jdbc-xxx.jar。该驱动包与PostgreSQL保持兼容,其中类名、类结构与 PostgreSQL 驱动完全一致,曾经运行于 PostgreSQL 的应用程序可以直接移植到当前系统使用。
Driver接口由数据库厂家提供,作为java开发人员,只需要使用Driver接口就可以了。在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序,不同的数据库有不同的装载方法。如:
装载瀚高驱动:Class.forName(“org.postgresql.Driver”);
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() :在此链接上回滚事务。
用于执行静态SQL语句并返回它所生成结果的对象。
三种Statement类:
常用Statement方法:
ResultSet提供检索不同类型字段的方法,常用的有:
ResultSet还提供了对结果集进行滚动的方法:
注意:使用后依次关闭对象及连接:ResultSet → Statement → Connection
加载JDBC驱动程序 → 建立数据库连接Connection → 创建执行SQL的语句Statement → 处理执行结果ResultSet → 释放资源
Class.forName("org.postgresql.Driver");
Connection conn = DriverManager.getConnection(url, user, password);
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();
ResultSet rs = ps.executeQuery();
While(rs.next()){
rs.getString("col_name");
rs.getInt(1);
//…
}
数据库连接(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();
}
}
}
通过 JDBC 连接瀚高数据库的过程如下:
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_URL 中 escapeSyntaxCallMode 是一个重要的配置参数,用于控制如何将 JDBC 转义调用语法(如 {call …} 或 {?= call …})转换为底层的 SQL 语句。这个参数对存储过程(PROCEDURE)和函数(FUNCTION)的调用行为有直接影响。
escapeSyntaxCallMode 的三种模式及行为:
通过 JDBC 连接瀚高数据库并执行 DDL 语句的过程如下:
/**
* 创建表
*/
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();
}
}
}
}
通过 JDBC 连接瀚高数据库并执行插入操作的过程如下:
/**
*插入数据
*/
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();
}
}
}
}
通过 JDBC 连接瀚高数据库并执行查询语句的过程如下:
/**
*查询数据
*/
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();
}
}
}
}
}
通过 JDBC 连接瀚高数据库并执行更新语句的过程如下:
/**
* 修改数据
*/
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();
}
}
}
}
通过 JDBC 连接瀚高数据库并执行删除语句的过程如下:
/**
* 删除数据
*/
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();
}
}
}
}
默认情况下,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();
}
}
}
}
通过 JDBC 连接瀚高数据库并执行函数调用的过程如下:
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();
}
}
}
}
}
通过 JDBC 连接瀚高数据库并执行存储过程调用的过程如下:
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();
}
}
}
}
}