使用jpa进行表分区会抛出OptimisticLockException

时间:2016-04-07 11:45:46

标签: postgresql jpa triggers partitioning openjpa

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

触发示例

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实现。

使用表格上的触发器

  

当我尝试在事务中将新实体持久存储到此log_table时   使用触发器,我在提交时遇到异常:

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.
     

当我使用INSERT手动插入一条记录时,它会起作用,但会返回

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

没有表格上的触发器

  

使用抛出异常的相同java代码可以正常工作,因此代码应该没问题。对于坚持实体,我没有做任何例外。   手动INSERT命令返回

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));
...

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

SUBQUESTION
有没有办法使触发结果表现相同?我的意思是它将返回1行受影响?以某种方式传播内部函数的结果?

1 个答案:

答案 0 :(得分:1)

使用Java Ebean ORM时遇到同样的问题。因为rowCount失败而在java端插入导致优化锁定错误。

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

我的解决方案(在您的条件之后,将NEW.id更改为表中的下一个序列并返回NEW将导致插入父表)并为您提供需要影响的元组响应 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。所以希望不存在对父表的唯一约束。如果他们这样做,你可能不得不删除它们。我们需要删除父表上的副本。

创建将返回插入后使用的触发器的函数。触发器将删除插入父表测量的新行。

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;

最后 INSERT触发后添加到父表测量

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

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