HHH-3819:

- adding an option to store the data when an entity is deleted in the revision (instead of having to read the last-but-one revision)

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@16981 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Adam Warski 2009-06-30 15:56:49 +00:00
parent 206283ad66
commit 8165b1d70c
5 changed files with 122 additions and 2 deletions

View File

@ -133,6 +133,21 @@
(their history won't be stored; it normally doesn't make sense to store it).
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.storeDataAtDelete</property>
</entry>
<entry>
false
</entry>
<entry>
Should the entity data be stored in the revision when the entity is deleted (instead of only
storing the id and all other properties as null). This is not normally needed, as the data is
present in the last-but-one revision. Sometimes, however, it is easier and more efficient to
access it in the last revision (then the data that the entity contained before deletion is
stored twice).
</entry>
</row>
</tbody>
</tgroup>
</table>

View File

@ -36,6 +36,9 @@ public class GlobalConfiguration {
// Should the optimistic locking property of an entity be considered unversioned
private final boolean doNotAuditOptimisticLockingField;
// Should entity data be stored when it is deleted
private final boolean storeDataAtDelete;
/*
Which operator to use in correlated subqueries (when we want a property to be equal to the result of
a correlated subquery, for example: e.p <operator> (select max(e2.p) where e2.p2 = e.p2 ...).
@ -53,6 +56,9 @@ public class GlobalConfiguration {
"true");
doNotAuditOptimisticLockingField = Boolean.parseBoolean(ignoreOptimisticLockingPropertyStr);
String storeDataDeletedEntityStr = properties.getProperty("org.hibernate.envers.storeDataAtDelete", "false");
storeDataAtDelete = Boolean.parseBoolean(storeDataDeletedEntityStr);
correlatedSubqueryOperator = "org.hibernate.dialect.HSQLDialect".equals(
properties.getProperty("hibernate.dialect")) ? "in" : "=";
}
@ -68,4 +74,8 @@ public class GlobalConfiguration {
public String getCorrelatedSubqueryOperator() {
return correlatedSubqueryOperator;
}
public boolean isStoreDataAtDelete() {
return storeDataAtDelete;
}
}

View File

@ -174,7 +174,7 @@ public class AuditEventListener implements PostInsertEventListener, PostUpdateEv
AuditSync verSync = verCfg.getSyncManager().get(event.getSession());
verSync.addWorkUnit(new DelWorkUnit(event.getSession(), event.getPersister().getEntityName(), verCfg,
event.getId()));
event.getId(), event.getPersister(), event.getDeletedState()));
generateBidirectionalCollectionChangeWorkUnits(verSync, event.getPersister(), entityName, null,
event.getDeletedState(), event.getSession());

View File

@ -31,14 +31,22 @@ import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.AuditConfiguration;
import org.hibernate.Session;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.engine.SessionImplementor;
/**
* @author Adam Warski (adam at warski dot org)
*/
public class DelWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit {
public DelWorkUnit(SessionImplementor sessionImplementor, String entityName, AuditConfiguration verCfg, Serializable id) {
private final Object[] state;
private final String[] propertyNames;
public DelWorkUnit(SessionImplementor sessionImplementor, String entityName, AuditConfiguration verCfg,
Serializable id, EntityPersister entityPersister, Object[] state) {
super(sessionImplementor, entityName, verCfg, id);
this.state = state;
this.propertyNames = entityPersister.getPropertyNames();
}
public boolean containsWork() {
@ -49,6 +57,11 @@ public class DelWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit
Map<String, Object> data = new HashMap<String, Object>();
fillDataWithId(data, revisionData, RevisionType.DEL);
if (verCfg.getGlobalCfg().isStoreDataAtDelete()) {
verCfg.getEntCfg().get(getEntityName()).getPropertyMapper().map(sessionImplementor, data,
propertyNames, state, state);
}
session.save(verCfg.getAuditEntCfg().getAuditEntityName(getEntityName()), data);
setPerformed(data);

View File

@ -0,0 +1,82 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.envers.test.integration.query;
import org.hibernate.ejb.Ejb3Configuration;
import org.hibernate.envers.query.AuditEntity;
import org.hibernate.envers.test.AbstractEntityTest;
import org.hibernate.envers.test.entities.StrIntTestEntity;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import javax.persistence.EntityManager;
import java.util.List;
/**
* A test which checks if the data of a deleted entity is stored when the setting is on.
* @author Adam Warski (adam at warski dot org)
*/
@SuppressWarnings({"unchecked"})
public class StoreDeletedData extends AbstractEntityTest {
private Integer id1;
public void configure(Ejb3Configuration cfg) {
cfg.addAnnotatedClass(StrIntTestEntity.class);
cfg.setProperty("org.hibernate.envers.storeDataAtDelete", "true");
}
@BeforeClass(dependsOnMethods = "init")
public void initData() {
// Revision 1
EntityManager em = getEntityManager();
em.getTransaction().begin();
StrIntTestEntity site1 = new StrIntTestEntity("a", 10);
em.persist(site1);
id1 = site1.getId();
em.getTransaction().commit();
// Revision 2
em.getTransaction().begin();
em.remove(site1);
em.getTransaction().commit();
}
@Test
public void testRevisionsPropertyEqQuery() {
List revs_id1 = getAuditReader().createQuery()
.forRevisionsOfEntity(StrIntTestEntity.class, false, true)
.add(AuditEntity.id().eq(id1))
.getResultList();
assert revs_id1.size() == 2;
assert ((Object[]) revs_id1.get(0))[0].equals(new StrIntTestEntity("a", 10, id1));
assert ((Object[]) revs_id1.get(1))[0].equals(new StrIntTestEntity("a", 10, id1));
}
}