HHH-16000 Add @HQLSelect annotation as abbreviation for @NamedQuery + @Loader
and deprecate @Loader
This commit is contained in:
parent
3ceb91d280
commit
f8f6e4d541
|
@ -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();
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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() );
|
||||
|
|
|
@ -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() );
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue