更新时间:2026-07-28 GMT+08:00
分享

执行SQL语句

应用程序通过执行SQL语句来操作数据库。本章节通过示例介绍CREATE TABLE、INSERT、UPDATE、DELETE和SELECT等操作。

执行普通SQL语句

前置条件:已连接数据库,连接对象为conn。

执行普通SQL语句,创建customer_t1表的步骤如下:

  1. 调用Connection接口的createStatement方法创建语句对象stmt。

    1
    Statement stmt = conn.createStatement();
    

  2. 调用Statement接口的executeUpdate方法执行SQL语句。

    1
    int rc = stmt.executeUpdate("CREATE TABLE customer_t1(c_customer_sk INTEGER, c_customer_name VARCHAR(32));");
    

  3. 调用Statement接口的close方法关闭语句对象stmt。

    1
    stmt.close();
    

  • 数据库接收不在事务块中的执行请求,若包含多条语句时,将被打包成一个事务。事务块中不支持VACUUM操作。若任一语句执行失败,整个请求将被回滚。
  • 使用Statement执行多条语句时,应以分号(;)作为各语句间的分隔符。存储过程、函数和匿名块不支持多条语句执行。当preferQueryMode=simple时,语句执行不进行逻辑解析,此场景下无法使用分号(;)作为多条语句间的分隔符。
  • 斜杠(/)可作为创建存储过程、函数、匿名块、包体的结束符。当preferQueryMode=simple时,语句执行不进行逻辑解析,此场景下无法使用斜杠(/)作为结束符。
  • JDBC会对prepareStatement中的SQL语句进行缓存,可能导致内存膨胀。若JVM内存较小,建议调整preparedStatementCacheSizeMiBpreparedStatementCacheQueries参数值。
  • 在编写SQL语句时,若需添加注释,请避免注释内容紧贴SQL关键字。

执行批量插入处理语句

预处理语句仅编译和优化一次,可通过设置不同的参数值多次使用。由于已经预先编译完成,后续使用会减少执行时间。因此,对于需要重复执行的SQL操作,建议使用预处理语句。

前置条件:已通过执行普通SQL语句创建customer_t1表。

执行预处理语句,批量插入数据的步骤如下:

  1. 调用Connection接口的prepareStatement方法创建预处理语句对象pst。

    1
    PreparedStatement pst = conn.prepareStatement("INSERT INTO customer_t1 VALUES (?,?)");
    

  2. 针对每条数据要调用对应接口设置参数,并调用addBatch方法将SQL语句添加到批处理中。

    1
    2
    3
    4
    5
    for (int i = 0; i < 3; i++) {
       pst.setInt(1, i);
       pst.setString(2, "data " + i);
       pst.addBatch();
    }
    

  3. 调用PreparedStatement接口的executeBatch方法执行批处理。

    1
    pst.executeBatch();
    

  4. 调用PreparedStatement接口的close方法关闭预处理语句对象pst。

    1
    pst.close();
    

在数据库批处理操作中,应遵循以下关键原则以确保性能:

  1. 持续执行原则
    • 批处理程序应保持持续执行,避免中途终止。
    • 异常终止会显著影响数据库整体性能。
  2. 事务管理优化
    • 必须禁用自动提交功能。
      conn.setAutoCommit(false);
    • 采用分批提交策略而非单行提交。
    • 根据数据量和系统资源确定合适的批量大小。

执行预处理更新语句

前置条件:已通过执行批量插入处理语句向customer_t1表插入数据。

执行预处理SQL语句对数据进行更新的步骤如下:

  1. 调用Connection接口的prepareStatement方法创建预处理语句对象pstmt。

    1
    PreparedStatement pstmt = conn.prepareStatement("UPDATE customer_t1 SET c_customer_name = ? WHERE c_customer_sk = 1");
    

  2. 调用PreparedStatement接口的setString方法设置参数。

    1
    pstmt.setString(1, "new Data");
    

  3. 调用PreparedStatement接口的executeUpdate方法执行预处理SQL语句。

    1
    int rowcount = pstmt.executeUpdate();
    

  4. 调用PreparedStatement接口的close方法关闭预处理语句对象pstmt。

    1
    pstmt.close();
    

prepareStatement设置绑定参数后,最终会构建成B报文或U报文,并在下一步执行SQL语句时发送给服务端。但B报文或U报文存在最大长度限制(不能超过1023MB)。若一次绑定的数据过大,可能因报文过长导致异常。因此,prepareStatement设置绑定参数时需评估并控制数据大小,以避免超出报文上限。

执行删除语句

前置条件:已执行预处理更新语句

执行删除语句的步骤如下:

  1. 调用Connection接口的createStatement方法创建语句对象stmtd。

    1
    Statement stmtd = conn.createStatement();
    

  2. 调用Statement接口的executeUpdate方法执行SQL语句,删除customer_t1表中c_customer_sk为0的记录。

    1
    int rd = stmtd.executeUpdate("DELETE FROM customer_t1 WHERE c_customer_sk = 0;");
    

  3. 调用Statement接口的close方法关闭语句对象stmtd。

    1
    stmtd.close();
    

执行查询语句

前置条件:已执行删除语句

执行查询语句的步骤如下:

  1. 调用Connection接口的createStatement()方法创建语句对象stmts。

    1
    Statement stmts = conn.createStatement();
    

  2. 调用Statement接口的executeQuery()方法执行SQL查询语句,并返回ResultSet。

    1
    ResultSet rs = stmts.executeQuery("SELECT * FROM customer_t1");
    

调用存储过程

GaussDB支持通过JDBC调用存储过程,前置条件是已连接数据库并且连接对象为conn。

创建存储过程的示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
//在数据库中创建如下存储过程,它带有out参数
CREATE OR REPLACE PROCEDURE testproc 
(
    psv_in1 in integer,
    psv_in2 in integer,
    psv_inout inout integer
)
AS
BEGIN
    psv_inout := psv_in1 + psv_in2 + psv_inout;
END;
/

调用存储过程的步骤如下:

  1. 调用Connection的prepareCall方法创建调用语句对象cstmt。

    1
    CallableStatement cstmt = conn.prepareCall("{? = CALL testproc(?,?,?)}");
    

  2. 调用CallableStatement的setInt方法设置参数。

    1
    2
    3
    cstmt.setInt(2, 50); 
    cstmt.setInt(1, 20);
    cstmt.setInt(3, 90);
    

  3. 调用CallableStatement的registerOutParameter方法注册输出参数。

    1
    cstmt.registerOutParameter(4, Types.INTEGER);  //注册out类型的参数,类型为整型。
    

  4. 调用CallableStatement的execute方法执行SQL语句。

    1
    cstmt.execute();
    

  5. 调用CallableStatement的getInt方法获取out输出参数。

    1
    int out = cstmt.getInt(4); 
    

  6. 调用CallableStatement的close方法关闭调用语句对象cstmt。

    1
    cstmt.close();
    

  • 一些JDBC驱动程序提供命名参数的方法来设置参数。命名参数的方法允许根据名称而不是顺序来设置参数。若参数有默认值,则可以不指定参数值,而使用此参数的默认值。即使存储过程中参数的顺序发生了变更,也不必修改应用程序。目前GaussDB数据库的JDBC驱动程序不支持此方法。
  • conn.prepareCall("{? = CALL testproc(?,?,?)}"),执行存储过程绑定参数时,可以按照占位符的顺序绑定参数并注册第一个参数为出参;也可以按照存储过程中的参数顺序绑定参数并注册第四个参数为出参,上述用例为此场景,注册第四个参数为出参。
  • 当游标作为存储过程的返回值时,如果使用JDBC调用该存储过程,返回的游标将不可用。
  • 存储过程不能和普通SQL在同一条语句中执行。
  • 存储过程中inout类型参数必须注册为出参。

调用存储过程(入参为复合数据类型)

以下用例展示A兼容模式下,入参为复合数据类型的存储过程的创建与调用。前置条件是已连接数据库,并且连接对象为conn。

