在向 SQL 数据库中插入记录时,我们通常希望取回生成的 ID 以及可能的其他触发器、序列或默认生成值。假设我们有下表:
-- DB2
CREATE TABLE x (
i INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
j VARCHAR(50),
k DATE DEFAULT CURRENT_DATE
);
-- PostgreSQL
CREATE TABLE x (
i SERIAL4 PRIMARY KEY,
j VARCHAR(50),
k DATE DEFAULT CURRENT_DATE
);
-- Oracle
CREATE TABLE x (
i INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
j VARCHAR2(50),
k DATE DEFAULT SYSDATE
);
数据库2
DB2 是 jOOQ 当前唯一支持的数据库,它实现了 SQL 标准,我们可以根据该标准从任何 INSERT 语句中进行 SELECT,包括:
SELECT *
FROM FINAL TABLE (
INSERT INTO x (j)
VALUES ('a'), ('b'), ('c')
);
上面的查询返回:
I |J |K |
--|--|-----------|
1 |a |2018-05-02 |
2 |b |2018-05-02 |
3 |c |2018-05-02 |
挺整洁的!此查询可以像 JDBC 中的任何其他查询一样简单地运行,您不必经历任何麻烦。
PostgreSQL 和火鸟
这些数据库有一个供应商特定的扩展,可以做同样的事情,几乎同样强大:
-- Simple INSERT .. RETURNING query
INSERT INTO x (j)
VALUES ('a'), ('b'), ('c')
RETURNING *;
-- If you want to do more fancy stuff
WITH t AS (
INSERT INTO x (j)
VALUES ('a'), ('b'), ('c')
RETURNING *
)
SELECT * FROM t;
两种语法都同样有效,后者与 DB2 一样强大,其中插入(或更新、删除、合并)的结果可以连接到其他表。同样,JDBC 没有问题
甲骨文
在 Oracle 中,这有点棘手。Oracle SQL 语言没有与 DB2 的FINAL TABLE (DML statement)
. 但是,Oracle PL/SQL 语言确实支持与 PostgreSQL 和 Firebird 相同的语法。这是完全有效的 PL/SQL
-- Create a few auxiliary types first
CREATE TYPE t_i AS TABLE OF NUMBER(38);
/
CREATE TYPE t_j AS TABLE OF VARCHAR2(50);
/
CREATE TYPE t_k AS TABLE OF DATE;
/
DECLARE
-- These are the input values
in_j t_j := t_j('a', 'b', 'c');
out_i t_i;
out_j t_j;
out_k t_k;
c1 SYS_REFCURSOR;
c2 SYS_REFCURSOR;
c3 SYS_REFCURSOR;
BEGIN
-- Use PL/SQL's FORALL command to bulk insert the
-- input array type and bulk return the results
FORALL i IN 1 .. in_j.COUNT
INSERT INTO x (j)
VALUES (in_j(i))
RETURNING i, j, k
BULK COLLECT INTO out_i, out_j, out_k;
-- Fetch the results and display them to the console
OPEN c1 FOR SELECT * FROM TABLE(out_i);
OPEN c2 FOR SELECT * FROM TABLE(out_j);
OPEN c3 FOR SELECT * FROM TABLE(out_k);
dbms_sql.return_result(c1);
dbms_sql.return_result(c2);
dbms_sql.return_result(c3);
END;
/
有点冗长,但它具有相同的效果。现在,从 JDBC:
try (Connection con = DriverManager.getConnection(url, props);
Statement s = con.createStatement();
// The statement itself is much more simple as we can
// use OUT parameters to collect results into, so no
// auxiliary local variables and cursors are needed
CallableStatement c = con.prepareCall(
"DECLARE "
+ " v_j t_j := ?; "
+ "BEGIN "
+ " FORALL j IN 1 .. v_j.COUNT "
+ " INSERT INTO x (j) VALUES (v_j(j)) "
+ " RETURNING i, j, k "
+ " BULK COLLECT INTO ?, ?, ?; "
+ "END;")) {
try {
// Create the table and the auxiliary types
s.execute(
"CREATE TABLE x ("
+ " i INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,"
+ " j VARCHAR2(50),"
+ " k DATE DEFAULT SYSDATE"
+ ")");
s.execute("CREATE TYPE t_i AS TABLE OF NUMBER(38)");
s.execute("CREATE TYPE t_j AS TABLE OF VARCHAR2(50)");
s.execute("CREATE TYPE t_k AS TABLE OF DATE");
// Bind input and output arrays
c.setArray(1, ((OracleConnection) con).createARRAY(
"T_J", new String[] { "a", "b", "c" })
);
c.registerOutParameter(2, Types.ARRAY, "T_I");
c.registerOutParameter(3, Types.ARRAY, "T_J");
c.registerOutParameter(4, Types.ARRAY, "T_K");
// Execute, fetch, and display output arrays
c.execute();
Object[] i = (Object[]) c.getArray(2).getArray();
Object[] j = (Object[]) c.getArray(3).getArray();
Object[] k = (Object[]) c.getArray(4).getArray();
System.out.println(Arrays.asList(i));
System.out.println(Arrays.asList(j));
System.out.println(Arrays.asList(k));
}
finally {
try {
s.execute("DROP TYPE t_i");
s.execute("DROP TYPE t_j");
s.execute("DROP TYPE t_k");
s.execute("DROP TABLE x");
}
catch (SQLException ignore) {}
}
}
上面的代码会显示:
[1, 2, 3]
[a, b, c]
[2018-05-02 10:40:34.0, 2018-05-02 10:40:34.0, 2018-05-02 10:40:34.0]
正是我们想要的。
的未来版本将从 jOOQINSERT .. RETURNING
语句中模拟上述 PL/SQL 块:
DSL.using(configuration)
.insertInto(X)
.columns(X.J)
.values("a")
.values("b")
.values("c")
.returning(X.I, X.J, X.K)
.fetch();
这将正确模拟所有原生支持该语法的数据库的查询。在 Oracle 的情况下,由于 jOOQ 无法创建或假设任何 SQL TABLE 类型,因此DBMS_SQL
将使用包中的PL/SQL 类型
原文链接:https://codingdict.com/