HHH-2907 - ability to apply 'generation strategy' to generated properties

This commit is contained in:
Steve Ebersole 2013-10-25 13:17:57 -05:00
parent 19057a4685
commit 77825fefd6
26 changed files with 1318 additions and 262 deletions

View File

@ -0,0 +1,46 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Identifies the DEFAULT value to apply to the associated column via DDL.
*
* @author Steve Ebersole
*/
@Target( {FIELD, METHOD} )
@Retention( RUNTIME )
public @interface ColumnDefault {
/**
* The DEFAULT definition to apply to the DDL.
*/
String value();
}

View File

@ -23,6 +23,8 @@
*/
package org.hibernate.annotations;
import org.hibernate.tuple.GenerationTiming;
/**
* At what time(s) will the generation occur?
*
@ -32,13 +34,23 @@ public enum GenerationTime {
/**
* Indicates the value is never generated.
*/
NEVER,
NEVER( GenerationTiming.NEVER ),
/**
* Indicates the value is generated on insert.
*/
INSERT,
INSERT( GenerationTiming.INSERT ),
/**
* Indicates the value is generated on insert and on update.
*/
ALWAYS
ALWAYS( GenerationTiming.ALWAYS );
private final GenerationTiming equivalent;
private GenerationTime(GenerationTiming equivalent) {
this.equivalent = equivalent;
}
public GenerationTiming getEquivalent() {
return equivalent;
}
}

View File

@ -288,7 +288,7 @@ public class BinderHelper {
clone.setInsertable( false );
clone.setUpdateable( false );
clone.setNaturalIdentifier( false );
clone.setGeneration( property.getGeneration() );
clone.setValueGenerationStrategy( property.getValueGenerationStrategy() );
embeddedComp.addProperty( clone );
}
synthProp = new SyntheticProperty();

View File

