Skip to content

Commit

Permalink
Clean up various legacy "read path" contracts
Browse files Browse the repository at this point in the history
- clean-up unused Type methods
    * Type#nullSafeGet
    * Type#hydrate
    * Type#resolve
    * Type#getSemiResolvedType
    * Type#semiResolve
    * related
- start removing usage of Tuplizer
- start removing usage of legacy Tuplizer-based Instantiator
  • Loading branch information
sebersole committed Oct 21, 2021
1 parent cf36d17 commit 62f7617
Show file tree
Hide file tree
Showing 24 changed files with 323 additions and 783 deletions.
Expand Up @@ -7,11 +7,14 @@
package org.hibernate.engine.internal;

import java.lang.reflect.Constructor;
import java.util.function.Supplier;

import org.hibernate.InstantiationException;
import org.hibernate.MappingException;
import org.hibernate.engine.spi.IdentifierValue;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.VersionValue;
import org.hibernate.mapping.KeyValue;
import org.hibernate.property.access.spi.Getter;
import org.hibernate.type.BasicType;
import org.hibernate.type.Type;
Expand All @@ -27,51 +30,28 @@
public class UnsavedValueFactory {

/**
* Instantiate a class using the provided Constructor
*
* @param constructor The constructor
*
* @return The instantiated object
*
* @throws InstantiationException if something went wrong
*/
private static Object instantiate(Constructor constructor) {
try {
return constructor.newInstance();
}
catch (Exception e) {
throw new InstantiationException( "could not instantiate test object", constructor.getDeclaringClass(), e );
}
}

/**
* Return an IdentifierValue for the specified unsaved-value. If none is specified,
* guess the unsaved value by instantiating a test instance of the class and
* reading it's id property, or if that is not possible, using the java default
* value for the type
*
* @param unsavedValue The mapping defined unsaved value
* @param identifierGetter The getter for the entity identifier attribute
* @param identifierType The mapping type for the identifier
* @param constructor The constructor for the entity
*
* @return The appropriate IdentifierValue
* Return the UnsavedValueStrategy for determining whether an entity instance is
* unsaved based on the identifier. If an explicit strategy is not specified, determine
* the unsaved value by instantiating an instance of the entity and reading the value of
* its id property, or if that is not possible, using the java default value for the type
*/
public static IdentifierValue getUnsavedIdentifierValue(
String unsavedValue,
Getter identifierGetter,
Type identifierType,
Constructor constructor) {
KeyValue bootIdMapping,
JavaType<?> idJtd,
Getter getter,
Supplier<?> templateInstanceAccess,
SessionFactoryImplementor sessionFactory) {
final String unsavedValue = bootIdMapping.getNullValue();

if ( unsavedValue == null ) {
if ( identifierGetter != null && constructor != null ) {
if ( getter != null && templateInstanceAccess != null ) {
// use the id value of a newly instantiated instance as the unsaved-value
final Object defaultValue = identifierGetter.get( instantiate( constructor ) );
final Object templateInstance = templateInstanceAccess.get();
final Object defaultValue = getter.get( templateInstance );
return new IdentifierValue( defaultValue );
}
final JavaType<?> jtd;
if ( identifierGetter != null && ( identifierType instanceof BasicType<?> ) && ( jtd = ( (BasicType<?>) identifierType ).getJavaTypeDescriptor() ) instanceof PrimitiveJavaType ) {
final Object defaultValue = ( (PrimitiveJavaType<?>) jtd ).getDefaultValue();
return new IdentifierValue( defaultValue );
else if ( idJtd instanceof PrimitiveJavaType ) {
return new IdentifierValue( ( (PrimitiveJavaType<?>) idJtd ).getDefaultValue() );
}
else {
return IdentifierValue.NULL;
Expand All @@ -90,16 +70,71 @@ else if ( "any".equals( unsavedValue ) ) {
return IdentifierValue.ANY;
}
else {
try {
return new IdentifierValue( ( (BasicType<?>) identifierType ).getJavaTypeDescriptor().fromString( unsavedValue ) );
}
catch ( ClassCastException cce ) {
throw new MappingException( "Bad identifier type: " + identifierType.getName() );
return new IdentifierValue( idJtd.fromString( unsavedValue ) );
}
}

/**
* Return the UnsavedValueStrategy for determining whether an entity instance is
* unsaved based on the version. If an explicit strategy is not specified, determine the
* unsaved value by instantiating an instance of the entity and reading the value of its
* version property, or if that is not possible, using the java default value for the type
*/
public static VersionValue getUnsavedVersionValue(
KeyValue bootVersionMapping,
VersionJavaType jtd,
Getter getter,
Supplier<?> templateInstanceAccess,
SessionFactoryImplementor sessionFactory) {
final String unsavedValue = bootVersionMapping.getNullValue();
if ( unsavedValue == null ) {
if ( getter != null && templateInstanceAccess != null ) {
final Object templateInstance = templateInstanceAccess.get();
final Object defaultValue = getter.get( templateInstance );

// if the version of a newly instantiated object is not the same
// as the version seed value, use that as the unsaved-value
final Object seedValue = jtd.seed( null );
return jtd.areEqual( seedValue, defaultValue )
? VersionValue.UNDEFINED
: new VersionValue( defaultValue );
}
catch ( Exception e ) {
throw new MappingException( "Could not parse identifier unsaved-value: " + unsavedValue );
else {
return VersionValue.UNDEFINED;
}
}
else if ( "undefined".equals( unsavedValue ) ) {
return VersionValue.UNDEFINED;
}
else if ( "null".equals( unsavedValue ) ) {
return VersionValue.NULL;
}
else if ( "negative".equals( unsavedValue ) ) {
return VersionValue.NEGATIVE;
}
else {
// this should not happen since the DTD prevents it
throw new MappingException( "Could not parse version unsaved-value: " + unsavedValue );
}

}

/**
* Instantiate a class using the provided Constructor
*
* @param constructor The constructor
*
* @return The instantiated object
*
* @throws InstantiationException if something went wrong
*/
private static Object instantiate(Constructor constructor) {
try {
return constructor.newInstance();
}
catch (Exception e) {
throw new InstantiationException( "could not instantiate test object", constructor.getDeclaringClass(), e );
}
}

/**
Expand Down
@@ -0,0 +1,40 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.internal.util;

import java.util.function.Supplier;

/**
* A lazily accessible object reference. Useful for cases where final references
* are needed (anon inner class, lambdas, etc).
*
* @param <T> The type of object referenced
*/
public class LazyValue<T> {
public static final Object NULL = new Object();

private final Supplier<T> supplier;
private Object value;

public LazyValue(Supplier<T> supplier) {
this.supplier = supplier;
}

public Object getValue() {
if ( value == null ) {
final T obtainedValue = supplier.get();
if ( obtainedValue == null ) {
value = NULL;
}
else {
value = obtainedValue;
}
}

return value == NULL ? null : value;
}
}
Expand Up @@ -61,6 +61,11 @@ protected static Constructor<?> resolveConstructor(Class<?> mappedPojoClass) {
return null;
}

@Override
public boolean canBeInstantiated() {
return constructor != null;
}

@Override
protected Object applyInterception(Object entity) {
if ( !applyBytecodeInterception ) {
Expand Down
Expand Up @@ -9,6 +9,7 @@

import java.util.List;

import org.hibernate.engine.spi.IdentifierValue;
import org.hibernate.mapping.IndexedConsumer;

/**
Expand All @@ -33,4 +34,9 @@ default void forEachAttribute(IndexedConsumer<SingularAttributeMapping> consumer
consumer.accept( i, attributes.get( i ) );
}
}

@Override
default IdentifierValue getUnsavedStrategy() {
return IdentifierValue.UNDEFINED;
}
}
Expand Up @@ -6,20 +6,30 @@
*/
package org.hibernate.metamodel.mapping;

import org.hibernate.engine.spi.IdentifierValue;
import org.hibernate.engine.spi.SharedSessionContractImplementor;

/**
* @author Steve Ebersole
* Describes the mapping of an entity's identifier.
*
* @see jakarta.persistence.Id
* @see jakarta.persistence.EmbeddedId
*/
public interface EntityIdentifierMapping extends ValueMapping, ModelPart {
String ROLE_LOCAL_NAME = "{id}";

Object getIdentifier(Object entity, SharedSessionContractImplementor session);
void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session);
Object instantiate();

@Override
default String getPartName() {
return ROLE_LOCAL_NAME;
}

/**
* The strategy for distinguishing between detached and transient
* state based on the identifier mapping
*/
IdentifierValue getUnsavedStrategy();

Object getIdentifier(Object entity, SharedSessionContractImplementor session);
void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session);
Object instantiate();
}
Expand Up @@ -6,11 +6,23 @@
*/
package org.hibernate.metamodel.mapping;

import org.hibernate.engine.spi.VersionValue;
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;

/**
* @author Steve Ebersole
* Describes the mapping of an entity's version
*
* @see jakarta.persistence.Version
*/
public interface EntityVersionMapping extends BasicValuedModelPart {
/**
* The attribute marked as the version
*/
BasicAttributeMapping getVersionAttribute();

/**
* The strategy for distinguishing between detached and transient
* state based on the version mapping
*/
VersionValue getUnsavedStrategy();
}
Expand Up @@ -7,20 +7,23 @@
package org.hibernate.metamodel.mapping.internal;

import java.util.Locale;
import java.util.function.Supplier;

import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.internal.UnsavedValueFactory;
import org.hibernate.engine.spi.IdentifierValue;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.mapping.IndexedConsumer;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.property.access.spi.PropertyAccess;
Expand Down Expand Up @@ -51,6 +54,8 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa
private final NavigableRole idRole;
private final String attributeName;

private final IdentifierValue unsavedStrategy;

private final PropertyAccess propertyAccess;
private final EntityPersister entityPersister;

Expand All @@ -63,6 +68,7 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa

public BasicEntityIdentifierMappingImpl(
EntityPersister entityPersister,
Supplier<?> instanceCreator,
String attributeName,
String rootTable,
String pkColumnName,
Expand All @@ -84,6 +90,14 @@ public BasicEntityIdentifierMappingImpl(

idRole = entityPersister.getNavigableRole().append( EntityIdentifierMapping.ROLE_LOCAL_NAME );
sessionFactory = creationProcess.getCreationContext().getSessionFactory();

unsavedStrategy = UnsavedValueFactory.getUnsavedIdentifierValue(
bootEntityDescriptor.getIdentifier(),
getJavaTypeDescriptor(),
propertyAccess.getGetter(),
instanceCreator,
sessionFactory
);
}

@Override
Expand All @@ -96,6 +110,11 @@ public String getAttributeName() {
return attributeName;
}

@Override
public IdentifierValue getUnsavedStrategy() {
return unsavedStrategy;
}

@Override
public Object getIdentifier(Object entity, SharedSessionContractImplementor session) {
if ( entity instanceof HibernateProxy ) {
Expand Down

0 comments on commit 62f7617

Please sign in to comment.