Merge remote-tracking branch 'upstream/main' into wip/6.0
This commit is contained in:
commit
e466c52002
|
@ -27,7 +27,7 @@ ext {
|
||||||
weldVersion = '3.1.5.Final'
|
weldVersion = '3.1.5.Final'
|
||||||
jakartaWeldVersion = '4.0.1.SP1'
|
jakartaWeldVersion = '4.0.1.SP1'
|
||||||
|
|
||||||
byteBuddyVersion = '1.11.12'
|
byteBuddyVersion = '1.11.16'
|
||||||
|
|
||||||
agroalVersion = '1.9'
|
agroalVersion = '1.9'
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.FIELD;
|
||||||
|
import static java.lang.annotation.ElementType.METHOD;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SQL column comment which can be defined at property level.
|
||||||
|
*
|
||||||
|
* @author Yanming Zhou
|
||||||
|
*/
|
||||||
|
@Target({METHOD, FIELD})
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
public @interface Comment {
|
||||||
|
/**
|
||||||
|
* The comment string.
|
||||||
|
*/
|
||||||
|
String value();
|
||||||
|
}
|
|
@ -76,15 +76,13 @@ final class FieldAccessEnhancer implements AsmVisitorWrapper.ForDeclaredMethods.
|
||||||
&& !field.hasAnnotation( Id.class )
|
&& !field.hasAnnotation( Id.class )
|
||||||
&& !field.getName().equals( "this$0" ) ) {
|
&& !field.getName().equals( "this$0" ) ) {
|
||||||
|
|
||||||
if ( log.isDebugEnabled() ) {
|
|
||||||
log.debugf(
|
log.debugf(
|
||||||
"Extended enhancement: Transforming access to field [%s.%s] from method [%s#%s]",
|
"Extended enhancement: Transforming access to field [%s#%s] from method [%s#%s()]",
|
||||||
field.getType().asErasure(),
|
declaredOwnerType.getName(),
|
||||||
field.getName(),
|
field.getName(),
|
||||||
field.getName(),
|
instrumentedType.getName(),
|
||||||
name
|
instrumentedMethod.getName()
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
switch ( opcode ) {
|
switch ( opcode ) {
|
||||||
case Opcodes.GETFIELD:
|
case Opcodes.GETFIELD:
|
||||||
|
@ -97,6 +95,11 @@ final class FieldAccessEnhancer implements AsmVisitorWrapper.ForDeclaredMethods.
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
case Opcodes.PUTFIELD:
|
case Opcodes.PUTFIELD:
|
||||||
|
if ( field.getFieldDescription().isFinal() ) {
|
||||||
|
// Final fields will only be written to from the constructor,
|
||||||
|
// so there's no point trying to replace final field writes with a method call.
|
||||||
|
break;
|
||||||
|
}
|
||||||
methodVisitor.visitMethodInsn(
|
methodVisitor.visitMethodInsn(
|
||||||
Opcodes.INVOKEVIRTUAL,
|
Opcodes.INVOKEVIRTUAL,
|
||||||
owner,
|
owner,
|
||||||
|
@ -109,10 +112,8 @@ final class FieldAccessEnhancer implements AsmVisitorWrapper.ForDeclaredMethods.
|
||||||
throw new EnhancementException( "Unexpected opcode: " + opcode );
|
throw new EnhancementException( "Unexpected opcode: " + opcode );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
super.visitFieldInsn( opcode, owner, name, desc );
|
super.visitFieldInsn( opcode, owner, name, desc );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.bytecode.enhance.internal.bytebuddy;
|
package org.hibernate.bytecode.enhance.internal.bytebuddy;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.anyOf;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
|
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||||
|
|
||||||
|
@ -15,10 +16,8 @@ import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import javax.persistence.Embedded;
|
import javax.persistence.Embedded;
|
||||||
|
|
||||||
import net.bytebuddy.utility.OpenedClassReader;
|
|
||||||
import org.hibernate.bytecode.enhance.internal.bytebuddy.EnhancerImpl.AnnotatedFieldDescription;
|
import org.hibernate.bytecode.enhance.internal.bytebuddy.EnhancerImpl.AnnotatedFieldDescription;
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||||
import org.hibernate.engine.spi.CompositeOwner;
|
import org.hibernate.engine.spi.CompositeOwner;
|
||||||
|
@ -27,8 +26,10 @@ import org.hibernate.internal.CoreMessageLogger;
|
||||||
|
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.asm.AsmVisitorWrapper;
|
import net.bytebuddy.asm.AsmVisitorWrapper;
|
||||||
|
import net.bytebuddy.asm.ModifierAdjustment;
|
||||||
import net.bytebuddy.description.field.FieldDescription;
|
import net.bytebuddy.description.field.FieldDescription;
|
||||||
import net.bytebuddy.description.method.MethodDescription;
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.modifier.ModifierContributor;
|
||||||
import net.bytebuddy.description.modifier.Visibility;
|
import net.bytebuddy.description.modifier.Visibility;
|
||||||
import net.bytebuddy.description.type.TypeDefinition;
|
import net.bytebuddy.description.type.TypeDefinition;
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
@ -42,12 +43,29 @@ import net.bytebuddy.jar.asm.Opcodes;
|
||||||
import net.bytebuddy.jar.asm.Type;
|
import net.bytebuddy.jar.asm.Type;
|
||||||
import net.bytebuddy.matcher.ElementMatcher.Junction;
|
import net.bytebuddy.matcher.ElementMatcher.Junction;
|
||||||
import net.bytebuddy.pool.TypePool;
|
import net.bytebuddy.pool.TypePool;
|
||||||
|
import net.bytebuddy.utility.OpenedClassReader;
|
||||||
|
|
||||||
final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper {
|
final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper {
|
||||||
|
|
||||||
private static final CoreMessageLogger log = CoreLogging.messageLogger( PersistentAttributeTransformer.class );
|
private static final CoreMessageLogger log = CoreLogging.messageLogger( PersistentAttributeTransformer.class );
|
||||||
|
|
||||||
private static final Junction<MethodDescription> NOT_HIBERNATE_GENERATED = not( nameStartsWith( "$$_hibernate_" ) );
|
private static final Junction<MethodDescription> NOT_HIBERNATE_GENERATED = not( nameStartsWith( "$$_hibernate_" ) );
|
||||||
|
private static final ModifierContributor.ForField REMOVE_FINAL_MODIFIER = new ModifierContributor.ForField() {
|
||||||
|
@Override
|
||||||
|
public int getMask() {
|
||||||
|
return EMPTY_MASK; // Do not add any modifier
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRange() {
|
||||||
|
return Opcodes.ACC_FINAL; // Remove the "final" modifier
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDefault() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private final TypeDescription managedCtClass;
|
private final TypeDescription managedCtClass;
|
||||||
|
|
||||||
|
@ -144,7 +162,8 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla
|
||||||
return new MethodVisitor( OpenedClassReader.ASM_API, methodVisitor ) {
|
return new MethodVisitor( OpenedClassReader.ASM_API, methodVisitor ) {
|
||||||
@Override
|
@Override
|
||||||
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
|
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
|
||||||
if ( isEnhanced( owner, name, desc ) ) {
|
AnnotatedFieldDescription enhancedField = getEnhancedField( owner, name, desc );
|
||||||
|
if ( enhancedField != null ) {
|
||||||
switch ( opcode ) {
|
switch ( opcode ) {
|
||||||
case Opcodes.GETFIELD:
|
case Opcodes.GETFIELD:
|
||||||
methodVisitor.visitMethodInsn(
|
methodVisitor.visitMethodInsn(
|
||||||
|
@ -156,6 +175,11 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
case Opcodes.PUTFIELD:
|
case Opcodes.PUTFIELD:
|
||||||
|
if ( enhancedField.getFieldDescription().isFinal() ) {
|
||||||
|
// Final fields will only be written to from the constructor,
|
||||||
|
// so there's no point trying to replace final field writes with a method call.
|
||||||
|
break;
|
||||||
|
}
|
||||||
methodVisitor.visitMethodInsn(
|
methodVisitor.visitMethodInsn(
|
||||||
Opcodes.INVOKEVIRTUAL,
|
Opcodes.INVOKEVIRTUAL,
|
||||||
owner,
|
owner,
|
||||||
|
@ -171,21 +195,31 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isEnhanced(String owner, String name, String desc) {
|
private AnnotatedFieldDescription getEnhancedField(String owner, String name, String desc) {
|
||||||
for ( AnnotatedFieldDescription enhancedField : enhancedFields ) {
|
for ( AnnotatedFieldDescription enhancedField : enhancedFields ) {
|
||||||
if ( enhancedField.getName().equals( name )
|
if ( enhancedField.getName().equals( name )
|
||||||
&& enhancedField.getDescriptor().equals( desc )
|
&& enhancedField.getDescriptor().equals( desc )
|
||||||
&& enhancedField.getDeclaringType().asErasure().getInternalName().equals( owner ) ) {
|
&& enhancedField.getDeclaringType().asErasure().getInternalName().equals( owner ) ) {
|
||||||
return true;
|
return enhancedField;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicType.Builder<?> applyTo(DynamicType.Builder<?> builder) {
|
DynamicType.Builder<?> applyTo(DynamicType.Builder<?> builder) {
|
||||||
boolean compositeOwner = false;
|
boolean compositeOwner = false;
|
||||||
|
|
||||||
builder = builder.visit( new AsmVisitorWrapper.ForDeclaredMethods().invokable( NOT_HIBERNATE_GENERATED, this ) );
|
builder = builder.visit( new AsmVisitorWrapper.ForDeclaredMethods().invokable( NOT_HIBERNATE_GENERATED, this ) );
|
||||||
|
// Remove the final modifier from all enhanced fields, because:
|
||||||
|
// 1. We sometimes need to write to final fields when they are lazy.
|
||||||
|
// 2. Those fields are already written to by Hibernate ORM through reflection anyway.
|
||||||
|
// 3. The compiler already makes sure that final fields are not written to from the user's source code.
|
||||||
|
List<FieldDescription.InDefinedShape> enhancedFieldsAsDefined = new ArrayList<>();
|
||||||
|
for ( AnnotatedFieldDescription f : enhancedFields ) {
|
||||||
|
enhancedFieldsAsDefined.add( f.asDefined() );
|
||||||
|
}
|
||||||
|
builder = builder.visit( new ModifierAdjustment().withFieldModifiers( anyOf( enhancedFieldsAsDefined ),
|
||||||
|
REMOVE_FINAL_MODIFIER ) );
|
||||||
for ( AnnotatedFieldDescription enhancedField : enhancedFields ) {
|
for ( AnnotatedFieldDescription enhancedField : enhancedFields ) {
|
||||||
builder = builder
|
builder = builder
|
||||||
.defineMethod(
|
.defineMethod(
|
||||||
|
@ -193,8 +227,11 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla
|
||||||
enhancedField.getType().asErasure(),
|
enhancedField.getType().asErasure(),
|
||||||
Visibility.PUBLIC
|
Visibility.PUBLIC
|
||||||
)
|
)
|
||||||
.intercept( fieldReader( enhancedField )
|
.intercept( fieldReader( enhancedField ) );
|
||||||
)
|
// Final fields will only be written to from the constructor,
|
||||||
|
// so there's no point trying to replace final field writes with a method call.
|
||||||
|
if ( !enhancedField.getFieldDescription().isFinal() ) {
|
||||||
|
builder = builder
|
||||||
.defineMethod(
|
.defineMethod(
|
||||||
EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + enhancedField.getName(),
|
EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + enhancedField.getName(),
|
||||||
TypeDescription.VOID,
|
TypeDescription.VOID,
|
||||||
|
@ -202,6 +239,7 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla
|
||||||
)
|
)
|
||||||
.withParameters( enhancedField.getType().asErasure() )
|
.withParameters( enhancedField.getType().asErasure() )
|
||||||
.intercept( fieldWriter( enhancedField ) );
|
.intercept( fieldWriter( enhancedField ) );
|
||||||
|
}
|
||||||
|
|
||||||
if ( !compositeOwner
|
if ( !compositeOwner
|
||||||
&& !enhancementContext.isMappedSuperclassClass( managedCtClass )
|
&& !enhancementContext.isMappedSuperclassClass( managedCtClass )
|
||||||
|
|
|
@ -83,6 +83,7 @@ import org.hibernate.annotations.CascadeType;
|
||||||
import org.hibernate.annotations.Check;
|
import org.hibernate.annotations.Check;
|
||||||
import org.hibernate.annotations.CollectionId;
|
import org.hibernate.annotations.CollectionId;
|
||||||
import org.hibernate.annotations.Columns;
|
import org.hibernate.annotations.Columns;
|
||||||
|
import org.hibernate.annotations.Comment;
|
||||||
import org.hibernate.annotations.DiscriminatorFormula;
|
import org.hibernate.annotations.DiscriminatorFormula;
|
||||||
import org.hibernate.annotations.DiscriminatorOptions;
|
import org.hibernate.annotations.DiscriminatorOptions;
|
||||||
import org.hibernate.annotations.Fetch;
|
import org.hibernate.annotations.Fetch;
|
||||||
|
@ -2070,6 +2071,7 @@ public final class AnnotationBinder {
|
||||||
elementColumns = Ejb3Column.buildColumnFromAnnotation(
|
elementColumns = Ejb3Column.buildColumnFromAnnotation(
|
||||||
new Column[] { ann },
|
new Column[] { ann },
|
||||||
formulaAnn,
|
formulaAnn,
|
||||||
|
property.getAnnotation( Comment.class ),
|
||||||
nullability,
|
nullability,
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
virtualProperty,
|
virtualProperty,
|
||||||
|
@ -2082,6 +2084,7 @@ public final class AnnotationBinder {
|
||||||
elementColumns = Ejb3Column.buildColumnFromAnnotation(
|
elementColumns = Ejb3Column.buildColumnFromAnnotation(
|
||||||
anns.columns(),
|
anns.columns(),
|
||||||
null,
|
null,
|
||||||
|
property.getAnnotation( Comment.class ),
|
||||||
nullability,
|
nullability,
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
virtualProperty,
|
virtualProperty,
|
||||||
|
@ -2093,6 +2096,7 @@ public final class AnnotationBinder {
|
||||||
elementColumns = Ejb3Column.buildColumnFromAnnotation(
|
elementColumns = Ejb3Column.buildColumnFromAnnotation(
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
property.getAnnotation( Comment.class ),
|
||||||
nullability,
|
nullability,
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
virtualProperty,
|
virtualProperty,
|
||||||
|
@ -2122,6 +2126,7 @@ public final class AnnotationBinder {
|
||||||
Ejb3Column[] mapColumns = Ejb3Column.buildColumnFromAnnotation(
|
Ejb3Column[] mapColumns = Ejb3Column.buildColumnFromAnnotation(
|
||||||
keyColumns,
|
keyColumns,
|
||||||
null,
|
null,
|
||||||
|
property.getAnnotation( Comment.class ),
|
||||||
Nullability.FORCED_NOT_NULL,
|
Nullability.FORCED_NOT_NULL,
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
isJPA2 ? inferredData : mapKeyVirtualProperty,
|
isJPA2 ? inferredData : mapKeyVirtualProperty,
|
||||||
|
@ -2170,6 +2175,7 @@ public final class AnnotationBinder {
|
||||||
PropertyData mapKeyVirtualProperty = new WrappedInferredData( inferredData, "mapkey" );
|
PropertyData mapKeyVirtualProperty = new WrappedInferredData( inferredData, "mapkey" );
|
||||||
Ejb3JoinColumn[] mapJoinColumns = Ejb3JoinColumn.buildJoinColumnsWithDefaultColumnSuffix(
|
Ejb3JoinColumn[] mapJoinColumns = Ejb3JoinColumn.buildJoinColumnsWithDefaultColumnSuffix(
|
||||||
joinKeyColumns,
|
joinKeyColumns,
|
||||||
|
property.getAnnotation( Comment.class ),
|
||||||
null,
|
null,
|
||||||
entityBinder.getSecondaryTables(),
|
entityBinder.getSecondaryTables(),
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
|
|
|
@ -1012,6 +1012,7 @@ public class BinderHelper {
|
||||||
Ejb3Column[] metaColumns = Ejb3Column.buildColumnFromAnnotation(
|
Ejb3Column[] metaColumns = Ejb3Column.buildColumnFromAnnotation(
|
||||||
new javax.persistence.Column[] { metaColumn },
|
new javax.persistence.Column[] { metaColumn },
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
nullability,
|
nullability,
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
inferredData,
|
inferredData,
|
||||||
|
|
|
@ -17,6 +17,7 @@ import javax.persistence.OneToOne;
|
||||||
|
|
||||||
import org.hibernate.AnnotationException;
|
import org.hibernate.AnnotationException;
|
||||||
import org.hibernate.annotations.Columns;
|
import org.hibernate.annotations.Columns;
|
||||||
|
import org.hibernate.annotations.Comment;
|
||||||
import org.hibernate.annotations.Formula;
|
import org.hibernate.annotations.Formula;
|
||||||
import org.hibernate.annotations.JoinColumnOrFormula;
|
import org.hibernate.annotations.JoinColumnOrFormula;
|
||||||
import org.hibernate.annotations.JoinColumnsOrFormulas;
|
import org.hibernate.annotations.JoinColumnsOrFormulas;
|
||||||
|
@ -78,6 +79,7 @@ class ColumnsBuilder {
|
||||||
columns = Ejb3Column.buildColumnFromAnnotation(
|
columns = Ejb3Column.buildColumnFromAnnotation(
|
||||||
new Column[] { ann },
|
new Column[] { ann },
|
||||||
formulaAnn,
|
formulaAnn,
|
||||||
|
property.getAnnotation( Comment.class ),
|
||||||
nullability,
|
nullability,
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
inferredData,
|
inferredData,
|
||||||
|
@ -90,6 +92,7 @@ class ColumnsBuilder {
|
||||||
columns = Ejb3Column.buildColumnFromAnnotation(
|
columns = Ejb3Column.buildColumnFromAnnotation(
|
||||||
anns.columns(),
|
anns.columns(),
|
||||||
null,
|
null,
|
||||||
|
property.getAnnotation( Comment.class ),
|
||||||
nullability,
|
nullability,
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
inferredData,
|
inferredData,
|
||||||
|
@ -115,6 +118,7 @@ class ColumnsBuilder {
|
||||||
"";
|
"";
|
||||||
joinColumns = Ejb3JoinColumn.buildJoinColumns(
|
joinColumns = Ejb3JoinColumn.buildJoinColumns(
|
||||||
null,
|
null,
|
||||||
|
property.getAnnotation( Comment.class ),
|
||||||
mappedBy,
|
mappedBy,
|
||||||
entityBinder.getSecondaryTables(),
|
entityBinder.getSecondaryTables(),
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
|
@ -131,6 +135,7 @@ class ColumnsBuilder {
|
||||||
columns = Ejb3Column.buildColumnFromAnnotation(
|
columns = Ejb3Column.buildColumnFromAnnotation(
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
property.getAnnotation( Comment.class ),
|
||||||
nullability,
|
nullability,
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
inferredData,
|
inferredData,
|
||||||
|
@ -154,6 +159,7 @@ class ColumnsBuilder {
|
||||||
if ( joinTableAnn != null ) {
|
if ( joinTableAnn != null ) {
|
||||||
joinColumns = Ejb3JoinColumn.buildJoinColumns(
|
joinColumns = Ejb3JoinColumn.buildJoinColumns(
|
||||||
joinTableAnn.inverseJoinColumns(),
|
joinTableAnn.inverseJoinColumns(),
|
||||||
|
property.getAnnotation( Comment.class ),
|
||||||
null,
|
null,
|
||||||
entityBinder.getSecondaryTables(),
|
entityBinder.getSecondaryTables(),
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
|
@ -174,6 +180,7 @@ class ColumnsBuilder {
|
||||||
: null;
|
: null;
|
||||||
joinColumns = Ejb3JoinColumn.buildJoinColumns(
|
joinColumns = Ejb3JoinColumn.buildJoinColumns(
|
||||||
null,
|
null,
|
||||||
|
property.getAnnotation( Comment.class ),
|
||||||
mappedBy,
|
mappedBy,
|
||||||
entityBinder.getSecondaryTables(),
|
entityBinder.getSecondaryTables(),
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
|
@ -203,6 +210,7 @@ class ColumnsBuilder {
|
||||||
if ( joinColumnAnnotations != null ) {
|
if ( joinColumnAnnotations != null ) {
|
||||||
return Ejb3JoinColumn.buildJoinColumns(
|
return Ejb3JoinColumn.buildJoinColumns(
|
||||||
joinColumnAnnotations,
|
joinColumnAnnotations,
|
||||||
|
property.getAnnotation( Comment.class ),
|
||||||
null,
|
null,
|
||||||
entityBinder.getSecondaryTables(),
|
entityBinder.getSecondaryTables(),
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
|
|
|
@ -14,6 +14,7 @@ import org.hibernate.AssertionFailure;
|
||||||
import org.hibernate.annotations.ColumnDefault;
|
import org.hibernate.annotations.ColumnDefault;
|
||||||
import org.hibernate.annotations.ColumnTransformer;
|
import org.hibernate.annotations.ColumnTransformer;
|
||||||
import org.hibernate.annotations.ColumnTransformers;
|
import org.hibernate.annotations.ColumnTransformers;
|
||||||
|
import org.hibernate.annotations.Comment;
|
||||||
import org.hibernate.annotations.Index;
|
import org.hibernate.annotations.Index;
|
||||||
import org.hibernate.annotations.common.reflection.XProperty;
|
import org.hibernate.annotations.common.reflection.XProperty;
|
||||||
import org.hibernate.boot.model.naming.Identifier;
|
import org.hibernate.boot.model.naming.Identifier;
|
||||||
|
@ -70,6 +71,8 @@ public class Ejb3Column {
|
||||||
|
|
||||||
private String defaultValue;
|
private String defaultValue;
|
||||||
|
|
||||||
|
private String comment;
|
||||||
|
|
||||||
public void setTable(Table table) {
|
public void setTable(Table table) {
|
||||||
this.table = table;
|
this.table = table;
|
||||||
}
|
}
|
||||||
|
@ -193,6 +196,14 @@ public class Ejb3Column {
|
||||||
this.defaultValue = defaultValue;
|
this.defaultValue = defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getComment() {
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setComment(String comment) {
|
||||||
|
this.comment = comment;
|
||||||
|
}
|
||||||
|
|
||||||
public Ejb3Column() {
|
public Ejb3Column() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,6 +220,9 @@ public class Ejb3Column {
|
||||||
if ( defaultValue != null ) {
|
if ( defaultValue != null ) {
|
||||||
mappingColumn.setDefaultValue( defaultValue );
|
mappingColumn.setDefaultValue( defaultValue );
|
||||||
}
|
}
|
||||||
|
if ( StringHelper.isNotEmpty( comment ) ) {
|
||||||
|
mappingColumn.setComment (comment );
|
||||||
|
}
|
||||||
if ( LOG.isDebugEnabled() ) {
|
if ( LOG.isDebugEnabled() ) {
|
||||||
LOG.debugf( "Binding column: %s", toString() );
|
LOG.debugf( "Binding column: %s", toString() );
|
||||||
}
|
}
|
||||||
|
@ -471,6 +485,7 @@ public class Ejb3Column {
|
||||||
public static Ejb3Column[] buildColumnFromAnnotation(
|
public static Ejb3Column[] buildColumnFromAnnotation(
|
||||||
javax.persistence.Column[] anns,
|
javax.persistence.Column[] anns,
|
||||||
org.hibernate.annotations.Formula formulaAnn,
|
org.hibernate.annotations.Formula formulaAnn,
|
||||||
|
Comment commentAnn,
|
||||||
Nullability nullability,
|
Nullability nullability,
|
||||||
PropertyHolder propertyHolder,
|
PropertyHolder propertyHolder,
|
||||||
PropertyData inferredData,
|
PropertyData inferredData,
|
||||||
|
@ -479,6 +494,7 @@ public class Ejb3Column {
|
||||||
return buildColumnFromAnnotation(
|
return buildColumnFromAnnotation(
|
||||||
anns,
|
anns,
|
||||||
formulaAnn,
|
formulaAnn,
|
||||||
|
commentAnn,
|
||||||
nullability,
|
nullability,
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
inferredData,
|
inferredData,
|
||||||
|
@ -490,6 +506,7 @@ public class Ejb3Column {
|
||||||
public static Ejb3Column[] buildColumnFromAnnotation(
|
public static Ejb3Column[] buildColumnFromAnnotation(
|
||||||
javax.persistence.Column[] anns,
|
javax.persistence.Column[] anns,
|
||||||
org.hibernate.annotations.Formula formulaAnn,
|
org.hibernate.annotations.Formula formulaAnn,
|
||||||
|
Comment commentAnn,
|
||||||
Nullability nullability,
|
Nullability nullability,
|
||||||
PropertyHolder propertyHolder,
|
PropertyHolder propertyHolder,
|
||||||
PropertyData inferredData,
|
PropertyData inferredData,
|
||||||
|
@ -525,6 +542,7 @@ public class Ejb3Column {
|
||||||
suffixForDefaultColumnName,
|
suffixForDefaultColumnName,
|
||||||
secondaryTables,
|
secondaryTables,
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
|
commentAnn,
|
||||||
nullability,
|
nullability,
|
||||||
context
|
context
|
||||||
);
|
);
|
||||||
|
@ -599,6 +617,9 @@ public class Ejb3Column {
|
||||||
column.setNullable(
|
column.setNullable(
|
||||||
col.nullable()
|
col.nullable()
|
||||||
); //TODO force to not null if available? This is a (bad) user choice.
|
); //TODO force to not null if available? This is a (bad) user choice.
|
||||||
|
if ( commentAnn != null ) {
|
||||||
|
column.setComment( commentAnn.value() );
|
||||||
|
}
|
||||||
column.setUnique( col.unique() );
|
column.setUnique( col.unique() );
|
||||||
column.setInsertable( col.insertable() );
|
column.setInsertable( col.insertable() );
|
||||||
column.setUpdatable( col.updatable() );
|
column.setUpdatable( col.updatable() );
|
||||||
|
@ -668,12 +689,17 @@ public class Ejb3Column {
|
||||||
String suffixForDefaultColumnName,
|
String suffixForDefaultColumnName,
|
||||||
Map<String, Join> secondaryTables,
|
Map<String, Join> secondaryTables,
|
||||||
PropertyHolder propertyHolder,
|
PropertyHolder propertyHolder,
|
||||||
|
Comment comment,
|
||||||
Nullability nullability,
|
Nullability nullability,
|
||||||
MetadataBuildingContext context) {
|
MetadataBuildingContext context) {
|
||||||
Ejb3Column column = new Ejb3Column();
|
Ejb3Column column = new Ejb3Column();
|
||||||
Ejb3Column[] columns = new Ejb3Column[1];
|
Ejb3Column[] columns = new Ejb3Column[1];
|
||||||
columns[0] = column;
|
columns[0] = column;
|
||||||
|
|
||||||
|
if ( comment != null ) {
|
||||||
|
column.setComment( comment.value() );
|
||||||
|
}
|
||||||
|
|
||||||
//not following the spec but more clean
|
//not following the spec but more clean
|
||||||
if ( nullability != Nullability.FORCED_NULL
|
if ( nullability != Nullability.FORCED_NULL
|
||||||
&& inferredData.getClassOrElement().isPrimitive()
|
&& inferredData.getClassOrElement().isPrimitive()
|
||||||
|
|
|
@ -17,6 +17,7 @@ import javax.persistence.PrimaryKeyJoinColumn;
|
||||||
import org.hibernate.AnnotationException;
|
import org.hibernate.AnnotationException;
|
||||||
import org.hibernate.AssertionFailure;
|
import org.hibernate.AssertionFailure;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
|
import org.hibernate.annotations.Comment;
|
||||||
import org.hibernate.annotations.JoinColumnOrFormula;
|
import org.hibernate.annotations.JoinColumnOrFormula;
|
||||||
import org.hibernate.annotations.JoinFormula;
|
import org.hibernate.annotations.JoinFormula;
|
||||||
import org.hibernate.annotations.common.reflection.XClass;
|
import org.hibernate.annotations.common.reflection.XClass;
|
||||||
|
@ -97,6 +98,7 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
||||||
private Ejb3JoinColumn(
|
private Ejb3JoinColumn(
|
||||||
String sqlType,
|
String sqlType,
|
||||||
String name,
|
String name,
|
||||||
|
String comment,
|
||||||
boolean nullable,
|
boolean nullable,
|
||||||
boolean unique,
|
boolean unique,
|
||||||
boolean insertable,
|
boolean insertable,
|
||||||
|
@ -113,6 +115,7 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
||||||
setImplicit( isImplicit );
|
setImplicit( isImplicit );
|
||||||
setSqlType( sqlType );
|
setSqlType( sqlType );
|
||||||
setLogicalColumnName( name );
|
setLogicalColumnName( name );
|
||||||
|
setComment( comment );
|
||||||
setNullable( nullable );
|
setNullable( nullable );
|
||||||
setUnique( unique );
|
setUnique( unique );
|
||||||
setInsertable( insertable );
|
setInsertable( insertable );
|
||||||
|
@ -149,7 +152,7 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
joinColumns[i] = buildJoinColumns(
|
joinColumns[i] = buildJoinColumns(
|
||||||
new JoinColumn[] { join.column() }, mappedBy, joins, propertyHolder, propertyName, buildingContext
|
new JoinColumn[] { join.column() }, null, mappedBy, joins, propertyHolder, propertyName, buildingContext
|
||||||
)[0];
|
)[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,18 +183,20 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
||||||
|
|
||||||
public static Ejb3JoinColumn[] buildJoinColumns(
|
public static Ejb3JoinColumn[] buildJoinColumns(
|
||||||
JoinColumn[] anns,
|
JoinColumn[] anns,
|
||||||
|
Comment comment,
|
||||||
String mappedBy,
|
String mappedBy,
|
||||||
Map<String, Join> joins,
|
Map<String, Join> joins,
|
||||||
PropertyHolder propertyHolder,
|
PropertyHolder propertyHolder,
|
||||||
String propertyName,
|
String propertyName,
|
||||||
MetadataBuildingContext buildingContext) {
|
MetadataBuildingContext buildingContext) {
|
||||||
return buildJoinColumnsWithDefaultColumnSuffix(
|
return buildJoinColumnsWithDefaultColumnSuffix(
|
||||||
anns, mappedBy, joins, propertyHolder, propertyName, "", buildingContext
|
anns, comment, mappedBy, joins, propertyHolder, propertyName, "", buildingContext
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Ejb3JoinColumn[] buildJoinColumnsWithDefaultColumnSuffix(
|
public static Ejb3JoinColumn[] buildJoinColumnsWithDefaultColumnSuffix(
|
||||||
JoinColumn[] anns,
|
JoinColumn[] anns,
|
||||||
|
Comment comment,
|
||||||
String mappedBy,
|
String mappedBy,
|
||||||
Map<String, Join> joins,
|
Map<String, Join> joins,
|
||||||
PropertyHolder propertyHolder,
|
PropertyHolder propertyHolder,
|
||||||
|
@ -206,6 +211,7 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
||||||
return new Ejb3JoinColumn[] {
|
return new Ejb3JoinColumn[] {
|
||||||
buildJoinColumn(
|
buildJoinColumn(
|
||||||
null,
|
null,
|
||||||
|
comment,
|
||||||
mappedBy,
|
mappedBy,
|
||||||
joins,
|
joins,
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
|
@ -221,6 +227,7 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
||||||
for (int index = 0; index < size; index++) {
|
for (int index = 0; index < size; index++) {
|
||||||
result[index] = buildJoinColumn(
|
result[index] = buildJoinColumn(
|
||||||
actualColumns[index],
|
actualColumns[index],
|
||||||
|
comment,
|
||||||
mappedBy,
|
mappedBy,
|
||||||
joins,
|
joins,
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
|
@ -238,6 +245,7 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
||||||
*/
|
*/
|
||||||
private static Ejb3JoinColumn buildJoinColumn(
|
private static Ejb3JoinColumn buildJoinColumn(
|
||||||
JoinColumn ann,
|
JoinColumn ann,
|
||||||
|
Comment comment,
|
||||||
String mappedBy, Map<String, Join> joins,
|
String mappedBy, Map<String, Join> joins,
|
||||||
PropertyHolder propertyHolder,
|
PropertyHolder propertyHolder,
|
||||||
String propertyName,
|
String propertyName,
|
||||||
|
@ -251,6 +259,7 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ejb3JoinColumn joinColumn = new Ejb3JoinColumn();
|
Ejb3JoinColumn joinColumn = new Ejb3JoinColumn();
|
||||||
|
joinColumn.setComment( comment != null ? comment.value() : null );
|
||||||
joinColumn.setBuildingContext( buildingContext );
|
joinColumn.setBuildingContext( buildingContext );
|
||||||
joinColumn.setJoinAnnotation( ann, null );
|
joinColumn.setJoinAnnotation( ann, null );
|
||||||
if ( StringHelper.isEmpty( joinColumn.getLogicalColumnName() )
|
if ( StringHelper.isEmpty( joinColumn.getLogicalColumnName() )
|
||||||
|
@ -375,6 +384,7 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
||||||
return new Ejb3JoinColumn(
|
return new Ejb3JoinColumn(
|
||||||
sqlType,
|
sqlType,
|
||||||
name,
|
name,
|
||||||
|
null,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
@ -394,6 +404,7 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
||||||
return new Ejb3JoinColumn(
|
return new Ejb3JoinColumn(
|
||||||
null,
|
null,
|
||||||
defaultName,
|
defaultName,
|
||||||
|
null,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
|
|
@ -80,6 +80,7 @@ public class IdBagBinder extends BagBinder {
|
||||||
final Ejb3Column[] idColumns = Ejb3Column.buildColumnFromAnnotation(
|
final Ejb3Column[] idColumns = Ejb3Column.buildColumnFromAnnotation(
|
||||||
collectionIdAnn.columns(),
|
collectionIdAnn.columns(),
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
Nullability.FORCED_NOT_NULL,
|
Nullability.FORCED_NOT_NULL,
|
||||||
propertyHolder,
|
propertyHolder,
|
||||||
propertyData,
|
propertyData,
|
||||||
|
|
|
@ -179,6 +179,14 @@ public class OneToOneType extends EntityType {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object assemble(Serializable oid, SharedSessionContractImplementor session, Object owner) throws HibernateException {
|
public Object assemble(Serializable oid, SharedSessionContractImplementor session, Object owner) throws HibernateException {
|
||||||
|
|
||||||
|
if ( oid == null ) {
|
||||||
|
if ( uniqueKeyPropertyName != null ) {
|
||||||
|
return resolve( session.getContextEntityIdentifier( owner ), session, owner );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
//the owner of the association is not the owner of the id
|
//the owner of the association is not the owner of the id
|
||||||
Object id = getIdentifierType( session ).assemble( oid, session, null );
|
Object id = getIdentifierType( session ).assemble( oid, session, null );
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.test.annotations.comment;
|
||||||
|
|
||||||
|
import static org.hamcrest.core.Is.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.Comment;
|
||||||
|
import org.hibernate.boot.Metadata;
|
||||||
|
import org.hibernate.boot.MetadataSources;
|
||||||
|
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||||
|
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||||
|
import org.hibernate.mapping.Column;
|
||||||
|
import org.hibernate.mapping.Table;
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Yanming Zhou
|
||||||
|
*/
|
||||||
|
public class CommentTest extends BaseUnitTestCase {
|
||||||
|
|
||||||
|
private static final String TABLE_NAME = "TestEntity";
|
||||||
|
private static final String TABLE_COMMENT = "I am table";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue(jiraKey = "HHH-4369")
|
||||||
|
public void testComments() {
|
||||||
|
StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build();
|
||||||
|
Metadata metadata = new MetadataSources(ssr).addAnnotatedClass(TestEntity.class).buildMetadata();
|
||||||
|
Table table = StreamSupport.stream(metadata.getDatabase().getNamespaces().spliterator(), false)
|
||||||
|
.flatMap(namespace -> namespace.getTables().stream()).filter(t -> t.getName().equals(TABLE_NAME))
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
assertThat(table.getComment(), is(TABLE_COMMENT));
|
||||||
|
Iterator<Column> it = table.getColumnIterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Column col = it.next();
|
||||||
|
assertThat(col.getComment(), is("I am " + col.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Person")
|
||||||
|
@javax.persistence.Table(name = TABLE_NAME)
|
||||||
|
@org.hibernate.annotations.Table(comment = TABLE_COMMENT, appliesTo = TABLE_NAME)
|
||||||
|
public static class TestEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
@Comment("I am id")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Comment("I am name")
|
||||||
|
@javax.persistence.Column(length = 50)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "other")
|
||||||
|
@Comment("I am other")
|
||||||
|
private TestEntity other;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,297 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.test.bytecode.enhancement.basic;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import javax.persistence.Embeddable;
|
||||||
|
import javax.persistence.Embedded;
|
||||||
|
import javax.persistence.EmbeddedId;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.Immutable;
|
||||||
|
|
||||||
|
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||||
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
@RunWith(BytecodeEnhancerRunner.class)
|
||||||
|
public class FinalFieldEnhancementTest extends BaseCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
return new Class[] {
|
||||||
|
EntityWithFinalField.class,
|
||||||
|
EntityWithEmbeddedIdWithFinalField.class, EntityWithEmbeddedIdWithFinalField.EmbeddableId.class,
|
||||||
|
EntityWithEmbeddedNonIdWithFinalField.class, EntityWithEmbeddedNonIdWithFinalField.EmbeddableNonId.class
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void entityWithFinalField_constructor() {
|
||||||
|
EntityWithFinalField entity = new EntityWithFinalField( "foo" );
|
||||||
|
assertThat( entity.immutableProperty ).isEqualTo( "foo" );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just test that the embedded non-ID works correctly over a persist/retrieve cycle
|
||||||
|
@Test
|
||||||
|
public void entityWithFinalField_smokeTest() {
|
||||||
|
EntityWithFinalField persistedEntity = new EntityWithFinalField( "foo" );
|
||||||
|
persistedEntity.setName( "Some name" );
|
||||||
|
inTransaction( s -> {
|
||||||
|
s.persist( persistedEntity );
|
||||||
|
} );
|
||||||
|
|
||||||
|
inTransaction( s -> {
|
||||||
|
EntityWithFinalField entity = s.find( EntityWithFinalField.class, persistedEntity.getId() );
|
||||||
|
assertThat( entity ).extracting( EntityWithFinalField::getImmutableProperty )
|
||||||
|
.isEqualTo( persistedEntity.getImmutableProperty() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just test that the embedded ID works correctly over a persist/retrieve cycle
|
||||||
|
@Test
|
||||||
|
public void embeddableIdWithFinalField_smokeTest() {
|
||||||
|
EntityWithEmbeddedIdWithFinalField persistedEntity = new EntityWithEmbeddedIdWithFinalField();
|
||||||
|
persistedEntity.setName( "Some name" );
|
||||||
|
inTransaction( s -> {
|
||||||
|
s.persist( persistedEntity );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Read with the same ID instance
|
||||||
|
inTransaction( s -> {
|
||||||
|
EntityWithEmbeddedIdWithFinalField entity = s.find( EntityWithEmbeddedIdWithFinalField.class, persistedEntity.getId() );
|
||||||
|
assertThat( entity ).extracting( EntityWithEmbeddedIdWithFinalField::getId ).extracting( i -> i.id )
|
||||||
|
.isEqualTo( persistedEntity.getId().id );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Read with a new ID instance
|
||||||
|
inTransaction( s -> {
|
||||||
|
EntityWithEmbeddedIdWithFinalField entity = s.find( EntityWithEmbeddedIdWithFinalField.class, EntityWithEmbeddedIdWithFinalField.EmbeddableId.of( persistedEntity.getId().id ) );
|
||||||
|
assertThat( entity ).extracting( EntityWithEmbeddedIdWithFinalField::getId ).extracting( i -> i.id )
|
||||||
|
.isEqualTo( persistedEntity.getId().id );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Read with a query
|
||||||
|
// This is special because in this particular test,
|
||||||
|
// we know Hibernate ORM *has to* instantiate the EmbeddableIdType itself:
|
||||||
|
// it cannot reuse the ID we passed.
|
||||||
|
// And since the EmbeddableIdType has a final field, instantiation will not be able to use a no-arg constructor...
|
||||||
|
inTransaction( s -> {
|
||||||
|
EntityWithEmbeddedIdWithFinalField entity =
|
||||||
|
s.createQuery( "from embidwithfinal e where e.name = :name", EntityWithEmbeddedIdWithFinalField.class )
|
||||||
|
.setParameter( "name", persistedEntity.getName() )
|
||||||
|
.uniqueResult();
|
||||||
|
assertThat( entity ).extracting( EntityWithEmbeddedIdWithFinalField::getId ).extracting( i -> i.id )
|
||||||
|
.isEqualTo( persistedEntity.getId().id );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void embeddableNonIdWithFinalField_constructor() {
|
||||||
|
EntityWithEmbeddedNonIdWithFinalField.EmbeddableNonId embeddable =
|
||||||
|
new EntityWithEmbeddedNonIdWithFinalField.EmbeddableNonId( "foo" );
|
||||||
|
assertThat( embeddable.immutableProperty ).isEqualTo( "foo" );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just test that the embedded non-ID works correctly over a persist/retrieve cycle
|
||||||
|
@Test
|
||||||
|
public void embeddableNonIdWithFinalField_smokeTest() {
|
||||||
|
EntityWithEmbeddedNonIdWithFinalField persistedEntity = new EntityWithEmbeddedNonIdWithFinalField();
|
||||||
|
persistedEntity.setName( "Some name" );
|
||||||
|
persistedEntity.setEmbedded( new EntityWithEmbeddedNonIdWithFinalField.EmbeddableNonId( "foo" ) );
|
||||||
|
inTransaction( s -> {
|
||||||
|
s.persist( persistedEntity );
|
||||||
|
} );
|
||||||
|
|
||||||
|
inTransaction( s -> {
|
||||||
|
EntityWithEmbeddedNonIdWithFinalField entity = s.find( EntityWithEmbeddedNonIdWithFinalField.class, persistedEntity.getId() );
|
||||||
|
assertThat( entity ).extracting( EntityWithEmbeddedNonIdWithFinalField::getEmbedded )
|
||||||
|
.extracting( EntityWithEmbeddedNonIdWithFinalField.EmbeddableNonId::getImmutableProperty )
|
||||||
|
.isEqualTo( persistedEntity.getEmbedded().getImmutableProperty() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "withfinal")
|
||||||
|
public static class EntityWithFinalField {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private final String immutableProperty;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
// For Hibernate ORM
|
||||||
|
protected EntityWithFinalField() {
|
||||||
|
this.immutableProperty = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EntityWithFinalField(String id) {
|
||||||
|
this.immutableProperty = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImmutableProperty() {
|
||||||
|
return immutableProperty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "embidwithfinal")
|
||||||
|
public static class EntityWithEmbeddedIdWithFinalField {
|
||||||
|
|
||||||
|
@EmbeddedId
|
||||||
|
private EmbeddableId id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public EntityWithEmbeddedIdWithFinalField() {
|
||||||
|
this.id = EmbeddableId.of( UUID.randomUUID().toString() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmbeddableId getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(EmbeddableId id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@Embeddable
|
||||||
|
public static class EmbeddableId implements Serializable {
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
// For Hibernate ORM
|
||||||
|
protected EmbeddableId() {
|
||||||
|
this.id = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EmbeddableId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EmbeddableId of(String string) {
|
||||||
|
return new EmbeddableId( string );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if ( this == o ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ( !( o instanceof EmbeddableId ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
EmbeddableId embeddableIdType = (EmbeddableId) o;
|
||||||
|
return Objects.equals( id, embeddableIdType.id );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash( id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "embwithfinal")
|
||||||
|
public static class EntityWithEmbeddedNonIdWithFinalField {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Embedded
|
||||||
|
private EmbeddableNonId embedded;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmbeddableNonId getEmbedded() {
|
||||||
|
return embedded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmbedded(
|
||||||
|
EmbeddableNonId embedded) {
|
||||||
|
this.embedded = embedded;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
public static class EmbeddableNonId {
|
||||||
|
private final String immutableProperty;
|
||||||
|
|
||||||
|
private String mutableProperty;
|
||||||
|
|
||||||
|
protected EmbeddableNonId() {
|
||||||
|
// For Hibernate ORM only - it will change the property value through reflection
|
||||||
|
this.immutableProperty = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EmbeddableNonId(String immutableProperty) {
|
||||||
|
this.immutableProperty = immutableProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImmutableProperty() {
|
||||||
|
return immutableProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMutableProperty() {
|
||||||
|
return mutableProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMutableProperty(String mutableProperty) {
|
||||||
|
this.mutableProperty = mutableProperty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
158
hibernate-core/src/test/java/org/hibernate/test/onetoone/cache/OneToOneCacheEnableSelectingTest.java
vendored
Normal file
158
hibernate-core/src/test/java/org/hibernate/test/onetoone/cache/OneToOneCacheEnableSelectingTest.java
vendored
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
package org.hibernate.test.onetoone.cache;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import javax.persistence.Cacheable;
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.OneToOne;
|
||||||
|
import javax.persistence.Version;
|
||||||
|
|
||||||
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
|
import org.hibernate.cfg.Configuration;
|
||||||
|
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
@TestForIssue( jiraKey = "HHH-14826")
|
||||||
|
public class OneToOneCacheEnableSelectingTest extends BaseCoreFunctionalTestCase {
|
||||||
|
@Override
|
||||||
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
return new Class[] {
|
||||||
|
Product.class,
|
||||||
|
ProductConfig.class
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(Configuration configuration) {
|
||||||
|
configuration.setProperty( AvailableSettings.USE_SECOND_LEVEL_CACHE, "true");
|
||||||
|
configuration.setProperty(AvailableSettings.JPA_SHARED_CACHE_MODE, "ENABLE_SELECTIVE");
|
||||||
|
configuration.setProperty(AvailableSettings.GENERATE_STATISTICS, "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFieldShouldNotBeNull() {
|
||||||
|
final AtomicLong pid = new AtomicLong();
|
||||||
|
|
||||||
|
// create Product
|
||||||
|
inTransaction(s -> {
|
||||||
|
Product product = new Product();
|
||||||
|
s.persist(product);
|
||||||
|
pid.set(product.getId());
|
||||||
|
});
|
||||||
|
|
||||||
|
// create ProductConfig and associate with a Product
|
||||||
|
inTransaction(s -> {
|
||||||
|
Product product = s.find(Product.class, pid.get());
|
||||||
|
ProductConfig config = new ProductConfig();
|
||||||
|
config.setProduct(product);
|
||||||
|
s.persist(config);
|
||||||
|
});
|
||||||
|
|
||||||
|
assertTrue(sessionFactory().getCache().containsEntity(Product.class, pid.get()));
|
||||||
|
|
||||||
|
sessionFactory().getStatistics().clear();
|
||||||
|
|
||||||
|
// now fetch the Product again
|
||||||
|
inTransaction(s -> {
|
||||||
|
Product product = s.find(Product.class, pid.get());
|
||||||
|
|
||||||
|
// should have been from cache
|
||||||
|
assertNotEquals (0, sessionFactory().getStatistics().getSecondLevelCacheHitCount());
|
||||||
|
|
||||||
|
// this should not fail
|
||||||
|
assertNotNull("one-to-one field should not be null", product.getConfig());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Product")
|
||||||
|
@Cacheable
|
||||||
|
public static class Product {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private Integer version;
|
||||||
|
|
||||||
|
@OneToOne(mappedBy = "product", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
|
||||||
|
private ProductConfig config;
|
||||||
|
|
||||||
|
public Product() {}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(Integer version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProductConfig getConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConfig(ProductConfig config) {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "ProductConfig")
|
||||||
|
@Cacheable
|
||||||
|
public static class ProductConfig {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private Integer version;
|
||||||
|
|
||||||
|
@OneToOne(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
|
||||||
|
private Product product;
|
||||||
|
|
||||||
|
public ProductConfig() {}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(Integer version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Product getProduct() {
|
||||||
|
return product;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProduct(Product product) {
|
||||||
|
this.product = product;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -75,6 +75,9 @@ logger.model-binder.level=debug
|
||||||
logger.java-type-descriptor-registry.name=org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry
|
logger.java-type-descriptor-registry.name=org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry
|
||||||
logger.java-type-descriptor-registry.level=debug
|
logger.java-type-descriptor-registry.level=debug
|
||||||
|
|
||||||
|
logger.bytecode-enhancement.name=org.hibernate.bytecode.enhance
|
||||||
|
logger.bytecode-enhancement.level=debug
|
||||||
|
|
||||||
logger.entity-action.name=org.hibernate.action.internal.EntityAction
|
logger.entity-action.name=org.hibernate.action.internal.EntityAction
|
||||||
#logger.entity-action.level=debug
|
#logger.entity-action.level=debug
|
||||||
logger.cascade.name=org.hibernate.engine.internal.Cascade
|
logger.cascade.name=org.hibernate.engine.internal.Cascade
|
||||||
|
|
Loading…
Reference in New Issue