table partitioning with jpa throws OptimisticLockException

99封情书 提交于 2019-12-13 07:20:09

问题


I have a log_table in my db that is partitioned according to partitioning docs. I have a function that inserts records to partition table depending on date and a trigger that calls that function, like in the documentation.

Trigger example

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

Function example

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;

I am using openjpa 2.3.0 as the JPA implementation.

With trigger on the 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.

Without the trigger on 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.

Can this difference in number of affected rows be the reason why openjpa cannot handle this correctly? On the code mentioned in the exception, I found this

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

It seems that because with the trigger the insert return 0 affected rows instead of 1, the code evaluates it as exceptional.

SUBQUESTION
IS there a way to make the trigger result behave the same? I mean that it would return that 1 row was affected? Somehow propagate the result from the inner function?


回答1:


I have the same problem using Java Ebean ORM. Inserting on java side causing Optimization lock error because of failing rowCount.

My Research into this problem did not lend a solution, i.e propogating row count from insert into child table or hacking the row count functionality.

My Solution (After your conditions, change NEW.id to next sequence in table and RETURN NEW that will cause insert into parent table) and give you the tuple response you need to affect 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;

Yes for a moment we do have duplicate entries that have 2 different ids. So hopefully no unique constraints on parent table exist. If they do you might have to remove them. We need to delete the duplicate on parent table.

Create Function that will return trigger to be used after insert. Trigger will delete the NEW row inserted into parent table measurement.

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;

Lastly Add 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();

I did consider another possible solution to this was to just have raw sql due the insert. Just did not like that approach as I would have to make sure all future constructors route to the raw sql. Approach above will allow the code to behave as expected as if this was just a regular table we were inserting to.



来源:https://stackoverflow.com/questions/36475235/table-partitioning-with-jpa-throws-optimisticlockexception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!