JDBC select 并在一个请求中更新
JDBC select and update in one request
这是 oracle 数据库中的 table:
ID varchar2() primary key,
STATUS varchar2(),
....
我需要 select 状态为 'DELAYED' 的行并同时设置状态 'PROGRESS'。
有什么方法可以使用 JDBC 吗?
如果可以创建辅助包
您可以使用 PL/SQL UPDATE .. RETURNING
语法。我认为 BULK COLLECT INTO
语义仍然不能直接与 jdbc 一起使用,所以你必须使用匿名 PL/SQL 块和一些技巧,而不是:
-- An auxiliary package is needed to declare the type.
create or replace package p as
type t is table of my_table%rowtype;
end p;
/
declare
r p.t;
c sys_refcursor;
begin
update my_table
set status = 'PROGRESS'
where status = 'DELAYED'
returning id, status, ...
bulk collect into r;
-- The package is needed for this syntax, which cannot yet work on types
-- declared in anonymous blocks:
open c for select * from table(r);
dbms_sql.return_result(c);
end;
/
然后获取结果。不幸的是,从这个博客 post 中可以看出这有点痛苦:
https://blog.jooq.org/2017/07/13/how-i-incorrectly-fetched-jdbc-resultsets-again/
(特别是,根据 ojdbc 版本,获取 DBMS_SQL.RETURN_RESULT
结果时存在错误)
// Alternatively, use a prepared statement if needed
try (Statement s = con.createStatement()) {
fetchLoop:
for (int i = 0, updateCount = 0; i < 256; i++) {
boolean result = (i == 0)
? s.execute(
"""
declare
r p.t;
c sys_refcursor;
begin
...
end;
""")
: s.getMoreResults();
if (result)
try (ResultSet rs = s.getResultSet()) {
// Consume result set
}
else if ((updateCount = s.getUpdateCount()) != -1)
System.out.println("Update Count: " + updateCount);
else
break fetchLoop;
}
}
您还可以获取 SYS_REFCURSOR
值作为 CallableStatement
的 OUT
参数
如果您无法创建包
如果您不能创建包,那么您可以使用 DBMS_SQL.NUMBER_TABLE
等序列化各个列,但这更复杂。它看起来大致像这样:
declare
o0 dbms_sql.number_table;
o1 dbms_sql.varchar2_table;
-- repeat for all columns
c0 sys_refcursor;
c1 sys_refcursor;
-- repeat for all columns
begin
update my_table
set status = 'PROGRESS'
where status = 'DELAYED'
returning id, status, ...
bulk collect into o0, o1;
-- Optional rowcount if you need that, as an OUT parameter
? := sql%rowcount;
open c0 for select * from table(o0);
open c1 for select * from table(o1);
-- These can also be fetched as OUT parameters if you want
? := c0;
? := c1;
end;
这可能会导致更多的网络流量,并且需要将多个每列结果集重新组装成一个结果集,所以如果可以的话,我推荐包方法。
这就是当您 运行 一个 UPDATE .. RETURNING
语句时 jOOQ 在幕后所做的事情。
由于您不能直接在 JDBC 中这样做,您可以使用以下代码片段分两步完成,希望对您有所帮助:
这里应该使用jdbc:oracle作为驱动,update ... set ... where
语法
首先你必须调用 updateTable() 将所有 DELAYED 行更改为 PROGRESS然后调用 getAllDelayedObjects() 获取所有 DELAYED 行作为 List.
(使用您自己的 实体类型,您希望保留数据库结果(在 getAllDelayedObjects() 中)而不是 YourEntityClass OR 你可以在控制台中打印 resultSet)
public class DataAccess {
private Connection connection;
private Statement statement;
public boolean updateTable() throws SQLException {
openConnection();
try (PreparedStatement statement=
connection.prepareStatement("update yourTableName set STATUS=? where STATUS=?")
) {
statement.setString(1, "DELAYED");
statement.setString(2, "PROGRESS");
if (statement.executeUpdate()>0) {
closeConnection();
return true;
}
else {
closeConnection();
return false;
}
}
catch (Exception e) {
e.printStackTrace();
return false;
}
}
public List<YourEntityClass> getAllDelayedObjects() throws SQLException {
openConnection();
try (
PreparedStatement statement = connection.prepareStatement("select * from users where STATUS=?")
) {
statement.setString(2, "PROGRESS");
ResultSet resultSet = statement.executeQuery();
List<YourEntityClass> entityObjects = new ArrayList<>();
while (resultSet.next()) {
YourDaType object = new YourDaType();
entityObject.setId(resultSet.getInt("ID"));
entityObject.setStatus(resultSet.getString("STATUS"));
// and whatever more properties you have in the table
entityObjects.add(object);
}
closeConnection();
return entityObjects;
}
catch (Exception e) {
closeConnection();
e.printStackTrace();
return null;
}
}
public void openConnection() throws SQLException {
String username = "yourUserNameForDB";
String pass = "yourPasswordForDB";
connection = DriverManager.getConnection("jdbc:oracle://yourDBdomain:yourDBport/yourDataBaseName", username, pass);
}
public void closeConnection() throws SQLException {
this.connection.close();
}
}
这是 oracle 数据库中的 table:
ID varchar2() primary key,
STATUS varchar2(),
....
我需要 select 状态为 'DELAYED' 的行并同时设置状态 'PROGRESS'。
有什么方法可以使用 JDBC 吗?
如果可以创建辅助包
您可以使用 PL/SQL UPDATE .. RETURNING
语法。我认为 BULK COLLECT INTO
语义仍然不能直接与 jdbc 一起使用,所以你必须使用匿名 PL/SQL 块和一些技巧,而不是:
-- An auxiliary package is needed to declare the type.
create or replace package p as
type t is table of my_table%rowtype;
end p;
/
declare
r p.t;
c sys_refcursor;
begin
update my_table
set status = 'PROGRESS'
where status = 'DELAYED'
returning id, status, ...
bulk collect into r;
-- The package is needed for this syntax, which cannot yet work on types
-- declared in anonymous blocks:
open c for select * from table(r);
dbms_sql.return_result(c);
end;
/
然后获取结果。不幸的是,从这个博客 post 中可以看出这有点痛苦: https://blog.jooq.org/2017/07/13/how-i-incorrectly-fetched-jdbc-resultsets-again/
(特别是,根据 ojdbc 版本,获取 DBMS_SQL.RETURN_RESULT
结果时存在错误)
// Alternatively, use a prepared statement if needed
try (Statement s = con.createStatement()) {
fetchLoop:
for (int i = 0, updateCount = 0; i < 256; i++) {
boolean result = (i == 0)
? s.execute(
"""
declare
r p.t;
c sys_refcursor;
begin
...
end;
""")
: s.getMoreResults();
if (result)
try (ResultSet rs = s.getResultSet()) {
// Consume result set
}
else if ((updateCount = s.getUpdateCount()) != -1)
System.out.println("Update Count: " + updateCount);
else
break fetchLoop;
}
}
您还可以获取 SYS_REFCURSOR
值作为 CallableStatement
OUT
参数
如果您无法创建包
如果您不能创建包,那么您可以使用 DBMS_SQL.NUMBER_TABLE
等序列化各个列,但这更复杂。它看起来大致像这样:
declare
o0 dbms_sql.number_table;
o1 dbms_sql.varchar2_table;
-- repeat for all columns
c0 sys_refcursor;
c1 sys_refcursor;
-- repeat for all columns
begin
update my_table
set status = 'PROGRESS'
where status = 'DELAYED'
returning id, status, ...
bulk collect into o0, o1;
-- Optional rowcount if you need that, as an OUT parameter
? := sql%rowcount;
open c0 for select * from table(o0);
open c1 for select * from table(o1);
-- These can also be fetched as OUT parameters if you want
? := c0;
? := c1;
end;
这可能会导致更多的网络流量,并且需要将多个每列结果集重新组装成一个结果集,所以如果可以的话,我推荐包方法。
这就是当您 运行 一个 UPDATE .. RETURNING
语句时 jOOQ 在幕后所做的事情。
由于您不能直接在 JDBC 中这样做,您可以使用以下代码片段分两步完成,希望对您有所帮助:
这里应该使用jdbc:oracle作为驱动,update ... set ... where
语法
首先你必须调用 updateTable() 将所有 DELAYED 行更改为 PROGRESS然后调用 getAllDelayedObjects() 获取所有 DELAYED 行作为 List.
(使用您自己的 实体类型,您希望保留数据库结果(在 getAllDelayedObjects() 中)而不是 YourEntityClass OR 你可以在控制台中打印 resultSet)
public class DataAccess {
private Connection connection;
private Statement statement;
public boolean updateTable() throws SQLException {
openConnection();
try (PreparedStatement statement=
connection.prepareStatement("update yourTableName set STATUS=? where STATUS=?")
) {
statement.setString(1, "DELAYED");
statement.setString(2, "PROGRESS");
if (statement.executeUpdate()>0) {
closeConnection();
return true;
}
else {
closeConnection();
return false;
}
}
catch (Exception e) {
e.printStackTrace();
return false;
}
}
public List<YourEntityClass> getAllDelayedObjects() throws SQLException {
openConnection();
try (
PreparedStatement statement = connection.prepareStatement("select * from users where STATUS=?")
) {
statement.setString(2, "PROGRESS");
ResultSet resultSet = statement.executeQuery();
List<YourEntityClass> entityObjects = new ArrayList<>();
while (resultSet.next()) {
YourDaType object = new YourDaType();
entityObject.setId(resultSet.getInt("ID"));
entityObject.setStatus(resultSet.getString("STATUS"));
// and whatever more properties you have in the table
entityObjects.add(object);
}
closeConnection();
return entityObjects;
}
catch (Exception e) {
closeConnection();
e.printStackTrace();
return null;
}
}
public void openConnection() throws SQLException {
String username = "yourUserNameForDB";
String pass = "yourPasswordForDB";
connection = DriverManager.getConnection("jdbc:oracle://yourDBdomain:yourDBport/yourDataBaseName", username, pass);
}
public void closeConnection() throws SQLException {
this.connection.close();
}
}