rough implementation of naturalid() function in HQL

and add tests for id(), version(), naturalid()
This commit is contained in:
Gavin King 2022-01-02 00:36:30 +01:00
parent 80b5c1e085
commit 5a0537aa04
9 changed files with 165 additions and 5 deletions

View File

@ -288,7 +288,12 @@ public class MetadataContext {
);
if ( attribute != null ) {
( (AttributeContainer<Object>) jpaMapping ).getInFlightAccess().addAttribute( attribute );
if ( property.isNaturalIdentifier() ) {
( ( AttributeContainer<Object>) jpaMapping ).getInFlightAccess()
.applyNaturalIdAttribute( attribute );
}
}
}
( (AttributeContainer<?>) jpaMapping ).getInFlightAccess().finishUp();
@ -313,6 +318,7 @@ public class MetadataContext {
applyIdMetadata( safeMapping, jpaType );
applyVersionAttribute( safeMapping, jpaType );
// applyNaturalIdAttribute( safeMapping, jpaType );
Iterator<Property> properties = safeMapping.getDeclaredPropertyIterator();
while ( properties.hasNext() ) {
@ -324,6 +330,10 @@ public class MetadataContext {
final PersistentAttribute<Object, ?> attribute = attributeFactory.buildAttribute( jpaType, property );
if ( attribute != null ) {
( (AttributeContainer<Object>) jpaType ).getInFlightAccess().addAttribute( attribute );
if ( property.isNaturalIdentifier() ) {
( ( AttributeContainer<Object>) jpaType ).getInFlightAccess()
.applyNaturalIdAttribute( attribute );
}
}
}

View File

@ -7,8 +7,10 @@
package org.hibernate.metamodel.model.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import jakarta.persistence.metamodel.Bindable;
@ -53,6 +55,7 @@ public abstract class AbstractIdentifiableType<J>
private final boolean isVersioned;
private SingularPersistentAttribute<J, ?> versionAttribute;
private List<PersistentAttribute<J,?>> naturalIdAttributes;
public AbstractIdentifiableType(
String typeName,
@ -250,7 +253,7 @@ public abstract class AbstractIdentifiableType<J>
return null;
}
SingularPersistentAttribute<J, ?> version = findVersionAttribute();
SingularPersistentAttribute<? super J, ?> version = findVersionAttribute();
if ( version != null ) {
checkType( version, javaType );
}
@ -258,14 +261,26 @@ public abstract class AbstractIdentifiableType<J>
}
@Override
@SuppressWarnings("unchecked")
public SingularPersistentAttribute<J, ?> findVersionAttribute() {
public SingularPersistentAttribute<? super J, ?> findVersionAttribute() {
if ( versionAttribute != null ) {
return versionAttribute;
}
if ( getSuperType() != null ) {
return (SingularPersistentAttribute<J, ?>) getSuperType().findVersionAttribute();
return getSuperType().findVersionAttribute();
}
return null;
}
@Override
public List<? extends PersistentAttribute<? super J, ?>> findNaturalIdAttributes() {
if ( naturalIdAttributes != null ) {
return naturalIdAttributes;
}
if ( getSuperType() != null ) {
return getSuperType().findNaturalIdAttributes();
}
return null;
@ -362,6 +377,14 @@ public abstract class AbstractIdentifiableType<J>
managedTypeAccess.addAttribute( versionAttribute );
}
@Override
public void applyNaturalIdAttribute(PersistentAttribute<J, ?> naturalIdAttribute) {
if ( AbstractIdentifiableType.this.naturalIdAttributes == null ) {
AbstractIdentifiableType.this.naturalIdAttributes = new ArrayList<>();
}
AbstractIdentifiableType.this.naturalIdAttributes.add( naturalIdAttribute );
}
@Override
public void addAttribute(PersistentAttribute<J, ?> attribute) {
managedTypeAccess.addAttribute( attribute );

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.metamodel.model.domain;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import jakarta.persistence.metamodel.IdentifiableType;
@ -49,4 +50,6 @@ public interface IdentifiableDomainType<J> extends ManagedDomainType<J>, Identif
void visitIdClassAttributes(Consumer<SingularPersistentAttribute<? super J,?>> action);
SingularPersistentAttribute<? super J, ?> findVersionAttribute();
List<? extends PersistentAttribute<? super J, ?>> findNaturalIdAttributes();
}

View File

@ -60,6 +60,11 @@ public interface AttributeContainer<J> {
);
}
default void applyNaturalIdAttribute(PersistentAttribute<J, ?> versionAttribute) {
throw new UnsupportedMappingException(
"AttributeContainer [" + getClass().getName() + "] does not support natural ids"
);
}
/**
* Called when configuration of the type is complete

View File

@ -33,6 +33,7 @@ import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import jakarta.persistence.metamodel.SingularAttribute;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.QueryException;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
@ -52,6 +53,7 @@ import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.IdentifiableDomainType;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.PersistentAttribute;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.query.BinaryArithmeticOperator;
@ -2175,7 +2177,33 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
@Override
public SqmPath<?> visitEntityNaturalIdReference(HqlParser.EntityNaturalIdReferenceContext ctx) {
throw new NotYetImplementedFor6Exception( "Support for HQL natural-id references not yet implemented" );
final SqmPath<Object> sqmPath = consumeDomainPath( (HqlParser.PathContext) ctx.getChild( 2 ) );
final DomainType<?> sqmPathType = sqmPath.getReferencedPathSource().getSqmPathType();
if ( sqmPathType instanceof IdentifiableDomainType<?> ) {
@SuppressWarnings("unchecked")
final IdentifiableDomainType<Object> identifiableType = (IdentifiableDomainType<? super Object>) sqmPathType;
final List<? extends PersistentAttribute<Object, ?>> attributes = identifiableType.findNaturalIdAttributes();
if ( attributes == null ) {
throw new SemanticException(
"`" + sqmPath.getNavigablePath().getFullPath() + "` resolved to an identifiable-type (`" +
identifiableType.getTypeName() + "`) which does not define a natural id"
);
}
else if ( attributes.size() >1 ) {
throw new SemanticException(
"`" + sqmPath.getNavigablePath().getFullPath() + "` resolved to an identifiable-type (`" +
identifiableType.getTypeName() + "`) which defines multiple natural ids"
);
}
@SuppressWarnings("unchecked")
SingularAttribute<Object, ?> naturalIdAttribute
= (SingularAttribute<Object, ?>) attributes.get(0);
return sqmPath.get( naturalIdAttribute );
}
throw new SemanticException( "Path does not reference an identifiable-type : " + sqmPath.getNavigablePath().getFullPath() );
}
@Override

View File

@ -467,6 +467,11 @@ public class SqmPolymorphicRootDescriptor<T> implements EntityDomainType<T> {
throw new UnsupportedOperationException( );
}
@Override
public List<? extends SingularPersistentAttribute<? super T, ?>> findNaturalIdAttributes() {
throw new UnsupportedOperationException( );
}
@Override
public boolean hasSingleIdAttribute() {
throw new UnsupportedOperationException( );

View File

@ -60,6 +60,20 @@ public class FunctionTests {
);
}
@Test
public void testIdVersionFunctions(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery("select id(w) from VersionedEntity w")
.list();
session.createQuery("select version(w) from VersionedEntity w")
.list();
session.createQuery("select naturalid(w) from VersionedEntity w")
.list();
}
);
}
@Test
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsCharCodeConversion.class)
public void testAsciiChrFunctions(SessionFactoryScope scope) {

View File

@ -17,6 +17,7 @@ public class GambitDomainModel extends AbstractDomainModelDescriptor {
public GambitDomainModel() {
super(
BasicEntity.class,
VersionedEntity.class,
Component.class,
EmbeddedIdEntity.class,
EntityOfArrays.class,

View File

@ -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.testing.orm.domain.gambit;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Version;
import org.hibernate.annotations.NaturalId;
import java.util.Objects;
/**
* @author Chris Cranford
*/
@Entity
public class VersionedEntity {
@Id
private Integer id;
@Version
private Integer version;
@NaturalId
private String code;
private String data;
public VersionedEntity() {
}
public VersionedEntity(Integer id, String data) {
this.id = id;
this.data = data;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
VersionedEntity that = (VersionedEntity) o;
return Objects.equals( id, that.id ) &&
Objects.equals( data, that.data );
}
@Override
public int hashCode() {
return Objects.hash( id, data );
}
}