在向 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
FINAL TABLE (DML statement)
-- 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 块:
INSERT .. RETURNING
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 类型
DBMS_SQL
原文链接:https://codingdict.com/