在存储的 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 中包含自动递增值的主要内容,如果未能解决你的问题,请参考以下文章

PHP mySQL - 将新记录插入表中,主键自动递增

SQL:插入期间自动递增值

使用 PDO for MySQL 时存储自动递增的 ID

MySQL 自动递增自定义值

MySQL自动递增自定义值

在oracle 怎样设置自动递增的的字段,也就是设置自动递增的ID 主键