more consistent formatting for error messages

This commit is contained in:
Gavin King 2022-10-23 20:12:14 +02:00
parent 412319819e
commit 264d3c711c
60 changed files with 654 additions and 731 deletions

1
.gitignore vendored
View File

@ -52,4 +52,3 @@ databases/postgis/
# Vim # Vim
*.swp *.swp
*.swo *.swo

View File

@ -15,13 +15,13 @@ import org.hibernate.id.IdentifierGenerator;
/** /**
* Meta-annotation used to mark another annotation as providing configuration * Meta-annotation used to mark another annotation as providing configuration
* for a custom {@link org.hibernate.id.IdentifierGenerator}. * for a custom {@link IdentifierGenerator}.
*/ */
@Target( value = ElementType.ANNOTATION_TYPE ) @Target( value = ElementType.ANNOTATION_TYPE )
@Retention( RetentionPolicy.RUNTIME ) @Retention( RetentionPolicy.RUNTIME )
public @interface IdGeneratorType { public @interface IdGeneratorType {
/** /**
* The IdentifierGenerator being configured * The {@link IdentifierGenerator} being configured
*/ */
Class<? extends IdentifierGenerator> value(); Class<? extends IdentifierGenerator> value();
} }

View File

@ -17,7 +17,7 @@ import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* Specify an explicit BasicJavaType to use for a particular * Specify an explicit {@link BasicJavaType} to use for a particular
* column mapping. <ul> * column mapping. <ul>
* <li> * <li>
* When applied to a Map-valued attribute, describes the Map value. Use * When applied to a Map-valued attribute, describes the Map value. Use

View File

@ -7,7 +7,6 @@
package org.hibernate.annotations; package org.hibernate.annotations;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import jakarta.persistence.Column;
import jakarta.persistence.FetchType; import jakarta.persistence.FetchType;
import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.FIELD;
@ -15,8 +14,9 @@ import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* This is the collection-valued form of @Any definitions. Defines a ToMany-style association pointing * Declares a many-valued association targeting one of several entity types,
* to one of several entity types depending on a local discriminator. * depending on a local discriminator column. This is the collection-valued
* form of {@link Any}.
* *
* @see Any * @see Any
* *
@ -27,10 +27,14 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME) @Retention(RUNTIME)
public @interface ManyToAny { public @interface ManyToAny {
/** /**
* Defines whether the value of the field or property should be lazily loaded or must be * Specifies whether the value of the field or property should be fetched
* eagerly fetched. The EAGER strategy is a requirement on the persistence provider runtime * lazily or eagerly:
* that the value must be eagerly fetched. The LAZY strategy is applied when bytecode * <ul>
* enhancement is used. If not specified, defaults to EAGER. * <li>{@link FetchType#EAGER}, the default, requires that the association
* be fetched immediately, but
* <li>{@link FetchType#LAZY} is a hint which has no effect unless bytecode
* enhancement is enabled.
* </ul>
*/ */
FetchType fetch() default FetchType.EAGER; FetchType fetch() default FetchType.EAGER;
} }

View File

@ -15,9 +15,9 @@ import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* Applies a custom {@link UserType} for the column mapping * Specifies a custom {@link UserType} for the annotated attribute mapping.
* * <p>
* Generally, mutually exclusive with the compositional approach of * This is usually mutually exclusive with the compositional approach of
* {@link JavaType}, {@link JdbcType}, etc. * {@link JavaType}, {@link JdbcType}, etc.
*/ */
@java.lang.annotation.Target({METHOD, FIELD}) @java.lang.annotation.Target({METHOD, FIELD})
@ -25,16 +25,15 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
public @interface Type { public @interface Type {
/** /**
* The custom type implementor class * The implementation class which implements {@link UserType}.
*/ */
Class<? extends UserType<?>> value(); Class<? extends UserType<?>> value();
/** /**
* Parameters to be injected into the custom type after * Parameters to be injected into the custom type after it is
* it is instantiated. * instantiated. The {@link UserType} implementation must implement
* * {@link org.hibernate.usertype.ParameterizedType} to receive the
* The type should implement {@link org.hibernate.usertype.ParameterizedType} * parameters.
* to receive the parameters
*/ */
Parameter[] parameters() default {}; Parameter[] parameters() default {};
} }

View File

@ -114,8 +114,9 @@ import jakarta.persistence.Entity;
import jakarta.persistence.MapsId; import jakarta.persistence.MapsId;
/** /**
* The implementation of the in-flight Metadata collector contract. * The implementation of the {@linkplain InFlightMetadataCollector in-flight
* * metadata collector contract}.
* <p>
* The usage expectation is that this class is used until all Metadata info is * The usage expectation is that this class is used until all Metadata info is
* collected and then {@link #buildMetadataInstance} is called to generate * collected and then {@link #buildMetadataInstance} is called to generate
* the complete (and immutable) Metadata object. * the complete (and immutable) Metadata object.
@ -161,7 +162,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
private final Set<String> defaultNamedNativeQueryNames = new HashSet<>(); private final Set<String> defaultNamedNativeQueryNames = new HashSet<>();
private final Set<String> defaultSqlResultSetMappingNames = new HashSet<>(); private final Set<String> defaultSqlResultSetMappingNames = new HashSet<>();
private final Set<String> defaultNamedProcedureNames = new HashSet<>(); private final Set<String> defaultNamedProcedureNames = new HashSet<>();
private Map<Class, MappedSuperclass> mappedSuperClasses; private Map<Class<?>, MappedSuperclass> mappedSuperClasses;
private Map<XClass, Map<String, PropertyData>> propertiesAnnotatedWithMapsId; private Map<XClass, Map<String, PropertyData>> propertiesAnnotatedWithMapsId;
private Map<XClass, Map<String, PropertyData>> propertiesAnnotatedWithIdAndToOne; private Map<XClass, Map<String, PropertyData>> propertiesAnnotatedWithIdAndToOne;
private Map<String, String> mappedByResolver; private Map<String, String> mappedByResolver;
@ -1845,24 +1846,21 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
Map<String, Set<FkSecondPass>> isADependencyOf, Map<String, Set<FkSecondPass>> isADependencyOf,
String startTable, String startTable,
String currentTable) { String currentTable) {
Set<FkSecondPass> dependencies = isADependencyOf.get( currentTable ); Set<FkSecondPass> dependencies = isADependencyOf.get( currentTable );
if ( dependencies != null ) {
// bottom out for ( FkSecondPass sp : dependencies ) {
if ( dependencies == null || dependencies.size() == 0 ) { String dependentTable = sp.getValue().getTable().getQualifiedTableName().render();
return; if ( dependentTable.compareTo( startTable ) == 0 ) {
} throw new AnnotationException( "Circular foreign key dependency involving tables '"
+ startTable + "' and '" + dependentTable + "'" );
for ( FkSecondPass sp : dependencies ) { }
String dependentTable = sp.getValue().getTable().getQualifiedTableName().render(); buildRecursiveOrderedFkSecondPasses( orderedFkSecondPasses, isADependencyOf, startTable, dependentTable );
if ( dependentTable.compareTo( startTable ) == 0 ) { if ( !orderedFkSecondPasses.contains( sp ) ) {
throw new AnnotationException( "Foreign key circularity dependency involving the following tables: " + startTable + ", " + dependentTable ); orderedFkSecondPasses.add( 0, sp );
} }
buildRecursiveOrderedFkSecondPasses( orderedFkSecondPasses, isADependencyOf, startTable, dependentTable );
if ( !orderedFkSecondPasses.contains( sp ) ) {
orderedFkSecondPasses.add( 0, sp );
} }
} }
// else bottom out
} }
private void processEndOfQueue(List<FkSecondPass> endOfQueueFkSecondPasses) { private void processEndOfQueue(List<FkSecondPass> endOfQueueFkSecondPasses) {
@ -2177,7 +2175,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
sb.append( columnName ).append( ", " ); sb.append( columnName ).append( ", " );
} }
sb.setLength( sb.length() - 2 ); sb.setLength( sb.length() - 2 );
sb.append( ") on table " ).append( table.getName() ).append( ": database column " ); sb.append( ") on table '" ).append( table.getName() ).append( "' since the column " );
for ( Column column : unbound ) { for ( Column column : unbound ) {
sb.append("'").append( column.getName() ).append( "', " ); sb.append("'").append( column.getName() ).append( "', " );
} }
@ -2185,7 +2183,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
sb.append("'").append( column.getName() ).append( "', " ); sb.append("'").append( column.getName() ).append( "', " );
} }
sb.setLength( sb.length() - 2 ); sb.setLength( sb.length() - 2 );
sb.append( " not found. Make sure that you use the correct column name which depends on the naming strategy in use (it may not be the same as the property name in the entity, especially for relational types)" ); sb.append( " was not found (specify the correct column name, which depends on the naming strategy, and may not be the same as the entity property name)" );
throw new AnnotationException( sb.toString() ); throw new AnnotationException( sb.toString() );
} }
} }

View File

@ -449,7 +449,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
throw new AnnotationException( throw new AnnotationException(
String.format( String.format(
Locale.ROOT, Locale.ROOT,
"`%s` should specify either `%s` or `%s` - %s", "'%s' should specify either '%s' or '%s' (was '%s')",
AvailableSettings.DEFAULT_LIST_SEMANTICS, AvailableSettings.DEFAULT_LIST_SEMANTICS,
java.util.List.class.getName(), java.util.List.class.getName(),
java.util.Collection.class.getName(), java.util.Collection.class.getName(),

View File

@ -79,7 +79,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
private final Map<String,PersistentClass> entityBindingMap; private final Map<String,PersistentClass> entityBindingMap;
private final List<Component> composites; private final List<Component> composites;
private final Map<Class, MappedSuperclass> mappedSuperclassMap; private final Map<Class<?>, MappedSuperclass> mappedSuperclassMap;
private final Map<String,Collection> collectionBindingMap; private final Map<String,Collection> collectionBindingMap;
private final Map<String, TypeDefinition> typeDefinitionMap; private final Map<String, TypeDefinition> typeDefinitionMap;
private final Map<String, FilterDefinition> filterDefinitionMap; private final Map<String, FilterDefinition> filterDefinitionMap;
@ -99,7 +99,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
MetadataBuildingOptions metadataBuildingOptions, MetadataBuildingOptions metadataBuildingOptions,
Map<String, PersistentClass> entityBindingMap, Map<String, PersistentClass> entityBindingMap,
List<Component> composites, List<Component> composites,
Map<Class, MappedSuperclass> mappedSuperclassMap, Map<Class<?>, MappedSuperclass> mappedSuperclassMap,
Map<String, Collection> collectionBindingMap, Map<String, Collection> collectionBindingMap,
Map<String, TypeDefinition> typeDefinitionMap, Map<String, TypeDefinition> typeDefinitionMap,
Map<String, FilterDefinition> filterDefinitionMap, Map<String, FilterDefinition> filterDefinitionMap,
@ -398,29 +398,40 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
final ConfigurationService cfgService = sessionFactoryServiceRegistry.getService( ConfigurationService.class ); final ConfigurationService cfgService = sessionFactoryServiceRegistry.getService( ConfigurationService.class );
final ClassLoaderService classLoaderService = sessionFactoryServiceRegistry.getService( ClassLoaderService.class ); final ClassLoaderService classLoaderService = sessionFactoryServiceRegistry.getService( ClassLoaderService.class );
for ( Map.Entry<?,?> entry : ( (Map<?, ?>) cfgService.getSettings() ).entrySet() ) { for ( Map.Entry<String,Object> entry : cfgService.getSettings().entrySet() ) {
if ( !(entry.getKey() instanceof String) ) { final String propertyName = entry.getKey();
continue; if ( propertyName.startsWith( AvailableSettings.EVENT_LISTENER_PREFIX ) ) {
} final String eventTypeName = propertyName.substring( AvailableSettings.EVENT_LISTENER_PREFIX.length() + 1 );
final String propertyName = (String) entry.getKey(); final EventType<?> eventType = EventType.resolveEventTypeByName( eventTypeName );
if ( ! propertyName.startsWith( AvailableSettings.EVENT_LISTENER_PREFIX ) ) { final String listeners = (String) entry.getValue();
continue; appendListeners( eventListenerRegistry, classLoaderService, listeners, eventType );
}
final String eventTypeName = propertyName.substring( AvailableSettings.EVENT_LISTENER_PREFIX.length() + 1 );
final EventType eventType = EventType.resolveEventTypeByName( eventTypeName );
final EventListenerGroup eventListenerGroup = eventListenerRegistry.getEventListenerGroup( eventType );
for ( String listenerImpl : LISTENER_SEPARATION_PATTERN.split( ( (String) entry.getValue() ) ) ) {
eventListenerGroup.appendListener( instantiate( listenerImpl, classLoaderService ) );
} }
} }
} }
private Object instantiate(String listenerImpl, ClassLoaderService classLoaderService) { private <T> void appendListeners(
EventListenerRegistry eventListenerRegistry,
ClassLoaderService classLoaderService,
String listeners,
EventType<T> eventType) {
final EventListenerGroup<T> eventListenerGroup = eventListenerRegistry.getEventListenerGroup( eventType );
for ( String listenerImpl : LISTENER_SEPARATION_PATTERN.split( listeners ) ) {
@SuppressWarnings("unchecked")
T listener = (T) instantiate( listenerImpl, classLoaderService );
if ( !eventType.baseListenerInterface().isInstance( listener ) ) {
throw new HibernateException( "Event listener '" + listenerImpl + "' must implement '"
+ eventType.baseListenerInterface().getName() + "'");
}
eventListenerGroup.appendListener( listener );
}
}
private static Object instantiate(String listenerImpl, ClassLoaderService classLoaderService) {
try { try {
return classLoaderService.classForName( listenerImpl ).newInstance(); return classLoaderService.classForName( listenerImpl ).newInstance();
} }
catch (Exception e) { catch (Exception e) {
throw new HibernateException( "Could not instantiate requested listener [" + listenerImpl + "]", e ); throw new HibernateException( "Could not instantiate event listener '" + listenerImpl + "'", e );
} }
} }
@ -484,7 +495,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
return fetchProfileMap; return fetchProfileMap;
} }
public Map<Class, MappedSuperclass> getMappedSuperclassMap() { public Map<Class<?>, MappedSuperclass> getMappedSuperclassMap() {
return mappedSuperclassMap; return mappedSuperclassMap;
} }

View File

@ -57,24 +57,22 @@ import static org.hibernate.mapping.MappingHelper.injectParameters;
* @author Steve Ebersole * @author Steve Ebersole
* @author John Verhaeg * @author John Verhaeg
*/ */
@SuppressWarnings({"rawtypes", "unchecked"})
public class TypeDefinition implements Serializable { public class TypeDefinition implements Serializable {
public static final AtomicInteger NAME_COUNTER = new AtomicInteger(); public static final AtomicInteger NAME_COUNTER = new AtomicInteger();
private final String name; private final String name;
private final Class typeImplementorClass; private final Class<?> typeImplementorClass;
private final String[] registrationKeys; private final String[] registrationKeys;
private final Properties parameters; private final Map<String,String> parameters;
private BasicValue.Resolution<?> reusableResolution; private BasicValue.Resolution<?> reusableResolution;
public TypeDefinition( public TypeDefinition(
String name, String name,
Class typeImplementorClass, Class<?> typeImplementorClass,
String[] registrationKeys, String[] registrationKeys,
Properties parameters, Map<String,String> parameters) {
TypeConfiguration typeConfiguration) {
this.name = name; this.name = name;
this.typeImplementorClass = typeImplementorClass; this.typeImplementorClass = typeImplementorClass;
this.registrationKeys= registrationKeys; this.registrationKeys= registrationKeys;
@ -85,7 +83,7 @@ public class TypeDefinition implements Serializable {
return name; return name;
} }
public Class getTypeImplementorClass() { public Class<?> getTypeImplementorClass() {
return typeImplementorClass; return typeImplementorClass;
} }
@ -93,13 +91,13 @@ public class TypeDefinition implements Serializable {
return registrationKeys; return registrationKeys;
} }
public Properties getParameters() { public Map<String,String> getParameters() {
return parameters; return parameters;
} }
public BasicValue.Resolution<?> resolve( public BasicValue.Resolution<?> resolve(
Map localConfigParameters, Map<?,?> localConfigParameters,
MutabilityPlan explicitMutabilityPlan, MutabilityPlan<?> explicitMutabilityPlan,
MetadataBuildingContext context, MetadataBuildingContext context,
JdbcTypeIndicators indicators) { JdbcTypeIndicators indicators) {
if ( CollectionHelper.isEmpty( localConfigParameters ) ) { if ( CollectionHelper.isEmpty( localConfigParameters ) ) {
@ -107,7 +105,6 @@ public class TypeDefinition implements Serializable {
if ( reusableResolution == null ) { if ( reusableResolution == null ) {
reusableResolution = createResolution( name, Collections.emptyMap(), indicators, context ); reusableResolution = createResolution( name, Collections.emptyMap(), indicators, context );
} }
return reusableResolution; return reusableResolution;
} }
else { else {
@ -134,7 +131,7 @@ public class TypeDefinition implements Serializable {
private static BasicValue.Resolution<?> createResolution( private static BasicValue.Resolution<?> createResolution(
String name, String name,
Class<?> typeImplementorClass, Class<?> typeImplementorClass,
Properties parameters, Map<?,?> parameters,
Map<?,?> usageSiteProperties, Map<?,?> usageSiteProperties,
JdbcTypeIndicators indicators, JdbcTypeIndicators indicators,
MetadataBuildingContext context) { MetadataBuildingContext context) {
@ -153,34 +150,32 @@ public class TypeDefinition implements Serializable {
( (TypeConfigurationAware) typeInstance ).setTypeConfiguration( typeConfiguration ); ( (TypeConfigurationAware) typeInstance ).setTypeConfiguration( typeConfiguration );
} }
final Properties combinedTypeParameters; final Properties combinedTypeParameters = new Properties();
if ( parameters!=null ) {
if ( CollectionHelper.isNotEmpty( usageSiteProperties ) ) { combinedTypeParameters.putAll( parameters );
combinedTypeParameters = new Properties( parameters );
combinedTypeParameters.putAll( usageSiteProperties );
} }
else { if ( usageSiteProperties!=null ) {
combinedTypeParameters = parameters; combinedTypeParameters.putAll( usageSiteProperties );
} }
injectParameters( typeInstance, combinedTypeParameters ); injectParameters( typeInstance, combinedTypeParameters );
if ( typeInstance instanceof UserType ) { if ( typeInstance instanceof UserType ) {
final UserType<Object> userType = (UserType<Object>) typeInstance; final UserType<?> userType = (UserType<?>) typeInstance;
final CustomType<Object> customType = new CustomType<>( userType, typeConfiguration ); final CustomType<?> customType = new CustomType<>( userType, typeConfiguration );
return new UserTypeResolution( customType, null, combinedTypeParameters ); return new UserTypeResolution( customType, null, combinedTypeParameters );
} }
if ( typeInstance instanceof BasicType ) { if ( typeInstance instanceof BasicType ) {
final BasicType resolvedBasicType = (BasicType) typeInstance; final BasicType<?> resolvedBasicType = (BasicType<?>) typeInstance;
return new BasicValue.Resolution<Object>() { return new BasicValue.Resolution<>() {
@Override @Override
public JdbcMapping getJdbcMapping() { public JdbcMapping getJdbcMapping() {
return resolvedBasicType; return resolvedBasicType;
} }
@Override @Override @SuppressWarnings({"rawtypes", "unchecked"})
public BasicType getLegacyResolvedBasicType() { public BasicType getLegacyResolvedBasicType() {
return resolvedBasicType; return resolvedBasicType;
} }
@ -190,8 +185,8 @@ public class TypeDefinition implements Serializable {
return combinedTypeParameters; return combinedTypeParameters;
} }
@Override @Override @SuppressWarnings({"rawtypes", "unchecked"})
public JavaType<Object> getDomainJavaType() { public JavaType getDomainJavaType() {
return resolvedBasicType.getMappedJavaType(); return resolvedBasicType.getMappedJavaType();
} }
@ -210,8 +205,8 @@ public class TypeDefinition implements Serializable {
return resolvedBasicType.getValueConverter(); return resolvedBasicType.getValueConverter();
} }
@Override @Override @SuppressWarnings({"rawtypes", "unchecked"})
public MutabilityPlan<Object> getMutabilityPlan() { public MutabilityPlan getMutabilityPlan() {
// a TypeDefinition does not explicitly provide a MutabilityPlan (yet?) // a TypeDefinition does not explicitly provide a MutabilityPlan (yet?)
return resolvedBasicType.isMutable() return resolvedBasicType.isMutable()
? getDomainJavaType().getMutabilityPlan() ? getDomainJavaType().getMutabilityPlan()
@ -229,22 +224,23 @@ public class TypeDefinition implements Serializable {
.resolveDescriptor( typeImplementorClass ); .resolveDescriptor( typeImplementorClass );
final JdbcType jdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.VARBINARY ); final JdbcType jdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.VARBINARY );
final BasicType<Serializable> resolved = typeConfiguration.getBasicTypeRegistry().resolve( jtd, jdbcType ); final BasicType<Serializable> resolved = typeConfiguration.getBasicTypeRegistry().resolve( jtd, jdbcType );
@SuppressWarnings({"rawtypes", "unchecked"})
final SerializableType legacyType = new SerializableType( typeImplementorClass ); final SerializableType legacyType = new SerializableType( typeImplementorClass );
return new BasicValue.Resolution<Object>() { return new BasicValue.Resolution<>() {
@Override @Override
public JdbcMapping getJdbcMapping() { public JdbcMapping getJdbcMapping() {
return resolved; return resolved;
} }
@Override @Override @SuppressWarnings({"rawtypes", "unchecked"})
public BasicType getLegacyResolvedBasicType() { public BasicType getLegacyResolvedBasicType() {
return legacyType; return legacyType;
} }
@Override @Override @SuppressWarnings({"rawtypes", "unchecked"})
public JavaType<Object> getDomainJavaType() { public JavaType getDomainJavaType() {
return (JavaType) resolved.getMappedJavaType(); return resolved.getMappedJavaType();
} }
@Override @Override
@ -262,8 +258,8 @@ public class TypeDefinition implements Serializable {
return resolved.getValueConverter(); return resolved.getValueConverter();
} }
@Override @Override @SuppressWarnings({"rawtypes", "unchecked"})
public MutabilityPlan<Object> getMutabilityPlan() { public MutabilityPlan getMutabilityPlan() {
// a TypeDefinition does not explicitly provide a MutabilityPlan (yet?) // a TypeDefinition does not explicitly provide a MutabilityPlan (yet?)
return resolved.isMutable() return resolved.isMutable()
? getDomainJavaType().getMutabilityPlan() ? getDomainJavaType().getMutabilityPlan()
@ -281,47 +277,31 @@ public class TypeDefinition implements Serializable {
String name, Class<?> typeImplementorClass, String name, Class<?> typeImplementorClass,
BeanInstanceProducer instanceProducer) { BeanInstanceProducer instanceProducer) {
if ( Helper.INSTANCE.shouldIgnoreBeanContainer( serviceRegistry ) ) { if ( Helper.INSTANCE.shouldIgnoreBeanContainer( serviceRegistry ) ) {
if ( name != null ) { return name != null
return instanceProducer.produceBeanInstance( name, typeImplementorClass ); ? instanceProducer.produceBeanInstance( name, typeImplementorClass )
} : instanceProducer.produceBeanInstance( typeImplementorClass );
else {
return instanceProducer.produceBeanInstance( typeImplementorClass );
}
} }
else { else {
final ManagedBean typeBean; ManagedBeanRegistry beanRegistry = serviceRegistry.getService(ManagedBeanRegistry.class);
if ( name != null ) { final ManagedBean<?> typeBean = name != null
typeBean = serviceRegistry.getService( ManagedBeanRegistry.class ) ? beanRegistry.getBean( name, typeImplementorClass, instanceProducer )
.getBean( name, typeImplementorClass, instanceProducer ); : beanRegistry.getBean( typeImplementorClass, instanceProducer );
}
else {
typeBean = serviceRegistry.getService( ManagedBeanRegistry.class )
.getBean( typeImplementorClass, instanceProducer );
}
return typeBean.getBeanInstance(); return typeBean.getBeanInstance();
} }
} }
public static BasicValue.Resolution<?> createLocalResolution( public static BasicValue.Resolution<?> createLocalResolution(
String name, String name,
Class typeImplementorClass, Class<?> typeImplementorClass,
MutabilityPlan explicitMutabilityPlan, MutabilityPlan<?> explicitMutabilityPlan,
Map localTypeParams, Map<?,?> localTypeParams,
MetadataBuildingContext buildingContext) { MetadataBuildingContext buildingContext) {
name = name + ':' + NAME_COUNTER.getAndIncrement();
final Properties properties = new Properties();
properties.putAll( localTypeParams );
final TypeConfiguration typeConfiguration = buildingContext.getBootstrapContext().getTypeConfiguration();
return createResolution( return createResolution(
name, name + ':' + NAME_COUNTER.getAndIncrement(),
typeImplementorClass, typeImplementorClass,
properties, localTypeParams,
null, null,
typeConfiguration.getCurrentBaseSqlTypeIndicators(), buildingContext.getBootstrapContext().getTypeConfiguration().getCurrentBaseSqlTypeIndicators(),
buildingContext buildingContext
); );
} }

View File

@ -30,7 +30,8 @@ import org.hibernate.internal.util.StringHelper;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import com.fasterxml.classmate.ResolvedType; import com.fasterxml.classmate.ResolvedType;
import jakarta.persistence.AttributeConverter;
import static org.hibernate.boot.model.convert.internal.ConverterHelper.resolveConverterClassParamTypes;
/** /**
* @implNote It is important that all {@link RegisteredConversion} be registered * @implNote It is important that all {@link RegisteredConversion} be registered
@ -95,22 +96,7 @@ public class AttributeConverterManager implements ConverterAutoApplyHandler {
final Class<?> domainType; final Class<?> domainType;
if ( conversion.getExplicitDomainType().equals( void.class ) ) { if ( conversion.getExplicitDomainType().equals( void.class ) ) {
// the registration did not define an explicit domain-type, so inspect the converter // the registration did not define an explicit domain-type, so inspect the converter
final ClassmateContext classmateContext = context.getClassmateContext(); final List<ResolvedType> converterParamTypes = resolveConverterClassParamTypes( conversion.getConverterType(), context.getClassmateContext() );
final ResolvedType converterType = classmateContext.getTypeResolver().resolve( conversion.getConverterType() );
final List<ResolvedType> converterParamTypes = converterType.typeParametersFor( AttributeConverter.class );
if ( converterParamTypes == null ) {
throw new AnnotationException(
"Could not extract type parameter information from AttributeConverter implementation ["
+ conversion.getConverterType().getName() + "]"
);
}
else if ( converterParamTypes.size() != 2 ) {
throw new AnnotationException(
"Unexpected type parameter information for AttributeConverter implementation [" +
conversion.getConverterType().getName() + "]; expected 2 parameter types, but found " + converterParamTypes.size()
);
}
domainType = converterParamTypes.get( 0 ).getErasedType(); domainType = converterParamTypes.get( 0 ).getErasedType();
} }
else { else {
@ -122,13 +108,13 @@ public class AttributeConverterManager implements ConverterAutoApplyHandler {
if ( existingRegistration != null ) { if ( existingRegistration != null ) {
if ( !conversion.equals( existingRegistration ) ) { if ( !conversion.equals( existingRegistration ) ) {
throw new AnnotationException( throw new AnnotationException(
"Attempt to register non-matching `@ConverterRegistration` descriptors for `AttributeConverter` " "Conflicting '@ConverterRegistration' descriptors for attribute converter '"
+ conversion.getConverterType().getName() + conversion.getConverterType().getName() + "'"
); );
} }
else { else {
if ( log.isDebugEnabled() ) { if ( log.isDebugEnabled() ) {
log.debugf( "Skipping duplicate `@ConverterRegistration` for `%s`", conversion.getConverterType().getName() ); log.debugf( "Skipping duplicate '@ConverterRegistration' for '%s'", conversion.getConverterType().getName() );
} }
} }
} }

View File

@ -97,14 +97,15 @@ public class ConverterHelper {
final List<ResolvedType> converterParamTypes = converterType.typeParametersFor( AttributeConverter.class ); final List<ResolvedType> converterParamTypes = converterType.typeParametersFor( AttributeConverter.class );
if ( converterParamTypes == null ) { if ( converterParamTypes == null ) {
throw new AnnotationException( throw new AnnotationException(
"Could not extract type parameter information from AttributeConverter implementation [" "Could not extract type argument from attribute converter class '"
+ converterClass.getName() + "]" + converterClass.getName() + "'"
); );
} }
else if ( converterParamTypes.size() != 2 ) { else if ( converterParamTypes.size() != 2 ) {
throw new AnnotationException( throw new AnnotationException(
"Unexpected type parameter information for AttributeConverter implementation [" + "Unexpected type argument for attribute converter class '"
converterClass.getName() + "]; expected 2 parameter types, but found " + converterParamTypes.size() + converterClass.getName()
+ "' (expected 2 type arguments, but found " + converterParamTypes.size() + ")"
); );
} }
return converterParamTypes; return converterParamTypes;

View File

@ -94,7 +94,7 @@ public class RegisteredConversion {
return explicitDomainType; return explicitDomainType;
} }
public Class<?> getConverterType() { public Class<? extends AttributeConverter<?,?>> getConverterType() {
return converterType; return converterType;
} }

View File

@ -21,8 +21,8 @@ import org.hibernate.type.descriptor.jdbc.JdbcType;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class UserTypeResolution implements BasicValue.Resolution { public class UserTypeResolution implements BasicValue.Resolution {
private final CustomType<Object> userTypeAdapter; private final CustomType<?> userTypeAdapter;
private final MutabilityPlan mutabilityPlan; private final MutabilityPlan<?> mutabilityPlan;
/** /**
* We need this for the way envers interprets the boot-model * We need this for the way envers interprets the boot-model
@ -31,8 +31,8 @@ public class UserTypeResolution implements BasicValue.Resolution {
private final Properties combinedTypeParameters; private final Properties combinedTypeParameters;
public UserTypeResolution( public UserTypeResolution(
CustomType<Object> userTypeAdapter, CustomType<?> userTypeAdapter,
MutabilityPlan explicitMutabilityPlan, MutabilityPlan<?> explicitMutabilityPlan,
Properties combinedTypeParameters) { Properties combinedTypeParameters) {
this.userTypeAdapter = userTypeAdapter; this.userTypeAdapter = userTypeAdapter;
this.combinedTypeParameters = combinedTypeParameters; this.combinedTypeParameters = combinedTypeParameters;

View File

@ -9,7 +9,6 @@ package org.hibernate.boot.model.source.internal.hbm;
import java.sql.Types; import java.sql.Types;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@ -2383,7 +2382,7 @@ public class ModelBinder {
private BasicType<?> resolveExplicitlyNamedAnyDiscriminatorType( private BasicType<?> resolveExplicitlyNamedAnyDiscriminatorType(
String typeName, String typeName,
Properties parameters, Map<String,String> parameters,
Any.MetaValue discriminatorMapping) { Any.MetaValue discriminatorMapping) {
final BootstrapContext bootstrapContext = metadataBuildingContext.getBootstrapContext(); final BootstrapContext bootstrapContext = metadataBuildingContext.getBootstrapContext();
@ -2431,7 +2430,9 @@ public class ModelBinder {
if ( typeInstance instanceof ParameterizedType ) { if ( typeInstance instanceof ParameterizedType ) {
if ( parameters != null ) { if ( parameters != null ) {
( (ParameterizedType) typeInstance ).setParameterValues( parameters ); Properties properties = new Properties();
properties.putAll( parameters );
( (ParameterizedType) typeInstance ).setParameterValues( properties );
} }
} }
@ -2807,9 +2808,9 @@ public class ModelBinder {
private static class TypeResolution { private static class TypeResolution {
private final String typeName; private final String typeName;
private final Properties parameters; private final Map<String,String> parameters;
public TypeResolution(String typeName, Properties parameters) { public TypeResolution(String typeName, Map<String,String> parameters) {
this.typeName = typeName; this.typeName = typeName;
this.parameters = parameters; this.parameters = parameters;
} }
@ -2823,7 +2824,7 @@ public class ModelBinder {
} }
String typeName = typeSource.getName(); String typeName = typeSource.getName();
Properties typeParameters = new Properties(); Map<String,String> typeParameters = new HashMap<>();
final TypeDefinition typeDefinition = sourceDocument.getMetadataCollector().getTypeDefinition( typeName ); final TypeDefinition typeDefinition = sourceDocument.getMetadataCollector().getTypeDefinition( typeName );
if ( typeDefinition != null ) { if ( typeDefinition != null ) {
@ -4245,12 +4246,11 @@ public class ModelBinder {
private String columns(Value value) { private String columns(Value value) {
final StringBuilder builder = new StringBuilder(); final StringBuilder builder = new StringBuilder();
final Iterator<Selectable> selectableItr = value.getColumnIterator(); for ( Selectable selectable : value.getSelectables() ) {
while ( selectableItr.hasNext() ) { if ( builder.length()>0) {
builder.append( selectableItr.next().getText() );
if ( selectableItr.hasNext() ) {
builder.append( ", " ); builder.append( ", " );
} }
builder.append( selectable.getText() );
} }
return builder.toString(); return builder.toString();
} }

View File

@ -33,8 +33,7 @@ public class TypeDefinitionBinder {
typeDefinitionBinding.getName(), typeDefinitionBinding.getName(),
cls.classForName( typeDefinitionBinding.getClazz() ), cls.classForName( typeDefinitionBinding.getClazz() ),
null, null,
ConfigParameterHelper.extractConfigParametersAsProperties( typeDefinitionBinding ), ConfigParameterHelper.extractConfigParameters( typeDefinitionBinding )
context.getMetadataCollector().getTypeConfiguration()
); );
if ( log.isDebugEnabled() ) { if ( log.isDebugEnabled() ) {

View File

@ -59,7 +59,7 @@ import org.hibernate.usertype.UserCollectionType;
import jakarta.persistence.AttributeConverter; import jakarta.persistence.AttributeConverter;
/** /**
* An in-flight representation of Metadata while Metadata is being built. * An in-flight representation of {@link org.hibernate.boot.Metadata} while it is being built.
* *
* @author Steve Ebersole * @author Steve Ebersole
* *

View File

@ -38,6 +38,7 @@ import org.hibernate.mapping.Table;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import static org.hibernate.cfg.BinderHelper.getOverridableAnnotation; import static org.hibernate.cfg.BinderHelper.getOverridableAnnotation;
import static org.hibernate.cfg.BinderHelper.getPath;
import static org.hibernate.cfg.BinderHelper.getRelativePath; import static org.hibernate.cfg.BinderHelper.getRelativePath;
import static org.hibernate.internal.util.StringHelper.isNotEmpty; import static org.hibernate.internal.util.StringHelper.isNotEmpty;
@ -297,8 +298,9 @@ public class AnnotatedColumn {
final int numberOfJdbcParams = StringHelper.count( writeExpression, '?' ); final int numberOfJdbcParams = StringHelper.count( writeExpression, '?' );
if ( numberOfJdbcParams != 1 ) { if ( numberOfJdbcParams != 1 ) {
throw new AnnotationException( throw new AnnotationException(
"@WriteExpression must contain exactly one value placeholder ('?') character: property [" "Write expression in '@ColumnTransformer' for property '" + propertyName
+ propertyName + "] and column [" + logicalColumnName + "]" + "' and column '" + logicalColumnName + "'"
+ " must contain exactly one placeholder character ('?')"
); );
} }
} }
@ -512,8 +514,8 @@ public class AnnotatedColumn {
if ( join == null ) { if ( join == null ) {
throw new AnnotationException( throw new AnnotationException(
"Cannot find the expected secondary table: no " "Secondary table '" + explicitTableName + "' for property '" + propertyHolder.getClassName()
+ explicitTableName + " available for " + propertyHolder.getClassName() + "' is not declared (use '@SecondaryTable' to declare the secondary table)"
); );
} }
@ -710,7 +712,12 @@ public class AnnotatedColumn {
if ( overriddenCols != null ) { if ( overriddenCols != null ) {
//check for overridden first //check for overridden first
if ( columnAnns != null && overriddenCols.length != columnAnns.length ) { if ( columnAnns != null && overriddenCols.length != columnAnns.length ) {
throw new AnnotationException( "AttributeOverride.column() should override all columns for now" ); //TODO: unfortunately, we never actually see this nice error message, since
// PersistentClass.validate() gets called first and produces a worse message
throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData )
+ "' specifies " + columnAnns.length
+ " '@AttributeOverride's but the overridden property has " + overriddenCols.length
+ " columns (every column must have exactly one '@AttributeOverride')" );
} }
LOG.debugf( "Column(s) overridden for property %s", inferredData.getPropertyName() ); LOG.debugf( "Column(s) overridden for property %s", inferredData.getPropertyName() );
return overriddenCols.length == 0 ? null : overriddenCols; return overriddenCols.length == 0 ? null : overriddenCols;
@ -959,24 +966,24 @@ public class AnnotatedColumn {
if ( nbrOfColumns > 1 ) { if ( nbrOfColumns > 1 ) {
for (int currentIndex = 1; currentIndex < nbrOfColumns; currentIndex++) { for (int currentIndex = 1; currentIndex < nbrOfColumns; currentIndex++) {
if ( !columns[currentIndex].isFormula() && !columns[currentIndex - 1].isFormula() ) { if ( !columns[currentIndex].isFormula() && !columns[currentIndex - 1].isFormula() ) {
if ( columns[currentIndex].isInsertable() != columns[currentIndex - 1].isInsertable() ) {
throw new AnnotationException(
"Mixing insertable and non insertable columns in a property is not allowed: " + propertyName
);
}
if ( columns[currentIndex].isNullable() != columns[currentIndex - 1].isNullable() ) { if ( columns[currentIndex].isNullable() != columns[currentIndex - 1].isNullable() ) {
throw new AnnotationException( throw new AnnotationException(
"Mixing nullable and non nullable columns in a property is not allowed: " + propertyName "Column mappings for property '" + propertyName + "' mix nullable with 'not null'"
);
}
if ( columns[currentIndex].isInsertable() != columns[currentIndex - 1].isInsertable() ) {
throw new AnnotationException(
"Column mappings for property '" + propertyName + "' mix insertable with 'insertable=false'"
); );
} }
if ( columns[currentIndex].isUpdatable() != columns[currentIndex - 1].isUpdatable() ) { if ( columns[currentIndex].isUpdatable() != columns[currentIndex - 1].isUpdatable() ) {
throw new AnnotationException( throw new AnnotationException(
"Mixing updatable and non updatable columns in a property is not allowed: " + propertyName "Column mappings for property '" + propertyName + "' mix updatable with 'updatable=false'"
); );
} }
if ( !columns[currentIndex].getTable().equals( columns[currentIndex - 1].getTable() ) ) { if ( !columns[currentIndex].getTable().equals( columns[currentIndex - 1].getTable() ) ) {
throw new AnnotationException( throw new AnnotationException(
"Mixing different tables in a property is not allowed: " + propertyName "Column mappings for property '" + propertyName + "' mix distinct secondary tables"
); );
} }
} }

View File

@ -258,8 +258,8 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
if ( ann != null ) { if ( ann != null ) {
if ( !BinderHelper.isEmptyOrNullAnnotationValue( mappedBy ) ) { if ( !BinderHelper.isEmptyOrNullAnnotationValue( mappedBy ) ) {
throw new AnnotationException( throw new AnnotationException(
"Illegal attempt to define a @JoinColumn with a mappedBy association: " "Association '" + getRelativePath( propertyHolder, propertyName )
+ getRelativePath( propertyHolder, propertyName ) + "' is 'mappedBy' a different entity and may not explicitly specify the '@JoinColumn'"
); );
} }
AnnotatedJoinColumn joinColumn = new AnnotatedJoinColumn(); AnnotatedJoinColumn joinColumn = new AnnotatedJoinColumn();
@ -428,10 +428,8 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
public static void checkIfJoinColumn(Object columns, PropertyHolder holder, PropertyData property) { public static void checkIfJoinColumn(Object columns, PropertyHolder holder, PropertyData property) {
if ( !( columns instanceof AnnotatedJoinColumn[] ) ) { if ( !( columns instanceof AnnotatedJoinColumn[] ) ) {
throw new AnnotationException( throw new AnnotationException(
"@Column cannot be used on an association property: " "Property '" + getRelativePath( holder, property.getPropertyName() )
+ holder.getEntityName() + "' is an association and may not use '@Column' to specify column mappings (use '@JoinColumn' instead)"
+ "."
+ property.getPropertyName()
); );
} }
} }
@ -559,9 +557,10 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
throw new AnnotationException( throw new AnnotationException(
String.format( String.format(
Locale.ENGLISH, Locale.ENGLISH,
"mapped-by [%s] defined for attribute [%s] referenced an invalid property (no columns)", "Association '%s' is 'mappedBy' a property '%s' of entity '%s' with no columns",
propertyHolder.getPath(),
mappedByPropertyName, mappedByPropertyName,
propertyHolder.getPath() mappedByEntityName
) )
); );
} }
@ -570,7 +569,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
throw new AnnotationException( throw new AnnotationException(
String.format( String.format(
Locale.ENGLISH, Locale.ENGLISH,
"mapped-by [%s] defined for attribute [%s] referenced an invalid property (formula)", "Association '%s' is 'mappedBy' a property '%s' of entity '%s' which maps to a formula",
mappedByPropertyName, mappedByPropertyName,
propertyHolder.getPath() propertyHolder.getPath()
) )
@ -580,7 +579,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
throw new AnnotationException( throw new AnnotationException(
String.format( String.format(
Locale.ENGLISH, Locale.ENGLISH,
"mapped-by [%s] defined for attribute [%s] referenced an invalid property (multiple columns)", "Association '%s' is 'mappedBy' a property '%s' of entity '%s' with multiple columns",
mappedByPropertyName, mappedByPropertyName,
propertyHolder.getPath() propertyHolder.getPath()
) )
@ -604,7 +603,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
else if ( ownerSide ) { else if ( ownerSide ) {
final String logicalTableName = metadataCollector.getLogicalTableName( referencedEntity.getTable() ); final String logicalTableName = metadataCollector.getLogicalTableName( referencedEntity.getTable() );
columnIdentifier =implicitNamingStrategy.determineJoinColumnName( columnIdentifier = implicitNamingStrategy.determineJoinColumnName(
new ImplicitJoinColumnNameSource() { new ImplicitJoinColumnNameSource() {
final ImplicitJoinColumnNameSource.Nature implicitNamingNature = getImplicitNature(); final ImplicitJoinColumnNameSource.Nature implicitNamingNature = getImplicitNature();
@ -794,10 +793,11 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
if ( columnOwner == null ) { if ( columnOwner == null ) {
try { try {
throw new MappingException( throw new MappingException(
"Unable to find column with logical name: " "No column with logical name '"
+ columns[0].getReferencedColumn() + columns[0].getReferencedColumn()
+ " in " + referencedEntity.getTable() + "' in table '" + referencedEntity.getTable().getName()
+ " and its related supertables and secondary tables" + "' for entity '" + referencedEntity.getEntityName()
+ "', nor in its related superclass tables and secondary tables"
); );
} }
catch (MappingException e) { catch (MappingException e) {
@ -821,8 +821,8 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
catch (MappingException me) { catch (MappingException me) {
//rewrite the exception //rewrite the exception
throw new MappingException( throw new MappingException(
"Unable to find column with logical name: " "No column with logical name '" + logicalReferencedColumnName
+ logicalReferencedColumnName + " in " + matchingTable.getName() + "' in table '" + matchingTable.getName() + "'"
); );
} }
noReferencedColumn = false; noReferencedColumn = false;

View File

@ -560,8 +560,7 @@ public final class AnnotationBinder {
//@Entity and @MappedSuperclass on the same class leads to a NPE down the road //@Entity and @MappedSuperclass on the same class leads to a NPE down the road
if ( clazzToProcess.isAnnotationPresent( Entity.class ) if ( clazzToProcess.isAnnotationPresent( Entity.class )
&& clazzToProcess.isAnnotationPresent( MappedSuperclass.class ) ) { && clazzToProcess.isAnnotationPresent( MappedSuperclass.class ) ) {
throw new AnnotationException( "An entity cannot be annotated with both @Entity and @MappedSuperclass: " throw new AnnotationException( "Type '"+ clazzToProcess.getName() + "' is annotated both '@Entity' and '@MappedSuperclass'");
+ clazzToProcess.getName() );
} }
if ( clazzToProcess.isAnnotationPresent( Inheritance.class ) if ( clazzToProcess.isAnnotationPresent( Inheritance.class )
@ -995,21 +994,17 @@ public final class AnnotationBinder {
final XAnnotatedElement element = propertyAnnotatedElement.getProperty(); final XAnnotatedElement element = propertyAnnotatedElement.getProperty();
if ( hasIdAnnotation(element) ) { if ( hasIdAnnotation(element) ) {
inFlightPropertyDataList.add( 0, propertyAnnotatedElement ); inFlightPropertyDataList.add( 0, propertyAnnotatedElement );
/* // The property must be put in hibernate.properties as it's a system wide property. Fixable?
* The property must be put in hibernate.properties as it's a system wide property. Fixable? //TODO support true/false/default on the property instead of present / not present
* TODO support true/false/default on the property instead of present / not present //TODO is @Column mandatory?
* TODO is @Column mandatory? //TODO add method support
* TODO add method support
*/
if ( context.getBuildingOptions().isSpecjProprietarySyntaxEnabled() ) { if ( context.getBuildingOptions().isSpecjProprietarySyntaxEnabled() ) {
if ( element.isAnnotationPresent( Id.class ) && element.isAnnotationPresent( Column.class ) ) { if ( element.isAnnotationPresent( Id.class ) && element.isAnnotationPresent( Column.class ) ) {
String columnName = element.getAnnotation( Column.class ).name(); String columnName = element.getAnnotation( Column.class ).name();
for ( XProperty prop : declaringClass.getDeclaredProperties( AccessType.FIELD.getType() ) ) { for ( XProperty prop : declaringClass.getDeclaredProperties( AccessType.FIELD.getType() ) ) {
if ( !prop.isAnnotationPresent( MapsId.class ) ) { if ( !prop.isAnnotationPresent( MapsId.class ) ) {
/* //The detection of a configured individual JoinColumn differs between Annotation
* The detection of a configured individual JoinColumn differs between Annotation //and XML configuration processing.
* and XML configuration processing.
*/
boolean isRequiredAnnotationPresent = false; boolean isRequiredAnnotationPresent = false;
JoinColumns groupAnnotation = prop.getAnnotation( JoinColumns.class ); JoinColumns groupAnnotation = prop.getAnnotation( JoinColumns.class );
if ( (prop.isAnnotationPresent( JoinColumn.class ) if ( (prop.isAnnotationPresent( JoinColumn.class )
@ -1106,8 +1101,9 @@ public final class AnnotationBinder {
} }
else { else {
throw new AnnotationException( throw new AnnotationException(
"@Parent cannot be applied outside an embeddable object: " "Property '" + getPath( propertyHolder, inferredData )
+ getPath( propertyHolder, inferredData ) + "' is annotated '@Parent' but is not a member of an embeddable class"
); );
} }
} }
@ -1292,19 +1288,20 @@ public final class AnnotationBinder {
PropertyBinder propertyBinder) { PropertyBinder propertyBinder) {
if (isIdentifierMapper) { if (isIdentifierMapper) {
throw new AnnotationException( throw new AnnotationException(
"@IdClass class should not have @Version property" "Class '" + propertyHolder.getEntityName()
+ "' is annotated '@IdClass' and may not have a property annotated '@Version'"
); );
} }
if ( !( propertyHolder.getPersistentClass() instanceof RootClass ) ) { if ( !( propertyHolder.getPersistentClass() instanceof RootClass ) ) {
throw new AnnotationException( throw new AnnotationException(
"Unable to define/override @Version on a subclass: " "Entity '" + propertyHolder.getEntityName()
+ propertyHolder.getEntityName() + "' is a subclass in an entity class hierarchy and may not have a property annotated '@Version'"
); );
} }
if ( !propertyHolder.isEntity() ) { if ( !propertyHolder.isEntity() ) {
throw new AnnotationException( throw new AnnotationException(
"Unable to define @Version on an embedded class: " "Embedded class '" + propertyHolder.getEntityName()
+ propertyHolder.getEntityName() + "' may not have a property annotated '@Version'"
); );
} }
if ( LOG.isTraceEnabled() ) { if ( LOG.isTraceEnabled() ) {
@ -1387,12 +1384,19 @@ public final class AnnotationBinder {
); );
if ( isComponent || compositeUserType != null ) { if ( isComponent || compositeUserType != null ) {
String referencedEntityName = null; final String referencedEntityName;
final String propertyName;
if ( isOverridden ) { if ( isOverridden ) {
// careful: not always a @MapsId property, sometimes it's from an @IdClass
PropertyData mapsIdProperty = getPropertyOverriddenByMapperOrMapsId( PropertyData mapsIdProperty = getPropertyOverriddenByMapperOrMapsId(
isId, propertyHolder, property.getName(), context isId, propertyHolder, property.getName(), context
); );
referencedEntityName = mapsIdProperty.getClassOrElementName(); referencedEntityName = mapsIdProperty.getClassOrElementName();
propertyName = mapsIdProperty.getPropertyName();
}
else {
referencedEntityName = null;
propertyName = null;
} }
propertyBinder = bindComponent( propertyBinder = bindComponent(
@ -1406,6 +1410,7 @@ public final class AnnotationBinder {
isId, isId,
inheritanceStatePerClass, inheritanceStatePerClass,
referencedEntityName, referencedEntityName,
propertyName,
determineCustomInstantiator( property, returnedClass, context ), determineCustomInstantiator( property, returnedClass, context ),
compositeUserType, compositeUserType,
isOverridden ? (AnnotatedJoinColumn[]) columns : null isOverridden ? (AnnotatedJoinColumn[]) columns : null
@ -1524,9 +1529,9 @@ public final class AnnotationBinder {
throw new AnnotationException( throw new AnnotationException(
String.format( String.format(
Locale.ROOT, Locale.ROOT,
"@Columns not allowed on a @Any property [%s]; @Column or @Formula is used to map the discriminator" + "Property '%s' is annotated '@Any' and may not have a '@Columns' annotation "
"and only one is allowed", + "(a single '@Column' or '@Formula' must be used to map the discriminator, and '@JoinColumn's must be used to map the foreign key) ",
getPath(propertyHolder, inferredData) getPath( propertyHolder, inferredData )
) )
); );
} }
@ -1561,8 +1566,8 @@ public final class AnnotationBinder {
if ( property.isAnnotationPresent( Column.class ) if ( property.isAnnotationPresent( Column.class )
|| property.isAnnotationPresent( Columns.class ) ) { || property.isAnnotationPresent( Columns.class ) ) {
throw new AnnotationException( throw new AnnotationException(
"@Column(s) not allowed on a @OneToOne property: " "Property '"+ getPath( propertyHolder, inferredData )
+ getPath(propertyHolder, inferredData) + "' is a '@OneToOne' association and may not use '@Column' to specify column mappings (use '@PrimaryKeyJoinColumn' instead)"
); );
} }
@ -1631,8 +1636,8 @@ public final class AnnotationBinder {
if ( property.isAnnotationPresent( Column.class ) if ( property.isAnnotationPresent( Column.class )
|| property.isAnnotationPresent( Columns.class ) ) { || property.isAnnotationPresent( Columns.class ) ) {
throw new AnnotationException( throw new AnnotationException(
"@Column(s) not allowed on a @ManyToOne property: " "Property '"+ getPath( propertyHolder, inferredData )
+ getPath(propertyHolder, inferredData) + "' is a '@ManyToOne' association and may not use '@Column' to specify column mappings (use '@JoinColumn' instead)"
); );
} }
@ -1830,8 +1835,9 @@ public final class AnnotationBinder {
MetadataBuildingContext buildingContext) { MetadataBuildingContext buildingContext) {
if ( isIdentifierMapper ) { if ( isIdentifierMapper ) {
throw new AnnotationException( throw new AnnotationException(
"@IdClass class should not have @Id nor @EmbeddedId properties: " "Property '"+ getPath( propertyHolder, inferredData )
+ getPath( propertyHolder, inferredData ) + "' belongs to an '@IdClass' and may not be annotated '@Id' or '@EmbeddedId'"
); );
} }
XClass entityXClass = inferredData.getClassOrElement(); XClass entityXClass = inferredData.getClassOrElement();
@ -1931,6 +1937,7 @@ public final class AnnotationBinder {
boolean isId, //is an identifier boolean isId, //is an identifier
Map<XClass, InheritanceState> inheritanceStatePerClass, Map<XClass, InheritanceState> inheritanceStatePerClass,
String referencedEntityName, //is a component who is overridden by a @MapsId String referencedEntityName, //is a component who is overridden by a @MapsId
String propertyName,
Class<? extends EmbeddableInstantiator> customInstantiatorImpl, Class<? extends EmbeddableInstantiator> customInstantiatorImpl,
Class<? extends CompositeUserType<?>> compositeUserTypeClass, Class<? extends CompositeUserType<?>> compositeUserTypeClass,
AnnotatedJoinColumn[] columns) { AnnotatedJoinColumn[] columns) {
@ -1947,6 +1954,7 @@ public final class AnnotationBinder {
SecondPass sp = new CopyIdentifierComponentSecondPass( SecondPass sp = new CopyIdentifierComponentSecondPass(
comp, comp,
referencedEntityName, referencedEntityName,
propertyName,
columns, columns,
buildingContext buildingContext
); );
@ -1972,16 +1980,18 @@ public final class AnnotationBinder {
comp.setKey( true ); comp.setKey( true );
if ( propertyHolder.getPersistentClass().getIdentifier() != null ) { if ( propertyHolder.getPersistentClass().getIdentifier() != null ) {
throw new AnnotationException( throw new AnnotationException(
comp.getComponentClassName() "Embeddable class '" + comp.getComponentClassName()
+ " must not have @Id properties when used as an @EmbeddedId: " + "' may not have a property annotated '@Id' since it is used by '"
+ getPath( propertyHolder, inferredData ) + getPath( propertyHolder, inferredData )
+ "' as an '@EmbeddedId'"
); );
} }
if ( referencedEntityName == null && comp.getPropertySpan() == 0 ) { if ( referencedEntityName == null && comp.getPropertySpan() == 0 ) {
throw new AnnotationException( throw new AnnotationException(
comp.getComponentClassName() "Embeddable class '" + comp.getComponentClassName()
+ " has no persistent id property: " + "' may not be used as an '@EmbeddedId' by '"
+ getPath( propertyHolder, inferredData ) + getPath( propertyHolder, inferredData )
+ "' because it has no properties"
); );
} }
} }
@ -2136,9 +2146,10 @@ public final class AnnotationBinder {
if ( propertyHolder.isInIdClass() ) { if ( propertyHolder.isInIdClass() ) {
if ( entityPropertyData == null ) { if ( entityPropertyData == null ) {
throw new AnnotationException( throw new AnnotationException(
"Property of @IdClass not found in entity " "Property '" + getPath( propertyHolder, idClassPropertyData )
+ baseInferredData.getPropertyClass().getName() + ": " + "' belongs to an '@IdClass' but has no matching property in entity class '"
+ idClassPropertyData.getPropertyName() + baseInferredData.getPropertyClass().getName()
+ "' (every property of the '@IdClass' must have a corresponding persistent property in the '@Entity' class)"
); );
} }
final boolean hasXToOneAnnotation = hasToOneAnnotation( entityPropertyData.getProperty() ); final boolean hasXToOneAnnotation = hasToOneAnnotation( entityPropertyData.getProperty() );
@ -2439,8 +2450,8 @@ public final class AnnotationBinder {
toOne.setFetchMode( FetchMode.SELECT ); toOne.setFetchMode( FetchMode.SELECT );
} }
else if ( fetch.value() == org.hibernate.annotations.FetchMode.SUBSELECT ) { else if ( fetch.value() == org.hibernate.annotations.FetchMode.SUBSELECT ) {
throw new AnnotationException( "Use of FetchMode.SUBSELECT not allowed for to-one associations: " throw new AnnotationException( "Association '" + property.getName()
+ property.getName() ); + "' is annotated '@Fetch(SUBSELECT)' but is not many-valued");
} }
else { else {
throw new AssertionFailure( "Unknown FetchMode: " + fetch.value() ); throw new AssertionFailure( "Unknown FetchMode: " + fetch.value() );

View File

@ -76,13 +76,14 @@ import jakarta.persistence.TableGenerator;
import jakarta.persistence.UniqueConstraint; import jakarta.persistence.UniqueConstraint;
import static org.hibernate.cfg.AnnotatedColumn.buildColumnOrFormulaFromAnnotation; import static org.hibernate.cfg.AnnotatedColumn.buildColumnOrFormulaFromAnnotation;
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
import static org.hibernate.cfg.AnnotatedJoinColumn.NON_PK_REFERENCE; import static org.hibernate.cfg.AnnotatedJoinColumn.NON_PK_REFERENCE;
import static org.hibernate.cfg.AnnotatedJoinColumn.checkReferencedColumnsType; import static org.hibernate.cfg.AnnotatedJoinColumn.checkReferencedColumnsType;
import static org.hibernate.internal.util.StringHelper.isEmpty; import static org.hibernate.internal.util.StringHelper.isEmpty;
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.EMBEDDED; import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.EMBEDDED;
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.NOOP; import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.NOOP;
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.interpret; import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.interpret;
/** /**
* @author Emmanuel Bernard * @author Emmanuel Bernard
*/ */
@ -749,7 +750,9 @@ public class BinderHelper {
buildingContext buildingContext
); );
if ( gen == null ) { if ( gen == null ) {
throw new AnnotationException( "Unknown named generator (@GeneratedValue#generatorName): " + generatorName ); throw new AnnotationException( "No id generator was declared with the name '" + generatorName
+ "' specified by '@GeneratedValue'"
+ " (define a named generator using '@SequenceGenerator', '@TableGenerator', or '@GenericGenerator')" );
} }
//This is quite vague in the spec but a generator could override the generator choice //This is quite vague in the spec but a generator could override the generator choice
String identifierGeneratorStrategy = gen.getStrategy(); String identifierGeneratorStrategy = gen.getStrategy();
@ -1000,9 +1003,7 @@ public class BinderHelper {
} }
public static String getAnnotationValueStringOrNull(String value) { public static String getAnnotationValueStringOrNull(String value) {
return isEmptyOrNullAnnotationValue( value ) return isEmptyOrNullAnnotationValue( value ) ? null : value;
? null
: value;
} }
public static Any buildAnyValue( public static Any buildAnyValue(
@ -1159,7 +1160,7 @@ public class BinderHelper {
public static Map<String,String> toAliasTableMap(SqlFragmentAlias[] aliases){ public static Map<String,String> toAliasTableMap(SqlFragmentAlias[] aliases){
Map<String,String> ret = new HashMap<>(); Map<String,String> ret = new HashMap<>();
for ( SqlFragmentAlias aliase : aliases ) { for ( SqlFragmentAlias aliase : aliases ) {
if ( StringHelper.isNotEmpty( aliase.table() ) ) { if ( isNotEmpty( aliase.table() ) ) {
ret.put( aliase.alias(), aliase.table() ); ret.put( aliase.alias(), aliase.table() );
} }
} }

View File

@ -140,8 +140,8 @@ class ColumnsBuilder {
); );
} }
else if ( joinColumns == null && property.isAnnotationPresent( org.hibernate.annotations.Any.class ) ) { else if ( joinColumns == null && property.isAnnotationPresent( org.hibernate.annotations.Any.class ) ) {
throw new AnnotationException( "@Any requires an explicit @JoinColumn(s): " throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData )
+ getPath( propertyHolder, inferredData ) ); + "' is annotated '@Any' and must declare at least one '@JoinColumn'" );
} }
if ( columns == null && !property.isAnnotationPresent( ManyToMany.class ) ) { if ( columns == null && !property.isAnnotationPresent( ManyToMany.class ) ) {
//useful for collection of embedded elements //useful for collection of embedded elements
@ -179,21 +179,19 @@ class ColumnsBuilder {
buildingContext buildingContext
); );
if ( StringHelper.isEmpty( joinTableAnn.name() ) ) { if ( StringHelper.isEmpty( joinTableAnn.name() ) ) {
//TODO: I don't see why this restriction makes sense (use the same defaulting rule as for many-valued)
throw new AnnotationException( throw new AnnotationException(
"JoinTable.name() on a @ToOne association has to be explicit: " "Single-valued association " + getPath( propertyHolder, inferredData )
+ getPath( propertyHolder, inferredData ) + " has a '@JoinTable' annotation with no explicit 'name'"
); );
} }
} }
else { else {
OneToOne oneToOneAnn = property.getAnnotation( OneToOne.class ); OneToOne oneToOneAnn = property.getAnnotation( OneToOne.class );
String mappedBy = oneToOneAnn != null
? oneToOneAnn.mappedBy()
: null;
joinColumns = AnnotatedJoinColumn.buildJoinColumns( joinColumns = AnnotatedJoinColumn.buildJoinColumns(
null, null,
comment, comment,
mappedBy, oneToOneAnn != null ? oneToOneAnn.mappedBy() : null,
entityBinder.getSecondaryTables(), entityBinder.getSecondaryTables(),
propertyHolder, propertyHolder,
inferredData.getPropertyName(), inferredData.getPropertyName(),
@ -215,7 +213,8 @@ class ColumnsBuilder {
joinColumnAnnotations = joinColumnAnnotation.value(); joinColumnAnnotations = joinColumnAnnotation.value();
int length = joinColumnAnnotations.length; int length = joinColumnAnnotations.length;
if ( length == 0 ) { if ( length == 0 ) {
throw new AnnotationException( "Cannot bind an empty @JoinColumns" ); throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData )
+ "' has an empty '@JoinColumns' annotation" );
} }
} }
@ -243,7 +242,8 @@ class ColumnsBuilder {
joinColumnOrFormulaAnnotations = joinColumnsOrFormulasAnnotations.value(); joinColumnOrFormulaAnnotations = joinColumnsOrFormulasAnnotations.value();
int length = joinColumnOrFormulaAnnotations.length; int length = joinColumnOrFormulaAnnotations.length;
if ( length == 0 ) { if ( length == 0 ) {
throw new AnnotationException( "Cannot bind an empty @JoinColumnsOrFormulas" ); throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData )
+ "' has an empty '@JoinColumnsOrFormulas' annotation" );
} }
} }

View File

@ -270,11 +270,9 @@ public class ComponentPropertyHolder extends AbstractPropertyHolder {
public void addProperty(Property prop, AnnotatedColumn[] columns, XClass declaringClass) { public void addProperty(Property prop, AnnotatedColumn[] columns, XClass declaringClass) {
//Ejb3Column.checkPropertyConsistency( ); //already called earlier //Ejb3Column.checkPropertyConsistency( ); //already called earlier
/* // Check table matches between the component and the columns
* Check table matches between the component and the columns // if not, change the component table if no properties are set
* if not, change the component table if no properties are set // if a property is set already the core cannot support that
* if a property is set already the core cannot support that
*/
if (columns != null) { if (columns != null) {
Table table = columns[0].getTable(); Table table = columns[0].getTable();
if ( !table.equals( component.getTable() ) ) { if ( !table.equals( component.getTable() ) ) {
@ -283,8 +281,9 @@ public class ComponentPropertyHolder extends AbstractPropertyHolder {
} }
else { else {
throw new AnnotationException( throw new AnnotationException(
"A component cannot hold properties split into 2 different tables: " "Embeddable class '" + component.getComponentClassName()
+ this.getPath() + "' has properties mapped to two different tables"
+ " (all properties of the embeddable class must map to the same table)"
); );
} }
} }

View File

@ -10,17 +10,16 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import org.hibernate.AnnotationException; import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy; import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.boot.model.relational.Database; import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.internal.util.MutableInteger; import org.hibernate.internal.util.MutableInteger;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.BasicValue; import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Column; import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable; import org.hibernate.mapping.Selectable;
@ -38,18 +37,20 @@ public class CopyIdentifierComponentSecondPass extends FkSecondPass {
private static final Logger log = Logger.getLogger( CopyIdentifierComponentSecondPass.class ); private static final Logger log = Logger.getLogger( CopyIdentifierComponentSecondPass.class );
private final String referencedEntityName; private final String referencedEntityName;
private final String propertyName;
private final Component component; private final Component component;
private final MetadataBuildingContext buildingContext; private final MetadataBuildingContext buildingContext;
private final AnnotatedJoinColumn[] joinColumns; private final AnnotatedJoinColumn[] joinColumns;
public CopyIdentifierComponentSecondPass( public CopyIdentifierComponentSecondPass(
Component comp, Component comp,
String referencedEntityName, String referencedEntityName, String propertyName,
AnnotatedJoinColumn[] joinColumns, AnnotatedJoinColumn[] joinColumns,
MetadataBuildingContext buildingContext) { MetadataBuildingContext buildingContext) {
super( comp, joinColumns ); super( comp, joinColumns );
this.component = comp; this.component = comp;
this.referencedEntityName = referencedEntityName; this.referencedEntityName = referencedEntityName;
this.propertyName = propertyName;
this.buildingContext = buildingContext; this.buildingContext = buildingContext;
this.joinColumns = joinColumns; this.joinColumns = joinColumns;
} }
@ -68,17 +69,26 @@ public class CopyIdentifierComponentSecondPass extends FkSecondPass {
@Override @Override
public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException { public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
PersistentClass referencedPersistentClass = persistentClasses.get( referencedEntityName ); PersistentClass referencedPersistentClass = persistentClasses.get( referencedEntityName );
// TODO better error names
if ( referencedPersistentClass == null ) { if ( referencedPersistentClass == null ) {
throw new AnnotationException( "Unknown entity name: " + referencedEntityName ); // TODO: much better error message if this is something that can really happen!
throw new AnnotationException( "Unknown entity name '" + referencedEntityName + "'");
} }
if ( ! ( referencedPersistentClass.getIdentifier() instanceof Component ) ) { KeyValue identifier = referencedPersistentClass.getIdentifier();
throw new AssertionFailure( if ( !(identifier instanceof Component) ) {
"Unexpected identifier type on the referenced entity when mapping a @MapsId: " // The entity with the @MapsId annotation has a composite
+ referencedEntityName // id type, but the referenced entity has a basic-typed id.
// Therefore, the @MapsId annotation should have specified
// a property of the composite id that has the foreign key
throw new AnnotationException(
"Missing 'value' in '@MapsId' annotation of association '" + propertyName
+ "' of entity '" + component.getOwner().getEntityName()
+ "' with composite identifier type"
+ " ('@MapsId' must specify a property of the '@EmbeddedId' class which has the foreign key of '"
+ referencedEntityName + "')"
); );
} }
Component referencedComponent = (Component) referencedPersistentClass.getIdentifier();
Component referencedComponent = (Component) identifier;
//prepare column name structure //prepare column name structure
boolean isExplicitReference = true; boolean isExplicitReference = true;
@ -184,22 +194,24 @@ public class CopyIdentifierComponentSecondPass extends FkSecondPass {
String logicalColumnName = null; String logicalColumnName = null;
if ( isExplicitReference ) { if ( isExplicitReference ) {
logicalColumnName = column.getName(); logicalColumnName = column.getName();
//JPA 2 requires referencedColumnNames to be case insensitive //JPA 2 requires referencedColumnNames to be case-insensitive
joinColumn = columnByReferencedName.get( logicalColumnName.toLowerCase(Locale.ROOT ) ); joinColumn = columnByReferencedName.get( logicalColumnName.toLowerCase(Locale.ROOT ) );
} }
else { else {
joinColumn = columnByReferencedName.get( String.valueOf( index.get() ) ); joinColumn = columnByReferencedName.get( String.valueOf( index.get() ) );
index.getAndIncrement(); index.getAndIncrement();
} }
if ( joinColumn == null && ! joinColumns[0].isNameDeferred() ) { if ( joinColumn == null && !joinColumns[0].isNameDeferred() ) {
throw new AnnotationException( throw new AnnotationException(
isExplicitReference ? "Property '" + propertyName
"Unable to find column reference in the @MapsId mapping: " + logicalColumnName : + "' of entity '" + component.getOwner().getEntityName()
"Implicit column reference in the @MapsId mapping fails, try to use explicit referenceColumnNames: " + referencedEntityName + "' must have a '@JoinColumn' which references the foreign key column '"
+ logicalColumnName + "'"
); );
} }
final String columnName = joinColumn == null || joinColumn.isNameDeferred() ? "tata_" + column.getName() : joinColumn final String columnName = joinColumn == null || joinColumn.isNameDeferred()
.getName(); ? "tata_" + column.getName()
: joinColumn.getName();
final Database database = buildingContext.getMetadataCollector().getDatabase(); final Database database = buildingContext.getMetadataCollector().getDatabase();
final PhysicalNamingStrategy physicalNamingStrategy = buildingContext.getBuildingOptions().getPhysicalNamingStrategy(); final PhysicalNamingStrategy physicalNamingStrategy = buildingContext.getBuildingOptions().getPhysicalNamingStrategy();

View File

@ -103,7 +103,8 @@ public class IndexOrUniqueKeySecondPass implements SecondPass {
); );
if ( column == null ) { if ( column == null ) {
throw new AnnotationException( throw new AnnotationException(
"@Index references a unknown column: " + columnName "Table '" + table.getName() + "' has no column named '" + columnName
+ "' matching the column specified in '@Index'"
); );
} }
if ( unique ) { if ( unique ) {

View File

@ -227,7 +227,8 @@ public class InheritanceState {
} }
if ( idPropertyCount == 0 && !inheritanceState.hasParents() ) { if ( idPropertyCount == 0 && !inheritanceState.hasParents() ) {
throw new AnnotationException( "No identifier specified for entity: " + clazz.getName() ); throw new AnnotationException( "Entity '" + clazz.getName() + "' has no identifier"
+ " (every '@Entity' class must declare or inherit at least one '@Id' or '@EmbeddedId' property)" );
} }
elements.trimToSize(); elements.trimToSize();
elementsToProcess = new ElementsToProcess( elements, idPropertyCount ); elementsToProcess = new ElementsToProcess( elements, idPropertyCount );
@ -268,7 +269,8 @@ public class InheritanceState {
} }
} }
} }
throw new AnnotationException( "No identifier specified for entity: " + clazz ); throw new AnnotationException( "Entity '" + clazz.getName() + "' has no identifier"
+ " (every '@Entity' class must declare or inherit at least one '@Id' or '@EmbeddedId' property)" );
} }
private void getMappedSuperclassesTillNextEntityOrdered() { private void getMappedSuperclassesTillNextEntityOrdered() {

View File

@ -18,7 +18,6 @@ import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.common.reflection.XClass; import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.annotations.PropertyBinder; import org.hibernate.cfg.annotations.PropertyBinder;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Column; import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.DependantValue; import org.hibernate.mapping.DependantValue;
@ -31,6 +30,7 @@ import org.hibernate.mapping.SortableValue;
import org.hibernate.type.ForeignKeyDirection; import org.hibernate.type.ForeignKeyDirection;
import static org.hibernate.cfg.BinderHelper.findPropertyByName; import static org.hibernate.cfg.BinderHelper.findPropertyByName;
import static org.hibernate.cfg.BinderHelper.getPath;
import static org.hibernate.cfg.BinderHelper.isEmptyAnnotationValue; import static org.hibernate.cfg.BinderHelper.isEmptyAnnotationValue;
import static org.hibernate.internal.util.StringHelper.qualify; import static org.hibernate.internal.util.StringHelper.qualify;
@ -125,12 +125,10 @@ public class OneToOneSecondPass implements SecondPass {
Property prop = binder.makeProperty(); Property prop = binder.makeProperty();
prop.setOptional( optional ); prop.setOptional( optional );
if ( isEmptyAnnotationValue( mappedBy ) ) { if ( isEmptyAnnotationValue( mappedBy ) ) {
/* // we need to check if the columns are in the right order
* we need to check if the columns are in the right order // if not, then we need to create a many to one and formula
* if not, then we need to create a many to one and formula // but actually, since entities linked by a one to one need
* but actually, since entities linked by a one to one need // to share the same composite id class, this cannot happen in hibernate
* to share the same composite id class, this cannot happen in hibernate
*/
boolean rightOrder = true; boolean rightOrder = true;
if ( rightOrder ) { if ( rightOrder ) {
@ -154,26 +152,21 @@ public class OneToOneSecondPass implements SecondPass {
else { else {
value.setMappedByProperty( mappedBy ); value.setMappedByProperty( mappedBy );
PersistentClass otherSide = persistentClasses.get( value.getReferencedEntityName() ); PersistentClass otherSide = persistentClasses.get( value.getReferencedEntityName() );
if ( otherSide == null ) {
throw new MappingException( "Association '" + getPath( propertyHolder, inferredData )
+ "' targets unknown entity type '" + value.getReferencedEntityName() + "'" );
}
Property otherSideProperty; Property otherSideProperty;
try { try {
if ( otherSide == null ) {
throw new MappingException( "Unable to find entity: " + value.getReferencedEntityName() );
}
otherSideProperty = findPropertyByName( otherSide, mappedBy ); otherSideProperty = findPropertyByName( otherSide, mappedBy );
} }
catch (MappingException e) { catch (MappingException e) {
throw new AnnotationException( otherSideProperty = null;
"Unknown mappedBy in: " + qualify( ownerEntity, ownerProperty )
+ ", referenced property unknown: "
+ qualify( value.getReferencedEntityName(), mappedBy )
);
} }
if ( otherSideProperty == null ) { if ( otherSideProperty == null ) {
throw new AnnotationException( throw new AnnotationException( "Association '" + getPath( propertyHolder, inferredData )
"Unknown mappedBy in: " + qualify( ownerEntity, ownerProperty ) + "' is 'mappedBy' a property named '" + mappedBy
+ ", referenced property unknown: " + "' which does not exist in the target entity type '" + value.getReferencedEntityName() + "'" );
+ qualify( value.getReferencedEntityName(), mappedBy )
);
} }
if ( otherSideProperty.getValue() instanceof OneToOne ) { if ( otherSideProperty.getValue() instanceof OneToOne ) {
propertyHolder.addProperty( prop, inferredData.getDeclaringClass() ); propertyHolder.addProperty( prop, inferredData.getDeclaringClass() );
@ -228,7 +221,9 @@ public class OneToOneSecondPass implements SecondPass {
// HHH-6813 // HHH-6813
// Foo: @Id long id, @OneToOne(mappedBy="foo") Bar bar // Foo: @Id long id, @OneToOne(mappedBy="foo") Bar bar
// Bar: @Id @OneToOne Foo foo // Bar: @Id @OneToOne Foo foo
boolean referenceToPrimaryKey = ( mappedBy == null ) || otherSide.getIdentifier() instanceof Component && ! ( (Component) otherSide.getIdentifier() ).hasProperty( mappedBy ) ; boolean referenceToPrimaryKey = mappedBy == null
|| otherSide.getIdentifier() instanceof Component
&& ! ( (Component) otherSide.getIdentifier() ).hasProperty( mappedBy );
value.setReferenceToPrimaryKey( referenceToPrimaryKey ); value.setReferenceToPrimaryKey( referenceToPrimaryKey );
String propertyRef = value.getReferencedPropertyName(); String propertyRef = value.getReferencedPropertyName();
@ -240,14 +235,10 @@ public class OneToOneSecondPass implements SecondPass {
} }
} }
else { else {
throw new AnnotationException( throw new AnnotationException( "Association '" + getPath( propertyHolder, inferredData )
"Referenced property not a (One|Many)ToOne: " + "' is 'mappedBy' a property named '" + mappedBy
+ qualify( + "' of the target entity type '" + value.getReferencedEntityName()
otherSide.getEntityName(), mappedBy + "' which is not a '@OneToOne' or '@ManyToOne' association" );
)
+ " in mappedBy of "
+ qualify( ownerEntity, ownerProperty )
);
} }
} }
value.sortProperties(); value.sortProperties();

View File

@ -44,9 +44,8 @@ public class PkDrivenByDefaultMapsIdSecondPass extends FkSecondPass {
public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException { public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
PersistentClass referencedEntity = persistentClasses.get( referencedEntityName ); PersistentClass referencedEntity = persistentClasses.get( referencedEntityName );
if ( referencedEntity == null ) { if ( referencedEntity == null ) {
throw new AnnotationException( // TODO: much better error message if this is something that can really happen!
"Unknown entity name: " + referencedEntityName throw new AnnotationException( "Unknown entity name '" + referencedEntityName + "'" );
);
} }
TableBinder.linkJoinColumnWithValueOverridingNameIfImplicit( TableBinder.linkJoinColumnWithValueOverridingNameIfImplicit(
referencedEntity, referencedEntity,

View File

@ -100,6 +100,7 @@ class PropertyContainer {
if ( !recordComponents.isEmpty() && recordComponents.size() == fields.size() && getters.isEmpty() ) { if ( !recordComponents.isEmpty() && recordComponents.size() == fields.size() && getters.isEmpty() ) {
localAttributeMap = new LinkedHashMap<>(); localAttributeMap = new LinkedHashMap<>();
} }
//otherwise we sort them in alphabetical order, since this is at least deterministic
else { else {
localAttributeMap = new TreeMap<>(); localAttributeMap = new TreeMap<>();
} }
@ -296,12 +297,12 @@ class PropertyContainer {
} }
private static List<XProperty> verifyAndInitializePersistentAttributes(XClass xClass, Map<String, XProperty> localAttributeMap) { private static List<XProperty> verifyAndInitializePersistentAttributes(XClass xClass, Map<String, XProperty> localAttributeMap) {
ArrayList<XProperty> output = new ArrayList( localAttributeMap.size() ); ArrayList<XProperty> output = new ArrayList<>( localAttributeMap.size() );
for ( XProperty xProperty : localAttributeMap.values() ) { for ( XProperty xProperty : localAttributeMap.values() ) {
if ( !xProperty.isTypeResolved() && !discoverTypeWithoutReflection( xProperty ) ) { if ( !xProperty.isTypeResolved() && !discoverTypeWithoutReflection( xClass, xProperty ) ) {
String msg = "Property " + StringHelper.qualify( xClass.getName(), xProperty.getName() ) + String msg = "Property '" + StringHelper.qualify( xClass.getName(), xProperty.getName() ) +
" has an unbound type and no explicit target entity. Resolve this Generic usage issue" + "' has an unbound type and no explicit target entity (resolve this generics usage issue" +
" or set an explicit target attribute (eg @OneToMany(target=) or use an explicit @Type"; " or set an explicit target attribute with '@OneToMany(target=)' or use an explicit '@Type')";
throw new AnnotationException( msg ); throw new AnnotationException( msg );
} }
output.add( xProperty ); output.add( xProperty );
@ -395,46 +396,47 @@ class PropertyContainer {
return classDefinedAccessType; return classDefinedAccessType;
} }
private static boolean discoverTypeWithoutReflection(XProperty p) { private static boolean discoverTypeWithoutReflection(XClass clazz, XProperty property) {
if ( p.isAnnotationPresent( OneToOne.class ) && !p.getAnnotation( OneToOne.class ) if ( property.isAnnotationPresent( OneToOne.class ) && !property.getAnnotation( OneToOne.class )
.targetEntity() .targetEntity()
.equals( void.class ) ) { .equals( void.class ) ) {
return true; return true;
} }
else if ( p.isAnnotationPresent( OneToMany.class ) && !p.getAnnotation( OneToMany.class ) else if ( property.isAnnotationPresent( OneToMany.class ) && !property.getAnnotation( OneToMany.class )
.targetEntity() .targetEntity()
.equals( void.class ) ) { .equals( void.class ) ) {
return true; return true;
} }
else if ( p.isAnnotationPresent( ManyToOne.class ) && !p.getAnnotation( ManyToOne.class ) else if ( property.isAnnotationPresent( ManyToOne.class ) && !property.getAnnotation( ManyToOne.class )
.targetEntity() .targetEntity()
.equals( void.class ) ) { .equals( void.class ) ) {
return true; return true;
} }
else if ( p.isAnnotationPresent( ManyToMany.class ) && !p.getAnnotation( ManyToMany.class ) else if ( property.isAnnotationPresent( ManyToMany.class ) && !property.getAnnotation( ManyToMany.class )
.targetEntity() .targetEntity()
.equals( void.class ) ) { .equals( void.class ) ) {
return true; return true;
} }
else if ( p.isAnnotationPresent( org.hibernate.annotations.Any.class ) ) { else if ( property.isAnnotationPresent( org.hibernate.annotations.Any.class ) ) {
return true; return true;
} }
else if ( p.isAnnotationPresent( ManyToAny.class ) ) { else if ( property.isAnnotationPresent( ManyToAny.class ) ) {
if ( !p.isCollection() && !p.isArray() ) { if ( !property.isCollection() && !property.isArray() ) {
throw new AnnotationException( "@ManyToAny used on a non collection non array property: " + p.getName() ); throw new AnnotationException( "Property '" + StringHelper.qualify( clazz.getName(), property.getName() )
+ "' annotated '@ManyToAny' is neither a collection nor an array" );
} }
return true; return true;
} }
else if ( p.isAnnotationPresent( Basic.class ) ) { else if ( property.isAnnotationPresent( Basic.class ) ) {
return true; return true;
} }
else if ( p.isAnnotationPresent( Type.class ) ) { else if ( property.isAnnotationPresent( Type.class ) ) {
return true; return true;
} }
else if ( p.isAnnotationPresent( JavaType.class ) ) { else if ( property.isAnnotationPresent( JavaType.class ) ) {
return true; return true;
} }
else if ( p.isAnnotationPresent( Target.class ) ) { else if ( property.isAnnotationPresent( Target.class ) ) {
return true; return true;
} }
return false; return false;

View File

@ -93,12 +93,8 @@ public class ToOneFkSecondPass extends FkSecondPass {
ManyToOne manyToOne = (ManyToOne) value; ManyToOne manyToOne = (ManyToOne) value;
PersistentClass ref = persistentClasses.get( manyToOne.getReferencedEntityName() ); PersistentClass ref = persistentClasses.get( manyToOne.getReferencedEntityName() );
if ( ref == null ) { if ( ref == null ) {
throw new AnnotationException( throw new AnnotationException( "Association '" + StringHelper.qualify( entityClassName, path )
"@OneToOne or @ManyToOne on " + "' targets an unknown entity named '" + manyToOne.getReferencedEntityName() + "'" );
+ StringHelper.qualify( entityClassName, path )
+ " references an unknown entity: "
+ manyToOne.getReferencedEntityName()
);
} }
manyToOne.setPropertyName( path ); manyToOne.setPropertyName( path );
createSyntheticPropertyReference( columns, ref, null, manyToOne, false, buildingContext ); createSyntheticPropertyReference( columns, ref, null, manyToOne, false, buildingContext );

View File

@ -8,7 +8,6 @@ package org.hibernate.cfg.annotations;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -132,7 +131,7 @@ public class BasicValueBinder implements JdbcTypeIndicators {
private String explicitBasicTypeName; private String explicitBasicTypeName;
private Class<? extends UserType<?>> explicitCustomType; private Class<? extends UserType<?>> explicitCustomType;
private Map explicitLocalTypeParams; private Map<String,String> explicitLocalTypeParams;
private Function<TypeConfiguration, JdbcType> explicitJdbcTypeAccess; private Function<TypeConfiguration, JdbcType> explicitJdbcTypeAccess;
private Function<TypeConfiguration, BasicJavaType> explicitJavaTypeAccess; private Function<TypeConfiguration, BasicJavaType> explicitJavaTypeAccess;
@ -302,9 +301,9 @@ public class BasicValueBinder implements JdbcTypeIndicators {
throw new AssertionFailure( "`BasicValueBinder#setColumns` should be called before `BasicValueBinder#setType`" ); throw new AssertionFailure( "`BasicValueBinder#setColumns` should be called before `BasicValueBinder#setType`" );
} }
if ( columns.length != 1 ) { // if ( columns.length != 1 ) {
throw new AssertionFailure( "Expecting just one column, but found `" + Arrays.toString( columns ) + "`" ); // throw new AssertionFailure( "Expecting just one column, but found `" + Arrays.toString( columns ) + "`" );
} // }
final XClass modelTypeXClass = isArray final XClass modelTypeXClass = isArray
? modelXProperty.getElementClass() ? modelXProperty.getElementClass()
@ -377,8 +376,7 @@ public class BasicValueBinder implements JdbcTypeIndicators {
this.explicitLocalTypeParams = extractTypeParams( params ); this.explicitLocalTypeParams = extractTypeParams( params );
} }
@SuppressWarnings("unchecked") private Map<String,String> extractTypeParams(Parameter[] parameters) {
private Map extractTypeParams(Parameter[] parameters) {
if ( parameters == null || parameters.length == 0 ) { if ( parameters == null || parameters.length == 0 ) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
@ -387,7 +385,7 @@ public class BasicValueBinder implements JdbcTypeIndicators {
return Collections.singletonMap( parameters[0].name(), parameters[0].value() ); return Collections.singletonMap( parameters[0].name(), parameters[0].value() );
} }
final Map map = new HashMap(); final Map<String,String> map = new HashMap<>();
for ( Parameter parameter: parameters ) { for ( Parameter parameter: parameters ) {
map.put( parameter.name(), parameter.value() ); map.put( parameter.name(), parameter.value() );
} }
@ -740,7 +738,7 @@ public class BasicValueBinder implements JdbcTypeIndicators {
else { else {
throw new AnnotationException( throw new AnnotationException(
String.format( String.format(
"Attribute [%s.%s] was annotated as enumerated, but its java type is not an enum [%s]", "Property '%s.%s' is annotated '@Enumerated' but its type '%s' is not an enum",
declaringClassName, declaringClassName,
attributeDescriptor.getName(), attributeDescriptor.getName(),
attributeType.getName() attributeType.getName()

View File

@ -199,7 +199,7 @@ public abstract class CollectionBinder {
PropertyHolder propertyHolder; PropertyHolder propertyHolder;
private int batchSize; private int batchSize;
private String mappedBy; private String mappedBy;
private XClass collectionType; private XClass collectionElementType;
private XClass targetEntity; private XClass targetEntity;
private AnnotatedJoinColumn[] inverseJoinColumns; private AnnotatedJoinColumn[] inverseJoinColumns;
private String cascadeStrategy; private String cascadeStrategy;
@ -261,19 +261,16 @@ public abstract class CollectionBinder {
final ElementCollection elementCollectionAnn = property.getAnnotation( ElementCollection.class ); final ElementCollection elementCollectionAnn = property.getAnnotation( ElementCollection.class );
if ( ( oneToManyAnn != null || manyToManyAnn != null || elementCollectionAnn != null ) if ( ( oneToManyAnn != null || manyToManyAnn != null || elementCollectionAnn != null )
&& isToManyAssociationWithinEmbeddableCollection(propertyHolder) ) { && isToManyAssociationWithinEmbeddableCollection( propertyHolder ) ) {
throw new AnnotationException( String ann = oneToManyAnn!=null ? "'@OneToMany'" : manyToManyAnn!=null ? "'@ManyToMany'" : "'@ElementCollection'";
"@OneToMany, @ManyToMany or @ElementCollection cannot be used inside an @Embeddable that is also contained within an @ElementCollection: " throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData ) +
+ getPath(propertyHolder, inferredData) "' belongs to an '@Embeddable' class that is contained in an '@ElementCollection' and may not be a " + ann );
);
} }
if ( property.isAnnotationPresent( OrderColumn.class ) if ( property.isAnnotationPresent( OrderColumn.class )
&& manyToManyAnn != null && !manyToManyAnn.mappedBy().isEmpty() ) { && manyToManyAnn != null && !manyToManyAnn.mappedBy().isEmpty() ) {
throw new AnnotationException( throw new AnnotationException("Collection '" + getPath( propertyHolder, inferredData ) +
"Explicit @OrderColumn on inverse side of @ManyToMany is illegal: " "' is the unowned side of a bidirectional '@ManyToMany' and may not have an '@OrderColumn'");
+ getPath(propertyHolder, inferredData)
);
} }
final IndexColumn indexColumn = IndexColumn.fromAnnotations( final IndexColumn indexColumn = IndexColumn.fromAnnotations(
@ -305,12 +302,12 @@ public abstract class CollectionBinder {
NotFound notFound = property.getAnnotation( NotFound.class ); NotFound notFound = property.getAnnotation( NotFound.class );
if ( notFound != null ) { if ( notFound != null ) {
if ( manyToManyAnn == null ) { if ( manyToManyAnn == null ) {
throw new AnnotationException("collection annotated @NotFound is not a @ManyToMany association: " throw new AnnotationException( "Collection '" + getPath( propertyHolder, inferredData )
+ getPath(propertyHolder, inferredData) ); + "' annotated '@NotFound' is not a '@ManyToMany' association" );
} }
collectionBinder.setNotFoundAction( notFound.action() ); collectionBinder.setNotFoundAction( notFound.action() );
} }
collectionBinder.setCollectionType( inferredData.getProperty().getElementClass() ); collectionBinder.setElementType( inferredData.getProperty().getElementClass() );
collectionBinder.setAccessType( inferredData.getDefaultAccess() ); collectionBinder.setAccessType( inferredData.getDefaultAccess() );
//do not use "element" if you are a JPA 2 @ElementCollection, only for legacy Hibernate mappings //do not use "element" if you are a JPA 2 @ElementCollection, only for legacy Hibernate mappings
@ -408,10 +405,8 @@ public abstract class CollectionBinder {
//TODO enhance exception with @ManyToAny and @CollectionOfElements //TODO enhance exception with @ManyToAny and @CollectionOfElements
if ( oneToManyAnn != null && manyToManyAnn != null ) { if ( oneToManyAnn != null && manyToManyAnn != null ) {
throw new AnnotationException( throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData )
"@OneToMany and @ManyToMany on the same property is not allowed: " + "' is annotated both '@OneToMany' and '@ManyToMany'" );
+ propertyHolder.getEntityName() + "." + inferredData.getPropertyName()
);
} }
String mappedBy = null; String mappedBy = null;
ReflectionManager reflectionManager = context.getBootstrapContext().getReflectionManager(); ReflectionManager reflectionManager = context.getBootstrapContext().getReflectionManager();
@ -568,10 +563,8 @@ public abstract class CollectionBinder {
index++; index++;
} }
if ( property.isAnnotationPresent( MapKeyJoinColumn.class ) ) { if ( property.isAnnotationPresent( MapKeyJoinColumn.class ) ) {
throw new AnnotationException( throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData )
"@MapKeyJoinColumn and @MapKeyJoinColumns used on the same property: " + "' is annotated both '@MapKeyJoinColumn' and '@MapKeyJoinColumns'" );
+ getPath(propertyHolder, inferredData)
);
} }
return joinKeyColumns; return joinKeyColumns;
} }
@ -1054,7 +1047,7 @@ public abstract class CollectionBinder {
throw new AnnotationException( throw new AnnotationException(
String.format( String.format(
Locale.ROOT, Locale.ROOT,
"Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @CollectionOfElements: %s.%s", "Property '%s.%s' is not a collection and may not be a '@OneToMany', '@ManyToMany', or '@ElementCollection'",
property.getDeclaringClass().getName(), property.getDeclaringClass().getName(),
property.getName() property.getName()
) )
@ -1080,9 +1073,8 @@ public abstract class CollectionBinder {
this.tableBinder = tableBinder; this.tableBinder = tableBinder;
} }
public void setCollectionType(XClass collectionType) { public void setElementType(XClass collectionElementType) {
// NOTE: really really badly named. This is actually NOT the collection-type, but rather the collection-element-type! this.collectionElementType = collectionElementType;
this.collectionType = collectionType;
} }
public void setTargetEntity(XClass targetEntity) { public void setTargetEntity(XClass targetEntity) {
@ -1113,12 +1105,8 @@ public abstract class CollectionBinder {
if ( property.isAnnotationPresent( MapKeyColumn.class ) if ( property.isAnnotationPresent( MapKeyColumn.class )
&& mapKeyPropertyName != null ) { && mapKeyPropertyName != null ) {
throw new AnnotationException( throw new AnnotationException( "Collection '" + qualify( propertyHolder.getPath(), propertyName )
"Cannot mix @jakarta.persistence.MapKey and @MapKeyColumn or @org.hibernate.annotations.MapKey " + "' is annotated both '@MapKey' and '@MapKeyColumn'" );
+ "on the same collection: " + qualify(
propertyHolder.getPath(), propertyName
)
);
} }
bindExplicitTypes(); bindExplicitTypes();
@ -1159,7 +1147,7 @@ public abstract class CollectionBinder {
private void scheduleSecondPass(boolean isMappedBy, InFlightMetadataCollector metadataCollector) { private void scheduleSecondPass(boolean isMappedBy, InFlightMetadataCollector metadataCollector) {
//many to many may need some second pass information //many to many may need some second pass information
if ( !oneToMany && isMappedBy ) { if ( !oneToMany && isMappedBy ) {
metadataCollector.addMappedBy( getCollectionType().getName(), mappedBy, propertyName ); metadataCollector.addMappedBy( getElementType().getName(), mappedBy, propertyName );
} }
if ( inheritanceStatePerClass == null) { if ( inheritanceStatePerClass == null) {
@ -1175,7 +1163,7 @@ public abstract class CollectionBinder {
mapKeyManyToManyColumns, mapKeyManyToManyColumns,
isEmbedded, isEmbedded,
property, property,
getCollectionType(), getElementType(),
notFoundAction, notFoundAction,
oneToMany, oneToMany,
tableBinder, tableBinder,
@ -1190,8 +1178,9 @@ public abstract class CollectionBinder {
Persister persisterAnn = property.getAnnotation( Persister.class ); Persister persisterAnn = property.getAnnotation( Persister.class );
if ( persisterAnn != null ) { if ( persisterAnn != null ) {
Class clazz = persisterAnn.impl(); Class clazz = persisterAnn.impl();
if ( !CollectionPersister.class.isAssignableFrom(clazz) ) { if ( !CollectionPersister.class.isAssignableFrom( clazz ) ) {
throw new AnnotationException( "persister class does not implement CollectionPersister: " + clazz.getName() ); throw new AnnotationException( "Persister class '" + clazz.getName()
+ "' does not implement 'CollectionPersister'" );
} }
collection.setCollectionPersisterClass( clazz ); collection.setCollectionPersisterClass( clazz );
} }
@ -1229,12 +1218,18 @@ public abstract class CollectionBinder {
private void detectMappedByProblem(boolean isMappedBy) { private void detectMappedByProblem(boolean isMappedBy) {
if (isMappedBy if (isMappedBy
&& (property.isAnnotationPresent( JoinColumn.class ) && ( property.isAnnotationPresent( JoinColumn.class )
|| property.isAnnotationPresent( JoinColumns.class ) || property.isAnnotationPresent( JoinColumns.class ) ) ) {
|| propertyHolder.getJoinTable( property ) != null ) ) { throw new AnnotationException( "Association '"
String message = "Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn: "; + qualify( propertyHolder.getPath(), propertyName )
message += qualify( propertyHolder.getPath(), propertyName ); + "' is 'mappedBy' another entity and may not specify the '@JoinColumn'" );
throw new AnnotationException( message ); }
if (isMappedBy
&& propertyHolder.getJoinTable( property ) != null ) {
throw new AnnotationException( "Association '"
+ qualify( propertyHolder.getPath(), propertyName )
+ "' is 'mappedBy' another entity and may not specify the '@JoinTable'" );
} }
if (!isMappedBy if (!isMappedBy
@ -1242,9 +1237,9 @@ public abstract class CollectionBinder {
&& property.isAnnotationPresent( OnDelete.class ) && property.isAnnotationPresent( OnDelete.class )
&& !property.isAnnotationPresent( JoinColumn.class ) && !property.isAnnotationPresent( JoinColumn.class )
&& !property.isAnnotationPresent( JoinColumns.class )) { && !property.isAnnotationPresent( JoinColumns.class )) {
String message = "Unidirectional one-to-many associations annotated with @OnDelete must define @JoinColumn: "; throw new AnnotationException( "Unidirectional '@OneToMany' association '"
message += qualify( propertyHolder.getPath(), propertyName ); + qualify( propertyHolder.getPath(), propertyName )
throw new AnnotationException( message ); + "' is annotated '@OnDelete' and must explicitly specify a '@JoinColumn'" );
} }
} }
@ -1363,10 +1358,11 @@ public abstract class CollectionBinder {
catch (Exception e) { catch (Exception e) {
throw new AnnotationException( throw new AnnotationException(
String.format( String.format(
"Could not instantiate comparator class [%s] for %s", "Could not instantiate comparator class '%s' for collection '%s'",
comparatorClass.getName(), comparatorClass.getName(),
safeCollectionRole() safeCollectionRole()
) ),
e
); );
} }
} }
@ -1376,10 +1372,10 @@ public abstract class CollectionBinder {
return new AnnotationException( return new AnnotationException(
String.format( String.format(
Locale.ROOT, Locale.ROOT,
"Illegal combination of ordering and sorting annotations (`%s`) - only one of `@%s` and `@%s` may be used", "Collection '%s' is annotated both '@%s' and '@%s'",
safeCollectionRole(),
jakarta.persistence.OrderBy.class.getName(), jakarta.persistence.OrderBy.class.getName(),
OrderBy.class.getName(), OrderBy.class.getName()
safeCollectionRole()
) )
); );
} }
@ -1388,7 +1384,7 @@ public abstract class CollectionBinder {
throw new AnnotationException( throw new AnnotationException(
String.format( String.format(
Locale.ROOT, Locale.ROOT,
"Illegal combination of ordering and sorting annotations (`%s`) - only one of `@%s`, `@%s`, `@%s` and `@%s` can be used", "Collection '%s' is both sorted and ordered (only one of '@%s', '@%s', '@%s', and '@%s' may be used)",
safeCollectionRole(), safeCollectionRole(),
jakarta.persistence.OrderBy.class.getName(), jakarta.persistence.OrderBy.class.getName(),
OrderBy.class.getName(), OrderBy.class.getName(),
@ -1401,7 +1397,7 @@ public abstract class CollectionBinder {
private AnnotationException buildIllegalSortCombination() { private AnnotationException buildIllegalSortCombination() {
return new AnnotationException( return new AnnotationException(
String.format( String.format(
"Illegal combination of sorting annotations (`%s`) - only one of `@%s` and `@%s` can be used", "Collection '%s' is annotated both '@%s' and '@%s'",
safeCollectionRole(), safeCollectionRole(),
SortNatural.class.getName(), SortNatural.class.getName(),
SortComparator.class.getName() SortComparator.class.getName()
@ -1470,15 +1466,14 @@ public abstract class CollectionBinder {
} }
} }
private XClass getCollectionType() { private XClass getElementType() {
if ( AnnotationBinder.isDefault( targetEntity, buildingContext ) ) { if ( AnnotationBinder.isDefault( targetEntity, buildingContext ) ) {
if ( collectionType != null ) { if ( collectionElementType != null ) {
return collectionType; return collectionElementType;
} }
else { else {
String errorMsg = "Collection has neither generic type or OneToMany.targetEntity() defined: " throw new AnnotationException( "Collection '" + safeCollectionRole()
+ safeCollectionRole(); + "' is declared with a raw type and has an explicit 'targetEntity'" );
throw new AnnotationException( errorMsg );
} }
} }
else { else {
@ -1495,7 +1490,7 @@ public abstract class CollectionBinder {
final AnnotatedJoinColumn[] mapKeyManyToManyColumns, final AnnotatedJoinColumn[] mapKeyManyToManyColumns,
final boolean isEmbedded, final boolean isEmbedded,
final XProperty property, final XProperty property,
final XClass collType, final XClass elementType,
final NotFoundAction notFoundAction, final NotFoundAction notFoundAction,
final boolean unique, final boolean unique,
final TableBinder assocTableBinder, final TableBinder assocTableBinder,
@ -1505,7 +1500,7 @@ public abstract class CollectionBinder {
public void secondPass(Map<String, PersistentClass> persistentClasses) throws MappingException { public void secondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
bindStarToManySecondPass( bindStarToManySecondPass(
persistentClasses, persistentClasses,
collType, elementType,
fkJoinColumns, fkJoinColumns,
keyColumns, keyColumns,
inverseColumns, inverseColumns,
@ -1526,7 +1521,7 @@ public abstract class CollectionBinder {
*/ */
protected boolean bindStarToManySecondPass( protected boolean bindStarToManySecondPass(
Map<String, PersistentClass> persistentClasses, Map<String, PersistentClass> persistentClasses,
XClass collType, XClass elementType,
AnnotatedJoinColumn[] fkJoinColumns, AnnotatedJoinColumn[] fkJoinColumns,
AnnotatedJoinColumn[] keyColumns, AnnotatedJoinColumn[] keyColumns,
AnnotatedJoinColumn[] inverseColumns, AnnotatedJoinColumn[] inverseColumns,
@ -1537,23 +1532,17 @@ public abstract class CollectionBinder {
TableBinder associationTableBinder, TableBinder associationTableBinder,
NotFoundAction notFoundAction, NotFoundAction notFoundAction,
MetadataBuildingContext buildingContext) { MetadataBuildingContext buildingContext) {
PersistentClass persistentClass = persistentClasses.get( collType.getName() ); PersistentClass persistentClass = persistentClasses.get( elementType.getName() );
boolean reversePropertyInJoin = false; boolean reversePropertyInJoin = false;
if ( persistentClass != null && isNotEmpty( mappedBy ) ) { if ( persistentClass != null && isNotEmpty( mappedBy ) ) {
try { try {
reversePropertyInJoin = 0 != persistentClass.getJoinNumber( reversePropertyInJoin =
persistentClass.getRecursiveProperty( mappedBy ) 0 != persistentClass.getJoinNumber( persistentClass.getRecursiveProperty( mappedBy ) );
);
} }
catch (MappingException e) { catch (MappingException e) {
throw new AnnotationException( throw new AnnotationException( "Collection '" + safeCollectionRole()
"mappedBy references an unknown target entity property: " + + "' is 'mappedBy' a property named '" + mappedBy
collType + "." + this.mappedBy + + "' which does not exist in the target entity '" + elementType.getName() + "'" );
" in " +
collection.getOwnerEntityName() +
"." +
property.getName()
);
} }
} }
if ( persistentClass != null if ( persistentClass != null
@ -1568,7 +1557,7 @@ public abstract class CollectionBinder {
getCollection(), getCollection(),
persistentClasses, persistentClasses,
fkJoinColumns, fkJoinColumns,
collType, elementType,
cascadeDeleteEnabled, cascadeDeleteEnabled,
notFoundAction, notFoundAction,
buildingContext, buildingContext,
@ -1585,7 +1574,7 @@ public abstract class CollectionBinder {
inverseColumns, inverseColumns,
elementColumns, elementColumns,
isEmbedded, isEmbedded,
collType, elementType,
notFoundAction, notFoundAction,
unique, unique,
cascadeDeleteEnabled, cascadeDeleteEnabled,
@ -1784,8 +1773,8 @@ public abstract class CollectionBinder {
} }
else { else {
throw new AnnotationException( throw new AnnotationException(
"Illegal use of @WhereJoinTable on an association without join table: " "Collection '" + qualify( propertyHolder.getPath(), propertyName )
+ qualify( propertyHolder.getPath(), propertyName ) + "' is an association with no join table and may not have a 'WhereJoinTable'"
); );
} }
} }
@ -1794,22 +1783,25 @@ public abstract class CollectionBinder {
private void addFilter(boolean hasAssociationTable, FilterJoinTable filter) { private void addFilter(boolean hasAssociationTable, FilterJoinTable filter) {
if ( hasAssociationTable ) { if ( hasAssociationTable ) {
final String condition; final String condition;
final String name = filter.name();
if ( isEmpty( filter.condition() ) ) { if ( isEmpty( filter.condition() ) ) {
final FilterDefinition filterDefinition = buildingContext.getMetadataCollector() final FilterDefinition definition = buildingContext.getMetadataCollector().getFilterDefinition( name );
.getFilterDefinition( filter.name() ); if ( definition == null ) {
if ( filterDefinition == null ) { throw new AnnotationException( "Collection '" + qualify( propertyHolder.getPath(), propertyName )
throw new AnnotationException( + "' has a '@FilterJoinTable' for an undefined filter named '" + name + "'" );
"@FilterJoinTable on an association without condition attribute and without an any @FilterDef with a default condition" }
+ qualify( propertyHolder.getPath(), propertyName ) condition = definition.getDefaultFilterCondition();
); if ( isEmpty( condition ) ) {
throw new AnnotationException( "Collection '" + qualify( propertyHolder.getPath(), propertyName )
+ "' has a '@FilterJoinTable' with no 'condition' and no default condition was given by the '@FilterDef' named '"
+ name + "'");
} }
condition = filterDefinition.getDefaultFilterCondition();
} }
else { else {
condition = filter.condition(); condition = filter.condition();
} }
collection.addFilter( collection.addFilter(
filter.name(), name,
condition, condition,
filter.deduceAliasInjectionPoints(), filter.deduceAliasInjectionPoints(),
toAliasTableMap( filter.aliases() ), toAliasTableMap( filter.aliases() ),
@ -1817,31 +1809,32 @@ public abstract class CollectionBinder {
); );
} }
else { else {
throw new AnnotationException( throw new AnnotationException( "Collection '" + qualify( propertyHolder.getPath(), propertyName )
"Illegal use of @FilterJoinTable on an association without join table: " + "' is an association with no join table and may not have a '@FilterJoinTable'" );
+ qualify( propertyHolder.getPath(), propertyName )
);
} }
} }
private String getCondition(Filter filter) { private String getCondition(Filter filter) {
//set filtering //set filtering
String name = filter.name(); final String condition = filter.condition();
String cond = filter.condition(); if ( isEmptyAnnotationValue( condition ) ) {
return getCondition( cond, name ); final String name = filter.name();
} final FilterDefinition definition = buildingContext.getMetadataCollector().getFilterDefinition( name );
if ( definition == null ) {
private String getCondition(String cond, String name) { throw new AnnotationException( "Collection '" + qualify( propertyHolder.getPath(), propertyName )
if ( isEmptyAnnotationValue( cond ) ) { + "' has a '@Filter' for an undefined filter named '" + name + "'" );
cond = buildingContext.getMetadataCollector().getFilterDefinition( name ).getDefaultFilterCondition();
if ( isEmpty( cond ) ) {
throw new AnnotationException(
"no filter condition found for filter " + name + " in "
+ qualify( propertyHolder.getPath(), propertyName )
);
} }
final String defaultCondition = definition.getDefaultFilterCondition();
if ( isEmpty( defaultCondition ) ) {
throw new AnnotationException( "Collection '" + qualify( propertyHolder.getPath(), propertyName ) +
"' has a '@Filter' with no 'condition' and no default condition was given by the '@FilterDef' named '"
+ name + "'" );
}
return defaultCondition;
}
else {
return condition;
} }
return cond;
} }
public void setCache(Cache cacheAnn) { public void setCache(Cache cacheAnn) {
@ -2057,7 +2050,7 @@ public abstract class CollectionBinder {
AnnotatedJoinColumn[] inverseJoinColumns, AnnotatedJoinColumn[] inverseJoinColumns,
AnnotatedColumn[] elementColumns, AnnotatedColumn[] elementColumns,
boolean isEmbedded, boolean isEmbedded,
XClass collType, XClass elementType,
NotFoundAction notFoundAction, NotFoundAction notFoundAction,
boolean unique, boolean unique,
boolean cascadeDeleteEnabled, boolean cascadeDeleteEnabled,
@ -2070,7 +2063,7 @@ public abstract class CollectionBinder {
throw new IllegalArgumentException( "null was passed for argument property" ); throw new IllegalArgumentException( "null was passed for argument property" );
} }
final PersistentClass collectionEntity = persistentClasses.get( collType.getName() ); final PersistentClass collectionEntity = persistentClasses.get( elementType.getName() );
final String hqlOrderBy = extractHqlOrderBy( jpaOrderBy ); final String hqlOrderBy = extractHqlOrderBy( jpaOrderBy );
boolean isCollectionOfEntities = collectionEntity != null; boolean isCollectionOfEntities = collectionEntity != null;
@ -2081,7 +2074,7 @@ public abstract class CollectionBinder {
detectManyToManyProblems( detectManyToManyProblems(
collValue, collValue,
joinColumns, joinColumns,
collType, elementType,
property, property,
parentPropertyHolder, parentPropertyHolder,
isCollectionOfEntities, isCollectionOfEntities,
@ -2092,7 +2085,7 @@ public abstract class CollectionBinder {
handleUnownedManyToMany( handleUnownedManyToMany(
collValue, collValue,
joinColumns, joinColumns,
collType, elementType,
collectionEntity, collectionEntity,
isCollectionOfEntities isCollectionOfEntities
); );
@ -2125,7 +2118,7 @@ public abstract class CollectionBinder {
if ( isCollectionOfEntities ) { if ( isCollectionOfEntities ) {
element = handleCollectionOfEntities( element = handleCollectionOfEntities(
collValue, collValue,
collType, elementType,
notFoundAction, notFoundAction,
property, property,
buildingContext, buildingContext,
@ -2147,7 +2140,7 @@ public abstract class CollectionBinder {
collValue, collValue,
elementColumns, elementColumns,
isEmbedded, isEmbedded,
collType, elementType,
property, property,
parentPropertyHolder, parentPropertyHolder,
buildingContext, buildingContext,
@ -2164,12 +2157,19 @@ public abstract class CollectionBinder {
} }
private void handleElementCollection(Collection collValue, AnnotatedColumn[] elementColumns, boolean isEmbedded, XClass collType, XProperty property, PropertyHolder parentPropertyHolder, MetadataBuildingContext buildingContext, String hqlOrderBy) { private void handleElementCollection(
Collection collValue,
AnnotatedColumn[] elementColumns,
boolean isEmbedded,
XClass elementType,
XProperty property,
PropertyHolder parentPropertyHolder,
MetadataBuildingContext buildingContext,
String hqlOrderBy) {
XClass elementClass; XClass elementClass;
AnnotatedClassType classType; AnnotatedClassType classType;
CollectionPropertyHolder holder; CollectionPropertyHolder holder;
if ( PRIMITIVE_NAMES.contains( collType.getName() ) ) { if ( PRIMITIVE_NAMES.contains( elementType.getName() ) ) {
classType = AnnotatedClassType.NONE; classType = AnnotatedClassType.NONE;
elementClass = null; elementClass = null;
@ -2183,7 +2183,7 @@ public abstract class CollectionBinder {
); );
} }
else { else {
elementClass = collType; elementClass = elementType;
classType = buildingContext.getMetadataCollector().getClassType( elementClass ); classType = buildingContext.getMetadataCollector().getClassType( elementClass );
holder = PropertyHolderBuilder.buildPropertyHolder( holder = PropertyHolderBuilder.buildPropertyHolder(
@ -2276,7 +2276,7 @@ public abstract class CollectionBinder {
final BasicValueBinder elementBinder = final BasicValueBinder elementBinder =
new BasicValueBinder( BasicValueBinder.Kind.COLLECTION_ELEMENT, buildingContext); new BasicValueBinder( BasicValueBinder.Kind.COLLECTION_ELEMENT, buildingContext);
elementBinder.setReturnedClassName( collType.getName() ); elementBinder.setReturnedClassName( elementType.getName() );
if ( elementColumns == null || elementColumns.length == 0 ) { if ( elementColumns == null || elementColumns.length == 0 ) {
elementColumns = new AnnotatedColumn[1]; elementColumns = new AnnotatedColumn[1];
AnnotatedColumn column = new AnnotatedColumn(); AnnotatedColumn column = new AnnotatedColumn();
@ -2313,16 +2313,15 @@ public abstract class CollectionBinder {
private ManyToOne handleCollectionOfEntities( private ManyToOne handleCollectionOfEntities(
Collection collValue, Collection collValue,
XClass collType, XClass elementType,
NotFoundAction notFoundAction, NotFoundAction notFoundAction,
XProperty property, XProperty property,
MetadataBuildingContext buildingContext, MetadataBuildingContext buildingContext,
PersistentClass collectionEntity, PersistentClass collectionEntity,
String hqlOrderBy) { String hqlOrderBy) {
ManyToOne element; ManyToOne element = new ManyToOne( buildingContext, collValue.getCollectionTable() );
element = new ManyToOne(buildingContext, collValue.getCollectionTable() );
collValue.setElement( element ); collValue.setElement( element );
element.setReferencedEntityName( collType.getName() ); element.setReferencedEntityName( elementType.getName() );
//element.setFetchMode( fetchMode ); //element.setFetchMode( fetchMode );
//element.setLazy( fetchMode != FetchMode.JOIN ); //element.setLazy( fetchMode != FetchMode.JOIN );
//make the second join non lazy //make the second join non lazy
@ -2331,9 +2330,7 @@ public abstract class CollectionBinder {
element.setNotFoundAction( notFoundAction ); element.setNotFoundAction( notFoundAction );
// as per 11.1.38 of JPA 2.0 spec, default to primary key if no column is specified by @OrderBy. // as per 11.1.38 of JPA 2.0 spec, default to primary key if no column is specified by @OrderBy.
if ( hqlOrderBy != null ) { if ( hqlOrderBy != null ) {
collValue.setManyToManyOrdering( collValue.setManyToManyOrdering( buildOrderByClauseFromHql( hqlOrderBy, collectionEntity ) );
buildOrderByClauseFromHql(hqlOrderBy, collectionEntity)
);
} }
final ForeignKey fk = property.getAnnotation( ForeignKey.class ); final ForeignKey fk = property.getAnnotation( ForeignKey.class );
@ -2366,7 +2363,12 @@ public abstract class CollectionBinder {
return element; return element;
} }
private void handleManyToAny(Collection collValue, AnnotatedJoinColumn[] inverseJoinColumns, boolean cascadeDeleteEnabled, XProperty property, MetadataBuildingContext buildingContext) { private void handleManyToAny(
Collection collValue,
AnnotatedJoinColumn[] inverseJoinColumns,
boolean cascadeDeleteEnabled,
XProperty property,
MetadataBuildingContext buildingContext) {
//@ManyToAny //@ManyToAny
//Make sure that collTyp is never used during the @ManyToAny branch: it will be set to void.class //Make sure that collTyp is never used during the @ManyToAny branch: it will be set to void.class
final PropertyData inferredData = new PropertyInferredData( final PropertyData inferredData = new PropertyInferredData(
@ -2473,17 +2475,13 @@ public abstract class CollectionBinder {
private void handleUnownedManyToMany( private void handleUnownedManyToMany(
Collection collValue, Collection collValue,
AnnotatedJoinColumn[] joinColumns, AnnotatedJoinColumn[] joinColumns,
XClass collType, XClass elementType,
PersistentClass collectionEntity, PersistentClass collectionEntity,
boolean isCollectionOfEntities) { boolean isCollectionOfEntities) {
if ( !isCollectionOfEntities) { if ( !isCollectionOfEntities) {
throw new AnnotationException( throw new AnnotationException( "Association '" + safeCollectionRole()
"Collection of elements must not have mappedBy or association reference an unmapped entity: " + + "' targets the type '" + elementType.getName() + "' which is not an '@Entity' type" );
collValue.getOwnerEntityName() +
"." +
joinColumns[0].getPropertyName()
);
} }
Property otherSideProperty; Property otherSideProperty;
@ -2491,14 +2489,12 @@ public abstract class CollectionBinder {
otherSideProperty = collectionEntity.getRecursiveProperty( joinColumns[0].getMappedBy() ); otherSideProperty = collectionEntity.getRecursiveProperty( joinColumns[0].getMappedBy() );
} }
catch (MappingException e) { catch (MappingException e) {
throw new AnnotationException( throw new AnnotationException( "Association '" + safeCollectionRole() +
"mappedBy references an unknown target entity property: " "is 'mappedBy' a property named '" + mappedBy
+ collType + "." + joinColumns[0].getMappedBy() + " in " + "' which does not exist in the target entity '" + elementType.getName() + "'" );
+ collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName()
);
} }
Table table = otherSideProperty.getValue() instanceof Collection Table table = otherSideProperty.getValue() instanceof Collection
? ((Collection) otherSideProperty.getValue()).getCollectionTable() ? ( (Collection) otherSideProperty.getValue() ).getCollectionTable()
: otherSideProperty.getValue().getTable(); : otherSideProperty.getValue().getTable();
//this is a collection on the other side //this is a collection on the other side
//This is a ToOne with a @JoinTable or a regular property //This is a ToOne with a @JoinTable or a regular property
@ -2513,7 +2509,7 @@ public abstract class CollectionBinder {
private void detectManyToManyProblems( private void detectManyToManyProblems(
Collection collValue, Collection collValue,
AnnotatedJoinColumn[] joinColumns, AnnotatedJoinColumn[] joinColumns,
XClass collType, XClass elementType,
XProperty property, XProperty property,
PropertyHolder parentPropertyHolder, PropertyHolder parentPropertyHolder,
boolean isCollectionOfEntities, boolean isCollectionOfEntities,
@ -2521,26 +2517,21 @@ public abstract class CollectionBinder {
if ( !isCollectionOfEntities) { if ( !isCollectionOfEntities) {
if ( property.isAnnotationPresent( ManyToMany.class ) || property.isAnnotationPresent( OneToMany.class ) ) { if ( property.isAnnotationPresent( ManyToMany.class ) || property.isAnnotationPresent( OneToMany.class ) ) {
String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName(); throw new AnnotationException( "Association '" + safeCollectionRole()
throw new AnnotationException( + "' targets the type '" + elementType.getName() + "' which is not an '@Entity' type" );
"Use of @OneToMany or @ManyToMany targeting an unmapped class: " + path + "[" + collType + "]"
);
} }
else if (isManyToAny) { else if (isManyToAny) {
if ( parentPropertyHolder.getJoinTable(property) == null ) { if ( parentPropertyHolder.getJoinTable( property ) == null ) {
String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName(); throw new AnnotationException( "Association '" + safeCollectionRole()
throw new AnnotationException( + "' is a '@ManyToAny' and must specify a '@JoinTable'" );
"@JoinTable is mandatory when @ManyToAny is used: " + path
);
} }
} }
else { else {
JoinTable joinTableAnn = parentPropertyHolder.getJoinTable(property); JoinTable joinTableAnn = parentPropertyHolder.getJoinTable( property );
if ( joinTableAnn != null && joinTableAnn.inverseJoinColumns().length > 0 ) { if ( joinTableAnn != null && joinTableAnn.inverseJoinColumns().length > 0 ) {
String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName(); throw new AnnotationException( "Association '" + safeCollectionRole()
throw new AnnotationException( + " has a '@JoinTable' with 'inverseJoinColumns' and targets the type '"
"Use of @JoinTable.inverseJoinColumns targeting an unmapped class: " + path + "[" + collType + "]" + elementType.getName() + "' which is not an '@Entity' type" );
);
} }
} }
} }
@ -2654,7 +2645,8 @@ public abstract class CollectionBinder {
); );
} }
catch (AnnotationException ex) { catch (AnnotationException ex) {
throw new AnnotationException( "Unable to map collection " + collValue.getOwner().getClassName() + "." + property.getName(), ex ); throw new AnnotationException( "Unable to map collection "
+ collValue.getOwner().getClassName() + "." + property.getName(), ex );
} }
DependantValue key = buildCollectionKey( collValue, joinColumns, cascadeDeleteEnabled, DependantValue key = buildCollectionKey( collValue, joinColumns, cascadeDeleteEnabled,
buildingContext.getBuildingOptions().isNoConstraintByDefault(), property, propertyHolder, buildingContext ); buildingContext.getBuildingOptions().isNoConstraintByDefault(), property, propertyHolder, buildingContext );
@ -2669,7 +2661,7 @@ public abstract class CollectionBinder {
this.cascadeDeleteEnabled = onDeleteCascade; this.cascadeDeleteEnabled = onDeleteCascade;
} }
private String safeCollectionRole() { String safeCollectionRole() {
return propertyHolder != null ? propertyHolder.getEntityName() + "." + propertyName : ""; return propertyHolder != null ? propertyHolder.getEntityName() + "." + propertyName : "";
} }

View File

@ -261,7 +261,7 @@ public class EntityBinder {
final InheritanceState.ElementsToProcess elementsToProcess = inheritanceState.getElementsToProcess(); final InheritanceState.ElementsToProcess elementsToProcess = inheritanceState.getElementsToProcess();
inheritanceState.postProcess( persistentClass, entityBinder ); inheritanceState.postProcess( persistentClass, entityBinder );
Set<String> idPropertiesIfIdClass = handleIdClass( final Set<String> idPropertiesIfIdClass = handleIdClass(
persistentClass, persistentClass,
inheritanceState, inheritanceState,
context, context,
@ -361,7 +361,7 @@ public class EntityBinder {
final XClass classWithIdClass = inheritanceState.getClassWithIdClass( false ); final XClass classWithIdClass = inheritanceState.getClassWithIdClass( false );
if ( classWithIdClass != null ) { if ( classWithIdClass != null ) {
final IdClass idClass = classWithIdClass.getAnnotation( IdClass.class ); final IdClass idClass = classWithIdClass.getAnnotation( IdClass.class );
//noinspection unchecked @SuppressWarnings("unchecked")
final XClass compositeClass = context.getBootstrapContext().getReflectionManager().toXClass( idClass.value() ); final XClass compositeClass = context.getBootstrapContext().getReflectionManager().toXClass( idClass.value() );
final PropertyData inferredData = new PropertyPreloadedData( final PropertyData inferredData = new PropertyPreloadedData(
entityBinder.getPropertyAccessType(), "id", compositeClass entityBinder.getPropertyAccessType(), "id", compositeClass
@ -502,18 +502,14 @@ public class EntityBinder {
MetadataBuildingContext buildingContext, MetadataBuildingContext buildingContext,
Map<XClass, InheritanceState> inheritanceStatePerClass) { Map<XClass, InheritanceState> inheritanceStatePerClass) {
/* // Fill simple value and property since and Id is a property
* Fill simple value and property since and Id is a property final PersistentClass persistentClass = propertyHolder.getPersistentClass();
*/
PersistentClass persistentClass = propertyHolder.getPersistentClass();
if ( !(persistentClass instanceof RootClass) ) { if ( !(persistentClass instanceof RootClass) ) {
throw new AnnotationException( throw new AnnotationException( "Entity '" + persistentClass.getEntityName()
"Unable to define/override @Id(s) on a subclass: " + "' is a subclass in an entity inheritance hierarchy and may not redefine the identifier of the root entity" );
+ propertyHolder.getEntityName()
);
} }
RootClass rootClass = (RootClass) persistentClass; final RootClass rootClass = (RootClass) persistentClass;
Component id = AnnotationBinder.fillComponent( final Component id = AnnotationBinder.fillComponent(
propertyHolder, propertyHolder,
inferredData, inferredData,
baseInferredData, baseInferredData,
@ -530,10 +526,13 @@ public class EntityBinder {
); );
id.setKey( true ); id.setKey( true );
if ( rootClass.getIdentifier() != null ) { if ( rootClass.getIdentifier() != null ) {
throw new AnnotationException( id.getComponentClassName() + " must not have @Id properties when used as an @EmbeddedId" ); throw new AssertionFailure( "Entity '" + persistentClass.getEntityName()
+ "' has an '@IdClass' and may not have an identifier property" );
} }
if ( id.getPropertySpan() == 0 ) { if ( id.getPropertySpan() == 0 ) {
throw new AnnotationException( id.getComponentClassName() + " has no persistent id property" ); throw new AnnotationException( "Class '" + id.getComponentClassName()
+ " is the '@IdClass' for the entity '" + persistentClass.getEntityName()
+ "' but has no persistent properties" );
} }
rootClass.setIdentifier( id ); rootClass.setIdentifier( id );
@ -927,19 +926,18 @@ public class EntityBinder {
InheritanceState.ElementsToProcess elementsToProcess, InheritanceState.ElementsToProcess elementsToProcess,
Map<XClass, InheritanceState> inheritanceStatePerClass) { Map<XClass, InheritanceState> inheritanceStatePerClass) {
Set<String> missingIdProperties = new HashSet<>( idPropertiesIfIdClass ); final Set<String> missingIdProperties = new HashSet<>( idPropertiesIfIdClass );
for ( PropertyData propertyAnnotatedElement : elementsToProcess.getElements() ) { for ( PropertyData propertyAnnotatedElement : elementsToProcess.getElements() ) {
String propertyName = propertyAnnotatedElement.getPropertyName(); String propertyName = propertyAnnotatedElement.getPropertyName();
if ( !idPropertiesIfIdClass.contains( propertyName ) ) { if ( !idPropertiesIfIdClass.contains( propertyName ) ) {
boolean subclassAndSingleTableStrategy = boolean subclassAndSingleTableStrategy =
inheritanceState.getType() == InheritanceType.SINGLE_TABLE inheritanceState.getType() == InheritanceType.SINGLE_TABLE
&& inheritanceState.hasParents(); && inheritanceState.hasParents();
Nullability nullability = subclassAndSingleTableStrategy
? Nullability.FORCED_NULL
: Nullability.NO_CONSTRAINT;
AnnotationBinder.processElementAnnotations( AnnotationBinder.processElementAnnotations(
propertyHolder, propertyHolder,
nullability, subclassAndSingleTableStrategy
? Nullability.FORCED_NULL
: Nullability.NO_CONSTRAINT,
propertyAnnotatedElement, propertyAnnotatedElement,
classGenerators, classGenerators,
entityBinder, entityBinder,
@ -956,15 +954,16 @@ public class EntityBinder {
} }
if ( missingIdProperties.size() != 0 ) { if ( missingIdProperties.size() != 0 ) {
StringBuilder missings = new StringBuilder(); final StringBuilder missings = new StringBuilder();
for ( String property : missingIdProperties ) { for ( String property : missingIdProperties ) {
missings.append( property ).append( ", " ); if ( missings.length() > 0 ) {
missings.append(", ");
}
missings.append("'").append( property ).append( "'" );
} }
throw new AnnotationException( throw new AnnotationException( "Entity '" + persistentClass.getEntityName()
"Unable to find properties (" + "' has an '@IdClass' with properties " + missings
+ missings.substring( 0, missings.length() - 2 ) + " which do not match properties of the entity class" );
+ ") in entity annotated with @IdClass:" + persistentClass.getEntityName()
);
} }
} }
@ -1293,10 +1292,15 @@ public class EntityBinder {
String condition = filter.condition(); String condition = filter.condition();
if ( isEmptyAnnotationValue( condition ) ) { if ( isEmptyAnnotationValue( condition ) ) {
final FilterDefinition definition = context.getMetadataCollector().getFilterDefinition( filterName ); final FilterDefinition definition = context.getMetadataCollector().getFilterDefinition( filterName );
condition = definition == null ? null : definition.getDefaultFilterCondition(); if ( definition == null ) {
throw new AnnotationException( "Entity '" + name
+ "' has a '@Filter' for an undefined filter named '" + filterName + "'" );
}
condition = definition.getDefaultFilterCondition();
if ( isEmpty( condition ) ) { if ( isEmpty( condition ) ) {
throw new AnnotationException( "no filter condition found for filter " throw new AnnotationException( "Entity '" + name +
+ filterName + " in " + this.name ); "' has a '@Filter' with no 'condition' and no default condition was given by the '@FilterDef' named '"
+ filterName + "'" );
} }
} }
persistentClass.addFilter( persistentClass.addFilter(
@ -1330,7 +1334,8 @@ public class EntityBinder {
if ( persisterAnn != null ) { if ( persisterAnn != null ) {
Class clazz = persisterAnn.impl(); Class clazz = persisterAnn.impl();
if ( !EntityPersister.class.isAssignableFrom(clazz) ) { if ( !EntityPersister.class.isAssignableFrom(clazz) ) {
throw new AnnotationException( "persister class does not implement EntityPersister: " + clazz.getName() ); throw new AnnotationException( "Persister class '" + clazz.getName()
+ "' does not implement EntityPersister" );
} }
persistentClass.setEntityPersisterClass( clazz ); persistentClass.setEntityPersisterClass( clazz );
} }
@ -1366,9 +1371,8 @@ public class EntityBinder {
persistentClass.setDiscriminatorValue( name ); persistentClass.setDiscriminatorValue( name );
} }
else if ( "character".equals( discriminator.getType().getName() ) ) { else if ( "character".equals( discriminator.getType().getName() ) ) {
throw new AnnotationException( throw new AnnotationException( "Entity '" + name
"Using default @DiscriminatorValue for a discriminator of type CHAR is not safe" + "' has a discriminator of character type and must specify its '@DiscriminatorValue'" );
);
} }
else if ( "integer".equals( discriminator.getType().getName() ) ) { else if ( "integer".equals( discriminator.getType().getName() ) ) {
persistentClass.setDiscriminatorValue( String.valueOf( name.hashCode() ) ); persistentClass.setDiscriminatorValue( String.valueOf( name.hashCode() ) );
@ -1528,9 +1532,8 @@ public class EntityBinder {
case "non-lazy": case "non-lazy":
return false; return false;
default: default:
throw new AnnotationException( "Unknown @Cache.include value [" + effectiveCache.include() + "] : " throw new AnnotationException( "Class '" + annotatedClass.getName()
+ annotatedClass.getName() + "' has a '@Cache' with undefined option 'include=\"" + effectiveCache.include() + "\"'" );
);
} }
} }
@ -2070,7 +2073,7 @@ public class EntityBinder {
public void processComplementaryTableDefinitions(org.hibernate.annotations.Table table) { public void processComplementaryTableDefinitions(org.hibernate.annotations.Table table) {
//comment and index are processed here //comment and index are processed here
if ( table == null ) return; if ( table == null ) return;
String appliedTable = table.appliesTo(); final String appliedTable = table.appliesTo();
Table hibTable = null; Table hibTable = null;
for ( Table pcTable : persistentClass.getTableClosure() ) { for ( Table pcTable : persistentClass.getTableClosure() ) {
if ( pcTable.getQuotedName().equals( appliedTable ) ) { if ( pcTable.getQuotedName().equals( appliedTable ) ) {
@ -2089,9 +2092,9 @@ public class EntityBinder {
} }
} }
if ( hibTable == null ) { if ( hibTable == null ) {
throw new AnnotationException( throw new AnnotationException( "Entity '" + name
"@org.hibernate.annotations.Table references an unknown table: " + appliedTable + "' has a '@org.hibernate.annotations.Table' annotation which 'appliesTo' an unknown table named '"
); + appliedTable + "'" );
} }
if ( !isEmptyAnnotationValue( table.comment() ) ) { if ( !isEmptyAnnotationValue( table.comment() ) ) {
hibTable.setComment( table.comment() ); hibTable.setComment( table.comment() );

View File

@ -16,7 +16,6 @@ import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.common.reflection.XClass; import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty; import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.BinderHelper;
import org.hibernate.cfg.AnnotatedColumn; import org.hibernate.cfg.AnnotatedColumn;
import org.hibernate.cfg.AnnotatedJoinColumn; import org.hibernate.cfg.AnnotatedJoinColumn;
import org.hibernate.cfg.IdGeneratorResolverSecondPass; import org.hibernate.cfg.IdGeneratorResolverSecondPass;

View File

@ -22,7 +22,6 @@ import org.hibernate.cfg.PropertyHolder;
import org.hibernate.cfg.PropertyHolderBuilder; import org.hibernate.cfg.PropertyHolderBuilder;
import org.hibernate.cfg.SecondPass; import org.hibernate.cfg.SecondPass;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Collection; import org.hibernate.mapping.Collection;
import org.hibernate.mapping.IndexBackref; import org.hibernate.mapping.IndexBackref;
import org.hibernate.mapping.List; import org.hibernate.mapping.List;
@ -73,7 +72,7 @@ public class ListBinder extends CollectionBinder {
final AnnotatedJoinColumn[] mapKeyManyToManyColumns, final AnnotatedJoinColumn[] mapKeyManyToManyColumns,
final boolean isEmbedded, final boolean isEmbedded,
final XProperty property, final XProperty property,
final XClass collType, final XClass elementType,
final NotFoundAction notFoundAction, final NotFoundAction notFoundAction,
final boolean unique, final boolean unique,
final TableBinder assocTableBinder, final TableBinder assocTableBinder,
@ -84,7 +83,7 @@ public class ListBinder extends CollectionBinder {
throws MappingException { throws MappingException {
bindStarToManySecondPass( bindStarToManySecondPass(
persistentClasses, persistentClasses,
collType, elementType,
fkJoinColumns, fkJoinColumns,
keyColumns, keyColumns,
inverseColumns, inverseColumns,
@ -96,12 +95,12 @@ public class ListBinder extends CollectionBinder {
notFoundAction, notFoundAction,
buildingContext buildingContext
); );
bindIndex( property, collType, buildingContext ); bindIndex( property, elementType, buildingContext );
} }
}; };
} }
private void bindIndex(XProperty property, XClass collType, final MetadataBuildingContext buildingContext) { private void bindIndex(XProperty property, XClass elementType, final MetadataBuildingContext buildingContext) {
final PropertyHolder valueHolder = PropertyHolderBuilder.buildPropertyHolder( final PropertyHolder valueHolder = PropertyHolderBuilder.buildPropertyHolder(
collection, collection,
qualify( collection.getRole(), "key" ), qualify( collection.getRole(), "key" ),
@ -120,7 +119,7 @@ public class ListBinder extends CollectionBinder {
final BasicValueBinder valueBinder = new BasicValueBinder( BasicValueBinder.Kind.LIST_INDEX, buildingContext ); final BasicValueBinder valueBinder = new BasicValueBinder( BasicValueBinder.Kind.LIST_INDEX, buildingContext );
valueBinder.setColumns( new AnnotatedColumn[] { indexColumn } ); valueBinder.setColumns( new AnnotatedColumn[] { indexColumn } );
valueBinder.setReturnedClassName( Integer.class.getName() ); valueBinder.setReturnedClassName( Integer.class.getName() );
valueBinder.setType( property, collType, null, null ); valueBinder.setType( property, elementType, null, null );
// valueBinder.setExplicitType( "integer" ); // valueBinder.setExplicitType( "integer" );
SimpleValue indexValue = valueBinder.make(); SimpleValue indexValue = valueBinder.make();
indexColumn.linkWithValue( indexValue ); indexColumn.linkWithValue( indexValue );

View File

@ -24,7 +24,6 @@ import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.AccessType; import org.hibernate.cfg.AccessType;
import org.hibernate.cfg.AnnotatedClassType; import org.hibernate.cfg.AnnotatedClassType;
import org.hibernate.cfg.AnnotationBinder; import org.hibernate.cfg.AnnotationBinder;
import org.hibernate.cfg.BinderHelper;
import org.hibernate.cfg.CollectionPropertyHolder; import org.hibernate.cfg.CollectionPropertyHolder;
import org.hibernate.cfg.CollectionSecondPass; import org.hibernate.cfg.CollectionSecondPass;
import org.hibernate.cfg.AnnotatedColumn; import org.hibernate.cfg.AnnotatedColumn;
@ -34,7 +33,6 @@ import org.hibernate.cfg.PropertyData;
import org.hibernate.cfg.PropertyPreloadedData; import org.hibernate.cfg.PropertyPreloadedData;
import org.hibernate.cfg.SecondPass; import org.hibernate.cfg.SecondPass;
import org.hibernate.engine.jdbc.Size; import org.hibernate.engine.jdbc.Size;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.BasicValue; import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Collection; import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column; import org.hibernate.mapping.Column;
@ -165,7 +163,7 @@ public class MapBinder extends CollectionBinder {
} }
private void bindKeyFromAssociationTable( private void bindKeyFromAssociationTable(
XClass collType, XClass elementType,
Map<String, PersistentClass> persistentClasses, Map<String, PersistentClass> persistentClasses,
String mapKeyPropertyName, String mapKeyPropertyName,
XProperty property, XProperty property,
@ -176,17 +174,19 @@ public class MapBinder extends CollectionBinder {
String targetPropertyName) { String targetPropertyName) {
if ( mapKeyPropertyName != null ) { if ( mapKeyPropertyName != null ) {
//this is an EJB3 @MapKey //this is an EJB3 @MapKey
PersistentClass associatedClass = persistentClasses.get( collType.getName() ); PersistentClass associatedClass = persistentClasses.get( elementType.getName() );
if ( associatedClass == null ) throw new AnnotationException( "Associated class not found: " + collType ); if ( associatedClass == null ) {
throw new AnnotationException( "Association '" + safeCollectionRole()
+ "' targets the type '" + elementType.getName() + "' which is not an '@Entity' type" );
}
Property mapProperty = findPropertyByName( associatedClass, mapKeyPropertyName ); Property mapProperty = findPropertyByName( associatedClass, mapKeyPropertyName );
if ( mapProperty == null ) { if ( mapProperty == null ) {
throw new AnnotationException( throw new AnnotationException( "Map key property '" + mapKeyPropertyName
"Map key property not found: " + collType + "." + mapKeyPropertyName + "' not found in target entity '" + associatedClass.getEntityName() + "'" );
);
} }
org.hibernate.mapping.Map map = (org.hibernate.mapping.Map) this.collection; org.hibernate.mapping.Map map = (org.hibernate.mapping.Map) this.collection;
// HHH-11005 - if InheritanceType.JOINED then need to find class defining the column // HHH-11005 - if InheritanceType.JOINED then need to find class defining the column
InheritanceState inheritanceState = inheritanceStatePerClass.get( collType ); InheritanceState inheritanceState = inheritanceStatePerClass.get( elementType );
PersistentClass targetPropertyPersistentClass = InheritanceType.JOINED.equals( inheritanceState.getType() ) ? PersistentClass targetPropertyPersistentClass = InheritanceType.JOINED.equals( inheritanceState.getType() ) ?
mapProperty.getPersistentClass() : mapProperty.getPersistentClass() :
associatedClass; associatedClass;

View File

@ -24,13 +24,11 @@ import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.AccessType; import org.hibernate.cfg.AccessType;
import org.hibernate.cfg.AnnotationBinder; import org.hibernate.cfg.AnnotationBinder;
import org.hibernate.cfg.BinderHelper;
import org.hibernate.cfg.AnnotatedColumn; import org.hibernate.cfg.AnnotatedColumn;
import org.hibernate.cfg.InheritanceState; import org.hibernate.cfg.InheritanceState;
import org.hibernate.cfg.PropertyHolder; import org.hibernate.cfg.PropertyHolder;
import org.hibernate.cfg.PropertyPreloadedData; import org.hibernate.cfg.PropertyPreloadedData;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Collection; import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.KeyValue; import org.hibernate.mapping.KeyValue;
@ -166,10 +164,9 @@ public class PropertyBinder {
private void validateBind() { private void validateBind() {
if ( property.isAnnotationPresent( Immutable.class ) ) { if ( property.isAnnotationPresent( Immutable.class ) ) {
throw new AnnotationException( throw new AnnotationException( "Property '" + qualify( holder.getPath(), name )
"@Immutable on property not allowed. " + + "' may not be '@Immutable'"
"Only allowed on entity level or on a collection." + " ('@Immutable' may only be applied to entities and collections)" );
);
} }
if ( !declaringClassSet ) { if ( !declaringClassSet ) {
throw new AssertionFailure( "declaringClass has not been set before a bind" ); throw new AssertionFailure( "declaringClass has not been set before a bind" );
@ -292,12 +289,14 @@ public class PropertyBinder {
} }
private Class<? extends EmbeddableInstantiator> resolveCustomInstantiator(XProperty property, XClass embeddableClass) { private Class<? extends EmbeddableInstantiator> resolveCustomInstantiator(XProperty property, XClass embeddableClass) {
final org.hibernate.annotations.EmbeddableInstantiator propertyAnnotation = property.getAnnotation( org.hibernate.annotations.EmbeddableInstantiator.class ); final org.hibernate.annotations.EmbeddableInstantiator propertyAnnotation =
property.getAnnotation( org.hibernate.annotations.EmbeddableInstantiator.class );
if ( propertyAnnotation != null ) { if ( propertyAnnotation != null ) {
return propertyAnnotation.value(); return propertyAnnotation.value();
} }
final org.hibernate.annotations.EmbeddableInstantiator classAnnotation = embeddableClass.getAnnotation( org.hibernate.annotations.EmbeddableInstantiator.class ); final org.hibernate.annotations.EmbeddableInstantiator classAnnotation =
embeddableClass.getAnnotation( org.hibernate.annotations.EmbeddableInstantiator.class );
if ( classAnnotation != null ) { if ( classAnnotation != null ) {
return classAnnotation.value(); return classAnnotation.value();
} }
@ -341,7 +340,9 @@ public class PropertyBinder {
NaturalId naturalId = property.getAnnotation(NaturalId.class); NaturalId naturalId = property.getAnnotation(NaturalId.class);
if ( naturalId != null ) { if ( naturalId != null ) {
if ( !entityBinder.isRootEntity() ) { if ( !entityBinder.isRootEntity() ) {
throw new AnnotationException( "@NaturalId only valid on root entity (or its @MappedSuperclasses)" ); throw new AnnotationException( "Property '" + qualify( holder.getPath(), name )
+ "' belongs to an entity subclass and may not be annotated '@NaturalId'" +
" (only a property of a root '@Entity' or a '@MappedSuperclass' may be a '@NaturalId')" );
} }
if ( !naturalId.mutable() ) { if ( !naturalId.mutable() ) {
updatable = false; updatable = false;
@ -368,22 +369,16 @@ public class PropertyBinder {
private void validateOptimisticLock(OptimisticLock lockAnn) { private void validateOptimisticLock(OptimisticLock lockAnn) {
if ( lockAnn.excluded() ) { if ( lockAnn.excluded() ) {
if ( property.isAnnotationPresent(Version.class) ) { if ( property.isAnnotationPresent(Version.class) ) {
throw new AnnotationException( throw new AnnotationException("Property '" + qualify( holder.getPath(), name )
"@OptimisticLock(excluded=true) incompatible with @Version: " + "' is annotated '@OptimisticLock(excluded=true)' and '@Version'" );
+ qualify( holder.getPath(), name )
);
} }
if ( property.isAnnotationPresent(Id.class) ) { if ( property.isAnnotationPresent(Id.class) ) {
throw new AnnotationException( throw new AnnotationException("Property '" + qualify( holder.getPath(), name )
"@OptimisticLock(excluded=true) incompatible with @Id: " + "' is annotated '@OptimisticLock(excluded=true)' and '@Id'" );
+ qualify( holder.getPath(), name )
);
} }
if ( property.isAnnotationPresent(EmbeddedId.class) ) { if ( property.isAnnotationPresent(EmbeddedId.class) ) {
throw new AnnotationException( throw new AnnotationException("Property '" + qualify( holder.getPath(), name )
"@OptimisticLock(excluded=true) incompatible with @EmbeddedId: " + "' is annotated '@OptimisticLock(excluded=true)' and '@EmbeddedId'" );
+ qualify( holder.getPath(), name )
);
} }
} }
} }
@ -409,25 +404,18 @@ public class PropertyBinder {
*/ */
private ValueGeneration getValueGenerationFromAnnotations(XProperty property) { private ValueGeneration getValueGenerationFromAnnotations(XProperty property) {
AnnotationValueGeneration<?> valueGeneration = null; AnnotationValueGeneration<?> valueGeneration = null;
for ( Annotation annotation : property.getAnnotations() ) { for ( Annotation annotation : property.getAnnotations() ) {
AnnotationValueGeneration<?> candidate = getValueGenerationFromAnnotation( property, annotation ); AnnotationValueGeneration<?> candidate = getValueGenerationFromAnnotation( property, annotation );
if ( candidate != null ) { if ( candidate != null ) {
if ( valueGeneration != null ) { if ( valueGeneration != null ) {
throw new AnnotationException( throw new AnnotationException( "Property '" + qualify( holder.getPath(), name )
"Only one generator annotation is allowed: " + qualify( + "' has multiple 'ValueGenerationType' annotations" );
holder.getPath(),
name
)
);
} }
else { else {
valueGeneration = candidate; valueGeneration = candidate;
} }
} }
} }
return valueGeneration; return valueGeneration;
} }
@ -439,7 +427,6 @@ public class PropertyBinder {
XProperty property, XProperty property,
A annotation) { A annotation) {
final ValueGenerationType generatorAnnotation = annotation.annotationType().getAnnotation( ValueGenerationType.class ); final ValueGenerationType generatorAnnotation = annotation.annotationType().getAnnotation( ValueGenerationType.class );
if ( generatorAnnotation == null ) { if ( generatorAnnotation == null ) {
return null; return null;
} }
@ -451,9 +438,9 @@ public class PropertyBinder {
&& property.isAnnotationPresent(Version.class) && property.isAnnotationPresent(Version.class)
&& valueGeneration.getGenerationTiming() == GenerationTiming.INSERT ) { && valueGeneration.getGenerationTiming() == GenerationTiming.INSERT ) {
throw new AnnotationException( throw new AnnotationException( "Property '" + qualify( holder.getPath(), name )
"@Generated(INSERT) on a @Version property not allowed, use ALWAYS (or NEVER): " + "' is annotated '@Generated(INSERT)' and '@Version' (use '@Generated(ALWAYS)' instead)"
+ qualify( holder.getPath(), name )
); );
} }

View File

@ -25,10 +25,8 @@ import org.hibernate.boot.query.NamedNativeQueryDefinition;
import org.hibernate.boot.query.NamedNativeQueryDefinitionBuilder; import org.hibernate.boot.query.NamedNativeQueryDefinitionBuilder;
import org.hibernate.boot.query.NamedProcedureCallDefinition; import org.hibernate.boot.query.NamedProcedureCallDefinition;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.BinderHelper;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.log.DeprecationLogger; import org.hibernate.internal.log.DeprecationLogger;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.jpa.HibernateHints; import org.hibernate.jpa.HibernateHints;
import org.hibernate.query.sql.internal.ParameterParser; import org.hibernate.query.sql.internal.ParameterParser;
import org.hibernate.query.sql.spi.ParameterRecognizer; import org.hibernate.query.sql.spi.ParameterRecognizer;
@ -68,7 +66,7 @@ public abstract class QueryBinder {
} }
if ( isEmptyAnnotationValue( queryAnn.name() ) ) { if ( isEmptyAnnotationValue( queryAnn.name() ) ) {
throw new AnnotationException( "A named query must have a name when used in class or package level" ); throw new AnnotationException( "Class or package level '@NamedQuery' annotation must specify a 'name'" );
} }
final String queryName = queryAnn.name(); final String queryName = queryAnn.name();
@ -111,7 +109,7 @@ public abstract class QueryBinder {
} }
if ( isEmptyAnnotationValue( queryAnn.name() ) ) { if ( isEmptyAnnotationValue( queryAnn.name() ) ) {
throw new AnnotationException( "A named query must have a name when used in class or package level" ); throw new AnnotationException( "Class or package level '@NamedNativeQuery' annotation must specify a 'name'" );
} }
final String registrationName = queryAnn.name(); final String registrationName = queryAnn.name();
@ -165,7 +163,7 @@ public abstract class QueryBinder {
//ResultSetMappingDefinition mappingDefinition = mappings.getJdbcValuesMappingProducer( queryAnn.resultSetMapping() ); //ResultSetMappingDefinition mappingDefinition = mappings.getJdbcValuesMappingProducer( queryAnn.resultSetMapping() );
if ( isEmptyAnnotationValue( registrationName ) ) { if ( isEmptyAnnotationValue( registrationName ) ) {
throw new AnnotationException( "A named query must have a name when used in class or package level" ); throw new AnnotationException( "Class or package level '@NamedNativeQuery' annotation must specify a 'name'" );
} }
final String resultSetMappingName = queryAnn.resultSetMapping(); final String resultSetMappingName = queryAnn.resultSetMapping();
@ -189,16 +187,11 @@ public abstract class QueryBinder {
.setComment( getAnnotationValueStringOrNull( queryAnn.comment() ) ); .setComment( getAnnotationValueStringOrNull( queryAnn.comment() ) );
if ( queryAnn.callable() ) { if ( queryAnn.callable() ) {
final NamedProcedureCallDefinition definition = createStoredProcedure( final NamedProcedureCallDefinition definition =
builder, context, createStoredProcedure( builder, context, () -> illegalCallSyntax( queryAnn ) );
() -> illegalCallSyntax(
queryAnn,
queryAnn.query()
)
);
context.getMetadataCollector().addNamedProcedureCallDefinition( definition ); context.getMetadataCollector().addNamedProcedureCallDefinition( definition );
DeprecationLogger.DEPRECATION_LOGGER.warn( DeprecationLogger.DEPRECATION_LOGGER.warn(
"Marking named native queries as callable is no longer supported; use `@jakarta.persistence.NamedStoredProcedureQuery` instead. Ignoring." "Marking named native queries as callable is no longer supported; use '@jakarta.persistence.NamedStoredProcedureQuery' instead. Ignoring."
); );
} }
else { else {
@ -343,7 +336,7 @@ public abstract class QueryBinder {
//ResultSetMappingDefinition mappingDefinition = mappings.getJdbcValuesMappingProducer( queryAnn.resultSetMapping() ); //ResultSetMappingDefinition mappingDefinition = mappings.getJdbcValuesMappingProducer( queryAnn.resultSetMapping() );
if ( isEmptyAnnotationValue( registrationName ) ) { if ( isEmptyAnnotationValue( registrationName ) ) {
throw new AnnotationException( "A named query must have a name when used in class or package level" ); throw new AnnotationException( "Class or package level '@NamedQuery' annotation must specify a 'name'" );
} }
@ -432,7 +425,7 @@ public abstract class QueryBinder {
final String registrationName = annotation.name(); final String registrationName = annotation.name();
if ( isEmptyAnnotationValue( registrationName ) ) { if ( isEmptyAnnotationValue( registrationName ) ) {
throw new AnnotationException( "A named query must have a name when used in class or package level" ); throw new AnnotationException( "Class or package level '@NamedStoredProcedureQuery' annotation must specify a 'name'" );
} }
final NamedProcedureCallDefinitionImpl def = new NamedProcedureCallDefinitionImpl( annotation ); final NamedProcedureCallDefinitionImpl def = new NamedProcedureCallDefinitionImpl( annotation );
@ -537,15 +530,8 @@ public abstract class QueryBinder {
return i; return i;
} }
private static AnnotationException illegalCallSyntax( private static AnnotationException illegalCallSyntax(org.hibernate.annotations.NamedNativeQuery queryAnn) {
org.hibernate.annotations.NamedNativeQuery queryAnn, return new AnnotationException( "Callable 'NamedNativeQuery' named '" + queryAnn.name()
String sqlString) { + "' does not use the JDBC call syntax" );
return new AnnotationException(
String.format(
"Callable named native query [%s] doesn't use the JDBC call syntax: %s",
queryAnn.name(),
sqlString
)
);
} }
} }

View File

@ -22,7 +22,6 @@ import org.hibernate.boot.model.naming.NamingStrategyHelper;
import org.hibernate.boot.model.source.spi.AttributePath; import org.hibernate.boot.model.source.spi.AttributePath;
import org.hibernate.boot.spi.InFlightMetadataCollector; import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.BinderHelper;
import org.hibernate.cfg.AnnotatedJoinColumn; import org.hibernate.cfg.AnnotatedJoinColumn;
import org.hibernate.cfg.IndexOrUniqueKeySecondPass; import org.hibernate.cfg.IndexOrUniqueKeySecondPass;
import org.hibernate.cfg.JPAIndexHolder; import org.hibernate.cfg.JPAIndexHolder;
@ -30,7 +29,6 @@ import org.hibernate.cfg.ObjectNameSource;
import org.hibernate.cfg.UniqueConstraintHolder; import org.hibernate.cfg.UniqueConstraintHolder;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Collection; import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column; import org.hibernate.mapping.Column;
@ -546,10 +544,8 @@ public class TableBinder {
} }
final String mappedByProperty = columns[0].getMappedBy(); final String mappedByProperty = columns[0].getMappedBy();
if ( isNotEmpty( mappedByProperty ) ) { if ( isNotEmpty( mappedByProperty ) ) {
/* // Get the columns of the mapped-by property
* Get the columns of the mapped-by property // copy them and link the copy to the actual value
* copy them and link the copy to the actual value
*/
LOG.debugf( "Retrieving property %s.%s", associatedClass.getEntityName(), mappedByProperty ); LOG.debugf( "Retrieving property %s.%s", associatedClass.getEntityName(), mappedByProperty );
final Value propertyVal = associatedClass.getRecursiveProperty( columns[0].getMappedBy() ).getValue(); final Value propertyVal = associatedClass.getRecursiveProperty( columns[0].getMappedBy() ).getValue();
@ -558,8 +554,9 @@ public class TableBinder {
Value element = ((Collection) propertyVal).getElement(); Value element = ((Collection) propertyVal).getElement();
if ( element == null ) { if ( element == null ) {
throw new AnnotationException( throw new AnnotationException(
"Illegal use of mappedBy on both sides of the relationship: " "Both sides of the bidirectional association '"
+ associatedClass.getEntityName() + "." + mappedByProperty + associatedClass.getEntityName() + "." + mappedByProperty
+ "' specify 'mappedBy'"
); );
} }
mappedByColumns = element.getColumns(); mappedByColumns = element.getColumns();
@ -573,10 +570,8 @@ public class TableBinder {
} }
} }
else if ( columns[0].isImplicit() ) { else if ( columns[0].isImplicit() ) {
/* // if columns are implicit, then create the columns based on the
* if columns are implicit, then create the columns based on the // referenced entity id columns
* referenced entity id columns
*/
List<Column> idColumns = referencedEntity instanceof JoinedSubclass List<Column> idColumns = referencedEntity instanceof JoinedSubclass
? referencedEntity.getKey().getColumns() ? referencedEntity.getKey().getColumns()
: referencedEntity.getIdentifier().getColumns(); : referencedEntity.getIdentifier().getColumns();
@ -600,7 +595,7 @@ public class TableBinder {
referencedPropertyName = collection.getReferencedPropertyName(); referencedPropertyName = collection.getReferencedPropertyName();
} }
else { else {
throw new AnnotationException( "SecondaryTable JoinColumn cannot reference a non primary key" ); throw new AnnotationException( "The '@JoinColumn' for a secondary table must reference the primary key" );
} }
} }
@ -621,9 +616,7 @@ public class TableBinder {
"Cannot find synthProp: " + referencedEntity.getEntityName() + "." + referencedPropertyName "Cannot find synthProp: " + referencedEntity.getEntityName() + "." + referencedPropertyName
); );
} }
linkJoinColumnWithValueOverridingNameIfImplicit( linkJoinColumnWithValueOverridingNameIfImplicit( referencedEntity, synthProp.getValue(), columns, value );
referencedEntity, synthProp.getValue(), columns, value
);
if ( value instanceof SortableValue ) { if ( value instanceof SortableValue ) {
( (SortableValue) value ).sortProperties(); ( (SortableValue) value ).sortProperties();
} }
@ -633,10 +626,10 @@ public class TableBinder {
//implicit case, we hope PK and FK columns are in the same order //implicit case, we hope PK and FK columns are in the same order
if ( columns.length != referencedEntity.getIdentifier().getColumnSpan() ) { if ( columns.length != referencedEntity.getIdentifier().getColumnSpan() ) {
throw new AnnotationException( throw new AnnotationException(
"A Foreign key referring " + referencedEntity.getEntityName() "A foreign key that references '" + referencedEntity.getEntityName()
+ " from " + associatedClass.getEntityName() + "' from entity '" + associatedClass.getEntityName()
+ " has the wrong number of column. should be " + referencedEntity.getIdentifier() + "' has " + columns.length + " columns but the primary key has "
.getColumnSpan() + referencedEntity.getIdentifier().getColumnSpan() + " columns"
); );
} }
linkJoinColumnWithValueOverridingNameIfImplicit( linkJoinColumnWithValueOverridingNameIfImplicit(
@ -667,7 +660,7 @@ public class TableBinder {
boolean match = false; boolean match = false;
//for each PK column, find the associated FK column. //for each PK column, find the associated FK column.
final String colName = col.getQuotedName(dialect); final String colName = col.getQuotedName(dialect);
for (AnnotatedJoinColumn joinCol : columns) { for ( AnnotatedJoinColumn joinCol : columns ) {
String referencedColumn = joinCol.getReferencedColumn(); String referencedColumn = joinCol.getReferencedColumn();
referencedColumn = buildingContext.getMetadataCollector().getPhysicalColumnName( referencedColumn = buildingContext.getMetadataCollector().getPhysicalColumnName(
referencedEntity.getTable(), referencedEntity.getTable(),

View File

@ -119,12 +119,11 @@ public final class EventType<T> {
* *
* @throws HibernateException If eventName is null, or if eventName does not correlate to any known event type. * @throws HibernateException If eventName is null, or if eventName does not correlate to any known event type.
*/ */
@SuppressWarnings("rawtypes") public static EventType<?> resolveEventTypeByName(final String eventName) {
public static EventType resolveEventTypeByName(final String eventName) {
if ( eventName == null ) { if ( eventName == null ) {
throw new HibernateException( "event name to resolve cannot be null" ); throw new HibernateException( "event name to resolve cannot be null" );
} }
final EventType eventType = STANDARD_TYPE_BY_NAME_MAP.get( eventName ); final EventType<?> eventType = STANDARD_TYPE_BY_NAME_MAP.get( eventName );
if ( eventType == null ) { if ( eventType == null ) {
throw new HibernateException( "Unable to locate proper event type for event name [" + eventName + "]" ); throw new HibernateException( "Unable to locate proper event type for event name [" + eventName + "]" );
} }

View File

@ -78,7 +78,7 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
// incoming "configuration" values // incoming "configuration" values
private String explicitTypeName; private String explicitTypeName;
private Map<Object,Object> explicitLocalTypeParams; private Map<String,String> explicitLocalTypeParams;
private Function<TypeConfiguration, BasicJavaType> explicitJavaTypeAccess; private Function<TypeConfiguration, BasicJavaType> explicitJavaTypeAccess;
private Function<TypeConfiguration, JdbcType> explicitJdbcTypeAccess; private Function<TypeConfiguration, JdbcType> explicitJdbcTypeAccess;
@ -117,7 +117,7 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
this.explicitTypeName = original.explicitTypeName; this.explicitTypeName = original.explicitTypeName;
this.explicitLocalTypeParams = original.explicitLocalTypeParams == null this.explicitLocalTypeParams = original.explicitLocalTypeParams == null
? null ? null
: new HashMap(original.explicitLocalTypeParams); : new HashMap<>(original.explicitLocalTypeParams);
this.explicitJavaTypeAccess = original.explicitJavaTypeAccess; this.explicitJavaTypeAccess = original.explicitJavaTypeAccess;
this.explicitJdbcTypeAccess = original.explicitJdbcTypeAccess; this.explicitJdbcTypeAccess = original.explicitJdbcTypeAccess;
this.explicitMutabilityPlanAccess = original.explicitMutabilityPlanAccess; this.explicitMutabilityPlanAccess = original.explicitMutabilityPlanAccess;
@ -264,11 +264,11 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
return; return;
} }
throw new IllegalStateException( // throw new IllegalStateException(
"BasicValue [" + ownerName + "." + propertyName + // "BasicValue [" + ownerName + "." + propertyName +
"] already had column associated: `" + column.getText() + // "] already had column associated: `" + column.getText() +
"` -> `" + incomingColumn.getText() + "`" // "` -> `" + incomingColumn.getText() + "`"
); // );
} }
@Override @Override
@ -665,8 +665,7 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
name, name,
typeNamedClass, typeNamedClass,
null, null,
null, null
typeConfiguration
); );
context.getTypeDefinitionRegistry().register( implicitDefinition ); context.getTypeDefinitionRegistry().register( implicitDefinition );
return implicitDefinition.resolve( return implicitDefinition.resolve(
@ -754,7 +753,7 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
return typeConfiguration; return typeConfiguration;
} }
public void setExplicitTypeParams(Map<Object,Object> explicitLocalTypeParams) { public void setExplicitTypeParams(Map<String,String> explicitLocalTypeParams) {
this.explicitLocalTypeParams = explicitLocalTypeParams; this.explicitLocalTypeParams = explicitLocalTypeParams;
} }
@ -772,7 +771,6 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
.getServiceRegistry() .getServiceRegistry()
.getService( ClassLoaderService.class ); .getService( ClassLoaderService.class );
try { try {
//noinspection rawtypes
final Class<AttributeConverter<?,?>> converterClass = cls.classForName( converterClassName ); final Class<AttributeConverter<?,?>> converterClass = cls.classForName( converterClassName );
setAttributeConverterDescriptor( new ClassBasedConverterDescriptor( setAttributeConverterDescriptor( new ClassBasedConverterDescriptor(
converterClass, converterClass,

View File

@ -196,18 +196,10 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
return object instanceof Column && equals( (Column) object ); return object instanceof Column && equals( (Column) object );
} }
@SuppressWarnings("SimplifiableIfStatement")
public boolean equals(Column column) { public boolean equals(Column column) {
if ( null == column ) { return column != null && (
return false; this == column || isQuoted() ? name.equals( column.name ) : name.equalsIgnoreCase( column.name )
} );
if ( this == column ) {
return true;
}
return isQuoted() ?
name.equals( column.name ) :
name.equalsIgnoreCase( column.name );
} }
public int getSqlTypeCode(Mapping mapping) throws MappingException { public int getSqlTypeCode(Mapping mapping) throws MappingException {

View File

@ -33,6 +33,7 @@ import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.jpa.event.spi.CallbackDefinition; import org.hibernate.jpa.event.spi.CallbackDefinition;
import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.Alias; import org.hibernate.sql.Alias;
import org.hibernate.type.Type;
/** /**
* Mapping for an entity. * Mapping for an entity.
@ -704,11 +705,14 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl
public void validate(Mapping mapping) throws MappingException { public void validate(Mapping mapping) throws MappingException {
for ( Property prop : getProperties() ) { for ( Property prop : getProperties() ) {
if ( !prop.isValid( mapping ) ) { if ( !prop.isValid( mapping ) ) {
Type type = prop.getType();
int actualColumns = prop.getColumnSpan();
int requiredColumns = type.getColumnSpan(mapping);
throw new MappingException( throw new MappingException(
"property mapping has wrong number of columns: " + "Property '" + StringHelper.qualify( getEntityName(), prop.getName() )
StringHelper.qualify( getEntityName(), prop.getName() ) + + "' maps to " + actualColumns + " columns but " + requiredColumns
" type: " + + " columns are required (type '" + type.getName()
prop.getType().getName() + "' spans " + requiredColumns + " columns)"
); );
} }
} }
@ -1001,11 +1005,9 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl
Column col = (Column) columnOrFormula; Column col = (Column) columnOrFormula;
if ( !distinctColumns.add( col.getName() ) ) { if ( !distinctColumns.add( col.getName() ) ) {
throw new MappingException( throw new MappingException(
"Repeated column in mapping for entity: " + "Column '" + col.getName()
getEntityName() + + "' is duplicated in mapping for entity '" + getEntityName()
" column: " + + "' (use '@Column(insertable=false, updatable=false)' when mapping multiple properties to the same column)"
col.getName() +
" (should be mapped with insert=\"false\" update=\"false\")"
); );
} }
} }
@ -1014,14 +1016,15 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl
protected void checkPropertyColumnDuplication(Set<String> distinctColumns, List<Property> properties) protected void checkPropertyColumnDuplication(Set<String> distinctColumns, List<Property> properties)
throws MappingException { throws MappingException {
for (Property prop : properties) { for ( Property prop : properties ) {
if ( prop.getValue() instanceof Component ) { //TODO: remove use of instanceof! Value value = prop.getValue();
Component component = (Component) prop.getValue(); if ( value instanceof Component ) {
Component component = (Component) value;
checkPropertyColumnDuplication( distinctColumns, component.getProperties() ); checkPropertyColumnDuplication( distinctColumns, component.getProperties() );
} }
else { else {
if ( prop.isUpdateable() || prop.isInsertable() ) { if ( prop.isUpdateable() || prop.isInsertable() ) {
checkColumnDuplication( distinctColumns, prop.getValue() ); checkColumnDuplication( distinctColumns, value);
} }
} }
} }

View File

@ -14,7 +14,7 @@ import org.hibernate.property.access.internal.PropertyAccessStrategyMixedImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyNoopImpl; import org.hibernate.property.access.internal.PropertyAccessStrategyNoopImpl;
/** /**
* Describes the built-in externally-nameable PropertyAccessStrategy implementations. * Describes the built-in externally-nameable {@link PropertyAccessStrategy} implementations.
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@ -24,8 +24,7 @@ public enum BuiltInPropertyAccessStrategies {
MIXED( "mixed", PropertyAccessStrategyMixedImpl.INSTANCE ), MIXED( "mixed", PropertyAccessStrategyMixedImpl.INSTANCE ),
MAP( "map", PropertyAccessStrategyMapImpl.INSTANCE ), MAP( "map", PropertyAccessStrategyMapImpl.INSTANCE ),
EMBEDDED( "embedded", PropertyAccessStrategyEmbeddedImpl.INSTANCE ), EMBEDDED( "embedded", PropertyAccessStrategyEmbeddedImpl.INSTANCE ),
NOOP( "noop", PropertyAccessStrategyNoopImpl.INSTANCE ) NOOP( "noop", PropertyAccessStrategyNoopImpl.INSTANCE );
;
private final String externalName; private final String externalName;
private final PropertyAccessStrategy strategy; private final PropertyAccessStrategy strategy;
@ -44,22 +43,11 @@ public enum BuiltInPropertyAccessStrategies {
} }
public static BuiltInPropertyAccessStrategies interpret(String name) { public static BuiltInPropertyAccessStrategies interpret(String name) {
if ( BASIC.externalName.equals( name ) ) { for ( BuiltInPropertyAccessStrategies strategy : values() ) {
return BASIC; if ( strategy.externalName.equals( name ) ) {
return strategy;
}
} }
else if ( FIELD.externalName.equals( name ) ) {
return FIELD;
}
else if ( MAP.externalName.equals( name ) ) {
return MAP;
}
else if ( EMBEDDED.externalName.equals( name ) ) {
return EMBEDDED;
}
else if ( NOOP.externalName.equals( name ) ) {
return NOOP;
}
return null; return null;
} }
} }

View File

@ -34,7 +34,7 @@ public class AttributeAccessorBinder implements AttributeBinder<AttributeAccesso
property.setPropertyAccessorName( type.getName() ); property.setPropertyAccessorName( type.getName() );
} }
else { else {
throw new AnnotationException("@AttributeAccessor must specify a PropertyAccessStrategy type"); throw new AnnotationException("'@AttributeAccessor' annotation must specify a 'strategy'");
} }
} }

View File

@ -7,15 +7,14 @@
package org.hibernate.usertype; package org.hibernate.usertype;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.Properties;
/** /**
* Types who implements this interface will have in the setParameterValues an * Types which implement this interface will have
* instance of the class DynamicParameterizedType$ParameterType instead of * {@link ParameterizedType#setParameterValues(Properties)} called with an
* the key PARAMETER_TYPE = "org.hibernate.type.ParameterType" * instance of the class {@link DynamicParameterizedType.ParameterType}
* * instead of the key {@value PARAMETER_TYPE}.
* The interface ParameterType provides some methods to read information *
* dynamically for build the type
*
* @author Janario Oliveira * @author Janario Oliveira
*/ */
public interface DynamicParameterizedType extends ParameterizedType { public interface DynamicParameterizedType extends ParameterizedType {

View File

@ -9,10 +9,10 @@ package org.hibernate.usertype;
import java.util.Properties; import java.util.Properties;
/** /**
* Support for parameterizable types. A UserType or CustomUserType may be * Support for parameterizable types. A {@link UserType} or {@link UserCollectionType}
* made parameterizable by implementing this interface. Parameters for a * may be made parameterizable by implementing this interface. Parameters for a type
* type may be set by using a nested type element for the property element * may be set by using a nested type element for the property element in the mapping
* in the mapping file, or by defining a typedef. * file, or by defining a typedef.
* *
* @author Michael Gloegl * @author Michael Gloegl
*/ */

View File

@ -16,8 +16,8 @@ import org.hibernate.type.spi.TypeConfiguration;
import org.hibernate.type.spi.TypeConfigurationAware; import org.hibernate.type.spi.TypeConfigurationAware;
/** /**
* Convenience UserType implementation to mimic the legacy `@Type` annotation * Convenience {@link UserType} implementation which mimics the legacy <code>@Type</code>
* which based on the {@code hbm.xml} mapping's String-based type support * annotation which was based on the {@code hbm.xml} mapping's string-based type support.
* *
* @see Type * @see Type
*/ */

View File

@ -9,10 +9,8 @@ public class Dependent {
@EmbeddedId @EmbeddedId
DependentId id; DependentId id;
@JoinColumns({ @JoinColumn(name = "FIRSTNAME", referencedColumnName = "FIRSTNAME")
@JoinColumn(name = "FIRSTNAME", referencedColumnName = "FIRSTNAME"), @JoinColumn(name = "LASTNAME", referencedColumnName = "lastName")
@JoinColumn(name = "LASTNAME", referencedColumnName = "lastName")
})
@MapsId("empPK") @MapsId("empPK")
@ManyToOne @ManyToOne
Employee emp; Employee emp;

View File

@ -6,8 +6,8 @@ import java.io.Serializable;
@Embeddable @Embeddable
public class EmployeeId implements Serializable { public class EmployeeId implements Serializable {
@Column(length = 32) @Column(name="firstname", length = 32)
String firstName; String firstName;
@Column(length = 32) @Column(name="lastname", length = 32)
String lastName; String lastName;
} }

View File

@ -28,8 +28,8 @@ public abstract class AbstractEmbeddableWithManyToManyTest {
fail( "Should throw AnnotationException!" ); fail( "Should throw AnnotationException!" );
} }
catch (AnnotationException expected) { catch (AnnotationException expected) {
assertTrue( expected.getMessage().startsWith( assertTrue( expected.getMessage().contains(
"@OneToMany, @ManyToMany or @ElementCollection cannot be used inside an @Embeddable that is also contained within an @ElementCollection" "belongs to an '@Embeddable' class that is contained in an '@ElementCollection' and may not be"
) ); ) );
} }
} }

View File

@ -16,7 +16,6 @@ import jakarta.persistence.FetchType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.IdClass; import jakarta.persistence.IdClass;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinColumns;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table; import jakarta.persistence.Table;
@ -30,7 +29,8 @@ public class BasketItems implements Serializable {
@Id @Id
@ManyToOne(cascade={ CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }) @ManyToOne(cascade={ CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH })
@JoinColumns({ @JoinColumn(name="basketDatetime", referencedColumnName="basketDatetime"), @JoinColumn(name="customerID", referencedColumnName="customerID") }) @JoinColumn(name="basketDatetime", referencedColumnName="basketDatetime")
@JoinColumn(name="customerID", referencedColumnName="customerID")
@Basic(fetch= FetchType.LAZY) @Basic(fetch= FetchType.LAZY)
private ShoppingBaskets shoppingBaskets; private ShoppingBaskets shoppingBaskets;

View File

@ -15,7 +15,6 @@ import jakarta.persistence.Embeddable;
import jakarta.persistence.FetchType; import jakarta.persistence.FetchType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinColumns;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
@Embeddable @Embeddable
@ -50,7 +49,8 @@ public class BasketItemsPK implements Serializable {
@Id @Id
@ManyToOne(cascade={ CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }) @ManyToOne(cascade={ CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH })
@JoinColumns({ @JoinColumn(name="basketDatetime", referencedColumnName="basketDatetime"), @JoinColumn(name="customerID", referencedColumnName="customerID") }) @JoinColumn(name="basketDatetime", referencedColumnName="basketDatetime")
@JoinColumn(name="customerID", referencedColumnName="customerID")
@Basic(fetch= FetchType.LAZY) @Basic(fetch= FetchType.LAZY)
private ShoppingBaskets shoppingBaskets; private ShoppingBaskets shoppingBaskets;

View File

@ -16,7 +16,6 @@ import jakarta.persistence.FetchType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.IdClass; import jakarta.persistence.IdClass;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinColumns;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany; import jakarta.persistence.OneToMany;
import jakarta.persistence.Table; import jakarta.persistence.Table;
@ -31,7 +30,7 @@ public class ShoppingBaskets implements Serializable {
@Id @Id
@ManyToOne(cascade={ CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }) @ManyToOne(cascade={ CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH })
@JoinColumns({ @JoinColumn(name="customerID", referencedColumnName="customerID") }) @JoinColumn(name="customerID", referencedColumnName="customerID")
@Basic(fetch=FetchType.LAZY) @Basic(fetch=FetchType.LAZY)
private Customers owner; private Customers owner;

View File

@ -15,7 +15,6 @@ import jakarta.persistence.Embeddable;
import jakarta.persistence.FetchType; import jakarta.persistence.FetchType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinColumns;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
@Embeddable @Embeddable
@ -48,7 +47,7 @@ public class ShoppingBasketsPK implements Serializable {
@Id @Id
@ManyToOne(cascade={ CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }) @ManyToOne(cascade={ CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH })
@JoinColumns({ @JoinColumn(name="customerID", referencedColumnName="customerID") }) @JoinColumn(name="customerID", referencedColumnName="customerID")
@Basic(fetch= FetchType.LAZY) @Basic(fetch= FetchType.LAZY)
private Customers owner; private Customers owner;

View File

@ -9,7 +9,6 @@ package org.hibernate.orm.test.annotations.index.jpa;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import jakarta.persistence.AttributeOverride; import jakarta.persistence.AttributeOverride;
import jakarta.persistence.AttributeOverrides;
import jakarta.persistence.CascadeType; import jakarta.persistence.CascadeType;
import jakarta.persistence.CollectionTable; import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column; import jakarta.persistence.Column;
@ -28,8 +27,8 @@ import jakarta.persistence.Table;
*/ */
@Entity @Entity
@Table(indexes = { @Table(indexes = {
@Index(unique = true, columnList = "brand, producer") @Index(unique = true, columnList = "brand, producer"),
, @Index(name = "Car_idx", columnList = "since DESC") @Index(name = "Car_idx", columnList = "since DESC")
}) })
@SecondaryTable(name = "T_DEALER", indexes = @Index(columnList = "dealer_name ASC, rate DESC")) @SecondaryTable(name = "T_DEALER", indexes = @Index(columnList = "dealer_name ASC, rate DESC"))
public class Car { public class Car {
@ -38,10 +37,8 @@ public class Car {
private String brand; private String brand;
private String producer; private String producer;
private long since; private long since;
@AttributeOverrides({ @AttributeOverride(name = "name", column = @Column(name = "dealer_name", table = "T_DEALER"))
@AttributeOverride(name = "name", column = @Column(name = "dealer_name", table = "T_DEALER")), @AttributeOverride(name = "rate", column = @Column(table = "T_DEALER"))
@AttributeOverride(name = "rate", column = @Column(table = "T_DEALER"))
})
@Embedded @Embedded
private Dealer dealer; private Dealer dealer;

View File

@ -397,7 +397,7 @@ public class OneToManyTest extends BaseNonConfigCoreFunctionalTestCase {
.build(); .build();
} }
catch ( AnnotationException e ) { catch ( AnnotationException e ) {
assertTrue(e.getMessage().contains( "Unidirectional one-to-many associations annotated with @OnDelete must define @JoinColumn" )); assertTrue(e.getMessage().contains( "is annotated '@OnDelete' and must explicitly specify a '@JoinColumn'" ));
} }
finally { finally {
StandardServiceRegistryBuilder.destroy( serviceRegistry ); StandardServiceRegistryBuilder.destroy( serviceRegistry );

View File

@ -73,7 +73,7 @@ public class OverrideOneToOneJoinColumnTest {
); );
assertTrue( assertTrue(
ex.getMessage().startsWith( "Illegal attempt to define a @JoinColumn with a mappedBy association:" ), ex.getMessage().contains( "is 'mappedBy' a different entity and may not explicitly specify the '@JoinColumn'" ),
"Should disallow exactly because of @JoinColumn override on side with mappedBy" "Should disallow exactly because of @JoinColumn override on side with mappedBy"
); );
} }

View File

@ -23,7 +23,6 @@ import jakarta.persistence.Basic;
import jakarta.persistence.ElementCollection; import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderBy; import jakarta.persistence.OrderBy;
import jakarta.persistence.Table; import jakarta.persistence.Table;
@ -47,7 +46,7 @@ public class SortAndOrderTests {
fail( "Expecting to fail" ); fail( "Expecting to fail" );
} }
catch (AnnotationException expected) { catch (AnnotationException expected) {
assertThat( expected ).hasMessageStartingWith( "Illegal combination of ordering and sorting annotations" ); assertThat( expected ).hasMessageContaining( "both sorted and ordered" );
} }
} }

View File

@ -63,8 +63,8 @@ public class UserWithUnimplementedCollectionTest extends BaseCoreFunctionalTestC
} }
catch (Exception e) { catch (Exception e) {
assertThat( e ).isInstanceOf( AnnotationException.class ); assertThat( e ).isInstanceOf( AnnotationException.class );
assertThat( e ).hasMessageStartingWith( "Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @CollectionOfElements:" ); assertThat( e ).hasMessageEndingWith( "is not a collection and may not be a '@OneToMany', '@ManyToMany', or '@ElementCollection'" );
assertThat( e ).hasMessageEndingWith( ".emailAddresses" ); assertThat( e ).hasMessageContaining( ".emailAddresses" );
} }
} }

View File

@ -16,7 +16,6 @@ import jakarta.persistence.Embeddable;
import jakarta.persistence.EmbeddedId; import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinColumns;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import jakarta.persistence.MapsId; import jakarta.persistence.MapsId;
import jakarta.persistence.Table; import jakarta.persistence.Table;
@ -122,10 +121,8 @@ public class ColumnLengthTest extends BaseUnitTestCase {
@EmbeddedId @EmbeddedId
DependentId id; DependentId id;
@MapsId("empPK") @MapsId("empPK")
@JoinColumns({ @JoinColumn(name = "FK1", referencedColumnName = "first_name")
@JoinColumn(name = "FK1", referencedColumnName = "first_name"), @JoinColumn(name = "FK2", referencedColumnName = "last_name")
@JoinColumn(name = "FK2", referencedColumnName = "last_name")
})
@ManyToOne @ManyToOne
Employee emp; Employee emp;
} }