mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-25 04:45:32 +00:00
HHH-6998 - Expand CustomEntityDirtinessStrategy to define findDirty
This commit is contained in:
parent
ebee6b731e
commit
91847d7027
@ -23,6 +23,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.hibernate;
|
package org.hibernate;
|
||||||
|
|
||||||
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* During a flush cycle, Hibernate needs to determine which of the entities associated with a {@link Session}.
|
* During a flush cycle, Hibernate needs to determine which of the entities associated with a {@link Session}.
|
||||||
* Dirty entities are the ones that get {@literal UPDATE}ed to the database.
|
* Dirty entities are the ones that get {@literal UPDATE}ed to the database.
|
||||||
@ -40,29 +43,132 @@ public interface CustomEntityDirtinessStrategy {
|
|||||||
* {@link #isDirty} will be called next as the definitive means to determine whether the entity is dirty.
|
* {@link #isDirty} will be called next as the definitive means to determine whether the entity is dirty.
|
||||||
*
|
*
|
||||||
* @param entity The entity to be check.
|
* @param entity The entity to be check.
|
||||||
|
* @param persister The persister corresponding to the given entity
|
||||||
* @param session The session from which this check originates.
|
* @param session The session from which this check originates.
|
||||||
*
|
*
|
||||||
* @return {@code true} indicates the dirty check can be done; {@code false} indicates it cannot.
|
* @return {@code true} indicates the dirty check can be done; {@code false} indicates it cannot.
|
||||||
*/
|
*/
|
||||||
public boolean canDirtyCheck(Object entity, Session session);
|
public boolean canDirtyCheck(Object entity, EntityPersister persister, Session session);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The callback used by Hibernate to determine if the given entity is dirty. Only called if the previous
|
* The callback used by Hibernate to determine if the given entity is dirty. Only called if the previous
|
||||||
* {@link #canDirtyCheck} returned {@code true}
|
* {@link #canDirtyCheck} returned {@code true}
|
||||||
*
|
*
|
||||||
* @param entity The entity to check.
|
* @param entity The entity to check.
|
||||||
|
* @param persister The persister corresponding to the given entity
|
||||||
* @param session The session from which this check originates.
|
* @param session The session from which this check originates.
|
||||||
*
|
*
|
||||||
* @return {@code true} indicates the entity is dirty; {@link false} indicates the entity is not dirty.
|
* @return {@code true} indicates the entity is dirty; {@link false} indicates the entity is not dirty.
|
||||||
*/
|
*/
|
||||||
public boolean isDirty(Object entity, Session session);
|
public boolean isDirty(Object entity, EntityPersister persister, Session session);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback used by Hibernate to signal that the entity dirty flag should be cleared. Generally this
|
* Callback used by Hibernate to signal that the entity dirty flag should be cleared. Generally this
|
||||||
* happens after previous dirty changes were written to the database.
|
* happens after previous dirty changes were written to the database.
|
||||||
*
|
*
|
||||||
* @param entity The entity to reset
|
* @param entity The entity to reset
|
||||||
|
* @param persister The persister corresponding to the given entity
|
||||||
* @param session The session from which this call originates.
|
* @param session The session from which this call originates.
|
||||||
*/
|
*/
|
||||||
public void resetDirty(Object entity, Session session);
|
public void resetDirty(Object entity, EntityPersister persister, Session session);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback used to hook into Hibernate algorithm for determination of which attributes have changed. Applications
|
||||||
|
* wanting to hook in to this would call back into the given {@link DirtyCheckContext#doDirtyChecking}
|
||||||
|
* method passing along an appropriate {@link AttributeChecker} implementation.
|
||||||
|
*
|
||||||
|
* @param entity The entity being checked
|
||||||
|
* @param persister The persister corresponding to the given entity
|
||||||
|
* @param session The session from which this call originates.
|
||||||
|
* @param dirtyCheckContext The callback context
|
||||||
|
*/
|
||||||
|
public void findDirty(Object entity, EntityPersister persister, Session session, DirtyCheckContext dirtyCheckContext);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback to drive dirty checking. Handed to the {@link CustomEntityDirtinessStrategy#findDirty} method
|
||||||
|
* so that it can callback on to it if it wants to handle dirty checking rather than using Hibernate's default
|
||||||
|
* checking
|
||||||
|
*
|
||||||
|
* @see CustomEntityDirtinessStrategy#findDirty
|
||||||
|
*/
|
||||||
|
public static interface DirtyCheckContext {
|
||||||
|
/**
|
||||||
|
* The callback to indicate that dirty checking (the dirty attribute determination phase) should be handled
|
||||||
|
* by the calling {@link CustomEntityDirtinessStrategy} using the given {@link AttributeChecker}
|
||||||
|
*
|
||||||
|
* @param attributeChecker The delegate usable by the context for determining which attributes are dirty.
|
||||||
|
*/
|
||||||
|
public void doDirtyChecking(AttributeChecker attributeChecker);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responsible for identifying when attributes are dirty.
|
||||||
|
*/
|
||||||
|
public static interface AttributeChecker {
|
||||||
|
/**
|
||||||
|
* Do the attribute dirty check.
|
||||||
|
*
|
||||||
|
* @param attributeInformation Information about the attribute which is useful to help determine if it is
|
||||||
|
* dirty.
|
||||||
|
*
|
||||||
|
* @return {@code true} indicates the attribute value has changed; {@code false} indicates it has not.
|
||||||
|
*/
|
||||||
|
public boolean isDirty(AttributeInformation attributeInformation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides {@link AttributeChecker} with meta information about the attributes being checked.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings( {"UnusedDeclaration"})
|
||||||
|
public static interface AttributeInformation {
|
||||||
|
/**
|
||||||
|
* Get a reference to the persister for the entity containing this attribute.
|
||||||
|
*
|
||||||
|
* @return The entity persister.
|
||||||
|
*/
|
||||||
|
public EntityPersister getContainingPersister();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Many of Hibernate internals use arrays to define information about attributes. This value
|
||||||
|
* provides this index into those arrays for this particular attribute.
|
||||||
|
* <p/>
|
||||||
|
* It can be useful if needing to leverage those Hibernate internals.
|
||||||
|
*
|
||||||
|
* @return The attribute index.
|
||||||
|
*/
|
||||||
|
public int getAttributeIndex();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of this attribute
|
||||||
|
*
|
||||||
|
* @return The attribute name
|
||||||
|
*/
|
||||||
|
public String getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the mapping type of this attribute.
|
||||||
|
*
|
||||||
|
* @return The mapping type.
|
||||||
|
*/
|
||||||
|
public Type getType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current value of this attribute.
|
||||||
|
*
|
||||||
|
* @return The attributes current value
|
||||||
|
*/
|
||||||
|
public Object getCurrentValue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the loaded value of this attribute.
|
||||||
|
* <p/>
|
||||||
|
* <b>NOTE : A call to this method may require hitting the database in cases where the loaded state is
|
||||||
|
* not known. In those cases the db hit is incurred only once per entity, not for each attribute.</b>
|
||||||
|
*
|
||||||
|
* @return The attributes loaded value
|
||||||
|
*/
|
||||||
|
public Object getLoadedValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -229,16 +229,17 @@ public void postUpdate(Object entity, Object[] updatedState, Object nextVersion)
|
|||||||
this.version = nextVersion;
|
this.version = nextVersion;
|
||||||
getPersister().setPropertyValue( entity, getPersister().getVersionProperty(), nextVersion );
|
getPersister().setPropertyValue( entity, getPersister().getVersionProperty(), nextVersion );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( getPersister().getInstrumentationMetadata().isInstrumented() ) {
|
if ( getPersister().getInstrumentationMetadata().isInstrumented() ) {
|
||||||
final FieldInterceptor interceptor = getPersister().getInstrumentationMetadata().extractInterceptor( entity );
|
final FieldInterceptor interceptor = getPersister().getInstrumentationMetadata().extractInterceptor( entity );
|
||||||
if ( interceptor != null ) {
|
if ( interceptor != null ) {
|
||||||
interceptor.clearDirty();
|
interceptor.clearDirty();
|
||||||
}
|
}
|
||||||
persistenceContext.getSession()
|
|
||||||
.getFactory()
|
|
||||||
.getCustomEntityDirtinessStrategy()
|
|
||||||
.resetDirty( entity, (Session) persistenceContext.getSession() );
|
|
||||||
}
|
}
|
||||||
|
persistenceContext.getSession()
|
||||||
|
.getFactory()
|
||||||
|
.getCustomEntityDirtinessStrategy()
|
||||||
|
.resetDirty( entity, getPersister(), (Session) persistenceContext.getSession() );
|
||||||
|
|
||||||
notifyLoadedStateUpdated();
|
notifyLoadedStateUpdated();
|
||||||
}
|
}
|
||||||
@ -301,8 +302,8 @@ private boolean isUnequivocallyNonDirty(Object entity) {
|
|||||||
|
|
||||||
final CustomEntityDirtinessStrategy customEntityDirtinessStrategy =
|
final CustomEntityDirtinessStrategy customEntityDirtinessStrategy =
|
||||||
persistenceContext.getSession().getFactory().getCustomEntityDirtinessStrategy();
|
persistenceContext.getSession().getFactory().getCustomEntityDirtinessStrategy();
|
||||||
if ( customEntityDirtinessStrategy.canDirtyCheck( entity, (Session) persistenceContext.getSession() ) ) {
|
if ( customEntityDirtinessStrategy.canDirtyCheck( entity, getPersister(), (Session) persistenceContext.getSession() ) ) {
|
||||||
return ! customEntityDirtinessStrategy.isDirty( entity, (Session) persistenceContext.getSession() );
|
return ! customEntityDirtinessStrategy.isDirty( entity, getPersister(), (Session) persistenceContext.getSession() );
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -24,11 +24,14 @@
|
|||||||
package org.hibernate.event.internal;
|
package org.hibernate.event.internal;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
import org.hibernate.AssertionFailure;
|
import org.hibernate.AssertionFailure;
|
||||||
|
import org.hibernate.CustomEntityDirtinessStrategy;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
|
import org.hibernate.Session;
|
||||||
import org.hibernate.StaleObjectStateException;
|
import org.hibernate.StaleObjectStateException;
|
||||||
import org.hibernate.action.internal.DelayedPostInsertIdentifier;
|
import org.hibernate.action.internal.DelayedPostInsertIdentifier;
|
||||||
import org.hibernate.action.internal.EntityUpdateAction;
|
import org.hibernate.action.internal.EntityUpdateAction;
|
||||||
@ -252,7 +255,7 @@ private boolean isUpdateNecessary(final FlushEntityEvent event, final boolean mi
|
|||||||
event.getSession()
|
event.getSession()
|
||||||
.getFactory()
|
.getFactory()
|
||||||
.getCustomEntityDirtinessStrategy()
|
.getCustomEntityDirtinessStrategy()
|
||||||
.resetDirty( event.getEntity(), event.getSession() );
|
.resetDirty( event.getEntity(), event.getEntityEntry().getPersister(), event.getSession() );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -477,7 +480,7 @@ private boolean isCollectionDirtyCheckNecessary(EntityPersister persister, Statu
|
|||||||
/**
|
/**
|
||||||
* Perform a dirty check, and attach the results to the event
|
* Perform a dirty check, and attach the results to the event
|
||||||
*/
|
*/
|
||||||
protected void dirtyCheck(FlushEntityEvent event) throws HibernateException {
|
protected void dirtyCheck(final FlushEntityEvent event) throws HibernateException {
|
||||||
|
|
||||||
final Object entity = event.getEntity();
|
final Object entity = event.getEntity();
|
||||||
final Object[] values = event.getPropertyValues();
|
final Object[] values = event.getPropertyValues();
|
||||||
@ -494,7 +497,29 @@ protected void dirtyCheck(FlushEntityEvent event) throws HibernateException {
|
|||||||
loadedState,
|
loadedState,
|
||||||
persister.getPropertyNames(),
|
persister.getPropertyNames(),
|
||||||
persister.getPropertyTypes()
|
persister.getPropertyTypes()
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( dirtyProperties == null ) {
|
||||||
|
// see if the custom dirtiness strategy can tell us...
|
||||||
|
class DirtyCheckContextImpl implements CustomEntityDirtinessStrategy.DirtyCheckContext {
|
||||||
|
int[] found = null;
|
||||||
|
@Override
|
||||||
|
public void doDirtyChecking(CustomEntityDirtinessStrategy.AttributeChecker attributeChecker) {
|
||||||
|
found = new DirtyCheckAttributeInfoImpl( event ).visitAttributes( attributeChecker );
|
||||||
|
if ( found != null && found.length == 0 ) {
|
||||||
|
found = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DirtyCheckContextImpl context = new DirtyCheckContextImpl();
|
||||||
|
session.getFactory().getCustomEntityDirtinessStrategy().findDirty(
|
||||||
|
entity,
|
||||||
|
persister,
|
||||||
|
(Session) session,
|
||||||
|
context
|
||||||
);
|
);
|
||||||
|
dirtyProperties = context.found;
|
||||||
|
}
|
||||||
|
|
||||||
event.setDatabaseSnapshot(null);
|
event.setDatabaseSnapshot(null);
|
||||||
|
|
||||||
@ -554,6 +579,68 @@ else if ( entry.getStatus() == Status.DELETED && ! event.getEntityEntry().isModi
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class DirtyCheckAttributeInfoImpl implements CustomEntityDirtinessStrategy.AttributeInformation {
|
||||||
|
private final FlushEntityEvent event;
|
||||||
|
private final EntityPersister persister;
|
||||||
|
private final int numberOfAttributes;
|
||||||
|
private int index = 0;
|
||||||
|
|
||||||
|
private DirtyCheckAttributeInfoImpl(FlushEntityEvent event) {
|
||||||
|
this.event = event;
|
||||||
|
this.persister = event.getEntityEntry().getPersister();
|
||||||
|
this.numberOfAttributes = persister.getPropertyNames().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityPersister getContainingPersister() {
|
||||||
|
return persister;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAttributeIndex() {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return persister.getPropertyNames()[ index ];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return persister.getPropertyTypes()[ index ];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCurrentValue() {
|
||||||
|
return event.getPropertyValues()[ index ];
|
||||||
|
}
|
||||||
|
|
||||||
|
Object[] databaseSnapshot;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getLoadedValue() {
|
||||||
|
if ( databaseSnapshot == null ) {
|
||||||
|
databaseSnapshot = getDatabaseSnapshot( event.getSession(), persister, event.getEntityEntry().getId() );
|
||||||
|
}
|
||||||
|
return databaseSnapshot[ index ];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] visitAttributes(CustomEntityDirtinessStrategy.AttributeChecker attributeChecker) {
|
||||||
|
databaseSnapshot = null;
|
||||||
|
index = 0;
|
||||||
|
|
||||||
|
final int[] indexes = new int[ numberOfAttributes ];
|
||||||
|
int count = 0;
|
||||||
|
for ( ; index < numberOfAttributes; index++ ) {
|
||||||
|
if ( attributeChecker.isDirty( this ) ) {
|
||||||
|
indexes[ count++ ] = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Arrays.copyOf( indexes, count );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void logDirtyProperties(Serializable id, int[] dirtyProperties, EntityPersister persister) {
|
private void logDirtyProperties(Serializable id, int[] dirtyProperties, EntityPersister persister) {
|
||||||
if ( LOG.isTraceEnabled() && dirtyProperties != null && dirtyProperties.length > 0 ) {
|
if ( LOG.isTraceEnabled() && dirtyProperties != null && dirtyProperties.length > 0 ) {
|
||||||
final String[] allPropertyNames = persister.getPropertyNames();
|
final String[] allPropertyNames = persister.getPropertyNames();
|
||||||
|
@ -578,17 +578,21 @@ private CustomEntityDirtinessStrategy determineCustomEntityDirtinessStrategy(Pro
|
|||||||
// last resort
|
// last resort
|
||||||
return new CustomEntityDirtinessStrategy() {
|
return new CustomEntityDirtinessStrategy() {
|
||||||
@Override
|
@Override
|
||||||
public boolean canDirtyCheck(Object entity, Session session) {
|
public boolean canDirtyCheck(Object entity, EntityPersister persister, Session session) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDirty(Object entity, Session session) {
|
public boolean isDirty(Object entity, EntityPersister persister, Session session) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resetDirty(Object entity, Session session) {
|
public void resetDirty(Object entity, EntityPersister persister, Session session) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void findDirty(Object entity, DirtyCheckContext dirtyCheckContext) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -27,17 +27,24 @@
|
|||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.hibernate.cfg.Configuration;
|
import org.hibernate.cfg.Configuration;
|
||||||
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class CustomDirtinessStrategyTest extends BaseCoreFunctionalTestCase {
|
public class CustomDirtinessStrategyTest extends BaseCoreFunctionalTestCase {
|
||||||
|
private static final String INITIAL_NAME = "thing 1";
|
||||||
|
private static final String SUBSEQUENT_NAME = "thing 2";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure(Configuration configuration) {
|
protected void configure(Configuration configuration) {
|
||||||
super.configure( configuration );
|
super.configure( configuration );
|
||||||
@ -50,47 +57,116 @@ protected Class<?>[] getAnnotatedClasses() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCustomStrategy() throws Exception {
|
public void testOnlyCustomStrategy() {
|
||||||
final String INITIAL_NAME = "thing 1";
|
|
||||||
final String SUBSEQUENT_NAME = "thing 2";
|
|
||||||
|
|
||||||
Session session = openSession();
|
Session session = openSession();
|
||||||
session.beginTransaction();
|
session.beginTransaction();
|
||||||
session.save( new Thing( INITIAL_NAME ) );
|
Long id = (Long) session.save( new Thing( INITIAL_NAME ) );
|
||||||
session.getTransaction().commit();
|
session.getTransaction().commit();
|
||||||
session.close();
|
session.close();
|
||||||
|
|
||||||
|
Strategy.INSTANCE.resetState();
|
||||||
|
|
||||||
session = openSession();
|
session = openSession();
|
||||||
session.beginTransaction();
|
session.beginTransaction();
|
||||||
Thing thing = (Thing) session.get( Thing.class, 1L );
|
Thing thing = (Thing) session.get( Thing.class, id );
|
||||||
thing.setName( SUBSEQUENT_NAME );
|
thing.setName( SUBSEQUENT_NAME );
|
||||||
session.getTransaction().commit();
|
session.getTransaction().commit();
|
||||||
session.close();
|
session.close();
|
||||||
|
|
||||||
|
assertEquals( 1, Strategy.INSTANCE.canDirtyCheckCount );
|
||||||
|
assertEquals( 1, Strategy.INSTANCE.isDirtyCount );
|
||||||
|
assertEquals( 1, Strategy.INSTANCE.resetDirtyCount );
|
||||||
|
assertEquals( 1, Strategy.INSTANCE.findDirtyCount );
|
||||||
|
|
||||||
session = openSession();
|
session = openSession();
|
||||||
session.beginTransaction();
|
session.beginTransaction();
|
||||||
thing = (Thing) session.get( Thing.class, 1L );
|
thing = (Thing) session.get( Thing.class, id );
|
||||||
assertEquals( INITIAL_NAME, thing.getName() );
|
assertEquals( SUBSEQUENT_NAME, thing.getName() );
|
||||||
session.delete( thing );
|
session.delete( thing );
|
||||||
session.getTransaction().commit();
|
session.getTransaction().commit();
|
||||||
session.close();
|
session.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnlyCustomStrategyConsultedOnNonDirty() throws Exception {
|
||||||
|
Session session = openSession();
|
||||||
|
session.beginTransaction();
|
||||||
|
Long id = (Long) session.save( new Thing( INITIAL_NAME ) );
|
||||||
|
session.getTransaction().commit();
|
||||||
|
session.close();
|
||||||
|
|
||||||
|
session = openSession();
|
||||||
|
session.beginTransaction();
|
||||||
|
Thing thing = (Thing) session.get( Thing.class, id );
|
||||||
|
// lets change the name
|
||||||
|
thing.setName( SUBSEQUENT_NAME );
|
||||||
|
assertTrue( Strategy.INSTANCE.isDirty( thing, null, null ) );
|
||||||
|
// but fool the dirty map
|
||||||
|
thing.changedValues.clear();
|
||||||
|
assertFalse( Strategy.INSTANCE.isDirty( thing, null, null ) );
|
||||||
|
session.getTransaction().commit();
|
||||||
|
session.close();
|
||||||
|
|
||||||
|
session = openSession();
|
||||||
|
session.beginTransaction();
|
||||||
|
thing = (Thing) session.get( Thing.class, id );
|
||||||
|
assertEquals( INITIAL_NAME, thing.getName() );
|
||||||
|
session.createQuery( "delete Thing" ).executeUpdate();
|
||||||
|
session.getTransaction().commit();
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
|
||||||
public static class Strategy implements CustomEntityDirtinessStrategy {
|
public static class Strategy implements CustomEntityDirtinessStrategy {
|
||||||
public static final Strategy INSTANCE = new Strategy();
|
public static final Strategy INSTANCE = new Strategy();
|
||||||
|
|
||||||
@Override
|
int canDirtyCheckCount = 0;
|
||||||
public boolean canDirtyCheck(Object entity, Session session) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDirty(Object entity, Session session) {
|
public boolean canDirtyCheck(Object entity, EntityPersister persister, Session session) {
|
||||||
return false;
|
canDirtyCheckCount++;
|
||||||
|
System.out.println( "canDirtyCheck called" );
|
||||||
|
return Thing.class.isInstance( entity );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int isDirtyCount = 0;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resetDirty(Object entity, Session session) {
|
public boolean isDirty(Object entity, EntityPersister persister, Session session) {
|
||||||
|
isDirtyCount++;
|
||||||
|
System.out.println( "isDirty called" );
|
||||||
|
return ! Thing.class.cast( entity ).changedValues.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
int resetDirtyCount = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resetDirty(Object entity, EntityPersister persister, Session session) {
|
||||||
|
resetDirtyCount++;
|
||||||
|
System.out.println( "resetDirty called" );
|
||||||
|
Thing.class.cast( entity ).changedValues.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
int findDirtyCount = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void findDirty(final Object entity, EntityPersister persister, Session session, DirtyCheckContext dirtyCheckContext) {
|
||||||
|
findDirtyCount++;
|
||||||
|
System.out.println( "findDirty called" );
|
||||||
|
dirtyCheckContext.doDirtyChecking(
|
||||||
|
new AttributeChecker() {
|
||||||
|
@Override
|
||||||
|
public boolean isDirty(AttributeInformation attributeInformation) {
|
||||||
|
return Thing.class.cast( entity ).changedValues.containsKey( attributeInformation.getName() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetState() {
|
||||||
|
canDirtyCheckCount = 0;
|
||||||
|
isDirtyCount = 0;
|
||||||
|
resetDirtyCount = 0;
|
||||||
|
findDirtyCount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,10 @@
|
|||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Transient;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.hibernate.annotations.GenericGenerator;
|
import org.hibernate.annotations.GenericGenerator;
|
||||||
|
|
||||||
@ -61,6 +65,11 @@ public String getName() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
|
// intentionally simple dirty tracking (i.e. no checking against previous state)
|
||||||
|
changedValues.put( "name", this.name );
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
Map<String,Object> changedValues = new HashMap<String, Object>();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user