创建存储过程的示例如下:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 在数据库创建复合数据类型。
CREATE TYPE compfoo AS (f1 int, f3 text);
// 在数据库中创建table类型
CREATE TYPE compfoo_table is table of compfoo;
// 在数据库中创建如下存储过程,它带有out参数
CREATE OR REPLACE PROCEDURE test_proc
(
    psv_in in compfoo,
    table_in in compfoo_table,
    psv_out out compfoo,
    table_out out compfoo_table
)
AS
BEGIN
    psv_out := psv_in;
    table_out:=compfoo_table();
    table_out.extend(table_in.count);
    for i in 1..table_in.count loop
         table_out(i):=table_in(i);
    end loop;
END;
/

调用存储过程的步骤如下:

  1. 设置GUC参数behavior_compat_options为'proc_outparam_override'后,调用Connection的prepareCall方法创建调用语句对象cs。

    1
    2
    3
    Statement statement = conn.createStatement();
    statement.execute("SET behavior_compat_options='proc_outparam_override'");
    CallableStatement cs = conn.prepareCall("{ CALL test_proc(?,?,?,?) }");
    

  2. 调用CallableStatement的set方法设置参数。

    1
    2
    3
    4
    5
    6
    7
    8
    PGobject pGobject = new PGobject();
    pGobject.setType("public.compfoo"); // 设置复合类型名,格式为“schema.typename”。
    pGobject.setValue("(1,demo)"); // 绑定复合类型值,格式为“(value1,value2)”。
    cs.setObject(1, pGobject);
    pGobject = new PGobject();
    pGobject.setType("public.compfoo_table"); // 设置Table类型名,格式为"schema.typename"。
    pGobject.setValue("{\"(10,demo10)\",\"(11,demo111)\"}"); //绑定Table类型值,格式为"{\"(value1,value2)\",\"(value1,value2)\",...}"。
    cs.setObject(2, pGobject);
    

  3. 调用CallableStatement的registerOutParameter方法注册输出参数。

    1
    2
    3
    4
    // 注册out类型的参数,类型为复合类型,格式为“schema.typename”。
    cs.registerOutParameter(3, Types.STRUCT, "public.compfoo");   
    // 注册out类型的参数,类型为Table类型,格式为“schema.typename”。
    cs.registerOutParameter(4, Types.ARRAY, "public.compfoo_table");
    

  4. 调用CallableStatement的execute方法执行SQL语句。

    1
    cs.execute();
    

  5. 调用CallableStatement的getObject方法获取输出参数。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    // 返回结构是自定义类型。
    PGobject result = (PGobject)cs.getObject(3);  // 获取out参数。 
    result.getValue(); // 获取复合类型字符串形式值。
    result.getArrayValue(); //获取复合类型数组形式值,以复合数据类型字段顺序排序。
    result.getStruct(); //获取复合类型子类型名,按创建顺序排序。
    result.getAttributes(); //返回自定义类型每列组成类型的对象,对于array类型和table类型返回的是PgArray,对于自定义类型,封装的是PGobject,对于其他类型数据存储方式为字符串类型。
    // 返回结果是Table类型。
    PgArray pgArray = (PgArray) cs.getObject(4);
    ResultSet rs = pgArray.getResultSet();
    while (rs.next()) {
      rs.getObject(2);// table类型每行的数据构建成的对象。
    }
    

    如果出参的table类型组成为自定义类型,例如create type compfoo_table is table of compfoo,此时接收到的返回对象为PgArray,通过rs.getObject(2)遍历获取到的组成对象也为PgArray,此时无法获取到组成它的compfoo类型对应的每列数据,需要通过getPGobject()获取到PgObject再操作获取。

  6. 调用CallableStatement的close方法关闭调用语句对象cs。

    1
    cs.close();
    

  • A兼容模式开启后,调用存储过程必须使用{CALL proc_name(?,?,?)}形式,调用函数必须使用{? = CALL func_name(?,?)}形式(等号左侧的“?”为函数返回值的占位符,用于注册函数返回值)。
  • behavior_compat_options参数值发生更改后,业务需要重新建立连接,否则无法正确调用存储过程和函数。
  • 函数和存储过程中包含复合类型时,参数的绑定与注册需要使用schema.typename形式。

相关文档