HHH-16000 Add @HQLSelect annotation as abbreviation for @NamedQuery + @Loader

and deprecate @Loader
This commit is contained in:
Gavin 2023-01-07 13:54:45 +01:00 committed by Gavin King
parent 3ceb91d280
commit f8f6e4d541
5 changed files with 157 additions and 0 deletions

View File

@ -0,0 +1,42 @@
/*
* 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.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Specifies a custom HQL/JPQL query to be used in place of the default SQL
* generated by Hibernate when an entity or collection is loaded from the
* database.
* <p>
* The given {@linkplain #query HQL query} must have exactly one parameter
* which accepts the {@linkplain jakarta.persistence.Id id} of the entity
* or of the entity which owns the collection. It must return a single item
* in the select list, and that item must be the correct type of entity or
* collection element.
*
* @author Gavin King
*
* @since 6.2
*
* @implNote This annotation is just an abbreviation for {@link Loader}
* together with {@link NamedQuery}.
*/
@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface HQLSelect {
/**
* The HQL {@code SELECT} statement.
*/
String query();
}

View File

@ -19,9 +19,12 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
* collection, overriding the SQL that Hibernate generates by default.
*
* @author László Benke
*
* @deprecated Use {@link SQLSelect} or {@link HQLSelect}.
*/
@Target( {TYPE, FIELD, METHOD} )
@Retention( RUNTIME )
@Deprecated(since = "6.2")
public @interface Loader {
/**
* The named query to use for loading the entity or collection.

View File

@ -38,6 +38,7 @@ import org.hibernate.annotations.FilterJoinTable;
import org.hibernate.annotations.FilterJoinTables;
import org.hibernate.annotations.Filters;
import org.hibernate.annotations.Formula;
import org.hibernate.annotations.HQLSelect;
import org.hibernate.annotations.Immutable;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
@ -1347,6 +1348,13 @@ public abstract class CollectionBinder {
QueryBinder.bindNativeQuery( loaderName, sqlSelect, null, buildingContext );
}
final HQLSelect hqlSelect = property.getAnnotation( HQLSelect.class );
if ( hqlSelect != null ) {
final String loaderName = collection.getRole() + "$HQLSelect";
collection.setLoaderName( loaderName );
QueryBinder.bindQuery( loaderName, hqlSelect, buildingContext );
}
final Loader loader = property.getAnnotation( Loader.class );
if ( loader != null ) {
collection.setLoaderName( loader.namedQuery() );

View File

@ -53,6 +53,7 @@ import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.Filter;
import org.hibernate.annotations.Filters;
import org.hibernate.annotations.ForeignKey;
import org.hibernate.annotations.HQLSelect;
import org.hibernate.annotations.Immutable;
import org.hibernate.annotations.Loader;
import org.hibernate.annotations.NaturalIdCache;
@ -1283,6 +1284,13 @@ public class EntityBinder {
QueryBinder.bindNativeQuery( loaderName, sqlSelect, annotatedClass, context );
}
final HQLSelect hqlSelect = annotatedClass.getAnnotation( HQLSelect.class );
if ( hqlSelect != null ) {
final String loaderName = persistentClass.getEntityName() + "$HQLSelect";
persistentClass.setLoaderName( loaderName );
QueryBinder.bindQuery( loaderName, hqlSelect, context );
}
final Loader loader = annotatedClass.getAnnotation( Loader.class );
if ( loader != null ) {
persistentClass.setLoaderName( loader.namedQuery() );

View File

@ -0,0 +1,96 @@
package org.hibernate.orm.test.loaders;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import org.hibernate.annotations.HQLSelect;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static jakarta.persistence.CascadeType.PERSIST;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
@SessionFactory
@DomainModel(annotatedClasses = {HqlSelectTest.WithHqlSelect.class, HqlSelectTest.UUID.class})
public class HqlSelectTest {
@Test
void test(SessionFactoryScope scope) {
WithHqlSelect withHqlSelect = new WithHqlSelect();
withHqlSelect.name = "Hibernate";
withHqlSelect.uuids.add( new UUID( withHqlSelect ) );
withHqlSelect.uuids.add( new UUID( withHqlSelect ) );
withHqlSelect.uuids.add( new UUID( withHqlSelect ) );
scope.inTransaction( s -> s.persist( withHqlSelect ) );
scope.inTransaction( s -> {
WithHqlSelect wss = s.get( WithHqlSelect.class, withHqlSelect.id );
assertEquals( "Hibernate", wss.name );
assertEquals( 3, wss.uuids.size() );
wss.uuids.get(2).deleted = true;
});
scope.inTransaction( s -> {
WithHqlSelect wss = s.get( WithHqlSelect.class, withHqlSelect.id );
assertEquals( "Hibernate", wss.name );
assertEquals( 2, wss.uuids.size() );
wss.deleted = true;
});
scope.inTransaction( s -> {
WithHqlSelect wss = s.get( WithHqlSelect.class, withHqlSelect.id );
assertNull( wss );
});
}
@Entity(name = "WithHqlSelect")
@Table(name = "With_Sql_Select")
@HQLSelect(query = "from WithHqlSelect where id = ?1 and deleted = false")
static class WithHqlSelect {
@Id @GeneratedValue
@Column(name = "Sql_Select_id")
Long id;
String name;
boolean deleted = false;
@OneToMany(mappedBy = "hqlSelect", cascade = PERSIST)
@HQLSelect(query = "from UUID where hqlSelect.id = ?1 and deleted = false")
List<UUID> uuids = new ArrayList<>();
}
@Entity(name = "UUID")
@Table(name = "With_Uuids")
static class UUID {
@Id @GeneratedValue
@Column(name = "Uuid_id")
Long id;
java.util.UUID uuid = java.util.UUID.randomUUID();
boolean deleted = false;
@ManyToOne
@JoinColumn(name = "Sql_Select_id")
WithHqlSelect hqlSelect;
UUID() {}
UUID(WithHqlSelect withHqlSelect) {
hqlSelect = withHqlSelect;
}
}
}