Merge remote-tracking branch 'upstream/main' into wip/6.0

This commit is contained in:
Andrea Boriero 2021-09-21 08:46:04 +02:00
commit e466c52002
15 changed files with 692 additions and 30 deletions

View File

@ -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'

View File

@ -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();
}

View File

@ -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]", declaredOwnerType.getName(),
field.getType().asErasure(), field.getName(),
field.getName(), instrumentedType.getName(),
field.getName(), instrumentedMethod.getName()
name );
);
}
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,9 +112,7 @@ 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 );
}
} }
}; };
} }

View File

@ -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,15 +227,19 @@ 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,
.defineMethod( // so there's no point trying to replace final field writes with a method call.
EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + enhancedField.getName(), if ( !enhancedField.getFieldDescription().isFinal() ) {
TypeDescription.VOID, builder = builder
Visibility.PUBLIC .defineMethod(
) EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + enhancedField.getName(),
.withParameters( enhancedField.getType().asErasure() ) TypeDescription.VOID,
.intercept( fieldWriter( enhancedField ) ); Visibility.PUBLIC
)
.withParameters( enhancedField.getType().asErasure() )
.intercept( fieldWriter( enhancedField ) );
}
if ( !compositeOwner if ( !compositeOwner
&& !enhancementContext.isMappedSuperclassClass( managedCtClass ) && !enhancementContext.isMappedSuperclassClass( managedCtClass )

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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()

View File

@ -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,

View File

@ -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,

View File

@ -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 );

View File

@ -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;
}
}

View File

@ -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;
}
}
}
}

View 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;
}
}
}

View File

@ -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