HHH-6970 - Expand notion of "natural id mutability" to ternary value
This commit is contained in:
parent
a2a1775c87
commit
d50a66bc20
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. 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 Inc.
|
||||
*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Possible values regarding the mutability of a natural id.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*
|
||||
* @see org.hibernate.annotations.NaturalId
|
||||
*/
|
||||
public enum NaturalIdMutability {
|
||||
/**
|
||||
* The natural id is mutable. Hibernate will write changes in the natural id value when flushing updates to the
|
||||
* the entity to the database. Also, it will invalidate any caching when such a change is detected.
|
||||
*/
|
||||
MUTABLE,
|
||||
|
||||
/**
|
||||
* The natural id is immutable. Hibernate will ignore any changes in the natural id value when flushing updates
|
||||
* to the entity to the database. Additionally Hibernate <b>will not</b> check with the database to check if the
|
||||
* natural id values change there. Essentially the user is assuring Hibernate that the values will not change.
|
||||
*/
|
||||
IMMUTABLE,
|
||||
|
||||
/**
|
||||
* The natural id is immutable. Hibernate will ignore any changes in the natural id value when flushing updates
|
||||
* to the entity to the database. However, Hibernate <b>will</b> check with the database to check if the natural
|
||||
* id values change there. This will ensure caching gets invalidated if the natural id value is changed in the
|
||||
* database (outside of this Hibernate SessionFactory).
|
||||
*
|
||||
* Note however that frequently changing natural ids are really not natural ids and should really not be mapped
|
||||
* as such. The overhead of maintaining caching of natural ids in these cases is far greater than the benefit
|
||||
* from such caching. In such cases, a database index is a much better solution.
|
||||
*/
|
||||
IMMUTABLE_CHECKED,
|
||||
|
||||
/**
|
||||
* @deprecated Added in deprecated form solely to allow seamless working until the deprecated attribute
|
||||
* {@link org.hibernate.annotations.NaturalId#mutable()} can be removed.
|
||||
*/
|
||||
@Deprecated
|
||||
UNSPECIFIED
|
||||
}
|
|
@ -26,8 +26,6 @@ package org.hibernate.annotations;
|
|||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.hibernate.NaturalIdMutability;
|
||||
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
@ -36,31 +34,14 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||
* This specifies that a property is part of the natural id of the entity.
|
||||
*
|
||||
* @author Nicol<EFBFBD>s Lichtmaier
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Target( { METHOD, FIELD } )
|
||||
@Retention( RUNTIME )
|
||||
public @interface NaturalId {
|
||||
/**
|
||||
* @deprecated Use {@link #mutability()} instead. For {@code mutable == false} (the default) use
|
||||
* {@link NaturalIdMutability#IMMUTABLE_CHECKED}; for {@code mutable == true} use
|
||||
* {@link NaturalIdMutability#MUTABLE}.
|
||||
* Is this natural id mutable (or immutable)?
|
||||
*
|
||||
* Note however the difference between {@link NaturalIdMutability#IMMUTABLE_CHECKED} which mimics the old behavior
|
||||
* of {@code mutable == false} and the new behavior available via {@link NaturalIdMutability#IMMUTABLE}
|
||||
* @return {@code true} indicates the natural id is mutable; {@code false} (the default) that it is immutable.
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings( {"JavaDoc"})
|
||||
boolean mutable() default false;
|
||||
|
||||
/**
|
||||
* The mutability behavior of this natural id.
|
||||
*
|
||||
* Note: the current default value is the {@link NaturalIdMutability#UNSPECIFIED} value which was added
|
||||
* in deprecated form until the deprecated {@link #mutable()} attribute here can be removed. This lets existing
|
||||
* applications continue to work seamlessly using their existing natural id annotations.
|
||||
*
|
||||
* @return The mutability behavior.
|
||||
*/
|
||||
NaturalIdMutability mutability() default NaturalIdMutability.UNSPECIFIED;
|
||||
}
|
||||
|
|
|
@ -104,7 +104,6 @@ public class BinderHelper {
|
|||
clone.setName( property.getName() );
|
||||
clone.setNodeName( property.getNodeName() );
|
||||
clone.setNaturalIdentifier( property.isNaturalIdentifier() );
|
||||
clone.setNaturalIdMutability( property.getNaturalIdMutability() );
|
||||
clone.setOptimisticLocked( property.isOptimisticLocked() );
|
||||
clone.setOptional( property.isOptional() );
|
||||
clone.setPersistentClass( property.getPersistentClass() );
|
||||
|
|
|
@ -40,7 +40,6 @@ import org.hibernate.EntityMode;
|
|||
import org.hibernate.FetchMode;
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.NaturalIdMutability;
|
||||
import org.hibernate.engine.internal.Versioning;
|
||||
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
|
||||
import org.hibernate.engine.spi.FilterDefinition;
|
||||
|
@ -2222,19 +2221,24 @@ public final class HbmBinder {
|
|||
}
|
||||
|
||||
if ( value != null ) {
|
||||
Property property = createProperty( value, propertyName, persistentClass
|
||||
.getClassName(), subnode, mappings, inheritedMetas );
|
||||
final Property property = createProperty(
|
||||
value,
|
||||
propertyName,
|
||||
persistentClass.getClassName(),
|
||||
subnode,
|
||||
mappings,
|
||||
inheritedMetas
|
||||
);
|
||||
if ( !mutable ) {
|
||||
property.setUpdateable(false);
|
||||
}
|
||||
if ( naturalId ) {
|
||||
property.setNaturalIdentifier(true);
|
||||
property.setNaturalIdMutability(
|
||||
mutable ? NaturalIdMutability.MUTABLE : NaturalIdMutability.IMMUTABLE_CHECKED
|
||||
);
|
||||
property.setNaturalIdentifier( true );
|
||||
}
|
||||
persistentClass.addProperty( property );
|
||||
if ( uniqueKey!=null ) uniqueKey.addColumns( property.getColumnIterator() );
|
||||
if ( uniqueKey!=null ) {
|
||||
uniqueKey.addColumns( property.getColumnIterator() );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ import javax.persistence.Id;
|
|||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.NaturalIdMutability;
|
||||
import org.hibernate.annotations.Generated;
|
||||
import org.hibernate.annotations.GenerationTime;
|
||||
import org.hibernate.annotations.Immutable;
|
||||
|
@ -286,20 +285,14 @@ public class PropertyBinder {
|
|||
}
|
||||
NaturalId naturalId = property != null ? property.getAnnotation( NaturalId.class ) : null;
|
||||
if ( naturalId != null ) {
|
||||
NaturalIdMutability mutability = naturalId.mutability();
|
||||
if ( mutability == NaturalIdMutability.UNSPECIFIED ) {
|
||||
mutability = naturalId.mutable()
|
||||
? NaturalIdMutability.MUTABLE
|
||||
: NaturalIdMutability.IMMUTABLE_CHECKED;
|
||||
}
|
||||
if ( mutability != NaturalIdMutability.MUTABLE ) {
|
||||
if ( ! naturalId.mutable() ) {
|
||||
updatable = false;
|
||||
}
|
||||
prop.setNaturalIdentifier( true );
|
||||
prop.setNaturalIdMutability( mutability );
|
||||
}
|
||||
prop.setInsertable( insertable );
|
||||
prop.setUpdateable( updatable );
|
||||
|
||||
OptimisticLock lockAnn = property != null ?
|
||||
property.getAnnotation( OptimisticLock.class ) :
|
||||
null;
|
||||
|
|
|
@ -96,34 +96,57 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
|
|||
Object[] loaded,
|
||||
SessionImplementor session) {
|
||||
if ( persister.hasNaturalIdentifier() && entry.getStatus() != Status.READ_ONLY ) {
|
||||
Object[] snapshot = null;
|
||||
Type[] types = persister.getPropertyTypes();
|
||||
int[] props = persister.getNaturalIdentifierProperties();
|
||||
boolean[] updateable = persister.getPropertyUpdateability();
|
||||
for ( int i=0; i<props.length; i++ ) {
|
||||
int prop = props[i];
|
||||
if ( !updateable[prop] ) {
|
||||
Object loadedVal;
|
||||
if ( loaded == null ) {
|
||||
if ( snapshot == null) {
|
||||
snapshot = session.getPersistenceContext().getNaturalIdSnapshot( entry.getId(), persister );
|
||||
}
|
||||
loadedVal = snapshot[i];
|
||||
} else {
|
||||
loadedVal = loaded[prop];
|
||||
}
|
||||
if ( !types[prop].isEqual( current[prop], loadedVal ) ) {
|
||||
throw new HibernateException(
|
||||
"immutable natural identifier of an instance of " +
|
||||
persister.getEntityName() +
|
||||
" was altered"
|
||||
);
|
||||
}
|
||||
if ( ! persister.getEntityMetamodel().hasImmutableNaturalId() ) {
|
||||
// SHORT-CUT: if the natural id is mutable (!immutable), no need to do the below checks
|
||||
// EARLY EXIT!!!
|
||||
return;
|
||||
}
|
||||
|
||||
final int[] naturalIdentifierPropertiesIndexes = persister.getNaturalIdentifierProperties();
|
||||
final Type[] propertyTypes = persister.getPropertyTypes();
|
||||
final boolean[] propertyUpdateability = persister.getPropertyUpdateability();
|
||||
|
||||
final Object[] snapshot = loaded == null
|
||||
? session.getPersistenceContext().getNaturalIdSnapshot( entry.getId(), persister )
|
||||
: extractNaturalIdValues( loaded, naturalIdentifierPropertiesIndexes );
|
||||
|
||||
for ( int i=0; i<naturalIdentifierPropertiesIndexes.length; i++ ) {
|
||||
final int naturalIdentifierPropertyIndex = naturalIdentifierPropertiesIndexes[i];
|
||||
if ( propertyUpdateability[ naturalIdentifierPropertyIndex ] ) {
|
||||
// if the given natural id property is updatable (mutable), there is nothing to check
|
||||
continue;
|
||||
}
|
||||
|
||||
final Type propertyType = propertyTypes[naturalIdentifierPropertyIndex];
|
||||
if ( ! propertyType.isEqual( current[naturalIdentifierPropertyIndex], snapshot[i] ) ) {
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
"An immutable natural identifier of entity %s was altered from %s to %s",
|
||||
persister.getEntityName(),
|
||||
propertyTypes[naturalIdentifierPropertyIndex].toLoggableString(
|
||||
snapshot[i],
|
||||
session.getFactory()
|
||||
),
|
||||
propertyTypes[naturalIdentifierPropertyIndex].toLoggableString(
|
||||
current[naturalIdentifierPropertyIndex],
|
||||
session.getFactory()
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Object[] extractNaturalIdValues(Object[] entitySnapshot, int[] naturalIdPropertyIndexes) {
|
||||
final Object[] naturalIdSnapshotSubSet = new Object[ naturalIdPropertyIndexes.length ];
|
||||
for ( int i = 0; i < naturalIdPropertyIndexes.length; i++ ) {
|
||||
naturalIdSnapshotSubSet[i] = entitySnapshot[ naturalIdPropertyIndexes[i] ];
|
||||
}
|
||||
return naturalIdSnapshotSubSet;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Flushes a single entity's state to the database, by scheduling
|
||||
* an update action, if necessary
|
||||
|
|
|
@ -22,13 +22,13 @@
|
|||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.mapping;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.hibernate.EntityMode;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.NaturalIdMutability;
|
||||
import org.hibernate.PropertyNotFoundException;
|
||||
import org.hibernate.engine.spi.CascadeStyle;
|
||||
import org.hibernate.engine.spi.Mapping;
|
||||
|
@ -61,7 +61,6 @@ public class Property implements Serializable, MetaAttributable {
|
|||
private java.util.Map metaAttributes;
|
||||
private PersistentClass persistentClass;
|
||||
private boolean naturalIdentifier;
|
||||
private NaturalIdMutability naturalIdMutability;
|
||||
|
||||
public boolean isBackRef() {
|
||||
return false;
|
||||
|
@ -147,20 +146,14 @@ public class Property implements Serializable, MetaAttributable {
|
|||
}
|
||||
|
||||
public boolean isUpdateable() {
|
||||
// if the property mapping consists of all formulas,
|
||||
// if the property mapping consists of all formulas,
|
||||
// make it non-updateable
|
||||
final boolean[] columnUpdateability = value.getColumnUpdateability();
|
||||
final boolean isImmutableNaturalId = isNaturalIdentifier()
|
||||
&& ( NaturalIdMutability.IMMUTABLE.equals( getNaturalIdMutability() )
|
||||
|| NaturalIdMutability.IMMUTABLE_CHECKED.equals( getNaturalIdMutability() ) );
|
||||
return updateable
|
||||
&& !isImmutableNaturalId
|
||||
&& !ArrayHelper.isAllFalse(columnUpdateability);
|
||||
return updateable && !ArrayHelper.isAllFalse( value.getColumnUpdateability() );
|
||||
}
|
||||
|
||||
public boolean isInsertable() {
|
||||
// if the property mapping consists of all formulas,
|
||||
// make it insertable
|
||||
// make it non-insertable
|
||||
final boolean[] columnInsertability = value.getColumnInsertability();
|
||||
return insertable && (
|
||||
columnInsertability.length==0 ||
|
||||
|
@ -176,8 +169,7 @@ public class Property implements Serializable, MetaAttributable {
|
|||
this.generation = generation;
|
||||
}
|
||||
|
||||
public void setUpdateable(
|
||||
boolean mutable) {
|
||||
public void setUpdateable(boolean mutable) {
|
||||
this.updateable = mutable;
|
||||
}
|
||||
|
||||
|
@ -299,7 +291,7 @@ public class Property implements Serializable, MetaAttributable {
|
|||
|
||||
// todo : remove
|
||||
public Getter getGetter(Class clazz) throws PropertyNotFoundException, MappingException {
|
||||
return getPropertyAccessor(clazz).getGetter(clazz, name);
|
||||
return getPropertyAccessor(clazz).getGetter( clazz, name );
|
||||
}
|
||||
|
||||
// todo : remove
|
||||
|
@ -320,12 +312,4 @@ public class Property implements Serializable, MetaAttributable {
|
|||
this.naturalIdentifier = naturalIdentifier;
|
||||
}
|
||||
|
||||
public NaturalIdMutability getNaturalIdMutability() {
|
||||
return naturalIdMutability;
|
||||
}
|
||||
|
||||
public void setNaturalIdMutability(NaturalIdMutability naturalIdMutability) {
|
||||
this.naturalIdMutability = naturalIdMutability;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@ import org.jboss.logging.Logger;
|
|||
import org.hibernate.EntityMode;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.NaturalIdMutability;
|
||||
import org.hibernate.engine.OptimisticLockStyle;
|
||||
import org.hibernate.engine.internal.Versioning;
|
||||
import org.hibernate.engine.spi.CascadeStyle;
|
||||
|
@ -196,7 +195,7 @@ public class EntityMetamodel implements Serializable {
|
|||
|
||||
if ( prop.isNaturalIdentifier() ) {
|
||||
naturalIdNumbers.add( i );
|
||||
if ( prop.isUpdateable() && NaturalIdMutability.MUTABLE.equals( prop.getNaturalIdMutability() ) ) {
|
||||
if ( prop.isUpdateable() ) {
|
||||
foundUpdateableNaturalIdProperty = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ import javax.persistence.Entity;
|
|||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.NaturalIdMutability;
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
|
||||
/**
|
||||
|
@ -56,7 +55,7 @@ public class Group {
|
|||
this.id = id;
|
||||
}
|
||||
|
||||
@NaturalId(mutability = NaturalIdMutability.MUTABLE)
|
||||
@NaturalId(mutable = true)
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue