Oracle 自动增量功能:11.2 中的触发器或 Oracle JDBC CallableStatement?
Oracle Autoincrement Functionality: Triggers or Oracle JDBC CallableStatement in 11.2?
当您需要使用 JDBC 检索新生成的键时,在 Oracle (11.2) 中实现自动增量功能的最佳方式(就插入性能而言)是什么?
我知道 Oracle 12 中有标识列,但我现在受困于 11.2。
与许多其他人一样,我没有运气让 JDBC getGeneratedKeys() 与 Oracle 一起工作。我最终在我的 Oracle (11.2) 数据库中有一个触发器,它的作用类似于 MySQL 自动增量函数,并从 table 特定序列插入 NextVal 作为其主键,只要有插入到那个 table。虽然这使得获取新插入的密钥变得困难,但我最终进行了第二次查询以获取新生成的密钥。
最近我发现了具有 return 个值的 CallableStatement,并且我看到了如何使用这些值通过 1 个调用来完成所有事情。
当您还需要新生成的密钥时,这最后一种方法通常是一种更快的插入方式吗?还是我缺少更好的选择?
Like many others, I have had no luck in getting the JDBC getGeneratedKeys() to work with Oracle
这其实很简单。
以下适用于我的触发器和 Oracle 11.2 以及驱动程序版本 11.2.0.3.0(和 11.2.0.43.0)
create sequence foo_seq;
create table foo (id integer not null primary key, some_data varchar(20));
触发器:
create trigger foo_trg
before insert on foo
for each row
begin
:new.id := foo_seq.nextval;
end;
/
和 Java 代码:
String insert = "insert into foo (some_data) values (?)";
PreparedStatement pstmt = conection.prepareStatement(insert, new String[]{"ID"});
pstmt.setString(1, "bla");
pstmt.executeUpdate();
ResultSet rs = pstmt.getGeneratedKeys();
while (rs.next()) {
long id = rs.getLong(1);
System.out.println("The generated ID was: " + id);
}
rs.close();
如果你出于性能考虑不想要触发器,上面的代码可以在没有触发器的情况下工作,如果你将插入语句更改为使用序列:
String insert = "insert into foo (id, some_data) values (foo_seq.nextval, ?)";
我的迷你基准测试结果很有趣,决定分享它。
测试代码:
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.util.Assert;
import org.springframework.util.StopWatch;
import java.sql.*;
public class TriggerPerformanceTest {
private static final int STEPS_COUNT = 1000;
public static void main(String[] args) throws SQLException {
final Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@host:1521/oradev", "user", "pass");
prepare(connection);
final StopWatch stopWatch = new StopWatch("mini-bench");
testTrigger(connection, stopWatch);
testSequence(connection, stopWatch);
testSeparateCalls(connection, stopWatch);
JdbcUtils.closeConnection(connection);
System.out.println(stopWatch.prettyPrint());
}
private static void testTrigger(Connection connection, StopWatch stopWatch) throws SQLException {
final PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO test_table_trigger (text) VALUES (?)", new String[]{"ID"});
stopWatch.start("with trigger");
for (int i = 0; i < STEPS_COUNT; i++) {
preparedStatement.setString(1, "test");
preparedStatement.executeUpdate();
final ResultSet resultSet = preparedStatement.getGeneratedKeys();
final boolean next = resultSet.next();
Assert.state(next, "Expected not empty result set with generated keys");
final long id = resultSet.getLong(1);
Assert.state(id > 0, "Expected generated key value");
JdbcUtils.closeResultSet(resultSet);
}
stopWatch.stop();
JdbcUtils.closeStatement(preparedStatement);
}
private static void testSequence(Connection connection, StopWatch stopWatch) throws SQLException {
final PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO test_table_sequence (id, text) VALUES (sq_test2.NEXTVAL, ?)", new String[]{"ID"});
stopWatch.start("without trigger");
for (int i = 0; i < STEPS_COUNT; i++) {
preparedStatement.setString(1, "test");
preparedStatement.executeUpdate();
final ResultSet resultSet = preparedStatement.getGeneratedKeys();
final boolean next = resultSet.next();
Assert.state(next, "Expected not empty result set with generated keys");
final long id = resultSet.getLong(1);
Assert.state(id > 0, "Expected generated key value");
JdbcUtils.closeResultSet(resultSet);
}
stopWatch.stop();
JdbcUtils.closeStatement(preparedStatement);
}
private static void testSeparateCalls(Connection connection, StopWatch stopWatch) throws SQLException {
final PreparedStatement preparedStatementSeq = connection.prepareStatement("SELECT sq_test3.NEXTVAL FROM dual");
final PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO test_table_generated (id, text) VALUES (?, ?)");
stopWatch.start("separate calls");
for (int i = 0; i < STEPS_COUNT; i++) {
final ResultSet resultSet = preparedStatementSeq.executeQuery();
resultSet.next();
final long id = resultSet.getLong(1);
JdbcUtils.closeResultSet(resultSet);
preparedStatement.setLong(1, id);
preparedStatement.setString(2, "test");
preparedStatement.executeUpdate();
}
stopWatch.stop();
JdbcUtils.closeStatement(preparedStatementSeq);
JdbcUtils.closeStatement(preparedStatement);
}
private static void prepare(Connection connection) throws SQLException {
Statement statement = connection.createStatement();
try {
statement.execute("DROP TABLE test_table_sequence");
statement.execute("DROP TABLE test_table_trigger");
statement.execute("DROP TABLE test_table_generated");
statement.execute("DROP SEQUENCE sq_test1");
statement.execute("DROP SEQUENCE sq_test2");
statement.execute("DROP SEQUENCE sq_test3");
} catch (SQLException sqle) {
//ignore
}
try {
statement.execute("CREATE TABLE test_table_sequence (id NUMBER, text VARCHAR2(10))");
statement.execute("CREATE TABLE test_table_trigger (id NUMBER, text VARCHAR2(10))");
statement.execute("CREATE TABLE test_table_generated (id NUMBER, text VARCHAR2(10))");
statement.execute("CREATE SEQUENCE sq_test1 START WITH 1 INCREMENT BY 1 CACHE 20");
statement.execute("CREATE SEQUENCE sq_test2 START WITH 1 INCREMENT BY 1 CACHE 20");
statement.execute("CREATE SEQUENCE sq_test3 START WITH 1 INCREMENT BY 1 CACHE 20");
statement.execute("CREATE OR REPLACE TRIGGER trg_increment BEFORE INSERT ON test_table_trigger FOR EACH ROW\n" +
"BEGIN\n" +
" SELECT sq_test1.NEXTVAL INTO :new.id FROM dual;\n" +
"END;");
} catch (SQLException sqle) {
sqle.printStackTrace();
}
try {
statement.execute("TRUNCATE TABLE test_table_sequence");
statement.execute("TRUNCATE TABLE test_table_trigger");
statement.execute("TRUNCATE TABLE test_table_generated");
} catch (SQLException sqle) {
sqle.printStackTrace();
}
}
}
输出:
StopWatch 'mini-bench': running time (millis) = 27430
-----------------------------------------
ms % Task name
-----------------------------------------
09214 034% with trigger
08916 033% without trigger
09300 034% separate calls
结论:差别很小...要考虑进去。
PS。专用 Oracle 11.2.0.4,LAN 1Gb/s,Java 1.7.0_65.
当您需要使用 JDBC 检索新生成的键时,在 Oracle (11.2) 中实现自动增量功能的最佳方式(就插入性能而言)是什么?
我知道 Oracle 12 中有标识列,但我现在受困于 11.2。
与许多其他人一样,我没有运气让 JDBC getGeneratedKeys() 与 Oracle 一起工作。我最终在我的 Oracle (11.2) 数据库中有一个触发器,它的作用类似于 MySQL 自动增量函数,并从 table 特定序列插入 NextVal 作为其主键,只要有插入到那个 table。虽然这使得获取新插入的密钥变得困难,但我最终进行了第二次查询以获取新生成的密钥。
最近我发现了具有 return 个值的 CallableStatement,并且我看到了如何使用这些值通过 1 个调用来完成所有事情。
当您还需要新生成的密钥时,这最后一种方法通常是一种更快的插入方式吗?还是我缺少更好的选择?
Like many others, I have had no luck in getting the JDBC getGeneratedKeys() to work with Oracle
这其实很简单。
以下适用于我的触发器和 Oracle 11.2 以及驱动程序版本 11.2.0.3.0(和 11.2.0.43.0)
create sequence foo_seq;
create table foo (id integer not null primary key, some_data varchar(20));
触发器:
create trigger foo_trg
before insert on foo
for each row
begin
:new.id := foo_seq.nextval;
end;
/
和 Java 代码:
String insert = "insert into foo (some_data) values (?)";
PreparedStatement pstmt = conection.prepareStatement(insert, new String[]{"ID"});
pstmt.setString(1, "bla");
pstmt.executeUpdate();
ResultSet rs = pstmt.getGeneratedKeys();
while (rs.next()) {
long id = rs.getLong(1);
System.out.println("The generated ID was: " + id);
}
rs.close();
如果你出于性能考虑不想要触发器,上面的代码可以在没有触发器的情况下工作,如果你将插入语句更改为使用序列:
String insert = "insert into foo (id, some_data) values (foo_seq.nextval, ?)";
我的迷你基准测试结果很有趣,决定分享它。
测试代码:
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.util.Assert;
import org.springframework.util.StopWatch;
import java.sql.*;
public class TriggerPerformanceTest {
private static final int STEPS_COUNT = 1000;
public static void main(String[] args) throws SQLException {
final Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@host:1521/oradev", "user", "pass");
prepare(connection);
final StopWatch stopWatch = new StopWatch("mini-bench");
testTrigger(connection, stopWatch);
testSequence(connection, stopWatch);
testSeparateCalls(connection, stopWatch);
JdbcUtils.closeConnection(connection);
System.out.println(stopWatch.prettyPrint());
}
private static void testTrigger(Connection connection, StopWatch stopWatch) throws SQLException {
final PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO test_table_trigger (text) VALUES (?)", new String[]{"ID"});
stopWatch.start("with trigger");
for (int i = 0; i < STEPS_COUNT; i++) {
preparedStatement.setString(1, "test");
preparedStatement.executeUpdate();
final ResultSet resultSet = preparedStatement.getGeneratedKeys();
final boolean next = resultSet.next();
Assert.state(next, "Expected not empty result set with generated keys");
final long id = resultSet.getLong(1);
Assert.state(id > 0, "Expected generated key value");
JdbcUtils.closeResultSet(resultSet);
}
stopWatch.stop();
JdbcUtils.closeStatement(preparedStatement);
}
private static void testSequence(Connection connection, StopWatch stopWatch) throws SQLException {
final PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO test_table_sequence (id, text) VALUES (sq_test2.NEXTVAL, ?)", new String[]{"ID"});
stopWatch.start("without trigger");
for (int i = 0; i < STEPS_COUNT; i++) {
preparedStatement.setString(1, "test");
preparedStatement.executeUpdate();
final ResultSet resultSet = preparedStatement.getGeneratedKeys();
final boolean next = resultSet.next();
Assert.state(next, "Expected not empty result set with generated keys");
final long id = resultSet.getLong(1);
Assert.state(id > 0, "Expected generated key value");
JdbcUtils.closeResultSet(resultSet);
}
stopWatch.stop();
JdbcUtils.closeStatement(preparedStatement);
}
private static void testSeparateCalls(Connection connection, StopWatch stopWatch) throws SQLException {
final PreparedStatement preparedStatementSeq = connection.prepareStatement("SELECT sq_test3.NEXTVAL FROM dual");
final PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO test_table_generated (id, text) VALUES (?, ?)");
stopWatch.start("separate calls");
for (int i = 0; i < STEPS_COUNT; i++) {
final ResultSet resultSet = preparedStatementSeq.executeQuery();
resultSet.next();
final long id = resultSet.getLong(1);
JdbcUtils.closeResultSet(resultSet);
preparedStatement.setLong(1, id);
preparedStatement.setString(2, "test");
preparedStatement.executeUpdate();
}
stopWatch.stop();
JdbcUtils.closeStatement(preparedStatementSeq);
JdbcUtils.closeStatement(preparedStatement);
}
private static void prepare(Connection connection) throws SQLException {
Statement statement = connection.createStatement();
try {
statement.execute("DROP TABLE test_table_sequence");
statement.execute("DROP TABLE test_table_trigger");
statement.execute("DROP TABLE test_table_generated");
statement.execute("DROP SEQUENCE sq_test1");
statement.execute("DROP SEQUENCE sq_test2");
statement.execute("DROP SEQUENCE sq_test3");
} catch (SQLException sqle) {
//ignore
}
try {
statement.execute("CREATE TABLE test_table_sequence (id NUMBER, text VARCHAR2(10))");
statement.execute("CREATE TABLE test_table_trigger (id NUMBER, text VARCHAR2(10))");
statement.execute("CREATE TABLE test_table_generated (id NUMBER, text VARCHAR2(10))");
statement.execute("CREATE SEQUENCE sq_test1 START WITH 1 INCREMENT BY 1 CACHE 20");
statement.execute("CREATE SEQUENCE sq_test2 START WITH 1 INCREMENT BY 1 CACHE 20");
statement.execute("CREATE SEQUENCE sq_test3 START WITH 1 INCREMENT BY 1 CACHE 20");
statement.execute("CREATE OR REPLACE TRIGGER trg_increment BEFORE INSERT ON test_table_trigger FOR EACH ROW\n" +
"BEGIN\n" +
" SELECT sq_test1.NEXTVAL INTO :new.id FROM dual;\n" +
"END;");
} catch (SQLException sqle) {
sqle.printStackTrace();
}
try {
statement.execute("TRUNCATE TABLE test_table_sequence");
statement.execute("TRUNCATE TABLE test_table_trigger");
statement.execute("TRUNCATE TABLE test_table_generated");
} catch (SQLException sqle) {
sqle.printStackTrace();
}
}
}
输出:
StopWatch 'mini-bench': running time (millis) = 27430
-----------------------------------------
ms % Task name
-----------------------------------------
09214 034% with trigger
08916 033% without trigger
09300 034% separate calls
结论:差别很小...要考虑进去。
PS。专用 Oracle 11.2.0.4,LAN 1Gb/s,Java 1.7.0_65.