当需要使用JDBC检索新生成的密钥时,哪种方法(就插入性能而言)是在Oracle(11.2)中实现自动增量功能的最佳方法?
我知道Oracle 12中有标识列,但是我现在停留在11.2。
像许多其他人一样,我没有让JDBC
getGeneratedKeys()与Oracle一起工作的运气。我最终在Oracle(11.2)数据库中获得了触发器,该触发器的行为类似于MySQL自动增量函数,并在该表中有插入内容时从表特定的序列中插入NextVal作为其主键。但是,这使获取新插入的密钥变得困难,我最终进行了第二次查询以获取新生成的密钥。
最近,我发现了带有返回值的CallableStatement,并且看到了如何使用这些值通过1次调用完成所有操作。
当您还需要新生成的密钥时,后一种方法通常是执行插入操作的较快方法吗?或者,我是否缺少更好的选择?
我在迷你基准测试中获得了有趣的结果,并决定共享它。
测试代码:
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。
问题内容: 我想在ala SQL Server列中实现标识或自动递增值: 如何才能做到这一点? 问题答案: 正如Orbman所说,实现此目标的标准方法是使用序列。大多数人也将其与插入触发器结合在一起。因此,当插入没有ID的行时,触发器将触发以从序列中为您填写ID。 这是在Oracle中使用触发器的少数情况之一。
触发器的定义就是说某个条件成立的时候,触发器里面所定义的语句就会被自动的执行。 因此触发器不需要人为的去调用,也不能调用。触发器的触发条件其实在定义的时候就已经设定好了。这里面需要说明一下,触发器可以分为语句级触发器和行级触发器。 触发器的基础知识和示例: Oracle之前插入触发器 - https://www.xnip.cn/oracle/before_insert.html Oracle之后插
问题内容: 使用事务使用实时数据库触发器来增加计数器是否可以接受? 问题答案: 绝对!实际上,这确实是此代码示例中的工作方式,尽管有一些小差异: 值得注意的是,此示例根据创建还是删除子节点来处理增量和减量情况。
我在Azure上有一个函数,包含以下function.json文件: 除非我错了,否则这个函数应该每天运行一次,在凌晨3点? 这是函数的签名: 我到底做错了什么?当我手动触发(在门户中单击“运行”)时,该功能工作正常,但它在今天凌晨3点没有运行,昨天也没有运行。 编辑:所以,正如建议的那样,我已经将计划更改为付费计划,并且我选择了一个动态计划。日志仍然没有说明功能在今天早上3点被激活。
问题内容: 我有一张上面有插入触发器的表。如果我从存储过程中的一条插入语句中向该表中插入6000条记录,那么在插入触发器完成之前,存储过程会返回吗? 只是为了确保我在正确地思考,触发器应该只被调用一次(我知道“被调用”不是正确的词)一次,因为只有1个insert语句,对吗? 我的主要问题是:即使触发器尚未完成,存储过程也会完成吗? 问题答案: 您的插入触发器将针对整个插入语句运行一次。这就是为什么
在过去的14个月里,我有一个Azure功能运行没有问题。每当在blob存储容器中创建新的blob时,它都使用BlobTrigger来运行。BLOB是间歇性创建的,因此该功能几乎完全依赖于冷启动(无需考虑延迟)。然而,截至一周前,BlobTrigger不再启动冷启动(如中所示,3天后该功能仍然没有运行)。但是当我访问门户中的功能时。为了唤醒它,BlobTrigger会为存储中的每个blob触发一次。