@ -29,6 +29,7 @@ import org.jboss.logging.Logger;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.ColumnTransformer;
import org.hibernate.annotations.ColumnTransformers;
import org.hibernate.annotations.Index;
@ -75,6 +76,8 @@ public class Ejb3Column {
private String readExpression;
private String writeExpression;
private String defaultValue;
public void setTable(Table table) {
this.table = table;
}
@ -208,6 +211,14 @@ public class Ejb3Column {
return mappingColumn.isNullable();
}
public String getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
public Ejb3Column() {
}
@ -221,6 +232,9 @@ public class Ejb3Column {
initMappingColumn(
logicalColumnName, propertyName, length, precision, scale, nullable, sqlType, unique, true
);
if ( defaultValue != null ) {
mappingColumn.setDefaultValue( defaultValue );
}
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Binding column: %s", toString() );
}
@ -472,6 +486,11 @@ public class Ejb3Column {
: "";
final String columnName = nameNormalizer.normalizeIdentifierQuoting( col.name() );
Ejb3Column column = new Ejb3Column();
if ( length == 1 ) {
applyColumnDefault( column, inferredData );
}
column.setImplicit( false );
column.setSqlType( sqlType );
column.setLength( col.length() );
@ -506,6 +525,21 @@ public class Ejb3Column {
return columns;
}
private static void applyColumnDefault(Ejb3Column column, PropertyData inferredData) {
final XProperty xProperty = inferredData.getProperty();
if ( xProperty != null ) {
ColumnDefault columnDefaultAnn = xProperty.getAnnotation( ColumnDefault.class );
if ( columnDefaultAnn != null ) {
column.setDefaultValue( columnDefaultAnn.value() );
}
}
else {
LOG.trace(
"Could not perform @ColumnDefault lookup as 'PropertyData' did not give access to XProperty"
);
}
}
//must only be called after all setters are defined and before bind
private void extractDataFromPropertyData(PropertyData inferredData) {
if ( inferredData != null ) {
@ -572,6 +606,7 @@ public class Ejb3Column {
else {
column.setImplicit( true );
}
applyColumnDefault( column, inferredData );
column.extractDataFromPropertyData( inferredData );
column.bind();
return columns;

View File

@ -83,7 +83,6 @@ import org.hibernate.mapping.OneToOne;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.PrimitiveArray;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.PropertyGeneration;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Set;
@ -97,6 +96,9 @@ import org.hibernate.mapping.TypeDef;
import org.hibernate.mapping.UnionSubclass;
import org.hibernate.mapping.UniqueKey;
import org.hibernate.mapping.Value;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.ValueGeneration;
import org.hibernate.tuple.ValueGenerator;
import org.hibernate.type.BasicType;
import org.hibernate.type.DiscriminatorType;
import org.hibernate.type.ForeignKeyDirection;
@ -524,9 +526,11 @@ public final class HbmBinder {
// for version properties marked as being generated, make sure they are "always"
// generated; aka, "insert" is invalid; this is dis-allowed by the DTD,
// but just to make sure...
if ( prop.getGeneration() == PropertyGeneration.INSERT ) {
if ( prop.getValueGenerationStrategy() != null ) {
if ( prop.getValueGenerationStrategy().getGenerationTiming() == GenerationTiming.INSERT ) {
throw new MappingException( "'generated' attribute cannot be 'insert' for versioning property" );
}
}
makeVersion( subnode, val );
entity.setVersion( prop );
entity.addProperty( prop );
@ -1297,10 +1301,14 @@ public final class HbmBinder {
Attribute generatedNode = node.attribute( "generated" );
String generationName = generatedNode == null ? null : generatedNode.getValue();
PropertyGeneration generation = PropertyGeneration.parse( generationName );
property.setGeneration( generation );
if ( generation == PropertyGeneration.ALWAYS || generation == PropertyGeneration.INSERT ) {
// Handle generated properties.
GenerationTiming generationTiming = GenerationTiming.parseFromName( generationName );
if ( generationTiming == GenerationTiming.ALWAYS || generationTiming == GenerationTiming.INSERT ) {
// we had generation specified...
// HBM only supports "database generated values"
property.setValueGenerationStrategy( new HbmDefinedValueGeneration( generationTiming ) );
// generated properties can *never* be insertable...
if ( property.isInsertable() ) {
if ( insertNode == null ) {
@ -1312,7 +1320,7 @@ public final class HbmBinder {
// the user specifically supplied insert="true",
// which constitutes an illegal combo
throw new MappingException(
"cannot specify both insert=\"true\" and generated=\"" + generation.getName() +
"cannot specify both insert=\"true\" and generated=\"" + generationTiming.name().toLowerCase() +
"\" for property: " +
propName
);
@ -1320,7 +1328,7 @@ public final class HbmBinder {
}
// properties generated on update can never be updateable...
if ( property.isUpdateable() && generation == PropertyGeneration.ALWAYS ) {
if ( property.isUpdateable() && generationTiming == GenerationTiming.ALWAYS ) {
if ( updateNode == null ) {
// updateable only because the user did not specify
// anything; just override it
@ -1330,7 +1338,7 @@ public final class HbmBinder {
// the user specifically supplied update="true",
// which constitutes an illegal combo
throw new MappingException(
"cannot specify both update=\"true\" and generated=\"" + generation.getName() +
"cannot specify both update=\"true\" and generated=\"" + generationTiming.name().toLowerCase() +
"\" for property: " +
propName
);
@ -1338,6 +1346,7 @@ public final class HbmBinder {
}
}
boolean isLazyable = "property".equals( node.getName() ) ||
"component".equals( node.getName() ) ||
"many-to-one".equals( node.getName() ) ||
@ -1361,6 +1370,37 @@ public final class HbmBinder {
}
private static class HbmDefinedValueGeneration implements ValueGeneration {
private final GenerationTiming timing;
private HbmDefinedValueGeneration(GenerationTiming timing) {
this.timing = timing;
}
@Override
public GenerationTiming getGenerationTiming() {
return timing;
}
@Override
public ValueGenerator getValueGenerator() {
// database generated values do not have a value generator
return null;
}
@Override
public boolean referenceColumnInSql() {
// historically these columns are not referenced in the SQL
return false;
}
@Override
public String getDatabaseGeneratedReferencedColumnValue() {
// the column is not referenced in the sql.
return null;
}
}
private static String columns(Value val) {
StringBuilder columns = new StringBuilder();
Iterator iter = val.getColumnIterator();

View File

@ -381,7 +381,7 @@ public class MapBinder extends CollectionBinder {
Property current = (Property) properties.next();
Property newProperty = new Property();
newProperty.setCascade( current.getCascade() );
newProperty.setGeneration( current.getGeneration() );
newProperty.setValueGenerationStrategy( current.getValueGenerationStrategy() );
newProperty.setInsertable( false );
newProperty.setUpdateable( false );
newProperty.setMetaAttributes( current.getMetaAttributes() );

View File

@ -52,11 +52,14 @@ import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.PropertyGeneration;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.ValueGeneration;
import org.hibernate.tuple.ValueGenerator;
import org.jboss.logging.Logger;
/**
@ -272,27 +275,8 @@ public class PropertyBinder {
prop.setCascade( cascade );
prop.setPropertyAccessorName( accessType.getType() );
Generated ann = property != null ?
property.getAnnotation( Generated.class ) :
null;
GenerationTime generated = ann != null ?
ann.value() :
null;
if ( generated != null ) {
if ( !GenerationTime.NEVER.equals( generated ) ) {
if ( property.isAnnotationPresent( javax.persistence.Version.class )
&& GenerationTime.INSERT.equals( generated ) ) {
throw new AnnotationException(
"@Generated(INSERT) on a @Version property not allowed, use ALWAYS: "
+ StringHelper.qualify( holder.getPath(), name )
);
}
insertable = false;
if ( GenerationTime.ALWAYS.equals( generated ) ) {
updatable = false;
}
prop.setGeneration( PropertyGeneration.parse( generated.toString().toLowerCase() ) );
}
if ( property != null ) {
prop.setValueGenerationStrategy( determineValueGenerationStrategy( property ) );
}
NaturalId naturalId = property != null ? property.getAnnotation( NaturalId.class ) : null;
@ -345,6 +329,89 @@ public class PropertyBinder {
return prop;
}
private ValueGeneration determineValueGenerationStrategy(XProperty property) {
// for now, we just handle the legacy '@Generated' annotation
Generated generatedAnnotation = property.getAnnotation( Generated.class );
if ( generatedAnnotation == null
|| generatedAnnotation.value() == null
|| generatedAnnotation.value() == GenerationTime.NEVER ) {
return NoValueGeneration.INSTANCE;
}
final GenerationTiming when = generatedAnnotation.value().getEquivalent();
if ( property.isAnnotationPresent( javax.persistence.Version.class ) && when == GenerationTiming.INSERT ) {
throw new AnnotationException(
"@Generated(INSERT) on a @Version property not allowed, use ALWAYS (or NEVER): "
+ StringHelper.qualify( holder.getPath(), name )
);
}
insertable = false;
if ( when == GenerationTiming.ALWAYS ) {
updatable = false;
}
return new LegacyValueGeneration( when );
}
private static class NoValueGeneration implements ValueGeneration {
/**
* Singleton access
*/
public static final NoValueGeneration INSTANCE = new NoValueGeneration();
@Override
public GenerationTiming getGenerationTiming() {
return GenerationTiming.NEVER;
}
@Override
public ValueGenerator getValueGenerator() {
return null;
}
@Override
public boolean referenceColumnInSql() {
return true;
}
@Override
public String getDatabaseGeneratedReferencedColumnValue() {
return null;
}
}
private static class LegacyValueGeneration implements ValueGeneration {
private final GenerationTiming timing;
private LegacyValueGeneration(GenerationTiming timing) {
this.timing = timing;
}
@Override
public GenerationTiming getGenerationTiming() {
return timing;
}
@Override
public ValueGenerator getValueGenerator() {
// database generated values do not have a value generator
return null;
}
@Override
public boolean referenceColumnInSql() {
// historically these columns are not referenced in the SQL
return false;
}
@Override
public String getDatabaseGeneratedReferencedColumnValue() {
return null;
}
}
private boolean isCollection(Value value) {
return Collection.class.isInstance( value );
}

View File

@ -23,6 +23,7 @@
*
*/
package org.hibernate.id;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@ -33,7 +34,6 @@ import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.ValueInclusion;
import org.hibernate.id.insert.AbstractSelectingDelegate;
import org.hibernate.id.insert.IdentifierGeneratingInsert;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
@ -80,8 +80,7 @@ public class SelectGenerator extends AbstractPostInsertGenerator implements Conf
"natural-id properties; need to specify [key] in generator parameters"
);
}
ValueInclusion inclusion = persister.getPropertyInsertGenerationInclusions() [ naturalIdPropertyIndices[0] ];
if ( inclusion != ValueInclusion.NONE ) {
if ( persister.getEntityMetamodel().isNaturalIdentifierInsertGenerated() ) {
throw new IdentifierGenerationException(
"natural-id also defined as insert-generated; need to specify [key] " +
"in generator parameters"

View File

@ -38,6 +38,7 @@ import org.hibernate.property.Getter;
import org.hibernate.property.PropertyAccessor;
import org.hibernate.property.PropertyAccessorFactory;
import org.hibernate.property.Setter;
import org.hibernate.tuple.ValueGeneration;
import org.hibernate.type.CompositeType;
import org.hibernate.type.Type;
@ -54,7 +55,7 @@ public class Property implements Serializable, MetaAttributable {
private boolean insertable = true;
private boolean selectable = true;
private boolean optimisticLocked = true;
private PropertyGeneration generation = PropertyGeneration.NEVER;
private ValueGeneration valueGenerationStrategy;
private String propertyAccessorName;
private boolean lazy;
private boolean optional;
@ -189,12 +190,12 @@ public class Property implements Serializable, MetaAttributable {
);
}
public PropertyGeneration getGeneration() {
return generation;
public ValueGeneration getValueGenerationStrategy() {
return valueGenerationStrategy;
}
public void setGeneration(PropertyGeneration generation) {
this.generation = generation;
public void setValueGenerationStrategy(ValueGeneration valueGenerationStrategy) {
this.valueGenerationStrategy = valueGenerationStrategy;
}
public void setUpdateable(boolean mutable) {

View File

@ -29,7 +29,10 @@ import java.io.Serializable;
* so, at what time(s) they are generated.
*
* @author Steve Ebersole
*
* @deprecated This is replaced by {@link org.hibernate.tuple.ValueGeneration} and {@link org.hibernate.tuple.GenerationTiming}
*/
@Deprecated
public class PropertyGeneration implements Serializable {
/**

View File

@ -124,6 +124,11 @@ import org.hibernate.sql.SelectFragment;
import org.hibernate.sql.SimpleSelect;
import org.hibernate.sql.Template;
import org.hibernate.sql.Update;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.InDatabaseValueGenerationStrategy;
import org.hibernate.tuple.InMemoryValueGenerationStrategy;
import org.hibernate.tuple.NonIdentifierAttribute;
import org.hibernate.tuple.ValueGeneration;
import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.tuple.entity.EntityTuplizer;
import org.hibernate.type.AssociationType;
@ -1679,14 +1684,14 @@ public abstract class AbstractEntityPersister
}
protected String generateInsertGeneratedValuesSelectString() {
return generateGeneratedValuesSelectString( getPropertyInsertGenerationInclusions() );
return generateGeneratedValuesSelectString( GenerationTiming.INSERT );
}
protected String generateUpdateGeneratedValuesSelectString() {
return generateGeneratedValuesSelectString( getPropertyUpdateGenerationInclusions() );
return generateGeneratedValuesSelectString( GenerationTiming.ALWAYS );
}
private String generateGeneratedValuesSelectString(ValueInclusion[] inclusions) {
private String generateGeneratedValuesSelectString(final GenerationTiming generationTimingToMatch) {
Select select = new Select( getFactory().getDialect() );
if ( getFactory().getSettings().isCommentsEnabled() ) {
@ -1698,7 +1703,18 @@ public abstract class AbstractEntityPersister
// Here we render the select column list based on the properties defined as being generated.
// For partial component generation, we currently just re-select the whole component
// rather than trying to handle the individual generated portions.
String selectClause = concretePropertySelectFragment( getRootAlias(), inclusions );
String selectClause = concretePropertySelectFragment(
getRootAlias(),
new InclusionChecker() {
@Override
public boolean includeProperty(int propertyNumber) {
final InDatabaseValueGenerationStrategy generationStrategy
= entityMetamodel.getInDatabaseValueGenerationStrategies()[propertyNumber];
return generationStrategy != null
&& generationStrategy.getGenerationTiming() == generationTimingToMatch;
}
}
);
selectClause = selectClause.substring( 2 );
String fromClause = fromTableFragment( getRootAlias() ) +
@ -1721,21 +1737,6 @@ public abstract class AbstractEntityPersister
public boolean includeProperty(int propertyNumber);
}
protected String concretePropertySelectFragment(String alias, final ValueInclusion[] inclusions) {
return concretePropertySelectFragment(
alias,
new InclusionChecker() {
// TODO : currently we really do not handle ValueInclusion.PARTIAL...
// ValueInclusion.PARTIAL would indicate parts of a component need to
// be included in the select; currently we then just render the entire
// component into the select clause in that case.
public boolean includeProperty(int propertyNumber) {
return inclusions[propertyNumber] != ValueInclusion.NONE;
}
}
);
}
protected String concretePropertySelectFragment(String alias, final boolean[] includeProperty) {
return concretePropertySelectFragment(
alias,
@ -2643,8 +2644,8 @@ public abstract class AbstractEntityPersister
}
private boolean checkVersion(final boolean[] includeProperty) {
return includeProperty[ getVersionProperty() ] ||
entityMetamodel.getPropertyUpdateGenerationInclusions()[ getVersionProperty() ] != ValueInclusion.NONE;
return includeProperty[ getVersionProperty() ]
|| entityMetamodel.isVersionGenerated();
}
protected String generateInsertString(boolean[] includeProperty, int j) {
@ -2658,8 +2659,7 @@ public abstract class AbstractEntityPersister
/**
* Generate the SQL that inserts a row
*/
protected String generateInsertString(boolean identityInsert,
boolean[] includeProperty, int j) {
protected String generateInsertString(boolean identityInsert, boolean[] includeProperty, int j) {
// todo : remove the identityInsert param and variations;
// identity-insert strings are now generated from generateIdentityInsertString()
@ -2669,13 +2669,37 @@ public abstract class AbstractEntityPersister
// add normal properties
for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
if ( includeProperty[i] && isPropertyOfTable( i, j )
&& !lobProperties.contains( i ) ) {
// this property belongs on the table and is to be inserted
insert.addColumns( getPropertyColumnNames(i),
propertyColumnInsertable[i],
propertyColumnWriters[i] );
// the incoming 'includeProperty' array only accounts for insertable defined at the root level, it
// does not account for partially generated composites etc. We also need to account for generation
// values
if ( isPropertyOfTable( i, j ) ) {
if ( !lobProperties.contains( i ) ) {
final InDatabaseValueGenerationStrategy generationStrategy = entityMetamodel.getInDatabaseValueGenerationStrategies()[i];
if ( generationStrategy != null && generationStrategy.getGenerationTiming().includesInsert() ) {
if ( generationStrategy.referenceColumnsInSql() ) {
final String[] values;
if ( generationStrategy.getReferencedColumnValues() == null ) {
values = propertyColumnWriters[i];
}
else {
final int numberOfColumns = propertyColumnWriters[i].length;
values = new String[ numberOfColumns ];
for ( int x = 0; x < numberOfColumns; x++ ) {
if ( generationStrategy.getReferencedColumnValues()[x] != null ) {
values[x] = generationStrategy.getReferencedColumnValues()[x];
}
else {
values[x] = propertyColumnWriters[i][x];
}
}
}
insert.addColumns( getPropertyColumnNames(i), propertyColumnInsertable[i], values );
}
}
else if ( includeProperty[i] ) {
insert.addColumns( getPropertyColumnNames(i), propertyColumnInsertable[i], propertyColumnWriters[i] );
}
}
}
}
@ -2702,9 +2726,11 @@ public abstract class AbstractEntityPersister
for ( int i : lobProperties ) {
if ( includeProperty[i] && isPropertyOfTable( i, j ) ) {
// this property belongs on the table and is to be inserted
insert.addColumns( getPropertyColumnNames(i),
insert.addColumns(
getPropertyColumnNames(i),
propertyColumnInsertable[i],
propertyColumnWriters[i] );
propertyColumnWriters[i]
);
}
}
@ -3422,6 +3448,18 @@ public abstract class AbstractEntityPersister
final Object rowId,
final SessionImplementor session) throws HibernateException {
// apply any pre-update in-memory value generation
if ( getEntityMetamodel().hasPreUpdateGeneratedValues() ) {
final InMemoryValueGenerationStrategy[] strategies = getEntityMetamodel().getInMemoryValueGenerationStrategies();
for ( int i = 0; i < strategies.length; i++ ) {
if ( strategies[i] != null && strategies[i].getGenerationTiming().includesUpdate() ) {
fields[i] = strategies[i].getValueGenerator().generateValue( session, object );
setPropertyValue( object, i, fields[i] );
// todo : probably best to add to dirtyFields if not-null
}
}
}
//note: dirtyFields==null means we had no snapshot, and we couldn't get one using select-before-update
// oldFields==null just means we had no snapshot to begin with (we might have used select-before-update to get the dirtyFields)
@ -3519,8 +3557,17 @@ public abstract class AbstractEntityPersister
return id;
}
public void insert(Serializable id, Object[] fields, Object object, SessionImplementor session)
throws HibernateException {
public void insert(Serializable id, Object[] fields, Object object, SessionImplementor session) {
// apply any pre-insert in-memory value generation
if ( getEntityMetamodel().hasPreInsertGeneratedValues() ) {
final InMemoryValueGenerationStrategy[] strategies = getEntityMetamodel().getInMemoryValueGenerationStrategies();
for ( int i = 0; i < strategies.length; i++ ) {
if ( strategies[i] != null && strategies[i].getGenerationTiming().includesInsert() ) {
fields[i] = strategies[i].getValueGenerator().generateValue( session, object );
setPropertyValue( object, i, fields[i] );
}
}
}
final int span = getTableSpan();
if ( entityMetamodel.isDynamicInsert() ) {
@ -4485,7 +4532,7 @@ public abstract class AbstractEntityPersister
}
public boolean isVersionPropertyGenerated() {
return isVersioned() && ( getPropertyUpdateGenerationInclusions() [ getVersionProperty() ] != ValueInclusion.NONE );
return isVersioned() && getEntityMetamodel().isVersionGenerated();
}
public boolean isVersionPropertyInsertable() {
@ -4524,12 +4571,14 @@ public abstract class AbstractEntityPersister
return entityMetamodel.getPropertyInsertability();
}
@Deprecated
public ValueInclusion[] getPropertyInsertGenerationInclusions() {
return entityMetamodel.getPropertyInsertGenerationInclusions();
return null;
}
@Deprecated
public ValueInclusion[] getPropertyUpdateGenerationInclusions() {
return entityMetamodel.getPropertyUpdateGenerationInclusions();
return null;
}
public boolean[] getPropertyNullability() {
@ -4663,14 +4712,14 @@ public abstract class AbstractEntityPersister
if ( !hasInsertGeneratedProperties() ) {
throw new AssertionFailure("no insert-generated properties");
}
processGeneratedProperties( id, entity, state, session, sqlInsertGeneratedValuesSelectString, getPropertyInsertGenerationInclusions() );
processGeneratedProperties( id, entity, state, session, sqlInsertGeneratedValuesSelectString, GenerationTiming.INSERT );
}
public void processUpdateGeneratedProperties(Serializable id, Object entity, Object[] state, SessionImplementor session) {
if ( !hasUpdateGeneratedProperties() ) {
throw new AssertionFailure("no update-generated properties");
}
processGeneratedProperties( id, entity, state, session, sqlUpdateGeneratedValuesSelectString, getPropertyUpdateGenerationInclusions() );
processGeneratedProperties( id, entity, state, session, sqlUpdateGeneratedValuesSelectString, GenerationTiming.ALWAYS );
}
private void processGeneratedProperties(
@ -4679,7 +4728,7 @@ public abstract class AbstractEntityPersister
Object[] state,
SessionImplementor session,
String selectionSQL,
ValueInclusion[] includeds) {
GenerationTiming matchTiming) {
// force immediate execution of the insert batch (if one)
session.getTransactionCoordinator().getJdbcCoordinator().executeBatch();
@ -4698,13 +4747,28 @@ public abstract class AbstractEntityPersister
MessageHelper.infoString( this, id, getFactory() )
);
}
for ( int i = 0; i < getPropertySpan(); i++ ) {
if ( includeds[i] != ValueInclusion.NONE ) {
Object hydratedState = getPropertyTypes()[i].hydrate( rs, getPropertyAliases( "", i ), session, entity );
state[i] = getPropertyTypes()[i].resolve( hydratedState, session, entity );
setPropertyValue( entity, i, state[i] );
int propertyIndex = -1;
for ( NonIdentifierAttribute attribute : entityMetamodel.getProperties() ) {
propertyIndex++;
final ValueGeneration valueGeneration = attribute.getValueGenerationStrategy();
if ( valueGeneration != null && valueGeneration.getGenerationTiming() == matchTiming ) {
final Object hydratedState = attribute.getType().hydrate(
rs, getPropertyAliases(
"",
propertyIndex
), session, entity
);
state[propertyIndex] = attribute.getType().resolve( hydratedState, session, entity );
setPropertyValue( entity, propertyIndex, state[propertyIndex] );
}
}
// for ( int i = 0; i < getPropertySpan(); i++ ) {
// if ( includeds[i] != ValueInclusion.NONE ) {
// Object hydratedState = getPropertyTypes()[i].hydrate( rs, getPropertyAliases( "", i ), session, entity );
// state[i] = getPropertyTypes()[i].resolve( hydratedState, session, entity );
// setPropertyValue( entity, i, state[i] );
// }
// }
}
finally {
if ( rs != null ) {

View File

@ -418,12 +418,18 @@ public interface EntityPersister extends OptimisticCacheSource, EntityDefinition
/**
* Which of the properties of this class are database generated values on insert?
*
* @deprecated Replaced internally with InMemoryValueGenerationStrategy / InDatabaseValueGenerationStrategy
*/
@Deprecated
public ValueInclusion[] getPropertyInsertGenerationInclusions();
/**
* Which of the properties of this class are database generated values on update?
*
* @deprecated Replaced internally with InMemoryValueGenerationStrategy / InDatabaseValueGenerationStrategy
*/
@Deprecated
public ValueInclusion[] getPropertyUpdateGenerationInclusions();
/**

View File

@ -87,13 +87,8 @@ public abstract class AbstractNonIdentifierAttribute extends AbstractAttribute i
}
@Override
public boolean isInsertGenerated() {
return attributeInformation.isInsertGenerated();
}
@Override
public boolean isUpdateGenerated() {
return attributeInformation.isUpdateGenerated();
public ValueGeneration getValueGenerationStrategy() {
return attributeInformation.getValueGenerationStrategy();
}
@Override

View File

@ -10,8 +10,7 @@ public class BaselineAttributeInformation {
private final boolean lazy;
private final boolean insertable;
private final boolean updateable;
private final boolean insertGenerated;
private final boolean updateGenerated;
private final ValueGeneration valueGenerationStrategy;
private final boolean nullable;
private final boolean dirtyCheckable;
private final boolean versionable;
@ -23,8 +22,7 @@ public class BaselineAttributeInformation {
boolean lazy,
boolean insertable,
boolean updateable,
boolean insertGenerated,
boolean updateGenerated,
ValueGeneration valueGenerationStrategy,
boolean nullable,
boolean dirtyCheckable,
boolean versionable,
@ -33,8 +31,7 @@ public class BaselineAttributeInformation {
this.lazy = lazy;
this.insertable = insertable;
this.updateable = updateable;
this.insertGenerated = insertGenerated;
this.updateGenerated = updateGenerated;
this.valueGenerationStrategy = valueGenerationStrategy;
this.nullable = nullable;
this.dirtyCheckable = dirtyCheckable;
this.versionable = versionable;
@ -54,12 +51,8 @@ public class BaselineAttributeInformation {
return updateable;
}
public boolean isInsertGenerated() {
return insertGenerated;
}
public boolean isUpdateGenerated() {
return updateGenerated;
public ValueGeneration getValueGenerationStrategy() {
return valueGenerationStrategy;
}
public boolean isNullable() {
@ -90,8 +83,7 @@ public class BaselineAttributeInformation {
private boolean lazy;
private boolean insertable;
private boolean updateable;
private boolean insertGenerated;
private boolean updateGenerated;
private ValueGeneration valueGenerationStrategy;
private boolean nullable;
private boolean dirtyCheckable;
private boolean versionable;
@ -113,13 +105,8 @@ public class BaselineAttributeInformation {
return this;
}
public Builder setInsertGenerated(boolean insertGenerated) {
this.insertGenerated = insertGenerated;
return this;
}
public Builder setUpdateGenerated(boolean updateGenerated) {
this.updateGenerated = updateGenerated;
public Builder setValueGenerationStrategy(ValueGeneration valueGenerationStrategy) {
this.valueGenerationStrategy = valueGenerationStrategy;
return this;
}
@ -153,8 +140,7 @@ public class BaselineAttributeInformation {
lazy,
insertable,
updateable,
insertGenerated,
updateGenerated,
valueGenerationStrategy,
nullable,
dirtyCheckable,
versionable,

View File

@ -0,0 +1,78 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.tuple;
/**
* @author Steve Ebersole
*/
public enum GenerationTiming {
NEVER {
@Override
public boolean includesInsert() {
return false;
}
@Override
public boolean includesUpdate() {
return false;
}
},
INSERT {
@Override
public boolean includesInsert() {
return true;
}
@Override
public boolean includesUpdate() {
return false;
}
},
ALWAYS {
@Override
public boolean includesInsert() {
return true;
}
@Override
public boolean includesUpdate() {
return true;
}
};
public abstract boolean includesInsert();
public abstract boolean includesUpdate();
public static GenerationTiming parseFromName(String name) {
if ( "insert".equalsIgnoreCase( name ) ) {
return INSERT;
}
else if ( "always".equalsIgnoreCase( name ) ) {
return ALWAYS;
}
else {
return NEVER;
}
}
}

View File

@ -0,0 +1,59 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.tuple;
/**
* Strategy for describing values which are generated in the database.
*
* @author Steve Ebersole
*/
public interface InDatabaseValueGenerationStrategy {
/**
* When is this value generated : NEVER, INSERT, ALWAYS (INSERT+UPDATE)
*
* @return When the value is generated.
*/
public GenerationTiming getGenerationTiming();
/**
* Should the column(s) be referenced in the INSERT / UPDATE SQL?
* <p/>
* This will be {@code false} most often to have a DDL-defined DEFAULT value be applied on INSERT. For
* trigger-generated values this could be {@code true} or {@code false} depending on whether the user wants
* the trigger to have access to some value for the column passed in.
*
* @return {@code true} indicates the column should be included in the SQL.
*/
public boolean referenceColumnsInSql();
/**
* For columns that will be referenced in the SQL (per {@link #referenceColumnsInSql()}), what value
* should be used in the SQL as the column value.
*
* @return The column value to be used in the SQL. {@code null} for any element indicates to use the Column
* defined value ({@link org.hibernate.mapping.Column#getWriteExpr}).
*/
public String[] getReferencedColumnValues();
}

View File

@ -0,0 +1,46 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.tuple;
/**
* @author Steve Ebersole
*/
public interface InMemoryValueGenerationStrategy {
/**
* When is this value generated : NEVER, INSERT, ALWAYS (INSERT+UPDATE)
*
* @return When the value is generated.
*/
public GenerationTiming getGenerationTiming();
/**
* Obtain the in-VM value generator.
* <p/>
* May return {@code null}. In fact for values that are generated "in the database" via execution of the
* INSERT/UPDATE statement, the expectation is that {@code null} be returned here
*
* @return The strategy for performing in-VM value generation
*/
public ValueGenerator getValueGenerator();
}

View File

@ -37,9 +37,7 @@ public interface NonIdentifierAttribute extends Attribute, AttributeDefinition {
public boolean isUpdateable();
public boolean isInsertGenerated();
public boolean isUpdateGenerated();
public ValueGeneration getValueGenerationStrategy();
public boolean isNullable();

View File

@ -40,7 +40,6 @@ import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.PropertyGeneration;
import org.hibernate.metamodel.binding.AbstractPluralAttributeBinding;
import org.hibernate.metamodel.binding.AssociationAttributeBinding;
import org.hibernate.metamodel.binding.AttributeBinding;
@ -195,8 +194,7 @@ public class PropertyFactory {
.setLazy( lazy )
.setInsertable( property.isInsertable() )
.setUpdateable( property.isUpdateable() )
.setInsertGenerated( property.getGeneration() == PropertyGeneration.INSERT || property.getGeneration() == PropertyGeneration.ALWAYS )
.setUpdateGenerated( property.getGeneration() == PropertyGeneration.ALWAYS )
.setValueGenerationStrategy( property.getValueGenerationStrategy() )
.setNullable( property.isOptional() )
.setDirtyCheckable( property.isUpdateable() && !lazy )
.setVersionable( property.isOptimisticLocked() )
@ -268,11 +266,7 @@ public class PropertyFactory {
.setLazy( lazyAvailable && property.isLazy() )
.setInsertable( property.isInsertable() )
.setUpdateable( property.isUpdateable() )
.setInsertGenerated(
property.getGeneration() == PropertyGeneration.INSERT
|| property.getGeneration() == PropertyGeneration.ALWAYS
)
.setUpdateGenerated( property.getGeneration() == PropertyGeneration.ALWAYS )
.setValueGenerationStrategy( property.getValueGenerationStrategy() )
.setNullable( property.isOptional() )
.setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() )
.setVersionable( property.isOptimisticLocked() )
@ -292,11 +286,7 @@ public class PropertyFactory {
.setLazy( lazyAvailable && property.isLazy() )
.setInsertable( property.isInsertable() )
.setUpdateable( property.isUpdateable() )
.setInsertGenerated(
property.getGeneration() == PropertyGeneration.INSERT
|| property.getGeneration() == PropertyGeneration.ALWAYS
)
.setUpdateGenerated( property.getGeneration() == PropertyGeneration.ALWAYS )
.setValueGenerationStrategy( property.getValueGenerationStrategy() )
.setNullable( property.isOptional() )
.setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() )
.setVersionable( property.isOptimisticLocked() )
@ -318,11 +308,7 @@ public class PropertyFactory {
.setLazy( lazyAvailable && property.isLazy() )
.setInsertable( property.isInsertable() )
.setUpdateable( property.isUpdateable() )
.setInsertGenerated(
property.getGeneration() == PropertyGeneration.INSERT
|| property.getGeneration() == PropertyGeneration.ALWAYS
)
.setUpdateGenerated( property.getGeneration() == PropertyGeneration.ALWAYS )
.setValueGenerationStrategy( property.getValueGenerationStrategy() )
.setNullable( property.isOptional() )
.setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() )
.setVersionable( property.isOptimisticLocked() )
@ -379,8 +365,7 @@ public class PropertyFactory {
lazyAvailable && property.isLazy(),
property.isInsertable(),
property.isUpdateable(),
property.getGeneration() == PropertyGeneration.INSERT || property.getGeneration() == PropertyGeneration.ALWAYS,
property.getGeneration() == PropertyGeneration.ALWAYS,
property.getValueGenerationStrategy(),
property.isOptional(),
alwaysDirtyCheck || property.isUpdateable(),
property.isOptimisticLocked(),
@ -426,9 +411,7 @@ public class PropertyFactory {
lazyAvailable && singularAttributeBinding.isLazy(),
true, // insertable
true, // updatable
singularAttributeBinding.getGeneration() == PropertyGeneration.INSERT
|| singularAttributeBinding.getGeneration() == PropertyGeneration.ALWAYS,
singularAttributeBinding.getGeneration() == PropertyGeneration.ALWAYS,
null,
singularAttributeBinding.isNullable(),
alwaysDirtyCheck || areAllValuesIncludedInUpdate( singularAttributeBinding ),
singularAttributeBinding.isIncludedInOptimisticLocking(),
@ -452,8 +435,7 @@ public class PropertyFactory {
// TODO: fix this when HHH-6356 is fixed; for now assume AbstractPluralAttributeBinding is updatable and insertable
true, // pluralAttributeBinding.isInsertable(),
true, //pluralAttributeBinding.isUpdatable(),
false,
false,
null,
false, // nullable - not sure what that means for a collection
// TODO: fix this when HHH-6356 is fixed; for now assume AbstractPluralAttributeBinding is updatable and insertable
//alwaysDirtyCheck || pluralAttributeBinding.isUpdatable(),

View File

@ -44,8 +44,7 @@ public class StandardProperty extends AbstractNonIdentifierAttribute implements
* @param lazy Should this property be handled lazily?
* @param insertable Is this property an insertable value?
* @param updateable Is this property an updateable value?
* @param insertGenerated Is this property generated in the database on insert?
* @param updateGenerated Is this property generated in the database on update?
* @param valueGenerationStrategy How (if) values for this attribute are generated
* @param nullable Is this property a nullable value?
* @param checkable Is this property a checkable value?
* @param versionable Is this property a versionable value?
@ -58,8 +57,7 @@ public class StandardProperty extends AbstractNonIdentifierAttribute implements
boolean lazy,
boolean insertable,
boolean updateable,
boolean insertGenerated,
boolean updateGenerated,
ValueGeneration valueGenerationStrategy,
boolean nullable,
boolean checkable,
boolean versionable,
@ -75,8 +73,7 @@ public class StandardProperty extends AbstractNonIdentifierAttribute implements
.setLazy( lazy )
.setInsertable( insertable )
.setUpdateable( updateable )
.setInsertGenerated( insertGenerated )
.setUpdateGenerated( updateGenerated )
.setValueGenerationStrategy( valueGenerationStrategy )
.setNullable( nullable )
.setDirtyCheckable( checkable )
.setVersionable( versionable )

View File

@ -0,0 +1,71 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.tuple;
/**
* Describes the generation of property values.
*
* @author Steve Ebersole
*/
public interface ValueGeneration {
/**
* When is this value generated : NEVER, INSERT, ALWAYS (INSERT+UPDATE)
*
* @return When the value is generated.
*/
public GenerationTiming getGenerationTiming();
/**
* Obtain the in-VM value generator.
* <p/>
* May return {@code null}. In fact for values that are generated "in the database" via execution of the
* INSERT/UPDATE statement, the expectation is that {@code null} be returned here
*
* @return The strategy for performing in-VM value generation
*/
public ValueGenerator getValueGenerator();
/**
* For values which are generated in the database ({@link #getValueGenerator()} == {@code null}), should the
* column be referenced in the INSERT / UPDATE SQL?
* <p/>
* This will be false most often to have a DDL-defined DEFAULT value be applied on INSERT
*
* @return {@code true} indicates the column should be included in the SQL.
*/
public boolean referenceColumnInSql();
/**
* For values which are generated in the database ({@link #getValueGenerator} == {@code null}), if the
* column will be referenced in the SQL ({@link #referenceColumnInSql()} == {@code true}), what value should be
* used in the SQL as the column value.
* <p/>
* Generally this will be a function call or a marker (DEFAULTS).
* <p/>
* NOTE : for in-VM generation, this will not be called and the column value will implicitly be a JDBC parameter ('?')
*
* @return The column value to be used in the SQL.
*/
public String getDatabaseGeneratedReferencedColumnValue();
}

View File

@ -0,0 +1,43 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.tuple;
import org.hibernate.engine.spi.SessionImplementor;
/**
* Defines a generator for in-VM generation of (non-identifier) attribute values.
*
* @author Steve Ebersole
*/
public interface ValueGenerator<T> {
/**
* Generate the value.
*
* @param session The Session from which the request originates.
* @param owner The instance of the object owning the attribute for which we are generating a value.
*
* @return The generated value
*/
public T generateValue(SessionImplementor session, Object owner);
}

View File

@ -51,8 +51,10 @@ import static org.hibernate.engine.internal.JoinHelper.getRHSColumnNames;
*
* @author Steve Ebersole
*/
public abstract class AbstractCompositionAttribute extends AbstractNonIdentifierAttribute implements
CompositionDefinition {
public abstract class AbstractCompositionAttribute
extends AbstractNonIdentifierAttribute
implements CompositionDefinition {
private final int columnStartPosition;
protected AbstractCompositionAttribute(
@ -163,8 +165,8 @@ public abstract class AbstractCompositionAttribute extends AbstractNonIdentifier
new BaselineAttributeInformation.Builder()
.setInsertable( AbstractCompositionAttribute.this.isInsertable() )
.setUpdateable( AbstractCompositionAttribute.this.isUpdateable() )
.setInsertGenerated( AbstractCompositionAttribute.this.isInsertGenerated() )
.setUpdateGenerated( AbstractCompositionAttribute.this.isUpdateGenerated() )
// todo : handle nested ValueGeneration strategies...
// disallow if our strategy != NEVER
.setNullable( nullable )
.setDirtyCheckable( true )
.setVersionable( AbstractCompositionAttribute.this.isVersionable() )
@ -186,8 +188,8 @@ public abstract class AbstractCompositionAttribute extends AbstractNonIdentifier
new BaselineAttributeInformation.Builder()
.setInsertable( AbstractCompositionAttribute.this.isInsertable() )
.setUpdateable( AbstractCompositionAttribute.this.isUpdateable() )
.setInsertGenerated( AbstractCompositionAttribute.this.isInsertGenerated() )
.setUpdateGenerated( AbstractCompositionAttribute.this.isUpdateGenerated() )
// todo : handle nested ValueGeneration strategies...
// disallow if our strategy != NEVER
.setNullable( getType().getPropertyNullability()[subAttributeNumber] )
.setDirtyCheckable( true )
.setVersionable( AbstractCompositionAttribute.this.isVersionable() )
@ -209,8 +211,8 @@ public abstract class AbstractCompositionAttribute extends AbstractNonIdentifier
new BaselineAttributeInformation.Builder()
.setInsertable( AbstractCompositionAttribute.this.isInsertable() )
.setUpdateable( AbstractCompositionAttribute.this.isUpdateable() )
.setInsertGenerated( AbstractCompositionAttribute.this.isInsertGenerated() )
.setUpdateGenerated( AbstractCompositionAttribute.this.isUpdateGenerated() )
// todo : handle nested ValueGeneration strategies...
// disallow if our strategy != NEVER
.setNullable( nullable )
.setDirtyCheckable( true )
.setVersionable( AbstractCompositionAttribute.this.isVersionable() )

View File

@ -63,7 +63,11 @@ public class CompositeBasedAssociationAttribute
public CompositeBasedAssociationAttribute(
AbstractCompositionAttribute source,
SessionFactoryImplementor factory,
int entityBasedAttributeNumber, String attributeName, AssociationType attributeType, BaselineAttributeInformation baselineInfo, int subAttributeNumber,
int entityBasedAttributeNumber,
String attributeName,
AssociationType attributeType,
BaselineAttributeInformation baselineInfo,
int subAttributeNumber,
AssociationKey associationKey) {
super( source, factory, entityBasedAttributeNumber, attributeName, attributeType, baselineInfo );
this.subAttributeNumber = subAttributeNumber;

View File

@ -25,6 +25,7 @@ package org.hibernate.tuple.entity;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@ -39,8 +40,8 @@ import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.bytecode.spi.EntityInstrumentationMetadata;
import org.hibernate.cfg.Environment;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.OptimisticLockStyle;
import org.hibernate.engine.internal.Versioning;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.CascadeStyles;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -51,18 +52,20 @@ import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.PropertyGeneration;
import org.hibernate.metamodel.binding.AttributeBinding;
import org.hibernate.metamodel.binding.BasicAttributeBinding;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.domain.Attribute;
import org.hibernate.metamodel.domain.SingularAttribute;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.IdentifierProperty;
import org.hibernate.tuple.InDatabaseValueGenerationStrategy;
import org.hibernate.tuple.InMemoryValueGenerationStrategy;
import org.hibernate.tuple.NonIdentifierAttribute;
import org.hibernate.tuple.PropertyFactory;
import org.hibernate.tuple.StandardProperty;
import org.hibernate.tuple.entity.VersionProperty;
import org.hibernate.tuple.ValueGeneration;
import org.hibernate.tuple.ValueGenerator;
import org.hibernate.type.AssociationType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
@ -100,14 +103,21 @@ public class EntityMetamodel implements Serializable {
private final boolean[] nonlazyPropertyUpdateability;
private final boolean[] propertyCheckability;
private final boolean[] propertyInsertability;
private final ValueInclusion[] insertInclusions;
private final ValueInclusion[] updateInclusions;
private final boolean[] propertyNullability;
private final boolean[] propertyVersionability;
private final CascadeStyle[] cascadeStyles;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// value generations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private final boolean hasPreInsertGeneratedValues;
private final boolean hasPreUpdateGeneratedValues;
private final boolean hasInsertGeneratedValues;
private final boolean hasUpdateGeneratedValues;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private final InMemoryValueGenerationStrategy[] inMemoryValueGenerationStrategies;
private final InDatabaseValueGenerationStrategy[] inDatabaseValueGenerationStrategies;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private final Map<String, Integer> propertyIndexes = new HashMap<String, Integer>();
private final boolean hasCollections;
private final boolean hasMutableProperties;
@ -171,8 +181,6 @@ public class EntityMetamodel implements Serializable {
propertyTypes = new Type[propertySpan];
propertyUpdateability = new boolean[propertySpan];
propertyInsertability = new boolean[propertySpan];
insertInclusions = new ValueInclusion[propertySpan];
updateInclusions = new ValueInclusion[propertySpan];
nonlazyPropertyUpdateability = new boolean[propertySpan];
propertyCheckability = new boolean[propertySpan];
propertyNullability = new boolean[propertySpan];
@ -181,6 +189,15 @@ public class EntityMetamodel implements Serializable {
cascadeStyles = new CascadeStyle[propertySpan];
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// generated value strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
this.inMemoryValueGenerationStrategies = new InMemoryValueGenerationStrategy[propertySpan];
this.inDatabaseValueGenerationStrategies = new InDatabaseValueGenerationStrategy[propertySpan];
boolean foundPreInsertGeneratedValues = false;
boolean foundPreUpdateGeneratedValues = false;
boolean foundPostInsertGeneratedValues = false;
boolean foundPostUpdateGeneratedValues = false;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Iterator iter = persistentClass.getPropertyClosureIterator();
int i = 0;
@ -237,8 +254,6 @@ public class EntityMetamodel implements Serializable {
propertyNullability[i] = properties[i].isNullable();
propertyUpdateability[i] = properties[i].isUpdateable();
propertyInsertability[i] = properties[i].isInsertable();
insertInclusions[i] = determineInsertValueGenerationType( prop, properties[i] );
updateInclusions[i] = determineUpdateValueGenerationType( prop, properties[i] );
propertyVersionability[i] = properties[i].isVersionable();
nonlazyPropertyUpdateability[i] = properties[i].isUpdateable() && !lazy;
propertyCheckability[i] = propertyUpdateability[i] ||
@ -247,6 +262,39 @@ public class EntityMetamodel implements Serializable {
cascadeStyles[i] = properties[i].getCascadeStyle();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// generated value strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GenerationStrategyPair pair = buildGenerationStrategyPair( sessionFactory, prop );
inMemoryValueGenerationStrategies[i] = pair.getInMemoryStrategy();
inDatabaseValueGenerationStrategies[i] = pair.getInDatabaseStrategy();
if ( pair.getInMemoryStrategy() != null ) {
final GenerationTiming timing = pair.getInMemoryStrategy().getGenerationTiming();
if ( timing != GenerationTiming.NEVER ) {
final ValueGenerator generator = pair.getInMemoryStrategy().getValueGenerator();
if ( generator != null ) {
// we have some level of generation indicated
if ( timing == GenerationTiming.INSERT ) {
foundPreInsertGeneratedValues = true;
}
else if ( timing == GenerationTiming.ALWAYS ) {
foundPreInsertGeneratedValues = true;
foundPreUpdateGeneratedValues = true;
}
}
}
}
if ( pair.getInDatabaseStrategy() != null ) {
final GenerationTiming timing = pair.getInDatabaseStrategy().getGenerationTiming();
if ( timing == GenerationTiming.INSERT ) {
foundPostInsertGeneratedValues = true;
}
else if ( timing == GenerationTiming.ALWAYS ) {
foundPostInsertGeneratedValues = true;
foundPostUpdateGeneratedValues = true;
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if ( properties[i].isLazy() ) {
hasLazy = true;
}
@ -263,14 +311,6 @@ public class EntityMetamodel implements Serializable {
foundMutable = true;
}
if ( insertInclusions[i] != ValueInclusion.NONE ) {
foundInsertGeneratedValue = true;
}
if ( updateInclusions[i] != ValueInclusion.NONE ) {
foundUpdateGeneratedValue = true;
}
mapPropertyToIndex(prop, i);
i++;
}
@ -286,8 +326,10 @@ public class EntityMetamodel implements Serializable {
hasCacheableNaturalId = persistentClass.getNaturalIdCacheRegionName() != null;
}
hasInsertGeneratedValues = foundInsertGeneratedValue;
hasUpdateGeneratedValues = foundUpdateGeneratedValue;
this.hasPreInsertGeneratedValues = foundPreInsertGeneratedValues;
this.hasPreUpdateGeneratedValues = foundPreUpdateGeneratedValues;
this.hasInsertGeneratedValues = foundPostInsertGeneratedValues;
this.hasUpdateGeneratedValues = foundPostUpdateGeneratedValues;
hasCascades = foundCascade;
hasNonIdentifierPropertyNamedId = foundNonIdentifierPropertyNamedId;
@ -365,6 +407,369 @@ public class EntityMetamodel implements Serializable {
}
}
private static GenerationStrategyPair buildGenerationStrategyPair(
final SessionFactoryImplementor sessionFactory,
final Property mappingProperty) {
final ValueGeneration valueGeneration = mappingProperty.getValueGenerationStrategy();
if ( valueGeneration != null && valueGeneration.getGenerationTiming() != GenerationTiming.NEVER ) {
// the property is generated in full. build the generation strategy pair.
if ( valueGeneration.getValueGenerator() != null ) {
// in-memory generation
return new GenerationStrategyPair(
FullInMemoryValueGenerationStrategy.create( valueGeneration )
);
}
else {
// in-db generation
return new GenerationStrategyPair(
create(
sessionFactory,
mappingProperty,
valueGeneration
)
);
}
}
else if ( mappingProperty.getValue() instanceof Component ) {
final CompositeGenerationStrategyPairBuilder builder = new CompositeGenerationStrategyPairBuilder( mappingProperty );
interpretPartialCompositeValueGeneration( sessionFactory, (Component) mappingProperty.getValue(), builder );
return builder.buildPair();
}
return NO_GEN_PAIR;
}
private static final GenerationStrategyPair NO_GEN_PAIR = new GenerationStrategyPair();
private static void interpretPartialCompositeValueGeneration(
SessionFactoryImplementor sessionFactory,
Component composite,
CompositeGenerationStrategyPairBuilder builder) {
Iterator subProperties = composite.getPropertyIterator();
while ( subProperties.hasNext() ) {
final Property subProperty = (Property) subProperties.next();
builder.addPair( buildGenerationStrategyPair( sessionFactory, subProperty ) );
}
}
public static InDatabaseValueGenerationStrategyImpl create(
SessionFactoryImplementor sessionFactoryImplementor,
Property mappingProperty,
ValueGeneration valueGeneration) {
final int numberOfMappedColumns = mappingProperty.getType().getColumnSpan( sessionFactoryImplementor );
if ( numberOfMappedColumns == 1 ) {
return new InDatabaseValueGenerationStrategyImpl(
valueGeneration.getGenerationTiming(),
valueGeneration.referenceColumnInSql(),
new String[] { valueGeneration.getDatabaseGeneratedReferencedColumnValue() }
);
}
else {
if ( valueGeneration.getDatabaseGeneratedReferencedColumnValue() != null ) {
LOG.debugf(
"Value generator specified column value in reference to multi-column attribute [%s -> %s]; ignoring",
mappingProperty.getPersistentClass(),
mappingProperty.getName()
);
}
return new InDatabaseValueGenerationStrategyImpl(
valueGeneration.getGenerationTiming(),
valueGeneration.referenceColumnInSql(),
new String[numberOfMappedColumns]
);
}
}
public static class GenerationStrategyPair {
private final InMemoryValueGenerationStrategy inMemoryStrategy;
private final InDatabaseValueGenerationStrategy inDatabaseStrategy;
public GenerationStrategyPair() {
this( NoInMemoryValueGenerationStrategy.INSTANCE, NoInDatabaseValueGenerationStrategy.INSTANCE );
}
public GenerationStrategyPair(FullInMemoryValueGenerationStrategy inMemoryStrategy) {
this( inMemoryStrategy, NoInDatabaseValueGenerationStrategy.INSTANCE );
}
public GenerationStrategyPair(InDatabaseValueGenerationStrategyImpl inDatabaseStrategy) {
this( NoInMemoryValueGenerationStrategy.INSTANCE, inDatabaseStrategy );
}
public GenerationStrategyPair(
InMemoryValueGenerationStrategy inMemoryStrategy,
InDatabaseValueGenerationStrategy inDatabaseStrategy) {
// perform some normalization. Also check that only one (if any) strategy is specified
if ( inMemoryStrategy == null ) {
inMemoryStrategy = NoInMemoryValueGenerationStrategy.INSTANCE;
}
if ( inDatabaseStrategy == null ) {
inDatabaseStrategy = NoInDatabaseValueGenerationStrategy.INSTANCE;
}
if ( inMemoryStrategy.getGenerationTiming() != GenerationTiming.NEVER
&& inDatabaseStrategy.getGenerationTiming() != GenerationTiming.NEVER ) {
throw new ValueGenerationStrategyException(
"in-memory and in-database value generation are mutually exclusive"
);
}
this.inMemoryStrategy = inMemoryStrategy;
this.inDatabaseStrategy = inDatabaseStrategy;
}
public InMemoryValueGenerationStrategy getInMemoryStrategy() {
return inMemoryStrategy;
}
public InDatabaseValueGenerationStrategy getInDatabaseStrategy() {
return inDatabaseStrategy;
}
}
public static class ValueGenerationStrategyException extends HibernateException {
public ValueGenerationStrategyException(String message) {
super( message );
}
public ValueGenerationStrategyException(String message, Throwable cause) {
super( message, cause );
}
}
private static class CompositeGenerationStrategyPairBuilder {
private final Property mappingProperty;
private boolean hadInMemoryGeneration;
private boolean hadInDatabaseGeneration;
private List<InMemoryValueGenerationStrategy> inMemoryStrategies;
private List<InDatabaseValueGenerationStrategy> inDatabaseStrategies;
public CompositeGenerationStrategyPairBuilder(Property mappingProperty) {
this.mappingProperty = mappingProperty;
}
public void addPair(GenerationStrategyPair generationStrategyPair) {
add( generationStrategyPair.getInMemoryStrategy() );
add( generationStrategyPair.getInDatabaseStrategy() );
}
private void add(InMemoryValueGenerationStrategy inMemoryStrategy) {
if ( inMemoryStrategies == null ) {
inMemoryStrategies = new ArrayList<InMemoryValueGenerationStrategy>();
}
inMemoryStrategies.add( inMemoryStrategy );
if ( inMemoryStrategy.getGenerationTiming() != GenerationTiming.NEVER ) {
hadInMemoryGeneration = true;
}
}
private void add(InDatabaseValueGenerationStrategy inDatabaseStrategy) {
if ( inDatabaseStrategies == null ) {
inDatabaseStrategies = new ArrayList<InDatabaseValueGenerationStrategy>();
}
inDatabaseStrategies.add( inDatabaseStrategy );
if ( inDatabaseStrategy.getGenerationTiming() != GenerationTiming.NEVER ) {
hadInDatabaseGeneration = true;
}
}
public GenerationStrategyPair buildPair() {
if ( hadInMemoryGeneration && hadInDatabaseGeneration ) {
throw new ValueGenerationStrategyException(
"Composite attribute [" + mappingProperty.getName() + "] contained both in-memory"
+ " and in-database value generation"
);
}
else if ( hadInMemoryGeneration ) {
throw new NotYetImplementedException( "Still need to wire in composite in-memory value generation" );
}
else if ( hadInDatabaseGeneration ) {
final Component composite = (Component) mappingProperty.getValue();
// we need the numbers to match up so we can properly handle 'referenced sql column values'
if ( inDatabaseStrategies.size() != composite.getPropertySpan() ) {
throw new ValueGenerationStrategyException(
"Internal error : mismatch between number of collected in-db generation strategies" +
" and number of attributes for composite attribute : " + mappingProperty.getName()
);
}
// the base-line values for the aggregated InDatabaseValueGenerationStrategy we will build here.
GenerationTiming timing = GenerationTiming.INSERT;
boolean referenceColumns = false;
String[] columnValues = new String[ composite.getColumnSpan() ];
// start building the aggregate values
int propertyIndex = -1;
int columnIndex = 0;
Iterator subProperties = composite.getPropertyIterator();
while ( subProperties.hasNext() ) {
propertyIndex++;
final Property subProperty = (Property) subProperties.next();
final InDatabaseValueGenerationStrategy subStrategy = inDatabaseStrategies.get( propertyIndex );
if ( subStrategy.getGenerationTiming() == GenerationTiming.ALWAYS ) {
// override the base-line to the more often "ALWAYS"...
timing = GenerationTiming.ALWAYS;
}
if ( subStrategy.referenceColumnsInSql() ) {
// override base-line value
referenceColumns = true;
}
if ( subStrategy.getReferencedColumnValues() != null ) {
if ( subStrategy.getReferencedColumnValues().length != subProperty.getColumnSpan() ) {
throw new ValueGenerationStrategyException(
"Internal error : mismatch between number of collected 'referenced column values'" +
" and number of columns for composite attribute : " + mappingProperty.getName() +
'.' + subProperty.getName()
);
}
System.arraycopy(
subStrategy.getReferencedColumnValues(),
0,
columnValues,
columnIndex,
subProperty.getColumnSpan()
);
}
}
// then use the aggregated values to build the InDatabaseValueGenerationStrategy
return new GenerationStrategyPair(
new InDatabaseValueGenerationStrategyImpl( timing, referenceColumns, columnValues )
);
}
else {
return NO_GEN_PAIR;
}
}
}
private static class NoInMemoryValueGenerationStrategy implements InMemoryValueGenerationStrategy {
/**
* Singleton access
*/
public static final NoInMemoryValueGenerationStrategy INSTANCE = new NoInMemoryValueGenerationStrategy();
@Override
public GenerationTiming getGenerationTiming() {
return GenerationTiming.NEVER;
}
@Override
public ValueGenerator getValueGenerator() {
return null;
}
}
private static class FullInMemoryValueGenerationStrategy implements InMemoryValueGenerationStrategy {
private final GenerationTiming timing;
private final ValueGenerator generator;
private FullInMemoryValueGenerationStrategy(GenerationTiming timing, ValueGenerator generator) {
this.timing = timing;
this.generator = generator;
}
public static FullInMemoryValueGenerationStrategy create(ValueGeneration valueGeneration) {
return new FullInMemoryValueGenerationStrategy(
valueGeneration.getGenerationTiming(),
valueGeneration.getValueGenerator()
);
}
@Override
public GenerationTiming getGenerationTiming() {
return timing;
}
@Override
public ValueGenerator getValueGenerator() {
return generator;
}
}
private static class NoInDatabaseValueGenerationStrategy implements InDatabaseValueGenerationStrategy {
/**
* Singleton access
*/
public static final NoInDatabaseValueGenerationStrategy INSTANCE = new NoInDatabaseValueGenerationStrategy();
@Override
public GenerationTiming getGenerationTiming() {
return GenerationTiming.NEVER;
}
@Override
public boolean referenceColumnsInSql() {
return true;
}
@Override
public String[] getReferencedColumnValues() {
return null;
}
}
private static class InDatabaseValueGenerationStrategyImpl implements InDatabaseValueGenerationStrategy {
private final GenerationTiming timing;
private final boolean referenceColumnInSql;
private final String[] referencedColumnValues;
private InDatabaseValueGenerationStrategyImpl(
GenerationTiming timing,
boolean referenceColumnInSql,
String[] referencedColumnValues) {
this.timing = timing;
this.referenceColumnInSql = referenceColumnInSql;
this.referencedColumnValues = referencedColumnValues;
}
@Override
public GenerationTiming getGenerationTiming() {
return timing;
}
@Override
public boolean referenceColumnsInSql() {
return referenceColumnInSql;
}
@Override
public String[] getReferencedColumnValues() {
return referencedColumnValues;
}
}
private ValueInclusion determineInsertValueGenerationType(Property mappingProperty, NonIdentifierAttribute runtimeProperty) {
if ( isInsertGenerated( runtimeProperty ) ) {
return ValueInclusion.FULL;
}
else if ( mappingProperty.getValue() instanceof Component ) {
if ( hasPartialInsertComponentGeneration( ( Component ) mappingProperty.getValue() ) ) {
return ValueInclusion.PARTIAL;
}
}
return ValueInclusion.NONE;
}
private boolean isInsertGenerated(NonIdentifierAttribute property) {
return property.getValueGenerationStrategy() != null
&& property.getValueGenerationStrategy().getGenerationTiming() != GenerationTiming.NEVER;
}
private boolean isInsertGenerated(Property property) {
return property.getValueGenerationStrategy() != null
&& property.getValueGenerationStrategy().getGenerationTiming() != GenerationTiming.NEVER;
}
public EntityMetamodel(
EntityBinding entityBinding,
AbstractEntityPersister persister,
@ -411,8 +816,6 @@ public class EntityMetamodel implements Serializable {
propertyTypes = new Type[propertySpan];
propertyUpdateability = new boolean[propertySpan];
propertyInsertability = new boolean[propertySpan];
insertInclusions = new ValueInclusion[propertySpan];
updateInclusions = new ValueInclusion[propertySpan];
nonlazyPropertyUpdateability = new boolean[propertySpan];
propertyCheckability = new boolean[propertySpan];
propertyNullability = new boolean[propertySpan];
@ -422,6 +825,17 @@ public class EntityMetamodel implements Serializable {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// todo : handle value generations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
this.hasPreInsertGeneratedValues = false;
this.hasPreUpdateGeneratedValues = false;
this.hasInsertGeneratedValues = false;
this.hasUpdateGeneratedValues = false;
this.inMemoryValueGenerationStrategies = new InMemoryValueGenerationStrategy[propertySpan];
Arrays.fill( this.inMemoryValueGenerationStrategies, NoInMemoryValueGenerationStrategy.INSTANCE );
this.inDatabaseValueGenerationStrategies = new InDatabaseValueGenerationStrategy[propertySpan];
Arrays.fill( this.inDatabaseValueGenerationStrategies, NoInDatabaseValueGenerationStrategy.INSTANCE );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int i = 0;
int tempVersionProperty = NO_VERSION_INDX;
boolean foundCascade = false;
@ -472,8 +886,6 @@ public class EntityMetamodel implements Serializable {
propertyNullability[i] = properties[i].isNullable();
propertyUpdateability[i] = properties[i].isUpdateable();
propertyInsertability[i] = properties[i].isInsertable();
insertInclusions[i] = determineInsertValueGenerationType( attributeBinding, properties[i] );
updateInclusions[i] = determineUpdateValueGenerationType( attributeBinding, properties[i] );
propertyVersionability[i] = properties[i].isVersionable();
nonlazyPropertyUpdateability[i] = properties[i].isUpdateable() && !lazy;
propertyCheckability[i] = propertyUpdateability[i] ||
@ -498,14 +910,6 @@ public class EntityMetamodel implements Serializable {
foundMutable = true;
}
if ( insertInclusions[i] != ValueInclusion.NONE ) {
foundInsertGeneratedValue = true;
}
if ( updateInclusions[i] != ValueInclusion.NONE ) {
foundUpdateGeneratedValue = true;
}
mapPropertyToIndex(attributeBinding.getAttribute(), i);
i++;
}
@ -521,9 +925,6 @@ public class EntityMetamodel implements Serializable {
hasCacheableNaturalId = false; //See previous TODO and HHH-6354
}
hasInsertGeneratedValues = foundInsertGeneratedValue;
hasUpdateGeneratedValues = foundUpdateGeneratedValue;
hasCascades = foundCascade;
hasNonIdentifierPropertyNamedId = foundNonIdentifierPropertyNamedId;
versionPropertyIndex = tempVersionProperty;
@ -602,20 +1003,8 @@ public class EntityMetamodel implements Serializable {
}
}
private ValueInclusion determineInsertValueGenerationType(Property mappingProperty, NonIdentifierAttribute runtimeProperty) {
if ( runtimeProperty.isInsertGenerated() ) {
return ValueInclusion.FULL;
}
else if ( mappingProperty.getValue() instanceof Component ) {
if ( hasPartialInsertComponentGeneration( ( Component ) mappingProperty.getValue() ) ) {
return ValueInclusion.PARTIAL;
}
}
return ValueInclusion.NONE;
}
private ValueInclusion determineInsertValueGenerationType(AttributeBinding mappingProperty, NonIdentifierAttribute runtimeProperty) {
if ( runtimeProperty.isInsertGenerated() ) {
if ( isInsertGenerated( runtimeProperty ) ) {
return ValueInclusion.FULL;
}
// TODO: fix the following when components are working (HHH-6173)
@ -630,12 +1019,12 @@ public class EntityMetamodel implements Serializable {
private boolean hasPartialInsertComponentGeneration(Component component) {
Iterator subProperties = component.getPropertyIterator();
while ( subProperties.hasNext() ) {
Property prop = ( Property ) subProperties.next();
if ( prop.getGeneration() == PropertyGeneration.ALWAYS || prop.getGeneration() == PropertyGeneration.INSERT ) {
final Property prop = ( Property ) subProperties.next();
if ( isInsertGenerated( prop ) ) {
return true;
}
else if ( prop.getValue() instanceof Component ) {
if ( hasPartialInsertComponentGeneration( ( Component ) prop.getValue() ) ) {
if ( hasPartialInsertComponentGeneration( (Component) prop.getValue() ) ) {
return true;
}
}
@ -644,7 +1033,7 @@ public class EntityMetamodel implements Serializable {
}
private ValueInclusion determineUpdateValueGenerationType(Property mappingProperty, NonIdentifierAttribute runtimeProperty) {
if ( runtimeProperty.isUpdateGenerated() ) {
if ( isUpdateGenerated( runtimeProperty ) ) {
return ValueInclusion.FULL;
}
else if ( mappingProperty.getValue() instanceof Component ) {
@ -655,8 +1044,18 @@ public class EntityMetamodel implements Serializable {
return ValueInclusion.NONE;
}
private static boolean isUpdateGenerated(Property property) {
return property.getValueGenerationStrategy() != null
&& property.getValueGenerationStrategy().getGenerationTiming() == GenerationTiming.ALWAYS;
}
private static boolean isUpdateGenerated(NonIdentifierAttribute property) {
return property.getValueGenerationStrategy() != null
&& property.getValueGenerationStrategy().getGenerationTiming() == GenerationTiming.ALWAYS;
}
private ValueInclusion determineUpdateValueGenerationType(AttributeBinding mappingProperty, NonIdentifierAttribute runtimeProperty) {
if ( runtimeProperty.isUpdateGenerated() ) {
if ( isUpdateGenerated( runtimeProperty ) ) {
return ValueInclusion.FULL;
}
// TODO: fix the following when components are working (HHH-6173)
@ -671,8 +1070,8 @@ public class EntityMetamodel implements Serializable {
private boolean hasPartialUpdateComponentGeneration(Component component) {
Iterator subProperties = component.getPropertyIterator();
while ( subProperties.hasNext() ) {
Property prop = ( Property ) subProperties.next();
if ( prop.getGeneration() == PropertyGeneration.ALWAYS ) {
Property prop = (Property) subProperties.next();
if ( isUpdateGenerated( prop ) ) {
return true;
}
else if ( prop.getValue() instanceof Component ) {
@ -717,6 +1116,23 @@ public class EntityMetamodel implements Serializable {
return entityTuplizer;
}
public boolean isNaturalIdentifierInsertGenerated() {
// the intention is for this call to replace the usage of the old ValueInclusion stuff (as exposed from
// persister) in SelectGenerator to determine if it is safe to use the natural identifier to find the
// insert-generated identifier. That wont work if the natural-id is also insert-generated.
//
// Assumptions:
// * That code checks that there is a natural identifier before making this call, so we assume the same here
// * That code assumes a non-composite natural-id, so we assume the same here
final InDatabaseValueGenerationStrategy strategy = inDatabaseValueGenerationStrategies[ naturalIdPropertyNumbers[0] ];
return strategy != null && strategy.getGenerationTiming() != GenerationTiming.NEVER;
}
public boolean isVersionGenerated() {
final InDatabaseValueGenerationStrategy strategy = inDatabaseValueGenerationStrategies[ versionPropertyIndex ];
return strategy != null && strategy.getGenerationTiming() != GenerationTiming.NEVER;
}
public int[] getNaturalIdentifierProperties() {
return naturalIdPropertyNumbers;
}
@ -925,14 +1341,6 @@ public class EntityMetamodel implements Serializable {
return propertyInsertability;
}
public ValueInclusion[] getPropertyInsertGenerationInclusions() {
return insertInclusions;
}
public ValueInclusion[] getPropertyUpdateGenerationInclusions() {
return updateInclusions;
}
public boolean[] getPropertyNullability() {
return propertyNullability;
}
@ -945,6 +1353,14 @@ public class EntityMetamodel implements Serializable {
return cascadeStyles;
}
public boolean hasPreInsertGeneratedValues() {
return hasPreInsertGeneratedValues;
}
public boolean hasPreUpdateGeneratedValues() {
return hasPreUpdateGeneratedValues;
}
public boolean hasInsertGeneratedValues() {
return hasInsertGeneratedValues;
}
@ -953,6 +1369,14 @@ public class EntityMetamodel implements Serializable {
return hasUpdateGeneratedValues;
}
public InMemoryValueGenerationStrategy[] getInMemoryValueGenerationStrategies() {
return inMemoryValueGenerationStrategies;
}
public InDatabaseValueGenerationStrategy[] getInDatabaseValueGenerationStrategies() {
return inDatabaseValueGenerationStrategies;
}
public EntityMode getEntityMode() {
return entityMode;
}

View File

@ -0,0 +1,98 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.test.generated;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.Generated;
import org.hibernate.annotations.GenerationTime;
import org.junit.Test;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
/**
* @author Steve Ebersole
*/
public class DefaultGeneratedValueTest extends BaseCoreFunctionalTestCase {
@Test
@TestForIssue( jiraKey = "HHH-2907" )
public void testGeneration() {
Session s = openSession();
s.beginTransaction();
TheEntity theEntity = new TheEntity( 1 );
assertNull( theEntity.createdDate );
s.save( theEntity );
assertNull( theEntity.createdDate );
s.getTransaction().commit();
s.close();
assertNotNull( theEntity.createdDate );
s = openSession();
s.beginTransaction();
theEntity = (TheEntity) session.get( TheEntity.class, 1 );
assertNotNull( theEntity.createdDate );
s.delete( theEntity );
s.getTransaction().commit();
s.close();
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { TheEntity.class };
}
@Entity( name = "TheEntity" )
@Table( name = "T_ENT_GEN_DEF" )
private static class TheEntity {
@Id
private Integer id;
@Generated( GenerationTime.INSERT )
@ColumnDefault( "CURRENT_TIMESTAMP" )
@Column( nullable = false )
private Date createdDate;
private TheEntity() {
}
private TheEntity(Integer id) {
this.id = id;
}
}
}