table 使用 jpa 分区抛出 OptimisticLockException

table partitioning with jpa throws OptimisticLockException

我的数据库中有一个 log_table 根据 partitioning docs 分区。我有一个根据日期将记录插入分区 table 的函数和一个调用该函数的触发器,如文档中所述。

触发器示例

CREATE TRIGGER insert_measurement_trigger
    BEFORE INSERT ON measurement
    FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();

函数示例

CREATE OR REPLACE FUNCTION measurement_insert_trigger()
RETURNS TRIGGER AS $$
BEGIN
    IF ( NEW.logdate >= DATE '2006-02-01' AND
         NEW.logdate < DATE '2006-03-01' ) THEN
        INSERT INTO measurement_y2006m02 VALUES (NEW.*);
    ELSIF ( NEW.logdate >= DATE '2006-03-01' AND
            NEW.logdate < DATE '2006-04-01' ) THEN
        INSERT INTO measurement_y2006m03 VALUES (NEW.*);
    ...
    ELSIF ( NEW.logdate >= DATE '2008-01-01' AND
            NEW.logdate < DATE '2008-02-01' ) THEN
        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
    ELSE
        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
    END IF;
    RETURN NULL;
END;
$$
LANGUAGE plpgsql;

我正在使用 openjpa 2.3.0 作为 JPA 实现。

在 table

上触发

When i try to persist new entity in transaction to this log_table with trigger, I get an exception on commiting:

Caused by: <openjpa-2.3.0-r422266:1540826 nonfatal store error> org.apache.openjpa.persistence.OptimisticLockException:
An optimistic lock violation was detected when flushing object instance "...entities.LogTable@67ec8ef4" to the data store. 
This indicates that the object was concurrently modified in another transaction.

When I insert one record manualy using INSERT it works, but returns

Query returned successfully: 0 rows affected, 54 ms execution time.

table

没有触发器

It works OK using the same java code that throwed the exception, so the code should be ok. I do nothing exceptional for persisting the entity. The manual INSERT command returns

Query returned successfully: one row affected, 51 ms execution time.

受影响行数的差异是否是 openjpa 无法正确处理的原因?在异常中提到的代码上,我发现了这个

        try {
            int count = executeUpdate(stmnt, sql, row);
            if (count != 1) {
                logSQLWarnings(stmnt);
                Object failed = row.getFailedObject();
                if (failed != null)
                    _exceptions.add(new OptimisticException(failed));
...

似乎因为使用触发器插入 return 0 个受影响的行而不是 1 个,代码将其评估为异常。

子问题
有没有办法使触发结果表现相同?我的意思是 return 1 行受到影响?以某种方式传播内部函数的结果?

我在使用 Java Ebean ORM 时遇到了同样的问题。由于 rowCount 失败,在 java 侧插入导致优化锁定错误。

我对这个问题的研究没有提供解决方案,即从插入到子项中传播行数 table 或破解行数功能。

我的解决方案(根据您的条件,将 NEW.id 更改为 table 和 RETURN NEW 中的下一个序列,这将导致插入到父 table 中)并为您提供您需要影响 row_count.

的元组响应
CREATE OR REPLACE FUNCTION measurement_insert_trigger() 
RETURNS TRIGGER AS $$
BEGIN
    IF ( NEW.logdate >= DATE '2006-02-01' AND
         NEW.logdate < DATE '2006-03-01' ) THEN
        INSERT INTO measurement_y2006m02 VALUES (NEW.*);
    ELSIF ( NEW.logdate >= DATE '2006-03-01' AND
            NEW.logdate < DATE '2006-04-01' ) THEN
        INSERT INTO measurement_y2006m03 VALUES (NEW.*);
    ...
    ELSIF ( NEW.logdate >= DATE '2008-01-01' AND
            NEW.logdate < DATE '2008-02-01' ) THEN
        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
    ELSE
        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
    END IF;

    --ORIGINAL CODE
    --RETURN NULL

    ------------------NEW CODE-----------------
    NEW.id = nextval('table_id_seq');
    RETURN NEW;

END;
$$
LANGUAGE plpgsql;

是的,暂时我们确实有具有 2 个不同 ID 的重复条目。所以希望不存在对父 table 的唯一约束。如果他们这样做,您可能必须删除它们。我们需要删除父 table.

上的重复项

创建将 return 触发以在插入后使用的函数。触发器将删除插入父 table 测量的新行。

CREATE OR REPLACE FUNCTION measurement_after_insert_trigger()
RETURNS TRIGGER AS $$
BEGIN
    EXECUTE 'DELETE FROM measurement where measurement.id = ' || NEW.id;
    RETURN NULL;
END;
$$
LANGUAGE plpgsql;

最后 添加 After INSERT Trigger to parent table measurement

CREATE TRIGGER measurement_after_insert_trigger_delete_entry
AFTER INSERT ON measurement
FOR EACH ROW EXECUTE PROCEDURE measurement_after_insert_trigger();

我确实考虑过另一种可能的解决方案是只使用原始 sql 由于插入。只是不喜欢这种方法,因为我必须确保所有未来的构造函数都路由到原始 sql。上面的方法将允许代码按预期运行,就好像这只是我们插入的常规 table。