HHH-16633 support for StatelessSession in query methods/DAOs

This commit is contained in:
Gavin King 2023-07-08 19:08:51 +02:00
parent a36b683870
commit 2409e1a49b
13 changed files with 218 additions and 56 deletions

View File

@ -26,14 +26,13 @@ import javax.tools.FileObject;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.Constants;
import org.hibernate.jpamodelgen.util.TypeUtils;
import org.checkerframework.checker.nullness.qual.Nullable;
import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation;
/**
* Helper class to write the actual meta model class using the {@link javax.annotation.processing.Filer} API.
* Helper class to write the actual metamodel class using the {@link javax.annotation.processing.Filer} API.
*
* @author Emmanuel Bernard
* @author Hardy Ferentschik
@ -194,8 +193,8 @@ public final class ClassWriter {
// to allow for the case that the metamodel class for the super entity is for example contained in another
// jar file we use reflection. However, we need to consider the fact that there is xml configuration
// and annotations should be ignored
if ( !entityMetaComplete && ( containsAnnotation( superClassElement, Constants.ENTITY )
|| containsAnnotation( superClassElement, Constants.MAPPED_SUPERCLASS ) ) ) {
if ( !entityMetaComplete
&& containsAnnotation( superClassElement, Constants.ENTITY, Constants.MAPPED_SUPERCLASS ) ) {
return true;
}

View File

@ -94,7 +94,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
/**
* True if this "metamodel class" is actually an instantiable DAO-style repository.
*/
private boolean dao;
private boolean dao = false;
private String sessionType = Constants.ENTITY_MANAGER;
public AnnotationMetaEntity(TypeElement element, Context context) {
this.element = element;
@ -242,7 +243,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
for ( ExecutableElement getterOrSetter : gettersAndSettersOfClass ) {
if ( isSessionGetter( getterOrSetter ) ) {
dao = true;
addDaoConstructor( getterOrSetter );
sessionType = addDaoConstructor( getterOrSetter );
}
}
}
@ -259,11 +260,13 @@ public class AnnotationMetaEntity extends AnnotationMeta {
initialized = true;
}
private void addDaoConstructor(ExecutableElement getterOrSetter) {
private String addDaoConstructor(ExecutableElement getterOrSetter) {
String name = getterOrSetter.getSimpleName().toString();
String typeName = element.getSimpleName().toString() + '_';
String type = getterOrSetter.getReturnType().toString();
putMember( name, new DaoConstructor(this, typeName, name, type, context.addInjectAnnotation() ) );
String sessionType = getterOrSetter.getReturnType().toString();
putMember( name,
new DaoConstructor(this, typeName, name, sessionType, context.addInjectAnnotation() ) );
return sessionType;
}
private static boolean isSessionGetter(ExecutableElement getterOrSetter) {
@ -426,32 +429,29 @@ public class AnnotationMetaEntity extends AnnotationMeta {
Diagnostic.Kind.ERROR );
}
else {
if ( containerType != null ) {
// it has to be a criteria finder (multiple results)
createContainerResultFinder( method, returnType, containerType, entity );
if ( containerType != null || Constants.HIB_STATELESS_SESSION.equals(sessionType) ) {
// multiple results or stateless session
// it has to be a criteria finder
createCriteriaFinder( method, returnType, containerType, entity );
}
else {
switch ( method.getParameters().size() ) {
case 0: {
context.message( method,
"missing parameter",
Diagnostic.Kind.ERROR );
case 0:
context.message( method, "missing parameter", Diagnostic.Kind.ERROR );
break;
}
case 1: {
case 1:
createSingleParameterFinder( method, returnType, entity );
break;
}
default: {
default:
createMultipleParameterFinder( method, returnType, entity );
}
}
}
}
}
}
private void createContainerResultFinder(ExecutableElement method, TypeMirror returnType, TypeElement containerType, TypeElement entity) {
private void createCriteriaFinder(
ExecutableElement method, TypeMirror returnType, @Nullable TypeElement containerType, TypeElement entity) {
final String methodName = method.getSimpleName().toString();
final List<String> paramNames = parameterNames(method);
final List<String> paramTypes = parameterTypes(method);
@ -464,10 +464,11 @@ public class AnnotationMetaEntity extends AnnotationMeta {
this,
methodName,
returnType.toString(),
containerType.toString(),
containerType == null ? null : containerType.toString(),
paramNames,
paramTypes,
dao
dao,
sessionType
)
);
}
@ -485,7 +486,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
returnType.toString(),
paramNames,
paramTypes,
dao
dao,
sessionType
)
);
}
@ -498,7 +500,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
null,
paramNames,
paramTypes,
dao
dao,
sessionType
)
);
}
@ -519,7 +522,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
returnType.toString(),
List.of( parameter.getSimpleName().toString() ),
List.of( parameter.asType().toString() ),
dao
dao,
sessionType
)
);
break;
@ -531,7 +535,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
returnType.toString(),
parameter.getSimpleName().toString(),
parameter.asType().toString(),
dao
dao,
sessionType
)
);
break;
@ -544,7 +549,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
null,
List.of( parameter.getSimpleName().toString() ),
List.of( parameter.asType().toString() ),
dao
dao,
sessionType
)
);
break;
@ -629,7 +635,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
paramNames,
paramTypes,
isNative,
dao
dao,
sessionType
);
putMember( attribute.getPropertyName() + paramTypes, attribute );

