|
20 | 20 | import org.hibernate.boot.models.spi.GlobalRegistrar; |
21 | 21 | import org.hibernate.boot.spi.MetadataBuildingContext; |
22 | 22 | import org.hibernate.boot.spi.PropertyData; |
| 23 | +import org.hibernate.dialect.Dialect; |
23 | 24 | import org.hibernate.engine.config.spi.ConfigurationService; |
24 | 25 | import org.hibernate.generator.AnnotationBasedGenerator; |
25 | 26 | import org.hibernate.generator.Assigned; |
26 | 27 | import org.hibernate.generator.BeforeExecutionGenerator; |
| 28 | +import org.hibernate.generator.EventType; |
27 | 29 | import org.hibernate.generator.Generator; |
28 | 30 | import org.hibernate.generator.GeneratorCreationContext; |
29 | 31 | import org.hibernate.generator.OnExecutionGenerator; |
| 32 | +import org.hibernate.id.CompositeNestedGeneratedValueGenerator; |
| 33 | +import org.hibernate.id.CompositeNestedGeneratedValueGenerator.GenerationPlan; |
30 | 34 | import org.hibernate.id.Configurable; |
| 35 | +import org.hibernate.id.IdentifierGenerationException; |
31 | 36 | import org.hibernate.id.IdentifierGenerator; |
32 | 37 | import org.hibernate.id.IdentityGenerator; |
33 | 38 | import org.hibernate.id.PersistentIdentifierGenerator; |
34 | 39 | import org.hibernate.id.enhanced.SequenceStyleGenerator; |
35 | 40 | import org.hibernate.id.uuid.UuidValueGenerator; |
| 41 | +import org.hibernate.mapping.Component; |
36 | 42 | import org.hibernate.mapping.GeneratorCreator; |
| 43 | +import org.hibernate.mapping.GeneratorSettings; |
37 | 44 | import org.hibernate.mapping.KeyValue; |
38 | 45 | import org.hibernate.mapping.PersistentClass; |
| 46 | +import org.hibernate.mapping.Property; |
| 47 | +import org.hibernate.mapping.RootClass; |
39 | 48 | import org.hibernate.mapping.SimpleValue; |
40 | 49 | import org.hibernate.mapping.Value; |
41 | 50 | import org.hibernate.models.spi.AnnotationTarget; |
42 | 51 | import org.hibernate.models.spi.MemberDetails; |
| 52 | +import org.hibernate.property.access.spi.Setter; |
43 | 53 | import org.hibernate.resource.beans.container.spi.BeanContainer; |
44 | 54 | import org.hibernate.resource.beans.internal.Helper; |
| 55 | +import org.hibernate.type.ComponentType; |
45 | 56 |
|
46 | 57 | import java.lang.annotation.Annotation; |
47 | 58 | import java.lang.reflect.InvocationTargetException; |
48 | 59 | import java.lang.reflect.Member; |
| 60 | +import java.util.ArrayList; |
49 | 61 | import java.util.HashMap; |
| 62 | +import java.util.List; |
50 | 63 | import java.util.Locale; |
51 | 64 | import java.util.Map; |
52 | 65 | import java.util.Properties; |
@@ -903,4 +916,161 @@ public static void applyIfNotEmpty(String name, String value, BiConsumer<String, |
903 | 916 | consumer.accept( name, value ); |
904 | 917 | } |
905 | 918 | } |
| 919 | + |
| 920 | + private static Setter injector(Property property, Class<?> attributeDeclarer) { |
| 921 | + return property.getPropertyAccessStrategy( attributeDeclarer ) |
| 922 | + .buildPropertyAccess( attributeDeclarer, property.getName(), true ) |
| 923 | + .getSetter(); |
| 924 | + } |
| 925 | + |
| 926 | + /** |
| 927 | + * Return the class that declares the composite pk attributes, |
| 928 | + * which might be an {@code @IdClass}, an {@code @EmbeddedId}, |
| 929 | + * of the entity class itself. |
| 930 | + */ |
| 931 | + private static Class<?> getAttributeDeclarer(RootClass rootClass, Component component) { |
| 932 | + // See the javadoc discussion on CompositeNestedGeneratedValueGenerator |
| 933 | + // for the various scenarios we need to account for here |
| 934 | + if ( rootClass.getIdentifierMapper() != null ) { |
| 935 | + // we have the @IdClass / <composite-id mapped="true"/> case |
| 936 | + return resolveComponentClass( component ); |
| 937 | + } |
| 938 | + else if ( rootClass.getIdentifierProperty() != null ) { |
| 939 | + // we have the "@EmbeddedId" / <composite-id name="idName"/> case |
| 940 | + return resolveComponentClass( component ); |
| 941 | + } |
| 942 | + else { |
| 943 | + // we have the "straight up" embedded (again the Hibernate term) |
| 944 | + // component identifier: the entity class itself is the id class |
| 945 | + return rootClass.getMappedClass(); |
| 946 | + } |
| 947 | + } |
| 948 | + |
| 949 | + private static Class<?> resolveComponentClass(Component component) { |
| 950 | + try { |
| 951 | + return component.getComponentClass(); |
| 952 | + } |
| 953 | + catch ( Exception e ) { |
| 954 | + return null; |
| 955 | + } |
| 956 | + } |
| 957 | + |
| 958 | + public static Generator buildIdentifierGenerator( |
| 959 | + Component component, |
| 960 | + Dialect dialect, |
| 961 | + RootClass rootClass, |
| 962 | + GeneratorSettings defaults) { |
| 963 | + final var properties = component.getProperties(); |
| 964 | + final List<Generator> generators = new ArrayList<>( properties.size() ); |
| 965 | + final int columnSpan = component.getColumnSpan(); |
| 966 | + String[] columnValues = null; |
| 967 | + boolean[] columnInclusions = null; |
| 968 | + boolean[] generatedOnExecutionColumns = null; |
| 969 | + int columnIndex = 0; |
| 970 | + final List<GenerationPlan> generationPlans = new ArrayList<>(); |
| 971 | + for ( int i = 0; i < properties.size(); i++ ) { |
| 972 | + final var property = properties.get( i ); |
| 973 | + final var propertyGenerator = |
| 974 | + propertyGenerator( component, dialect, rootClass, defaults, property, generationPlans, i ); |
| 975 | + generators.add( propertyGenerator ); |
| 976 | + |
| 977 | + final int span = property.getColumnSpan(); |
| 978 | + if ( propertyGenerator instanceof OnExecutionGenerator onExecutionGenerator |
| 979 | + && propertyGenerator.generatedOnExecution() ) { |
| 980 | + if ( columnValues == null ) { |
| 981 | + columnValues = new String[columnSpan]; |
| 982 | + columnInclusions = new boolean[columnSpan]; |
| 983 | + generatedOnExecutionColumns = new boolean[columnSpan]; |
| 984 | + for ( int j = 0; j < columnSpan; j++ ) { |
| 985 | + columnValues[j] = "?"; |
| 986 | + columnInclusions[j] = true; |
| 987 | + } |
| 988 | + } |
| 989 | + for ( int j = 0; j < span; j++ ) { |
| 990 | + generatedOnExecutionColumns[columnIndex + j] = true; |
| 991 | + } |
| 992 | + if ( onExecutionGenerator.generatesOnInsert() ) { |
| 993 | + if ( !onExecutionGenerator.referenceColumnsInSql( dialect, EventType.INSERT ) ) { |
| 994 | + for ( int j = 0; j < span; j++ ) { |
| 995 | + columnInclusions[columnIndex + j] = false; |
| 996 | + } |
| 997 | + } |
| 998 | + else if ( onExecutionGenerator.writePropertyValue( EventType.INSERT ) ) { |
| 999 | + // leave default parameter markers in place |
| 1000 | + } |
| 1001 | + else { |
| 1002 | + final String[] referencedColumnValues = |
| 1003 | + onExecutionGenerator.getReferencedColumnValues( dialect, EventType.INSERT ); |
| 1004 | + if ( referencedColumnValues == null ) { |
| 1005 | + throw new IdentifierGenerationException( |
| 1006 | + "Generated column values were not provided for composite id property: " |
| 1007 | + + property.getName() |
| 1008 | + ); |
| 1009 | + } |
| 1010 | + if ( referencedColumnValues.length != span ) { |
| 1011 | + throw new IdentifierGenerationException( |
| 1012 | + "Mismatch between generated column values and column count for composite id property: " |
| 1013 | + + property.getName() |
| 1014 | + ); |
| 1015 | + } |
| 1016 | + System.arraycopy( referencedColumnValues, 0, columnValues, columnIndex, span ); |
| 1017 | + } |
| 1018 | + } |
| 1019 | + else if ( !onExecutionGenerator.allowMutation() ) { |
| 1020 | + for ( int j = 0; j < span; j++ ) { |
| 1021 | + columnInclusions[columnIndex + j] = false; |
| 1022 | + } |
| 1023 | + } |
| 1024 | + } |
| 1025 | + columnIndex += span; |
| 1026 | + } |
| 1027 | + |
| 1028 | + final var generator = |
| 1029 | + new CompositeNestedGeneratedValueGenerator( |
| 1030 | + new Component.StandardGenerationContextLocator( rootClass.getEntityName() ), |
| 1031 | + (ComponentType) component.getType(), |
| 1032 | + generators, |
| 1033 | + columnValues, |
| 1034 | + columnInclusions, |
| 1035 | + generatedOnExecutionColumns |
| 1036 | + ); |
| 1037 | + for ( var plan : generationPlans ) { |
| 1038 | + generator.addGeneratedValuePlan( plan ); |
| 1039 | + } |
| 1040 | + return generator; |
| 1041 | + } |
| 1042 | + |
| 1043 | + private static Generator propertyGenerator( |
| 1044 | + Component component, |
| 1045 | + Dialect dialect, |
| 1046 | + RootClass rootClass, |
| 1047 | + GeneratorSettings defaults, |
| 1048 | + Property property, |
| 1049 | + List<GenerationPlan> generationPlans, |
| 1050 | + int propertyIndex) { |
| 1051 | + final var value = property.getValue(); |
| 1052 | + if ( value instanceof SimpleValue simpleValue ) { |
| 1053 | + if ( !simpleValue.getCustomIdGeneratorCreator().isAssigned() ) { |
| 1054 | + // skip any 'assigned' generators, they would have been |
| 1055 | + // handled by the StandardGenerationContextLocator |
| 1056 | + final var propertyGenerator = simpleValue.createGenerator( dialect, rootClass, property, defaults ); |
| 1057 | + if ( propertyGenerator instanceof BeforeExecutionGenerator beforeExecutionGenerator ) { |
| 1058 | + generationPlans.add( new Component.ValueGenerationPlan( |
| 1059 | + beforeExecutionGenerator, |
| 1060 | + component.getType().isMutable() |
| 1061 | + ? injector( property, getAttributeDeclarer( rootClass, component ) ) |
| 1062 | + : null, |
| 1063 | + propertyIndex |
| 1064 | + ) ); |
| 1065 | + } |
| 1066 | + return propertyGenerator; |
| 1067 | + } |
| 1068 | + else { |
| 1069 | + return null; |
| 1070 | + } |
| 1071 | + } |
| 1072 | + else { |
| 1073 | + return null; |
| 1074 | + } |
| 1075 | + } |
906 | 1076 | } |
0 commit comments