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

This commit is contained in:
Andrea Boriero 2020-09-30 07:37:55 +01:00
commit 6d349bac5b
10 changed files with 539 additions and 57 deletions

View File

@ -230,38 +230,55 @@ public class EnhancerImpl implements Enhancer {
Implementation isDirty = StubMethod.INSTANCE, getDirtyNames = StubMethod.INSTANCE, clearDirtyNames = StubMethod.INSTANCE;
for ( AnnotatedFieldDescription collectionField : collectionFields ) {
String collectionFieldName = collectionField.getName();
Class adviceIsDirty;
Class adviceGetDirtyNames;
Class adviceClearDirtyNames;
if ( collectionField.getType().asErasure().isAssignableTo( Map.class ) ) {
adviceIsDirty = CodeTemplates.MapAreCollectionFieldsDirty.class;
adviceGetDirtyNames = CodeTemplates.MapGetCollectionFieldDirtyNames.class;
adviceClearDirtyNames = CodeTemplates.MapGetCollectionClearDirtyNames.class;
}
else {
adviceIsDirty = CodeTemplates.CollectionAreCollectionFieldsDirty.class;
adviceGetDirtyNames = CodeTemplates.CollectionGetCollectionFieldDirtyNames.class;
adviceClearDirtyNames = CodeTemplates.CollectionGetCollectionClearDirtyNames.class;
}
if ( collectionField.isVisibleTo( managedCtClass ) ) {
FieldDescription fieldDescription = collectionField.getFieldDescription();
isDirty = Advice.withCustomMapping()
.bind( CodeTemplates.FieldName.class, collectionField.getName() )
.bind( CodeTemplates.FieldValue.class, collectionField.getFieldDescription() )
.to( CodeTemplates.MapAreCollectionFieldsDirty.class, adviceLocator )
.bind( CodeTemplates.FieldName.class, collectionFieldName )
.bind( CodeTemplates.FieldValue.class, fieldDescription )
.to( adviceIsDirty, adviceLocator )
.wrap( isDirty );
getDirtyNames = Advice.withCustomMapping()
.bind( CodeTemplates.FieldName.class, collectionField.getName() )
.bind( CodeTemplates.FieldValue.class, collectionField.getFieldDescription() )
.to( CodeTemplates.MapGetCollectionFieldDirtyNames.class, adviceLocator )
.bind( CodeTemplates.FieldName.class, collectionFieldName )
.bind( CodeTemplates.FieldValue.class, fieldDescription )
.to( adviceGetDirtyNames, adviceLocator )
.wrap( getDirtyNames );
clearDirtyNames = Advice.withCustomMapping()
.bind( CodeTemplates.FieldName.class, collectionField.getName() )
.bind( CodeTemplates.FieldValue.class, collectionField.getFieldDescription() )
.to( CodeTemplates.MapGetCollectionClearDirtyNames.class, adviceLocator )
.bind( CodeTemplates.FieldName.class, collectionFieldName )
.bind( CodeTemplates.FieldValue.class, fieldDescription )
.to( adviceClearDirtyNames, adviceLocator )
.wrap( clearDirtyNames );
}
else {
CodeTemplates.GetterMapping getterMapping = new CodeTemplates.GetterMapping(
collectionField.getFieldDescription() );
isDirty = Advice.withCustomMapping()
.bind( CodeTemplates.FieldName.class, collectionField.getName() )
.bind( CodeTemplates.FieldValue.class, collectionField.getFieldDescription() )
.to( CodeTemplates.CollectionAreCollectionFieldsDirty.class, adviceLocator )
.bind( CodeTemplates.FieldName.class, collectionFieldName )
.bind( CodeTemplates.FieldValue.class, getterMapping )
.to( adviceIsDirty, adviceLocator )
.wrap( isDirty );
getDirtyNames = Advice.withCustomMapping()
.bind( CodeTemplates.FieldName.class, collectionField.getName() )
.bind( CodeTemplates.FieldValue.class, collectionField.getFieldDescription() )
.to( CodeTemplates.CollectionGetCollectionFieldDirtyNames.class, adviceLocator )
.bind( CodeTemplates.FieldName.class, collectionFieldName )
.bind( CodeTemplates.FieldValue.class, getterMapping )
.to( adviceGetDirtyNames, adviceLocator )
.wrap( getDirtyNames );
clearDirtyNames = Advice.withCustomMapping()
.bind( CodeTemplates.FieldName.class, collectionField.getName() )
.bind( CodeTemplates.FieldValue.class, collectionField.getFieldDescription() )
.to( CodeTemplates.CollectionGetCollectionClearDirtyNames.class, adviceLocator )
.bind( CodeTemplates.FieldName.class, collectionFieldName )
.bind( CodeTemplates.FieldValue.class, getterMapping )
.to( adviceClearDirtyNames, adviceLocator )
.wrap( clearDirtyNames );
}
}

View File

@ -8,12 +8,14 @@ package org.hibernate.mapping;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
@ -39,9 +41,10 @@ import org.jboss.logging.Logger;
*
* @author Gavin King
*/
@SuppressWarnings("unchecked")
@SuppressWarnings("deprecation")
public class Table implements RelationalModel, Serializable, Exportable {
private static final Logger log = Logger.getLogger( Table.class );
private static final Column[] EMPTY_COLUMN_ARRAY = new Column[0];
private Identifier catalog;
private Identifier schema;
@ -50,7 +53,7 @@ public class Table implements RelationalModel, Serializable, Exportable {
/**
* contains all columns, including the primary key
*/
private Map columns = new LinkedHashMap();
private Map<String, Column> columns = new LinkedHashMap<>();
private KeyValue idValue;
private PrimaryKey primaryKey;
private Map<ForeignKeyKey, ForeignKey> foreignKeys = new LinkedHashMap<>();
@ -246,7 +249,7 @@ public class Table implements RelationalModel, Serializable, Exportable {
}
public Column getColumn(int n) {
Iterator iter = columns.values().iterator();
Iterator<Column> iter = columns.values().iterator();
for ( int i = 0; i < n - 1; i++ ) {
iter.next();
}
@ -282,7 +285,7 @@ public class Table implements RelationalModel, Serializable, Exportable {
return columns.size();
}
public Iterator getColumnIterator() {
public Iterator<Column> getColumnIterator() {
return columns.values().iterator();
}
@ -290,7 +293,7 @@ public class Table implements RelationalModel, Serializable, Exportable {
return indexes.values().iterator();
}
public Iterator getForeignKeyIterator() {
public Iterator<ForeignKey> getForeignKeyIterator() {
return foreignKeys.values().iterator();
}
@ -412,7 +415,7 @@ public class Table implements RelationalModel, Serializable, Exportable {
}
public void validateColumns(Dialect dialect, Mapping mapping, TableMetadata tableInfo) {
Iterator iter = getColumnIterator();
Iterator<Column> iter = getColumnIterator();
while ( iter.hasNext() ) {
Column col = (Column) iter.next();
@ -440,7 +443,7 @@ public class Table implements RelationalModel, Serializable, Exportable {
}
public Iterator sqlAlterStrings(
public Iterator<String> sqlAlterStrings(
Dialect dialect,
Metadata metadata,
TableInformation tableInfo,
@ -462,8 +465,8 @@ public class Table implements RelationalModel, Serializable, Exportable {
.append( ' ' )
.append( dialect.getAddColumnString() );
Iterator iter = getColumnIterator();
List results = new ArrayList();
Iterator<Column> iter = getColumnIterator();
List<String> results = new ArrayList<>();
while ( iter.hasNext() ) {
final Column column = (Column) iter.next();
@ -539,7 +542,7 @@ public class Table implements RelationalModel, Serializable, Exportable {
pkname = getPrimaryKey().getColumnIterator().next().getQuotedName( dialect );
}
Iterator iter = getColumnIterator();
Iterator<Column> iter = getColumnIterator();
while ( iter.hasNext() ) {
Column col = (Column) iter.next();
@ -667,7 +670,7 @@ public class Table implements RelationalModel, Serializable, Exportable {
return uniqueKey;
}
public UniqueKey createUniqueKey(List keyColumns) {
public UniqueKey createUniqueKey(List<Column> keyColumns) {
String keyName = Constraint.generateName( "UK_", this, keyColumns );
UniqueKey uk = getOrCreateUniqueKey( keyName );
uk.addColumns( keyColumns.iterator() );
@ -693,16 +696,16 @@ public class Table implements RelationalModel, Serializable, Exportable {
public void createForeignKeys() {
}
public ForeignKey createForeignKey(String keyName, List keyColumns, String referencedEntityName, String keyDefinition) {
public ForeignKey createForeignKey(String keyName, List<Column> keyColumns, String referencedEntityName, String keyDefinition) {
return createForeignKey( keyName, keyColumns, referencedEntityName, keyDefinition, null );
}
public ForeignKey createForeignKey(
String keyName,
List keyColumns,
List<Column> keyColumns,
String referencedEntityName,
String keyDefinition,
List referencedColumns) {
List<Column> referencedColumns) {
final ForeignKeyKey key = new ForeignKeyKey( keyColumns, referencedEntityName, referencedColumns );
ForeignKey fk = foreignKeys.get( key );
@ -826,14 +829,14 @@ public class Table implements RelationalModel, Serializable, Exportable {
return checkConstraints.iterator();
}
public Iterator sqlCommentStrings(Dialect dialect, String defaultCatalog, String defaultSchema) {
List comments = new ArrayList();
public Iterator<String> sqlCommentStrings(Dialect dialect, String defaultCatalog, String defaultSchema) {
List<String> comments = new ArrayList<>();
if ( dialect.supportsCommentOn() ) {
String tableName = getQualifiedName( dialect, defaultCatalog, defaultSchema );
if ( comment != null ) {
comments.add( "comment on table " + tableName + " is '" + comment + "'" );
}
Iterator iter = getColumnIterator();
Iterator<Column> iter = getColumnIterator();
while ( iter.hasNext() ) {
Column column = (Column) iter.next();
String columnComment = column.getComment();
@ -858,40 +861,38 @@ public class Table implements RelationalModel, Serializable, Exportable {
return identifier == null ? null : identifier.render();
}
public static class ForeignKeyKey implements Serializable {
String referencedClassName;
List columns;
List referencedColumns;
private final String referencedClassName;
private final Column[] columns;
private final Column[] referencedColumns;
ForeignKeyKey(List columns, String referencedClassName, List referencedColumns) {
ForeignKeyKey(List<Column> columns, String referencedClassName, List<Column> referencedColumns) {
Objects.requireNonNull( columns );
Objects.requireNonNull( referencedClassName );
this.referencedClassName = referencedClassName;
this.columns = new ArrayList();
this.columns.addAll( columns );
this.columns = columns.toArray( EMPTY_COLUMN_ARRAY );
if ( referencedColumns != null ) {
this.referencedColumns = new ArrayList();
this.referencedColumns.addAll( referencedColumns );
this.referencedColumns = referencedColumns.toArray( EMPTY_COLUMN_ARRAY );
}
else {
this.referencedColumns = Collections.EMPTY_LIST;
this.referencedColumns = EMPTY_COLUMN_ARRAY;
}
}
public int hashCode() {
return columns.hashCode() + referencedColumns.hashCode();
return Arrays.hashCode( columns ) + Arrays.hashCode( referencedColumns );
}
public boolean equals(Object other) {
ForeignKeyKey fkk = (ForeignKeyKey) other;
return fkk != null && fkk.columns.equals( columns ) && fkk.referencedColumns.equals( referencedColumns );
return fkk != null && Arrays.equals( fkk.columns, columns ) && Arrays.equals( fkk.referencedColumns, referencedColumns );
}
@Override
public String toString() {
return "ForeignKeyKey{" +
"columns=" + String.join( ",", columns ) +
", referencedClassName='" + referencedClassName + '\'' +
", referencedColumns=" + String.join( ",", referencedColumns ) +
return "ForeignKeyKey{columns=" + Arrays.toString( columns ) +
", referencedClassName='" + referencedClassName +
"', referencedColumns=" + Arrays.toString( referencedColumns ) +
'}';
}
}

View File

@ -361,6 +361,7 @@ public abstract class AbstractPropertyMapping implements PropertyMapping {
columns,
columnReaders,
columnReaderTemplates,
formulaTemplates != null && formulaTemplates.length > 0 ? formulaTemplates : null,
factory
);
}
@ -373,6 +374,17 @@ public abstract class AbstractPropertyMapping implements PropertyMapping {
final String[] columnReaders,
final String[] columnReaderTemplates,
final Mapping factory) throws MappingException {
initIdentifierPropertyPaths(path, etype, columns, columnReaders, columnReaderTemplates, null, factory);
}
protected void initIdentifierPropertyPaths(
final String path,
final EntityType etype,
final String[] columns,
final String[] columnReaders,
final String[] columnReaderTemplates,
final String[] formulaTemplates,
final Mapping factory) throws MappingException {
Type idtype = etype.getIdentifierOrUniqueKeyType( factory );
String idPropName = etype.getIdentifierOrUniqueKeyPropertyName( factory );
@ -381,15 +393,15 @@ public abstract class AbstractPropertyMapping implements PropertyMapping {
if ( etype.isReferenceToPrimaryKey() ) {
if ( !hasNonIdentifierPropertyNamedId ) {
String idpath1 = extendPath( path, EntityPersister.ENTITY_ID );
addPropertyPath( idpath1, idtype, columns, columnReaders, columnReaderTemplates, null, factory );
initPropertyPaths( idpath1, idtype, columns, columnReaders, columnReaderTemplates, null, factory );
addPropertyPath( idpath1, idtype, columns, columnReaders, columnReaderTemplates, formulaTemplates, factory );
initPropertyPaths( idpath1, idtype, columns, columnReaders, columnReaderTemplates, formulaTemplates, factory );
}
}
if ( (! etype.isNullable() ) && idPropName != null ) {
String idpath2 = extendPath( path, idPropName );
addPropertyPath( idpath2, idtype, columns, columnReaders, columnReaderTemplates, null, factory );
initPropertyPaths( idpath2, idtype, columns, columnReaders, columnReaderTemplates, null, factory );
addPropertyPath( idpath2, idtype, columns, columnReaders, columnReaderTemplates, formulaTemplates, factory );
initPropertyPaths( idpath2, idtype, columns, columnReaders, columnReaderTemplates, formulaTemplates, factory );
}
}

View File

@ -154,7 +154,7 @@ public class FetchGraphTest extends BaseEntityManagerFunctionalTestCase {
}
@Entity(name = "Trigger")
@Table(name = "Trigger")
@Table(name = "TriggerEntity")
static class Trigger {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)

View File

@ -0,0 +1,167 @@
package org.hibernate.persister.entity;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import java.util.List;
import java.util.Map;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.hibernate.annotations.JoinColumnOrFormula;
import org.hibernate.annotations.JoinColumnsOrFormulas;
import org.hibernate.annotations.JoinFormula;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.TestForIssue;
import org.junit.Before;
import org.junit.Test;
@TestForIssue(jiraKey = "HHH-14223")
public class JoinFormulaImplicitJoinTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Person.class, PersonVersion.class
};
}
@Override
protected void addConfigOptions(Map options) {
options.put(
AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS,
Boolean.TRUE
);
}
@Before
public void setUp() {
doInJPA( this::entityManagerFactory, entityManager -> {
final Person person = new Person();
entityManager.persist(person);
for (int i = 0; i < 3; i++) {
final PersonVersion personVersion = new PersonVersion();
personVersion.setName("Name" + i);
personVersion.setVersion(i);
personVersion.setPerson(person);
entityManager.persist(personVersion);
}
});
}
protected int entityCount() {
return 5;
}
@Test
public void testImplicitJoin() {
doInJPA( this::entityManagerFactory, entityManager -> {
entityManager.createQuery(
"SELECT person\n" +
"FROM Person AS person\n" +
" LEFT JOIN FETCH person.latestPersonVersion\n" +
"order by person.latestPersonVersion.id desc\n"
);
});
}
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PersonVersion> personVersions;
@ManyToOne
@JoinColumnsOrFormulas({
@JoinColumnOrFormula(
formula = @JoinFormula(
value = "(SELECT person_version.id FROM person_version WHERE person_version.person_id = id ORDER BY person_version.version DESC LIMIT 1)",
referencedColumnName = "id")
)
})
private PersonVersion latestPersonVersion;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<PersonVersion> getPersonVersions() {
return personVersions;
}
public void setPersonVersions(List<PersonVersion> personVersions) {
this.personVersions = personVersions;
}
public PersonVersion getLatestPersonVersion() {
return latestPersonVersion;
}
public void setLatestPersonVersion(PersonVersion latestPersonVersion) {
this.latestPersonVersion = latestPersonVersion;
}
}
@Entity(name = "PersonVersion")
public static class PersonVersion {
@Id
@GeneratedValue
private Long id;
private String name;
private Integer version;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "person_id")
private Person person;
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 Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.query.criteria.internal;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Order;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
/**
* @author seregamorph
*/
@TestForIssue(jiraKey = "HHH-13884")
public class HHH13884Test {
@Test
public void testDefaultReversedOrderImpl() {
Expression<?> expression = mock( Expression.class );
OrderImpl order = new OrderImpl( expression );
assertEquals( expression, order.getExpression() );
assertTrue( "Order should be ascending by default", order.isAscending() );
Order reversed = order.reverse();
assertEquals( expression, reversed.getExpression() );
assertFalse( "Reversed Order should be descending", reversed.isAscending() );
assertNotSame( "Order.reverse() should create new instance by the contract", order, reversed );
}
}

View File

@ -7,6 +7,7 @@
package org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking;
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
import org.hibernate.bytecode.enhance.spi.UnloadedField;
import org.hibernate.testing.bytecode.enhancement.EnhancerTestContext;
@ -18,4 +19,9 @@ public class DirtyCheckEnhancementContext extends EnhancerTestContext {
public boolean doExtendedEnhancement(UnloadedClass classDescriptor) {
return false;
}
@Override
public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
return false;
}
}

View File

@ -0,0 +1,151 @@
/*
* 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.lazy.proxy.inlinedirtychecking;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.persistence.AttributeConverter;
import javax.persistence.Convert;
import javax.persistence.Converter;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* @author Andrea Boriero
*/
@RunWith(BytecodeEnhancerRunner.class)
@CustomEnhancementContext({ DirtyCheckEnhancementContext.class })
public class DirtyCheckPrivateUnMappedCollectionTest extends BaseNonConfigCoreFunctionalTestCase {
boolean skipTest;
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder( ssrb );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
ssrb.applySetting( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "100" );
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
}
@Override
protected void applyMetadataSources(MetadataSources sources) {
String byteCodeProvider = Environment.getProperties().getProperty( AvailableSettings.BYTECODE_PROVIDER );
if ( byteCodeProvider != null && !Environment.BYTECODE_PROVIDER_NAME_BYTEBUDDY.equals( byteCodeProvider ) ) {
// skip the test if the bytecode provider is Javassist
skipTest = true;
}
else {
sources.addAnnotatedClass( Measurement.class );
}
}
@Test
public void testIt() {
if ( skipTest ) {
return;
}
inTransaction(
session -> {
Tag tag = new Tag();
tag.setName( "tag1" );
Measurement measurementDescriptor = new Measurement();
measurementDescriptor.addTag( tag );
session.save( measurementDescriptor );
}
);
}
@MappedSuperclass
public static class AbstractMeasurement {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
@Convert(converter = TagAttributeConverter.class)
private List<Tag> tags = new ArrayList<>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Tag> getTags() {
return tags;
}
public void setTags(List<Tag> tags) {
this.tags = tags;
}
public void addTag(Tag tag) {
this.tags.add( tag );
}
}
@Entity(name = "Measurement")
public static class Measurement extends AbstractMeasurement {
}
public static class Tag {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Converter
public static class TagAttributeConverter implements AttributeConverter<List<Tag>, String> {
@Override
public String convertToDatabaseColumn(List<Tag> attribute) {
return "empty";
}
@Override
public List<Tag> convertToEntityAttribute(String dbData) {
return Collections.emptyList();
}
}
}

View File

@ -7,6 +7,7 @@
package org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking;
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
import org.hibernate.bytecode.enhance.spi.UnloadedField;
import org.hibernate.testing.bytecode.enhancement.EnhancerTestContext;
@ -23,4 +24,9 @@ public class NoDirtyCheckEnhancementContext extends EnhancerTestContext {
public boolean doExtendedEnhancement(UnloadedClass classDescriptor) {
return false;
}
@Override
public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
return false;
}
}

View File

@ -0,0 +1,79 @@
/*
* 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.foreignkeys;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.stream.StreamSupport;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.mapping.Table;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
/**
* @author Yanming Zhou
*/
@TestForIssue(jiraKey = "HHH-14230")
public class HHH14230 {
private static final String TABLE_NAME = "test_entity";
private static final String JOIN_COLUMN_NAME = "parent";
@Test
public void test() {
Metadata metadata = new MetadataSources(new StandardServiceRegistryBuilder().build())
.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);
assertNotNull(table);
assertEquals(1, table.getForeignKeys().size());
// ClassCastException before HHH-14230
assertTrue(table.getForeignKeys().keySet().iterator().next().toString().contains(JOIN_COLUMN_NAME));
}
@Entity
@javax.persistence.Table(name = TABLE_NAME)
public static class TestEntity {
@Id
private Long id;
@ManyToOne
@JoinColumn(name = JOIN_COLUMN_NAME)
private TestEntity parent;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public TestEntity getParent() {
return parent;
}
public void setParent(TestEntity parent) {
this.parent = parent;
}
}
}