在存储的 CLOB 中包含自动递增值
Posted
技术标签:
【中文标题】在存储的 CLOB 中包含自动递增值【英文标题】:Include auto increment value in stored CLOB 【发布时间】:2018-10-12 23:49:57 【问题描述】:我将一个 json 对象作为 clob 存储在一个表中,该表具有一个自动增量 ID 字段。我想在存储时在 clob 中包含自动增量 ID。有什么办法可以做到这一点,而无需在插入后再次更新记录。 (我用 jdbc 和 h2 引擎)
【问题讨论】:
您的意思是增量 id 值 - 存储在 ID 列中?而且,clob 中的这个 json 是否需要添加这个递增的字段? 您是否考虑过为此目的使用应用程序生成的自动增量 ID? 另一种方法是创建两个表 - 一个带有自动递增 ID 列的主表。并且,带有 clob(带有 json 数据)列的辅助表。插入两个表可能需要在事务中执行。我认为这也可能有效。 是的。我想将递增的 ID 列的值存储到 CLOB。除非我读取表中的最后一个 ID,否则我无法获得应用程序生成的 ID(递增),对吗?所以这将是 2 笔交易。拥有 2 个表将增加复杂性,还有 2 个事务。我正在寻找最简单的方法来做到这一点。 _除非我读取表中的最后一个 ID,否则我无法获得应用程序生成的 ID(递增),对吗? _ 好吧,你可以有一个application_d
表,它有一列,它跟踪 ID 号。
【参考方案1】:
基本上有两种解决方案:
-
使用序列并检索值,使用 ID 生成 clob 内容,然后使用 id 和 clob 插入
使用修改您的 clob 的触发器
我已经写了两个小例子来说明如何做到这一点。
我个人认为第一个在大多数情况下会更可取,使用触发器的解决方案可能非常脆弱且难以正确执行。如果 clob 内容是 - 例如 - 从对象模型生成的 XML 或 JSON,在我的示例中放入像 ##ID##
这样的占位符并不总是可行或难以做到(例如,如果字段键入为一个整数)。
1。使用序列
public class WithSequence
private static final String URL = "jdbc:h2:mem:seqdb";
public static void main(String[] args) throws SQLException
try (Connection connection = DriverManager.getConnection(URL))
initDb(connection);
int id = getNextId(connection);
insertClobWithId(id, connection);
outputTableContent(connection);
// creates table and sequence
private static void initDb(Connection connection) throws SQLException
try (Statement ddlStmt = connection.createStatement())
ddlStmt.execute("create table clobwithid ("
+ " id integer primary key,"
+ " clobvalue clob"
+ ")");
ddlStmt.execute("create sequence seq_clobwithid");
// obtain next id from sequence
private static int getNextId(Connection connection) throws SQLException
try (Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("select next value for seq_clobwithid"))
if (rs.next())
return rs.getInt(1);
else
throw new AssertionError(" next value for should produce value");
// generate and insert clob
private static void insertClobWithId(int id, Connection connection) throws SQLException
String clobValue = "Something with id=" + id + " and other stuff";
try (PreparedStatement pstmt = connection.prepareStatement(
"insert into clobwithid(id, clobvalue) values(?, ?)"))
pstmt.setInt(1, id);
pstmt.setString(2, clobValue);
pstmt.executeUpdate();
private static void outputTableContent(Connection connection) throws SQLException
try (Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("select id, clobvalue from clobwithid"))
while (rs.next())
System.out.printf("%d : %s%n", rs.getInt(1), rs.getString(2));
2。使用触发器
public class WithTrigger
private static final String URL = "jdbc:h2:mem:seqdb";
public static void main(String[] args) throws SQLException
try (Connection connection = DriverManager.getConnection(URL))
initDb(connection);
insertClob(connection);
outputTableContent(connection);
// create database and trigger
private static void initDb(Connection connection) throws SQLException
try (Statement ddlStmt = connection.createStatement())
ddlStmt.execute("create table clobwithid ("
+ " id integer auto_increment primary key,"
+ " clobvalue clob"
+ ")");
ddlStmt.execute("create trigger bi_clobwithid before insert on clobwithid for each row call \"example.ClobUpdateTrigger\"");
// insert clob with a placeholder to be modified by the trigger
private static void insertClob(Connection connection) throws SQLException
String clobValue = "Something with id=##ID## and other stuff";
try (PreparedStatement pstmt = connection.prepareStatement(
"insert into clobwithid(clobvalue) values(?)"))
pstmt.setString(1, clobValue);
pstmt.executeUpdate();
private static void outputTableContent(Connection connection) throws SQLException
try (Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("select id, clobvalue from clobwithid"))
while (rs.next())
System.out.printf("%d : %s%n", rs.getInt(1), rs.getString(2));
触发器类在哪里:
public class ClobUpdateTrigger extends TriggerAdapter
@Override
public void fire(Connection connection, ResultSet oldRs, ResultSet newRs) throws SQLException
int generatedId = newRs.getInt(1);
// We need to use reader to avoid internal casting problems
String insertedClobValue = toString(newRs.getCharacterStream("clobvalue"));
String updatedClobValue = insertedClobValue != null
? insertedClobValue.replace("##ID##", String.valueOf(generatedId))
: null;
newRs.updateString("clobvalue", updatedClobValue);
private static String toString(Reader reader) throws SQLException
try
StringWriter sw = new StringWriter();
char[] buffer = new char[1024];
int read;
while ((read = reader.read(buffer)) != -1)
sw.write(buffer, 0, read);
return sw.toString();
catch (IOException e)
throw new SQLException(e);
【讨论】:
很好的建议,但我觉得这个问题是XY problem(或者可能只是一个“'为什么?'问题”)。如果关键是将 CLOB 与自动增量 ID 相关联,那么这是将(原始)CLOB 插入表中的自然结果。没有真正需要修改 CLOB 本身,因为 CLOB 和 ID 都是“链接”的,因为它们位于数据库表的同一行中。如果在某些时候需要更新 CLOB 以包含 ID,则可能会在 提取而不是插入时发生这种情况。 @GordThompson 这可能确实是一个 XY 问题。我只是喜欢以这个问题作为起点进行实验;)也就是说,我可以想到一个 clob 可以包含记录的序列化表示的情况,包括 id(例如,可能是为了加速一些休息 API 以避免它不得不在每个请求上构建和序列化相同的消息)。在这种情况下,也许解决方案 2 中的触发器应该完成创建 clob 的所有工作(对于更新触发器以使 clob 保持最新也很有用)。 @MarkRotteveel 这回答了问题。谢谢。【参考方案2】:这是一个解决方案:
(1) 使用序列:
CREATE TABLE test1 (my_id INT PRIMARY KEY, my_id_2 INT);
CREATE SEQUENCE my_seq;
INSERT INTO test1 VALUES (my_seq.nextval, my_seq.currval)
SELECT * FROM test1;
输出:
MY_ID MY_ID_2
1 1
注意这两列具有相同的生成值。
(1a) 序列的替代应用:
使用语句运行 SQL,使用以下脚本:“CALL NEXT VALUE FOR my_seq”以获取下一个序列值。
ResultSet rs = statement.executeQuery("call next value for my_seq");
rs.next();
long value = rs.getLong(1); // NOTE: the return value is a long
使用此序列值插入到目标表的多个列中。
【讨论】:
这并没有真正解决问题,尽管解决方案 1a 将是一个起点。 谢谢。这为解决方案提供了基础以上是关于在存储的 CLOB 中包含自动递增值的主要内容,如果未能解决你的问题,请参考以下文章