Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public boolean isSecondary() {
final String explicitTableName = firstColumn.getExplicitTableName();
//note: checkPropertyConsistency() is responsible for ensuring they all have the same table name
return isNotEmpty( explicitTableName )
&& !getPropertyHolder().getTable().getName().equals( explicitTableName );
&& !getOwnerTable().getName().equals( explicitTableName );
}

/**
Expand All @@ -125,10 +125,18 @@ public Table getTable() {
// all the columns have to be mapped to the same table
// even though at the annotation level it looks like
// they could each specify a different table
return isSecondary() ? getJoin().getTable() : getPropertyHolder().getTable();
return isSecondary() ? getJoin().getTable() : getOwnerTable();
}
}

private Table getOwnerTable() {
PropertyHolder holder = getPropertyHolder();
while ( holder instanceof ComponentPropertyHolder ) {
holder = ( (ComponentPropertyHolder) holder ).parent;
}
return holder.getTable();
}

public void setTable(Table table) {
this.table = table;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,26 +266,31 @@ public String getEntityName() {

@Override
public void addProperty(Property property, AnnotatedColumns columns, XClass declaringClass) {
//Ejb3Column.checkPropertyConsistency( ); //already called earlier
//AnnotatedColumns.checkPropertyConsistency( ); //already called earlier
// Check table matches between the component and the columns
// if not, change the component table if no properties are set
// if a property is set already the core cannot support that
if ( columns != null ) {
final Table table = columns.getTable();
if ( !table.equals( getTable() ) ) {
if ( component.getPropertySpan() == 0 ) {
component.setTable( table );
}
else {
throw new AnnotationException(
"Embeddable class '" + component.getComponentClassName()
+ "' has properties mapped to two different tables"
+ " (all properties of the embeddable class must map to the same table)"
);
}
assert columns == null || property.getValue().getTable() == columns.getTable();
setTable( property.getValue().getTable() );
addProperty( property, declaringClass );
}

private void setTable(Table table) {
if ( !table.equals( getTable() ) ) {
if ( component.getPropertySpan() == 0 ) {
component.setTable( table );
}
else {
throw new AnnotationException(
"Embeddable class '" + component.getComponentClassName()
+ "' has properties mapped to two different tables"
+ " (all properties of the embeddable class must map to the same table)"
);
}
if ( parent instanceof ComponentPropertyHolder ) {
( (ComponentPropertyHolder) parent ).setTable( table );
}
}
addProperty( property, declaringClass );
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,15 +244,15 @@ private static PropertyBinder createEmbeddedProperty(
final PropertyBinder binder = new PropertyBinder();
binder.setDeclaringClass( inferredData.getDeclaringClass() );
binder.setName( inferredData.getPropertyName() );
binder.setValue(component);
binder.setValue( component );
binder.setProperty( inferredData.getProperty() );
binder.setAccessType( inferredData.getDefaultAccess() );
binder.setEmbedded(isComponentEmbedded);
binder.setHolder(propertyHolder);
binder.setId(isId);
binder.setEntityBinder(entityBinder);
binder.setInheritanceStatePerClass(inheritanceStatePerClass);
binder.setBuildingContext(context);
binder.setEmbedded( isComponentEmbedded );
binder.setHolder( propertyHolder );
binder.setId( isId );
binder.setEntityBinder( entityBinder );
binder.setInheritanceStatePerClass( inheritanceStatePerClass );
binder.setBuildingContext( context );
binder.makePropertyAndBind();
return binder;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
*/
@Embeddable
public class EmbeddableA {

@Embedded
@AttributeOverrides({@AttributeOverride(name = "embedAttrB" , column = @Column(table = "TableB"))})
private EmbeddableB embedB;

@Column(table = "TableB")
private String embedAttrA;

public EmbeddableB getEmbedB() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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.orm.test.mapping.embeddable;

import jakarta.persistence.AttributeOverride;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.SecondaryTable;
import jakarta.persistence.Table;
import org.hibernate.boot.MetadataSources;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
import org.junit.jupiter.api.Test;

/**
* Test passes if Author#name is renamed to Author#aname because it will be read before house (alphabetical order).
* The issue occurs if the nested embedded is read first, due to the table calculation (ComponentPropertyHolder#addProperty) used by the embedded, which retrieves the table from the first property.
*
* @author Vincent Bouthinon
*/
@SuppressWarnings("JUnitMalformedDeclaration")
@ServiceRegistry()
@JiraKey("HHH-19272")
class NestedEmbeddedObjectWithASecondaryTableTest {

@Test
void testNestedEmbeddedAndSecondaryTables(ServiceRegistryScope registryScope) {
final MetadataSources metadataSources = new MetadataSources( registryScope.getRegistry() )
.addAnnotatedClasses( Book.class, Author.class, House.class );
metadataSources.buildMetadata();
}

@Entity(name = "book")
@Table(name = "TBOOK")
@SecondaryTable(name = "TSECONDARYTABLE")
public static class Book {

@Id
@GeneratedValue
private Long id;

@AttributeOverride(name = "name", column = @Column(name = "authorName", table = "TSECONDARYTABLE"))
@Embedded
private Author author;

}

@Embeddable
public static class Author {

@AttributeOverride(name = "name", column = @Column(name = "houseName", table = "TSECONDARYTABLE"))
@Embedded
private House house;

private String name;
}

@Embeddable
public static class House {
private String name;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* 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.orm.test.records;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.SecondaryTable;
import jakarta.persistence.Table;
import org.hibernate.AnnotationException;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;

@JiraKey("HHH-19542")
@DomainModel(annotatedClasses = {
RecordNestedEmbeddedWithASecondaryTableTest.UserEntity.class
})
@SessionFactory
class RecordNestedEmbeddedWithASecondaryTableTest {

private UserEntity user;

@BeforeAll
void prepare(SessionFactoryScope scope) {
scope.inTransaction( session -> {
Person person = new Person( new FullName( "Sylvain", "Lecoy" ), 38 );
user = new UserEntity( person );
session.persist( user );
} );
}

@Test
void test(SessionFactoryScope scope) {
scope.inTransaction(session -> {
UserEntity entity = session.find( UserEntity.class, user.id );
assertThat( entity ).isNotNull();
assertThat( entity.id ).isEqualTo( user.id );
assertThat( entity.person ).isNotNull();
assertThat( entity.person.age ).isEqualTo( 38 );
assertThat( entity.person.fullName.firstName ).isEqualTo( "Sylvain" );
assertThat( entity.person.fullName.lastName ).isEqualTo( "Lecoy" );
});
}

@Test
void test(ServiceRegistryScope scope) {
final StandardServiceRegistry registry = scope.getRegistry();
final MetadataSources sources = new MetadataSources( registry ).addAnnotatedClass( UserEntity1.class );

try {
sources.buildMetadata();
fail( "Expecting to fail" );
} catch (AnnotationException expected) {
assertThat( expected ).hasMessageContaining( "all properties of the embeddable class must map to the same table" );
}
}

@Entity
@Table(name = "UserEntity")
@SecondaryTable(name = "Person")
static class UserEntity {
@Id
@GeneratedValue
private Integer id;
private Person person;

public UserEntity(
final Person person) {
this.person = person;
}

protected UserEntity() {

}
}

@Embeddable
record Person(
FullName fullName,
@Column(table = "Person")
Integer age) {

}

@Embeddable
record FullName(
@Column(table = "Person")
String firstName,
@Column(table = "Person")
String lastName) {

}

@Entity
@Table(name = "UserEntity")
@SecondaryTable(name = "Person")
public static class UserEntity1 {
@Id
@GeneratedValue
private Integer id;
private Person1 person;

public UserEntity1(
final Person1 person) {
this.person = person;
}

protected UserEntity1() {

}
}

@Embeddable
public record Person1(
FullName1 fullName,
@Column(table = "Person")
Integer age) {

}

@Embeddable
public record FullName1(
@Column(table = "Person")
String firstName,
String lastName) {

}
}
Loading