mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-03-07 02:09:31 +00:00
HHH-17249 record as a composite type embeddable results in a PropertyNotFoundException
This commit is contained in:
parent
bcac1e3299
commit
e9fbf23ec8
@ -42,7 +42,7 @@
|
||||
import org.hibernate.models.spi.SourceModelBuildingContext;
|
||||
import org.hibernate.models.spi.TypeDetails;
|
||||
import org.hibernate.property.access.internal.PropertyAccessStrategyCompositeUserTypeImpl;
|
||||
import org.hibernate.property.access.internal.PropertyAccessStrategyMixedImpl;
|
||||
import org.hibernate.property.access.internal.PropertyAccessStrategyGetterImpl;
|
||||
import org.hibernate.property.access.spi.PropertyAccessStrategy;
|
||||
import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer;
|
||||
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
|
||||
@ -786,7 +786,7 @@ private static void processCompositeUserType(Component component, CompositeUserT
|
||||
for ( Property property : component.getProperties() ) {
|
||||
sortedPropertyNames.add( property.getName() );
|
||||
sortedPropertyTypes.add(
|
||||
PropertyAccessStrategyMixedImpl.INSTANCE.buildPropertyAccess(
|
||||
PropertyAccessStrategyGetterImpl.INSTANCE.buildPropertyAccess(
|
||||
compositeUserType.embeddable(),
|
||||
property.getName(),
|
||||
false
|
||||
|
@ -23,7 +23,7 @@
|
||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.property.access.internal.PropertyAccessStrategyMixedImpl;
|
||||
import org.hibernate.property.access.internal.PropertyAccessStrategyGetterImpl;
|
||||
import org.hibernate.property.access.spi.Getter;
|
||||
|
||||
import jakarta.persistence.Transient;
|
||||
@ -279,7 +279,7 @@ public static Class<?> reflectedPropertyClass(Class<?> clazz, String name) throw
|
||||
}
|
||||
|
||||
private static Getter getter(Class<?> clazz, String name) throws MappingException {
|
||||
return PropertyAccessStrategyMixedImpl.INSTANCE.buildPropertyAccess( clazz, name, true ).getGetter();
|
||||
return PropertyAccessStrategyGetterImpl.INSTANCE.buildPropertyAccess( clazz, name, true ).getGetter();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
* Copyright Red Hat Inc. and Hibernate Authors
|
||||
*/
|
||||
package org.hibernate.property.access.internal;
|
||||
|
||||
import jakarta.persistence.AccessType;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.hibernate.property.access.spi.Getter;
|
||||
import org.hibernate.property.access.spi.GetterFieldImpl;
|
||||
import org.hibernate.property.access.spi.GetterMethodImpl;
|
||||
import org.hibernate.property.access.spi.PropertyAccess;
|
||||
import org.hibernate.property.access.spi.PropertyAccessBuildingException;
|
||||
import org.hibernate.property.access.spi.PropertyAccessStrategy;
|
||||
import org.hibernate.property.access.spi.Setter;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static org.hibernate.internal.util.ReflectHelper.getterMethodOrNull;
|
||||
|
||||
/**
|
||||
* A {@link PropertyAccess} based on mix of getter method or field.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class PropertyAccessGetterImpl implements PropertyAccess {
|
||||
private final PropertyAccessStrategy strategy;
|
||||
|
||||
private final Getter getter;
|
||||
|
||||
public PropertyAccessGetterImpl(PropertyAccessStrategy strategy, Class<?> containerJavaType, String propertyName) {
|
||||
this.strategy = strategy;
|
||||
|
||||
final AccessType propertyAccessType = AccessStrategyHelper.getAccessType( containerJavaType, propertyName );
|
||||
switch ( propertyAccessType ) {
|
||||
case FIELD: {
|
||||
Field field = AccessStrategyHelper.fieldOrNull( containerJavaType, propertyName );
|
||||
if ( field == null ) {
|
||||
throw new PropertyAccessBuildingException(
|
||||
"Could not locate field for property named [" + containerJavaType.getName() + "#" + propertyName + "]"
|
||||
);
|
||||
}
|
||||
this.getter = fieldGetter( containerJavaType, propertyName, field );
|
||||
break;
|
||||
}
|
||||
case PROPERTY: {
|
||||
Method getterMethod = getterMethodOrNull( containerJavaType, propertyName );
|
||||
if ( getterMethod == null ) {
|
||||
throw new PropertyAccessBuildingException(
|
||||
"Could not locate getter for property named [" + containerJavaType.getName() + "#" + propertyName + "]"
|
||||
);
|
||||
}
|
||||
this.getter = propertyGetter( containerJavaType, propertyName, getterMethod );
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new PropertyAccessBuildingException(
|
||||
"Invalid access type " + propertyAccessType + " for property named [" + containerJavaType.getName() + "#" + propertyName + "]"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- //
|
||||
|
||||
private static Getter fieldGetter(Class<?> containerJavaType, String propertyName, Field field) {
|
||||
return new GetterFieldImpl( containerJavaType, propertyName, field );
|
||||
}
|
||||
|
||||
private static Getter propertyGetter(Class<?> containerJavaType, String propertyName, Method method) {
|
||||
return new GetterMethodImpl( containerJavaType, propertyName, method );
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyAccessStrategy getPropertyAccessStrategy() {
|
||||
return strategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Getter getGetter() {
|
||||
return getter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Setter getSetter() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
* Copyright Red Hat Inc. and Hibernate Authors
|
||||
*/
|
||||
package org.hibernate.property.access.internal;
|
||||
|
||||
import org.hibernate.property.access.spi.PropertyAccess;
|
||||
import org.hibernate.property.access.spi.PropertyAccessStrategy;
|
||||
|
||||
/**
|
||||
* A PropertyAccessStrategy that selects between available getter method or field.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class PropertyAccessStrategyGetterImpl implements PropertyAccessStrategy {
|
||||
/**
|
||||
* Singleton access
|
||||
*/
|
||||
public static final PropertyAccessStrategyGetterImpl INSTANCE = new PropertyAccessStrategyGetterImpl();
|
||||
|
||||
@Override
|
||||
public PropertyAccess buildPropertyAccess(Class<?> containerJavaType, String propertyName, boolean setterRequired) {
|
||||
return new PropertyAccessGetterImpl( this, containerJavaType, propertyName );
|
||||
}
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
* Copyright Red Hat Inc. and Hibernate Authors
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.embeddable.strategy.usertype.embedded.record;
|
||||
|
||||
import jakarta.persistence.AttributeOverride;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embedded;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.annotations.CompositeType;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.spi.ValueAccess;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
|
||||
import org.hibernate.usertype.CompositeUserType;
|
||||
|
||||
import org.javamoney.moneta.FastMoney;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.money.MonetaryAmount;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class RecordAsCompositeTypeEmbeddableTest extends BaseCoreFunctionalTestCase {
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] { RecordAsCompositeTypeEmbeddableEntity.class };
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
MonetaryAmount amount = FastMoney.of( 1, "BRL" );
|
||||
|
||||
RecordAsCompositeTypeEmbeddableEntity entity = new RecordAsCompositeTypeEmbeddableEntity();
|
||||
entity.setAmount( amount );
|
||||
entity.setId( 1L );
|
||||
|
||||
inTransaction( session -> {
|
||||
session.persist( entity );
|
||||
} );
|
||||
|
||||
inTransaction( session -> {
|
||||
RecordAsCompositeTypeEmbeddableEntity result = session.find(
|
||||
RecordAsCompositeTypeEmbeddableEntity.class,
|
||||
1L
|
||||
);
|
||||
assertEquals( result.getAmount(), entity.getAmount() );
|
||||
} );
|
||||
}
|
||||
|
||||
public static class MonetaryAmountType implements CompositeUserType<MonetaryAmount> {
|
||||
public record MonetaryAmountMapper(
|
||||
BigDecimal amount,
|
||||
String currency
|
||||
) {
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
public MonetaryAmountType() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPropertyValue(MonetaryAmount component, int property) throws HibernateException {
|
||||
//Alphabetical
|
||||
switch ( property ) {
|
||||
case 0:
|
||||
return component.getNumber().numberValueExact( BigDecimal.class );
|
||||
case 1:
|
||||
return component.getCurrency().getCurrencyCode();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MonetaryAmount instantiate(ValueAccess values, SessionFactoryImplementor sessionFactory) {
|
||||
//Alphabetical
|
||||
BigDecimal amount = values.getValue( 0, BigDecimal.class );
|
||||
String currency = values.getValue( 1, String.class );
|
||||
return FastMoney.of( amount, currency );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> embeddable() {
|
||||
return MonetaryAmountMapper.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<MonetaryAmount> returnedClass() {
|
||||
return MonetaryAmount.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(MonetaryAmount x, MonetaryAmount y) {
|
||||
return Objects.equals( x, y );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(MonetaryAmount x) {
|
||||
return x.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MonetaryAmount deepCopy(MonetaryAmount value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable disassemble(MonetaryAmount value) {
|
||||
return (Serializable) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MonetaryAmount assemble(Serializable cached, Object owner) {
|
||||
return (MonetaryAmount) cached;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MonetaryAmount replace(MonetaryAmount detached, MonetaryAmount managed, Object owner) {
|
||||
return detached;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name="RecordAsCompositeTypeEmbeddableEntity")
|
||||
public static class RecordAsCompositeTypeEmbeddableEntity {
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
@Embedded
|
||||
@AttributeOverride(name = "currency", column = @Column(name = "CURRENCY"))
|
||||
@AttributeOverride(name = "amount", column = @Column(name = "AMOUNT"))
|
||||
@CompositeType(MonetaryAmountType.class)
|
||||
MonetaryAmount amount;
|
||||
|
||||
public RecordAsCompositeTypeEmbeddableEntity() {
|
||||
}
|
||||
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public MonetaryAmount getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(MonetaryAmount amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user