我的数据库中有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行受影响?以某种方式传播内部函数的结果?
答案 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。上面的方法将允许代码按预期运行,就像这只是我们插入的常规表一样。