HHH-2907 - ability to apply 'generation strategy' to generated properties
This commit is contained in:
parent
19057a4685
commit
77825fefd6
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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() );
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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();
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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() )
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue