问题
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 returnsQuery 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 returnsQuery 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