One very interesting approach is given by Christian Bauer (Hibernate committer and author of Hibernate in Action and Java Persistence with Hibernate) in this article .
You create the HISTORY table:
create table ITEM (
ITEM_ID NUMBER(19) NOT NULL,
DESC VARCHAR(255) NOT NULL,
PRICE NUMBER(19,2) NOT NULL,
PRIMARY KEY(ITEM_ID)
)
create table ITEM_HISTORY (
ITEM_ID NUMBER(19) NOT NULL,
DESC VARCHAR(255) NOT NULL,
PRICE NUMBER(19,2) NOT NULL,
VERSION NUMBER(10) NOT NULL,
PRIMARY KEY(ITEM_ID, VERSION)
)
Then, instead, you snap objects to the view:
create or replace view ITEM_VERSIONED (ITEM_ID, VERSION, DESC, PRICE) as
select I.ITEM_ID as ITEM_ID,
(select max(IH.VERSION)
from ITEM_HISTORY HI
where HI.ITEM_ID = I.ITEM_ID) as VERSION,
I.DESC as DESC,
I.PRICE as PRICE
from ITEM I
and DML statements are allowed by INSTEAD OF TRIGGERS, which are supported by PostgreSQL and Oracle:
create or replace trigger ITEM_INSERT
instead of insert on ITEM_VERSIONED begin
insert into ITEM(ITEM_ID, DESC, PRICE)
values (:n.ITEM_ID, :n.DESC, :n.PRICE);
insert into ITEM_HISTORY(ITEM_ID, DESC, PRICE, VERSION)
values (:n.ITEM_ID, :n.DESC, :n.PRICE, :n.VERSION);
end;
create or replace trigger ITEM_UPDATE
instead of update on ITEM_VERSIONED begin
update ITEM set
DESC = :n.DESC,
PRICE = :n.PRICE,
where
ITEM_ID = :n.ITEM_ID;
insert into ITEM_HISTORY(ITEM_ID, DESC, PRICE, VERSION)
values (:n.ITEM_ID, :n.DESC, :n.PRICE, :n.VERSION);
end;
This will work even for other applications that cannot use Hibernate, but they work with the same database.