View File

@ -26,13 +26,15 @@ public class CriteriaFinderMethod implements MetaAttribute {
private final List<String> paramNames;
private final List<String> paramTypes;
private final boolean belongsToDao;
private final String sessionType;
public CriteriaFinderMethod(
Metamodel annotationMetaEntity,
String methodName, String entity,
@Nullable String containerType,
List<String> paramNames, List<String> paramTypes,
boolean belongsToDao) {
boolean belongsToDao,
String sessionType) {
this.annotationMetaEntity = annotationMetaEntity;
this.methodName = methodName;
this.entity = entity;
@ -40,6 +42,7 @@ public class CriteriaFinderMethod implements MetaAttribute {
this.paramNames = paramNames;
this.paramTypes = paramTypes;
this.belongsToDao = belongsToDao;
this.sessionType = sessionType;
}
@Override
@ -102,7 +105,11 @@ public class CriteriaFinderMethod implements MetaAttribute {
}
declaration
.append(") {")
.append("\n\tvar builder = entityManager.getEntityManagerFactory().getCriteriaBuilder();")
.append("\n\tvar builder = entityManager")
.append(Constants.ENTITY_MANAGER.equals(sessionType)
? ".getEntityManagerFactory()"
: ".getFactory()")
.append(".getCriteriaBuilder();")
.append("\n\tvar query = builder.createQuery(")
.append(annotationMetaEntity.importType(entity))
.append(".class);")
@ -129,7 +136,9 @@ public class CriteriaFinderMethod implements MetaAttribute {
declaration
.append("\n\t);")
.append("\n\treturn ");
if ( containerType != null && containerType.startsWith("org.hibernate") ) {
if ( containerType != null
&& Constants.ENTITY_MANAGER.equals(sessionType)
&& containerType.startsWith("org.hibernate") ) {
declaration
.append("(")
.append(type)
@ -139,11 +148,11 @@ public class CriteriaFinderMethod implements MetaAttribute {
.append("entityManager.createQuery(query)");
if ( containerType == null) {
declaration
.append("\n\t\t\t.getSingleResult()");
.append(".getSingleResult()");
}
else if ( containerType.equals(Constants.LIST) ) {
declaration
.append("\n\t\t\t.getResultList()");
.append(".getResultList()");
}
declaration
.append(";\n}");

View File

@ -20,18 +20,21 @@ public class IdFinderMethod implements MetaAttribute {
private final String paramName;
private final String paramType;
private final boolean belongsToDao;
private final String sessionType;
public IdFinderMethod(
Metamodel annotationMetaEntity,
String methodName, String entity,
String paramName, String paramType,
boolean belongsToDao) {
boolean belongsToDao,
String sessionType) {
this.annotationMetaEntity = annotationMetaEntity;
this.methodName = methodName;
this.entity = entity;
this.paramName = paramName;
this.paramType = paramType;
this.belongsToDao = belongsToDao;
this.sessionType = sessionType;
}
@Override
@ -80,7 +83,8 @@ public class IdFinderMethod implements MetaAttribute {
.append(" ")
.append(paramName)
.append(") {")
.append("\n\treturn entityManager.find(")
.append("\n\treturn entityManager")
.append(Constants.HIB_STATELESS_SESSION.equals(sessionType) ? ".get(" : ".find(")
.append(annotationMetaEntity.importType(entity))
.append(".class, ")
.append(paramName)

View File

@ -12,8 +12,6 @@ import org.hibernate.jpamodelgen.util.Constants;
import java.util.List;
import static org.hibernate.internal.util.StringHelper.join;
/**
* @author Gavin King
*/
@ -24,18 +22,21 @@ public class NaturalIdFinderMethod implements MetaAttribute {
private final List<String> paramNames;
private final List<String> paramTypes;
private final boolean belongsToDao;
private final String sessionType;
public NaturalIdFinderMethod(
Metamodel annotationMetaEntity,
String methodName, String entity,
List<String> paramNames, List<String> paramTypes,
boolean belongsToDao) {
boolean belongsToDao,
String sessionType) {
this.annotationMetaEntity = annotationMetaEntity;
this.methodName = methodName;
this.entity = entity;
this.paramNames = paramNames;
this.paramTypes = paramTypes;
this.belongsToDao = belongsToDao;
this.sessionType = sessionType;
}
@Override
@ -57,7 +58,7 @@ public class NaturalIdFinderMethod implements MetaAttribute {
.append("#")
.append(methodName)
.append("(")
.append(join(",", paramTypes.stream().map(this::strip).map(annotationMetaEntity::importType).toArray()))
.append(parameterList())
.append(")")
.append("\n **/\n");
if ( belongsToDao ) {
@ -91,11 +92,14 @@ public class NaturalIdFinderMethod implements MetaAttribute {
}
declaration
.append(") {")
.append("\n\treturn entityManager")
//TODO: skip if unnecessary:
.append(".unwrap(")
.append(annotationMetaEntity.importType(Constants.HIB_SESSION))
.append(".class)\n\t\t\t")
.append("\n\treturn entityManager");
if ( Constants.ENTITY_MANAGER.equals(sessionType) ) {
declaration
.append(".unwrap(")
.append(annotationMetaEntity.importType(Constants.HIB_SESSION))
.append(".class)\n\t\t\t");
}
declaration
.append(".byNaturalId(")
.append(annotationMetaEntity.importType(entity))
.append(".class)");
@ -114,6 +118,14 @@ public class NaturalIdFinderMethod implements MetaAttribute {
return declaration.toString();
}
private String parameterList() {
return paramTypes.stream()
.map(this::strip)
.map(annotationMetaEntity::importType)
.reduce((x, y) -> x + y)
.orElse("");
}
private String strip(String type) {
int index = type.indexOf("<");
String stripped = index > 0 ? type.substring(0, index) : type;

View File

@ -13,12 +13,10 @@ import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.Constants;
import org.hibernate.query.Order;
import org.hibernate.query.Page;
import org.hibernate.query.SelectionQuery;
import java.util.List;
import static java.util.stream.Collectors.toList;
import static org.hibernate.internal.util.StringHelper.join;
import static org.hibernate.jpamodelgen.util.StringUtil.getUpperUnderscoreCaseFromLowerCamelCase;
/**
@ -34,6 +32,7 @@ public class QueryMethod implements MetaAttribute {
private final List<String> paramTypes;
private final boolean isNative;
private final boolean belongsToDao;
private final String sessionType;
public QueryMethod(
Metamodel annotationMetaEntity,
@ -46,7 +45,8 @@ public class QueryMethod implements MetaAttribute {
List<String> paramNames,
List<String> paramTypes,
boolean isNative,
boolean belongsToDao) {
boolean belongsToDao,
String sessionType) {
this.annotationMetaEntity = annotationMetaEntity;
this.methodName = methodName;
this.queryString = queryString;
@ -56,6 +56,7 @@ public class QueryMethod implements MetaAttribute {
this.paramTypes = paramTypes;
this.isNative = isNative;
this.belongsToDao = belongsToDao;
this.sessionType = sessionType;
}
@Override
@ -82,8 +83,7 @@ public class QueryMethod implements MetaAttribute {
.append("#")
.append(methodName)
.append("(")
.append(join(",", paramTypes.stream().map(this::strip)
.map(annotationMetaEntity::importType).toArray()))
.append(parameterList())
.append(")")
.append("\n **/\n");
boolean hasVarargs = paramTypes.stream().anyMatch(ptype -> ptype.endsWith("..."));
@ -147,7 +147,9 @@ public class QueryMethod implements MetaAttribute {
.append(" {")
.append("\n\treturn ");
if ( isNative && returnTypeName != null
|| containerTypeName != null && containerTypeName.startsWith("org.hibernate") ) {
|| containerTypeName != null
&& Constants.ENTITY_MANAGER.equals(sessionType)
&& containerTypeName.startsWith("org.hibernate") ) {
declaration.append("(").append(type).append(") ");
}
declaration
@ -162,7 +164,7 @@ public class QueryMethod implements MetaAttribute {
.append(".class");
}
declaration.append(")");
boolean unwrapped = false;
boolean unwrapped = !Constants.ENTITY_MANAGER.equals(sessionType);
for (int i = 1; i <= paramNames.size(); i++) {
String param = paramNames.get(i-1);
String ptype = paramTypes.get(i-1);
@ -221,6 +223,14 @@ public class QueryMethod implements MetaAttribute {
return declaration.toString();
}
private String parameterList() {
return paramTypes.stream()
.map(this::strip)
.map(annotationMetaEntity::importType)
.reduce((x, y) -> x + y)
.orElse("");
}
private String strip(String type) {
int index = type.indexOf("<");
String stripped = index > 0 ? type.substring(0, index) : type;
@ -240,7 +250,7 @@ public class QueryMethod implements MetaAttribute {
if ( !unwrapped ) {
declaration
.append("\n\t\t\t.unwrap(")
.append(annotationMetaEntity.importType(SelectionQuery.class.getName()))
.append(annotationMetaEntity.importType(Constants.HIB_SELECTION_QUERY))
.append(".class)");
}
}

View File

@ -68,6 +68,7 @@ public final class Constants {
public static final String HIB_QUERY = "org.hibernate.query.Query";
public static final String HIB_SELECTION_QUERY = "org.hibernate.query.SelectionQuery";
public static final String HIB_SESSION = "org.hibernate.Session";
public static final String HIB_STATELESS_SESSION = "org.hibernate.StatelessSession";
public static final String SINGULAR_ATTRIBUTE = "jakarta.persistence.metamodel.SingularAttribute";
public static final String COLLECTION_ATTRIBUTE = "jakarta.persistence.metamodel.CollectionAttribute";

View File

@ -31,6 +31,9 @@ public interface Dao {
@HQL("from Book where title like ?1")
TypedQuery<Book> findByTitle(String title);
@HQL("from Book where title like ?1")
SelectionQuery<Book> findByTitleSelectionQuery(String title);
@HQL("from Book where title like ?1 order by title fetch first ?2 rows only")
List<Book> findFirstNByTitle(String title, int N);
//

View File

@ -18,10 +18,14 @@ import static org.hibernate.jpamodelgen.test.util.TestUtil.assertMetamodelClassG
*/
public class DaoTest extends CompilationTest {
@Test
@WithClasses({ Book.class, Dao.class })
@WithClasses({ Book.class, Dao.class, StatefulDao.class, StatelessDao.class })
public void testGeneratedAnnotationNotGenerated() {
System.out.println( TestUtil.getMetaModelSourceAsString( Dao.class ) );
System.out.println( TestUtil.getMetaModelSourceAsString( StatefulDao.class ) );
System.out.println( TestUtil.getMetaModelSourceAsString( StatelessDao.class ) );
assertMetamodelClassGeneratedFor( Book.class );
assertMetamodelClassGeneratedFor( Dao.class );
assertMetamodelClassGeneratedFor( StatefulDao.class );
assertMetamodelClassGeneratedFor( StatelessDao.class );
}
}

View File

@ -0,0 +1,54 @@
package org.hibernate.jpamodelgen.test.dao;
import jakarta.persistence.TypedQuery;
import org.hibernate.Session;
import org.hibernate.annotations.processing.Find;
import org.hibernate.annotations.processing.HQL;
import org.hibernate.annotations.processing.SQL;
import org.hibernate.query.SelectionQuery;
import java.util.List;
public interface StatefulDao {
Session getSession();
@Find
Book getBook(String isbn);
@Find
Book getBook(String title, String author);
@Find
Book getBook(String title, String isbn, String author);
@Find
List<Book> getBooks(String title);
@Find
SelectionQuery<Book> createBooksSelectionQuery(String title);
@HQL("from Book where title like ?1")
TypedQuery<Book> findByTitle(String title);
@HQL("from Book where title like ?1")
SelectionQuery<Book> findByTitleSelectionQuery(String title);
@HQL("from Book where title like ?1 order by title fetch first ?2 rows only")
List<Book> findFirstNByTitle(String title, int N);
//
// @HQL("from Book where title like :title")
// List<Book> findByTitleWithPagination(String title, Order<? super Book> order, Page page);
//
// @HQL("from Book where title like :title")
// SelectionQuery<Book> findByTitleWithOrdering(String title, List<Order<? super Book>> order);
//
// @HQL("from Book where title like :title")
// SelectionQuery<Book> findByTitleWithOrderingByVarargs(String title, Order<? super Book>... order);
@HQL("from Book where isbn = :isbn")
Book findByIsbn(String isbn);
@SQL("select * from Book where isbn = :isbn")
Book findByIsbnNative(String isbn);
}

View File

@ -0,0 +1,54 @@
package org.hibernate.jpamodelgen.test.dao;
import jakarta.persistence.TypedQuery;
import org.hibernate.StatelessSession;
import org.hibernate.annotations.processing.Find;
import org.hibernate.annotations.processing.HQL;
import org.hibernate.annotations.processing.SQL;
import org.hibernate.query.SelectionQuery;
import java.util.List;
public interface StatelessDao {
StatelessSession getSession();
@Find
Book getBook(String isbn);
@Find
Book getBook(String title, String author);
@Find
Book getBook(String title, String isbn, String author);
@Find
List<Book> getBooks(String title);
@Find
SelectionQuery<Book> createBooksSelectionQuery(String title);
@HQL("from Book where title like ?1")
TypedQuery<Book> findByTitle(String title);
@HQL("from Book where title like ?1")
SelectionQuery<Book> findByTitleSelectionQuery(String title);
@HQL("from Book where title like ?1 order by title fetch first ?2 rows only")
List<Book> findFirstNByTitle(String title, int N);
//
// @HQL("from Book where title like :title")
// List<Book> findByTitleWithPagination(String title, Order<? super Book> order, Page page);
//
// @HQL("from Book where title like :title")
// SelectionQuery<Book> findByTitleWithOrdering(String title, List<Order<? super Book>> order);
//
// @HQL("from Book where title like :title")
// SelectionQuery<Book> findByTitleWithOrderingByVarargs(String title, Order<? super Book>... order);
@HQL("from Book where isbn = :isbn")
Book findByIsbn(String isbn);
@SQL("select * from Book where isbn = :isbn")
Book findByIsbnNative(String isbn);
}

View File

@ -2,10 +2,12 @@ package org.hibernate.jpamodelgen.test.hqlsql;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.hibernate.annotations.NaturalId;
@Entity
public class Book {
@Id String isbn;
String title;
@NaturalId String title;
String text;
@NaturalId String authorName;
}

View File

@ -18,6 +18,9 @@ public interface Dao {
@Find
Book getBook(String title, String isbn);
@Find
Book getBookByNaturalKey(String authorName, String title);
@HQL("from Book where title like ?1")
TypedQuery<Book> findByTitle(String title);