HHH-16633 add ability to generate @Find methods for @NaturalIds and arbitrary field lists
This commit is contained in:
parent
0c40711563
commit
d7fd5bd78a
|
@ -16,8 +16,44 @@ import static java.lang.annotation.RetentionPolicy.CLASS;
|
|||
|
||||
/**
|
||||
* Identifies a method of an abstract class or interface as defining
|
||||
* the signature of a finder method, and is generated automatically by
|
||||
* the Hibernate Metamodel Generator.
|
||||
* the signature of a finder method, and being generated automatically
|
||||
* by the Hibernate Metamodel Generator.
|
||||
* <p>
|
||||
* For example:
|
||||
* <pre>
|
||||
* @Find
|
||||
* Book getBookForIsbn(String isbn);
|
||||
*
|
||||
* @Find
|
||||
* List<Book> getBooksWithTitle(String title);
|
||||
* </pre>
|
||||
* <p>
|
||||
* The return type of an annotated method must be an entity type {@code E},
|
||||
* or one of the following types:
|
||||
* <ul>
|
||||
* <li>{@link java.util.List java.util.List<E>},
|
||||
* <li>{@link org.hibernate.query.Query org.hibernate.query.Query<E>},
|
||||
* <li>{@link org.hibernate.query.SelectionQuery org.hibernate.query.SelectionQuery<E>},
|
||||
* <li>{@link jakarta.persistence.Query jakarta.persistence.Query<E>}, or
|
||||
* <li>{@link jakarta.persistence.TypedQuery jakarta.persistence.TypedQuery<E>}.
|
||||
* </ul>
|
||||
* <p>
|
||||
* The names and types of the parameters of a finder method must match
|
||||
* exactly with the names and types of persistent fields of the entity
|
||||
* type returned by the finder method.
|
||||
* <ul>
|
||||
* <li>If there is one parameter, and it matches the {@code @Id} or
|
||||
* {@code @EmbeddedId} field of the entity, the finder method uses
|
||||
* {@link jakarta.persistence.EntityManager#find(Class, Object)}
|
||||
* to retrieve the entity.
|
||||
* <li>If the parameters match exactly with the {@code @NaturalId}
|
||||
* fieldd of the entity, the finder method uses
|
||||
* {@link org.hibernate.Session#byNaturalId(Class)} to retrieve the
|
||||
* entity.
|
||||
* <li>Otherwise, the finder method builds and executes a
|
||||
* {@linkplain jakarta.persistence.criteria.CriteriaBuilder criteria
|
||||
* query}.
|
||||
* </ul>
|
||||
*
|
||||
* @author Gavin King
|
||||
* @since 6.3
|
||||
|
|
|
@ -30,7 +30,7 @@ import static java.lang.annotation.RetentionPolicy.CLASS;
|
|||
* List<Book> findBooksByTitleWithPagination(String title, int max, int start);
|
||||
*
|
||||
* @HQL("from Book where title like ?1")
|
||||
* TypedQuery<Book> findBooksByTitle(String title);
|
||||
* TypedQuery<Book> createBooksByTitleQuery(String title);
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
|
|
|
@ -272,11 +272,9 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
final Element element = ((DeclaredType) type).asElement();
|
||||
if ( element.getKind() == ElementKind.INTERFACE ) {
|
||||
final Name name = ((TypeElement) element).getQualifiedName();
|
||||
if ( name.contentEquals( Session.class.getName() )
|
||||
|| name.contentEquals( EntityManager.class.getName() )
|
||||
|| name.contentEquals( StatelessSession.class.getName() ) ) {
|
||||
return true;
|
||||
}
|
||||
return name.contentEquals(Session.class.getName())
|
||||
|| name.contentEquals(EntityManager.class.getName())
|
||||
|| name.contentEquals(StatelessSession.class.getName());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -414,12 +412,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
ExecutableElement method,
|
||||
@Nullable TypeMirror returnType,
|
||||
@Nullable TypeElement containerType) {
|
||||
if ( containerType != null ) {
|
||||
context.message( method,
|
||||
"incorrect return type '" + containerType.getQualifiedName() + "' is not an entity type",
|
||||
Diagnostic.Kind.ERROR );
|
||||
}
|
||||
else if ( returnType == null || returnType.getKind() != TypeKind.DECLARED ) {
|
||||
if ( returnType == null || returnType.getKind() != TypeKind.DECLARED ) {
|
||||
context.message( method,
|
||||
"incorrect return type '" + returnType + "' is not an entity type",
|
||||
Diagnostic.Kind.ERROR );
|
||||
|
@ -433,62 +426,184 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
Diagnostic.Kind.ERROR );
|
||||
}
|
||||
else {
|
||||
switch ( method.getParameters().size() ) {
|
||||
case 0:
|
||||
context.message( method,
|
||||
"missing parameter",
|
||||
Diagnostic.Kind.ERROR );
|
||||
break;
|
||||
case 1:
|
||||
final VariableElement parameter = method.getParameters().get(0);
|
||||
validateFinderParameter( entity, parameter);
|
||||
final String methodName = method.getSimpleName().toString();
|
||||
putMember( methodName,
|
||||
new FinderMethod(
|
||||
this,
|
||||
methodName,
|
||||
returnType.toString(),
|
||||
parameter.getSimpleName().toString(),
|
||||
parameter.asType().toString(),
|
||||
dao
|
||||
)
|
||||
);
|
||||
break;
|
||||
default:
|
||||
context.message( method,
|
||||
"too many parameters ('@IdClass' not yet supported)",
|
||||
Diagnostic.Kind.ERROR );
|
||||
if ( containerType != null ) {
|
||||
// it has to be a criteria finder (multiple results)
|
||||
createContainerResultFinder( method, returnType, containerType, entity );
|
||||
}
|
||||
else {
|
||||
switch ( method.getParameters().size() ) {
|
||||
case 0: {
|
||||
context.message( method,
|
||||
"missing parameter",
|
||||
Diagnostic.Kind.ERROR );
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
createSingleParameterFinder( method, returnType, entity );
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
createMultipleParameterFinder( method, returnType, entity );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateFinderParameter(TypeElement entity, VariableElement param) {
|
||||
entity.getEnclosedElements().stream()
|
||||
.filter(member -> member.getSimpleName().contentEquals( param.getSimpleName() )
|
||||
&& member.getAnnotationMirrors().stream()
|
||||
.anyMatch(annotation -> {
|
||||
final TypeElement annotationType = (TypeElement)
|
||||
annotation.getAnnotationType().asElement();
|
||||
final Name annotatioName = annotationType.getQualifiedName();
|
||||
return annotatioName.contentEquals(Constants.ID)
|
||||
|| annotatioName.contentEquals(Constants.EMBEDDED_ID);
|
||||
}))
|
||||
.findAny()
|
||||
.ifPresentOrElse(
|
||||
member -> {
|
||||
final String memberType = member.asType().toString();
|
||||
final String paramType = param.asType().toString();
|
||||
if ( !memberType.equals(paramType)) {
|
||||
context.message( param,
|
||||
"matching '@Id' field in entity class has type '" + memberType + "'",
|
||||
Diagnostic.Kind.ERROR );
|
||||
}
|
||||
},
|
||||
() -> context.message( param,
|
||||
"no matching '@Id' field in entity class",
|
||||
Diagnostic.Kind.ERROR )
|
||||
);
|
||||
private void createContainerResultFinder(ExecutableElement method, TypeMirror returnType, TypeElement containerType, TypeElement entity) {
|
||||
final String methodName = method.getSimpleName().toString();
|
||||
final List<String> paramNames = parameterNames(method);
|
||||
final List<String> paramTypes = parameterTypes(method);
|
||||
final String methodKey = methodName + paramTypes;
|
||||
for ( VariableElement param : method.getParameters() ) {
|
||||
validateFinderParameter(entity, param );
|
||||
}
|
||||
putMember( methodKey,
|
||||
new CriteriaFinderMethod(
|
||||
this,
|
||||
methodName,
|
||||
returnType.toString(),
|
||||
containerType.toString(),
|
||||
paramNames,
|
||||
paramTypes,
|
||||
dao
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private void createMultipleParameterFinder(ExecutableElement method, TypeMirror returnType, TypeElement entity) {
|
||||
final String methodName = method.getSimpleName().toString();
|
||||
final List<String> paramNames = parameterNames(method);
|
||||
final List<String> paramTypes = parameterTypes(method);
|
||||
final String methodKey = methodName + paramTypes;
|
||||
if ( matchesNaturalKey(method, entity) ) {
|
||||
putMember( methodKey,
|
||||
new NaturalIdFinderMethod(
|
||||
this,
|
||||
methodName,
|
||||
returnType.toString(),
|
||||
paramNames,
|
||||
paramTypes,
|
||||
dao
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
putMember( methodKey,
|
||||
new CriteriaFinderMethod(
|
||||
this,
|
||||
methodName,
|
||||
returnType.toString(),
|
||||
null,
|
||||
paramNames,
|
||||
paramTypes,
|
||||
dao
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void createSingleParameterFinder(ExecutableElement method, TypeMirror returnType, TypeElement entity) {
|
||||
final String methodName = method.getSimpleName().toString();
|
||||
final VariableElement parameter = method.getParameters().get(0);
|
||||
final FieldType fieldType = validateFinderParameter(entity, parameter );
|
||||
if ( fieldType != null ) {
|
||||
final String methodKey = methodName + "!";
|
||||
switch ( fieldType ) {
|
||||
case NATURAL_ID:
|
||||
putMember( methodKey,
|
||||
new NaturalIdFinderMethod(
|
||||
this,
|
||||
methodName,
|
||||
returnType.toString(),
|
||||
List.of( parameter.getSimpleName().toString() ),
|
||||
List.of( parameter.asType().toString() ),
|
||||
dao
|
||||
)
|
||||
);
|
||||
break;
|
||||
case ID:
|
||||
putMember( methodKey,
|
||||
new IdFinderMethod(
|
||||
this,
|
||||
methodName,
|
||||
returnType.toString(),
|
||||
parameter.getSimpleName().toString(),
|
||||
parameter.asType().toString(),
|
||||
dao
|
||||
)
|
||||
);
|
||||
break;
|
||||
case BASIC:
|
||||
putMember( methodKey,
|
||||
new CriteriaFinderMethod(
|
||||
this,
|
||||
methodName,
|
||||
returnType.toString(),
|
||||
null,
|
||||
List.of( parameter.getSimpleName().toString() ),
|
||||
List.of( parameter.asType().toString() ),
|
||||
dao
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean matchesNaturalKey(ExecutableElement method, TypeElement entity) {
|
||||
boolean result = true;
|
||||
List<? extends VariableElement> parameters = method.getParameters();
|
||||
for ( VariableElement param : parameters) {
|
||||
if ( validateFinderParameter( entity, param ) != FieldType.NATURAL_ID ) {
|
||||
// no short-circuit here because we want to validate
|
||||
// all of them and get the nice error report
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return result && countNaturalIdFields( entity ) == parameters.size() ;
|
||||
}
|
||||
|
||||
enum FieldType {
|
||||
ID, NATURAL_ID, BASIC
|
||||
}
|
||||
|
||||
private int countNaturalIdFields(TypeElement entity) {
|
||||
int count = 0;
|
||||
for ( Element member : entity.getEnclosedElements() ) {
|
||||
if ( containsAnnotation( member, Constants.NATURAL_ID ) ) {
|
||||
count ++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private @Nullable FieldType validateFinderParameter(TypeElement entity, VariableElement param) {
|
||||
for ( Element member : entity.getEnclosedElements() ) {
|
||||
if ( member.getSimpleName().contentEquals( param.getSimpleName() ) ) {
|
||||
final String memberType = member.asType().toString();
|
||||
final String paramType = param.asType().toString();
|
||||
if ( !memberType.equals(paramType) ) {
|
||||
context.message( param,
|
||||
"matching field in entity class has type '" + memberType + "'",
|
||||
Diagnostic.Kind.ERROR );
|
||||
}
|
||||
if ( containsAnnotation( member, Constants.ID, Constants.EMBEDDED_ID ) ) {
|
||||
return FieldType.ID;
|
||||
}
|
||||
else if ( containsAnnotation( member, Constants.NATURAL_ID ) ) {
|
||||
return FieldType.NATURAL_ID;
|
||||
}
|
||||
else {
|
||||
return FieldType.BASIC;
|
||||
}
|
||||
}
|
||||
}
|
||||
context.message( param,
|
||||
"no matching field named '" + param.getSimpleName() + "' in entity class",
|
||||
Diagnostic.Kind.ERROR );
|
||||
return null;
|
||||
}
|
||||
|
||||
private void addQueryMethod(
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* 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.jpamodelgen.annotation;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.hibernate.jpamodelgen.model.MetaAttribute;
|
||||
import org.hibernate.jpamodelgen.model.Metamodel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.hibernate.internal.util.StringHelper.join;
|
||||
|
||||
/**
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class CriteriaFinderMethod implements MetaAttribute {
|
||||
private final Metamodel annotationMetaEntity;
|
||||
private final String methodName;
|
||||
private final String entity;
|
||||
private final @Nullable String containerType;
|
||||
private final List<String> paramNames;
|
||||
private final List<String> paramTypes;
|
||||
private final boolean belongsToDao;
|
||||
|
||||
public CriteriaFinderMethod(
|
||||
Metamodel annotationMetaEntity,
|
||||
String methodName, String entity,
|
||||
@Nullable String containerType,
|
||||
List<String> paramNames, List<String> paramTypes,
|
||||
boolean belongsToDao) {
|
||||
this.annotationMetaEntity = annotationMetaEntity;
|
||||
this.methodName = methodName;
|
||||
this.entity = entity;
|
||||
this.containerType = containerType;
|
||||
this.paramNames = paramNames;
|
||||
this.paramTypes = paramTypes;
|
||||
this.belongsToDao = belongsToDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTypedAttribute() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStringAttribute() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttributeDeclarationString() {
|
||||
StringBuilder declaration = new StringBuilder();
|
||||
declaration
|
||||
.append("\n/**\n * @see ")
|
||||
.append(annotationMetaEntity.getQualifiedName())
|
||||
.append("#")
|
||||
.append(methodName)
|
||||
.append("(")
|
||||
.append(join(",", paramTypes.stream().map(this::strip).map(annotationMetaEntity::importType).toArray()))
|
||||
.append(")")
|
||||
.append("\n **/\n");
|
||||
if ( belongsToDao ) {
|
||||
declaration
|
||||
.append("@Override\npublic ");
|
||||
}
|
||||
else {
|
||||
declaration
|
||||
.append("public static ");
|
||||
}
|
||||
StringBuilder type = new StringBuilder();
|
||||
if ( containerType != null ) {
|
||||
type.append(annotationMetaEntity.importType(containerType)).append('<');
|
||||
}
|
||||
type.append(annotationMetaEntity.importType(entity));
|
||||
if ( containerType != null ) {
|
||||
type.append('>');
|
||||
}
|
||||
declaration
|
||||
.append(type)
|
||||
.append(" ")
|
||||
.append(methodName)
|
||||
.append("(");
|
||||
if ( !belongsToDao ) {
|
||||
declaration
|
||||
.append(annotationMetaEntity.importType("jakarta.persistence.EntityManager"))
|
||||
.append(" entityManager");
|
||||
}
|
||||
for ( int i = 0; i < paramNames.size(); i ++ ) {
|
||||
if ( !belongsToDao || i > 0 ) {
|
||||
declaration
|
||||
.append(", ");
|
||||
}
|
||||
declaration
|
||||
.append(annotationMetaEntity.importType(paramTypes.get(i)))
|
||||
.append(" ")
|
||||
.append(paramNames.get(i));
|
||||
}
|
||||
declaration
|
||||
.append(") {")
|
||||
.append("\n\tvar builder = entityManager.getEntityManagerFactory().getCriteriaBuilder();")
|
||||
.append("\n\tvar query = builder.createQuery(")
|
||||
.append(annotationMetaEntity.importType(entity))
|
||||
.append(".class);")
|
||||
.append("\n\tvar entity = query.from(")
|
||||
.append(annotationMetaEntity.importType(entity))
|
||||
.append(".class);")
|
||||
.append("\n\tquery.where(");
|
||||
for ( int i = 0; i < paramNames.size(); i ++ ) {
|
||||
if ( i>0 ) {
|
||||
declaration
|
||||
.append(", ");
|
||||
}
|
||||
declaration
|
||||
.append("\n\t\t\tbuilder.equal(entity.get(")
|
||||
.append(annotationMetaEntity.importType(entity+'_'))
|
||||
.append('.')
|
||||
.append(paramNames.get(i))
|
||||
.append("), ")
|
||||
//TODO: only safe if we are binding literals as parameters!!!
|
||||
.append(paramNames.get(i))
|
||||
.append(")");
|
||||
}
|
||||
|
||||
declaration
|
||||
.append("\n\t);")
|
||||
.append("\n\treturn ");
|
||||
if ( containerType != null && containerType.startsWith("org.hibernate") ) {
|
||||
declaration
|
||||
.append("(")
|
||||
.append(type)
|
||||
.append(") ");
|
||||
}
|
||||
declaration
|
||||
.append("entityManager.createQuery(query)");
|
||||
if ( containerType == null) {
|
||||
declaration
|
||||
.append("\n\t\t\t.getSingleResult()");
|
||||
}
|
||||
else if ( containerType.equals("java.util.List") ) {
|
||||
declaration
|
||||
.append("\n\t\t\t.getResultList()");
|
||||
}
|
||||
declaration
|
||||
.append(";\n}");
|
||||
return declaration.toString();
|
||||
}
|
||||
|
||||
private String strip(String type) {
|
||||
int index = type.indexOf("<");
|
||||
String stripped = index > 0 ? type.substring(0, index) : type;
|
||||
return type.endsWith("...") ? stripped + "..." : stripped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttributeNameDeclarationString() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMetaType() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPropertyName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeDeclaration() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Metamodel getHostingEntity() {
|
||||
return annotationMetaEntity;
|
||||
}
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
* 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.jpamodelgen.annotation;
|
||||
|
||||
import org.hibernate.jpamodelgen.model.MetaAttribute;
|
||||
import org.hibernate.jpamodelgen.model.Metamodel;
|
||||
|
||||
/**
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class FinderMethod implements MetaAttribute {
|
||||
private final Metamodel annotationMetaEntity;
|
||||
private final String methodName;
|
||||
private final String entity;
|
||||
private final String paramName;
|
||||
private final String paramType;
|
||||
private final boolean belongsToDao;
|
||||
|
||||
public FinderMethod(Metamodel annotationMetaEntity, String methodName, String entity, String paramName, String paramType, boolean belongsToDao) {
|
||||
this.annotationMetaEntity = annotationMetaEntity;
|
||||
this.methodName = methodName;
|
||||
this.entity = entity;
|
||||
this.paramName = paramName;
|
||||
this.paramType = paramType;
|
||||
this.belongsToDao = belongsToDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTypedAttribute() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStringAttribute() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttributeDeclarationString() {
|
||||
StringBuilder declaration = new StringBuilder();
|
||||
declaration
|
||||
.append("\n/**\n * @see ")
|
||||
.append(annotationMetaEntity.getQualifiedName())
|
||||
.append("#")
|
||||
.append(methodName)
|
||||
.append("(")
|
||||
.append(annotationMetaEntity.importType(paramType))
|
||||
.append(")")
|
||||
.append("\n **/\n");
|
||||
if ( belongsToDao ) {
|
||||
declaration
|
||||
.append("@Override\npublic ");
|
||||
}
|
||||
else {
|
||||
declaration
|
||||
.append("public static ");
|
||||
}
|
||||
declaration
|
||||
.append(annotationMetaEntity.importType(entity));
|
||||
declaration
|
||||
.append(" ")
|
||||
.append(methodName)
|
||||
.append("(");
|
||||
if ( !belongsToDao ) {
|
||||
declaration
|
||||
.append(annotationMetaEntity.importType("jakarta.persistence.EntityManager"))
|
||||
.append(" entityManager, ");
|
||||
}
|
||||
declaration
|
||||
.append(annotationMetaEntity.importType(paramType))
|
||||
.append(" ")
|
||||
.append(paramName)
|
||||
.append(") {")
|
||||
.append("\n\treturn entityManager.find(")
|
||||
.append(annotationMetaEntity.importType(entity))
|
||||
.append(".class, ")
|
||||
.append(paramName)
|
||||
.append(");")
|
||||
.append("\n}");
|
||||
return declaration.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttributeNameDeclarationString() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMetaType() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPropertyName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeDeclaration() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Metamodel getHostingEntity() {
|
||||
return annotationMetaEntity;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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.jpamodelgen.annotation;
|
||||
|
||||
import org.hibernate.jpamodelgen.model.MetaAttribute;
|
||||
import org.hibernate.jpamodelgen.model.Metamodel;
|
||||
|
||||
/**
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class IdFinderMethod implements MetaAttribute {
|
||||
private final Metamodel annotationMetaEntity;
|
||||
private final String methodName;
|
||||
private final String entity;
|
||||
private final String paramName;
|
||||
private final String paramType;
|
||||
private final boolean belongsToDao;
|
||||
|
||||
public IdFinderMethod(
|
||||
Metamodel annotationMetaEntity,
|
||||
String methodName, String entity,
|
||||
String paramName, String paramType,
|
||||
boolean belongsToDao) {
|
||||
this.annotationMetaEntity = annotationMetaEntity;
|
||||
this.methodName = methodName;
|
||||
this.entity = entity;
|
||||
this.paramName = paramName;
|
||||
this.paramType = paramType;
|
||||
this.belongsToDao = belongsToDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTypedAttribute() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStringAttribute() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttributeDeclarationString() {
|
||||
StringBuilder declaration = new StringBuilder();
|
||||
declaration
|
||||
.append("\n/**\n * @see ")
|
||||
.append(annotationMetaEntity.getQualifiedName())
|
||||
.append("#")
|
||||
.append(methodName)
|
||||
.append("(")
|
||||
.append(annotationMetaEntity.importType(paramType))
|
||||
.append(")")
|
||||
.append("\n **/\n");
|
||||
if ( belongsToDao ) {
|
||||
declaration
|
||||
.append("@Override\npublic ");
|
||||
}
|
||||
else {
|
||||
declaration
|
||||
.append("public static ");
|
||||
}
|
||||
declaration
|
||||
.append(annotationMetaEntity.importType(entity));
|
||||
declaration
|
||||
.append(" ")
|
||||
.append(methodName)
|
||||
.append("(");
|
||||
if ( !belongsToDao ) {
|
||||
declaration
|
||||
.append(annotationMetaEntity.importType("jakarta.persistence.EntityManager"))
|
||||
.append(" entityManager, ");
|
||||
}
|
||||
declaration
|
||||
.append(annotationMetaEntity.importType(paramType))
|
||||
.append(" ")
|
||||
.append(paramName)
|
||||
.append(") {")
|
||||
.append("\n\treturn entityManager.find(")
|
||||
.append(annotationMetaEntity.importType(entity))
|
||||
.append(".class, ")
|
||||
.append(paramName)
|
||||
.append(");")
|
||||
.append("\n}");
|
||||
return declaration.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttributeNameDeclarationString() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMetaType() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPropertyName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeDeclaration() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Metamodel getHostingEntity() {
|
||||
return annotationMetaEntity;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* 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.jpamodelgen.annotation;
|
||||
|
||||
import org.hibernate.jpamodelgen.model.MetaAttribute;
|
||||
import org.hibernate.jpamodelgen.model.Metamodel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.hibernate.internal.util.StringHelper.join;
|
||||
|
||||
/**
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class NaturalIdFinderMethod implements MetaAttribute {
|
||||
private final Metamodel annotationMetaEntity;
|
||||
private final String methodName;
|
||||
private final String entity;
|
||||
private final List<String> paramNames;
|
||||
private final List<String> paramTypes;
|
||||
private final boolean belongsToDao;
|
||||
|
||||
public NaturalIdFinderMethod(
|
||||
Metamodel annotationMetaEntity,
|
||||
String methodName, String entity,
|
||||
List<String> paramNames, List<String> paramTypes,
|
||||
boolean belongsToDao) {
|
||||
this.annotationMetaEntity = annotationMetaEntity;
|
||||
this.methodName = methodName;
|
||||
this.entity = entity;
|
||||
this.paramNames = paramNames;
|
||||
this.paramTypes = paramTypes;
|
||||
this.belongsToDao = belongsToDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTypedAttribute() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStringAttribute() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttributeDeclarationString() {
|
||||
StringBuilder declaration = new StringBuilder();
|
||||
declaration
|
||||
.append("\n/**\n * @see ")
|
||||
.append(annotationMetaEntity.getQualifiedName())
|
||||
.append("#")
|
||||
.append(methodName)
|
||||
.append("(")
|
||||
.append(join(",", paramTypes.stream().map(this::strip).map(annotationMetaEntity::importType).toArray()))
|
||||
.append(")")
|
||||
.append("\n **/\n");
|
||||
if ( belongsToDao ) {
|
||||
declaration
|
||||
.append("@Override\npublic ");
|
||||
}
|
||||
else {
|
||||
declaration
|
||||
.append("public static ");
|
||||
}
|
||||
declaration
|
||||
.append(annotationMetaEntity.importType(entity));
|
||||
declaration
|
||||
.append(" ")
|
||||
.append(methodName)
|
||||
.append("(");
|
||||
if ( !belongsToDao ) {
|
||||
declaration
|
||||
.append(annotationMetaEntity.importType("jakarta.persistence.EntityManager"))
|
||||
.append(" entityManager");
|
||||
}
|
||||
for ( int i = 0; i < paramNames.size(); i ++ ) {
|
||||
if ( !belongsToDao || i > 0 ) {
|
||||
declaration
|
||||
.append(", ");
|
||||
}
|
||||
declaration
|
||||
.append(annotationMetaEntity.importType(paramTypes.get(i)))
|
||||
.append(" ")
|
||||
.append(paramNames.get(i));
|
||||
}
|
||||
declaration
|
||||
.append(") {")
|
||||
.append("\n\treturn entityManager")
|
||||
//TODO: skip if unnecessary:
|
||||
.append(".unwrap(")
|
||||
.append(annotationMetaEntity.importType("org.hibernate.Session"))
|
||||
.append(".class)\n\t\t\t")
|
||||
.append(".byNaturalId(")
|
||||
.append(annotationMetaEntity.importType(entity))
|
||||
.append(".class)");
|
||||
for ( int i = 0; i < paramNames.size(); i ++ ) {
|
||||
declaration
|
||||
.append("\n\t\t\t.using(")
|
||||
.append(annotationMetaEntity.importType(entity+'_'))
|
||||
.append('.')
|
||||
.append(paramNames.get(i))
|
||||
.append(", ")
|
||||
.append(paramNames.get(i))
|
||||
.append(")");
|
||||
}
|
||||
declaration
|
||||
.append("\n\t\t\t.load();\n}");
|
||||
return declaration.toString();
|
||||
}
|
||||
|
||||
private String strip(String type) {
|
||||
int index = type.indexOf("<");
|
||||
String stripped = index > 0 ? type.substring(0, index) : type;
|
||||
return type.endsWith("...") ? stripped + "..." : stripped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttributeNameDeclarationString() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMetaType() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPropertyName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeDeclaration() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Metamodel getHostingEntity() {
|
||||
return annotationMetaEntity;
|
||||
}
|
||||
}
|
|
@ -70,7 +70,9 @@ public class QueryMethod implements MetaAttribute {
|
|||
@Override
|
||||
public String getAttributeDeclarationString() {
|
||||
List<String> paramTypes = this.paramTypes.stream()
|
||||
.map(ptype->isOrderParam(ptype) && ptype.endsWith("[]") ? ptype.substring(0, ptype.length()-2) + "..." : ptype)
|
||||
.map(ptype -> isOrderParam(ptype) && ptype.endsWith("[]")
|
||||
? ptype.substring(0, ptype.length()-2) + "..."
|
||||
: ptype)
|
||||
.collect(toList());
|
||||
StringBuilder declaration = new StringBuilder();
|
||||
declaration
|
||||
|
@ -79,7 +81,8 @@ public class QueryMethod implements MetaAttribute {
|
|||
.append("#")
|
||||
.append(methodName)
|
||||
.append("(")
|
||||
.append(join(",", paramTypes.stream().map(this::strip).map(annotationMetaEntity::importType).toArray()))
|
||||
.append(join(",", paramTypes.stream().map(this::strip)
|
||||
.map(annotationMetaEntity::importType).toArray()))
|
||||
.append(")")
|
||||
.append("\n **/\n");
|
||||
boolean hasVarargs = paramTypes.stream().anyMatch(ptype -> ptype.endsWith("..."));
|
||||
|
@ -103,7 +106,8 @@ public class QueryMethod implements MetaAttribute {
|
|||
if (containerTypeName != null) {
|
||||
type.append(annotationMetaEntity.importType(containerTypeName));
|
||||
if (returnTypeName != null) {
|
||||
type.append("<").append(annotationMetaEntity.importType(returnTypeName)).append(">");
|
||||
type.append("<")
|
||||
.append(annotationMetaEntity.importType(returnTypeName)).append(">");
|
||||
}
|
||||
}
|
||||
else if (returnTypeName != null) {
|
||||
|
@ -205,10 +209,12 @@ public class QueryMethod implements MetaAttribute {
|
|||
}
|
||||
}
|
||||
if ( containerTypeName == null) {
|
||||
declaration.append("\n\t\t\t.getSingleResult()");
|
||||
declaration
|
||||
.append("\n\t\t\t.getSingleResult()");
|
||||
}
|
||||
else if ( containerTypeName.equals("java.util.List") ) {
|
||||
declaration.append("\n\t\t\t.getResultList()");
|
||||
declaration
|
||||
.append("\n\t\t\t.getResultList()");
|
||||
}
|
||||
declaration.append(";\n}");
|
||||
return declaration.toString();
|
||||
|
|
|
@ -19,6 +19,7 @@ public final class Constants {
|
|||
public static final String EMBEDDABLE = "jakarta.persistence.Embeddable";
|
||||
public static final String ID = "jakarta.persistence.Id";
|
||||
public static final String EMBEDDED_ID = "jakarta.persistence.EmbeddedId";
|
||||
public static final String NATURAL_ID = "org.hibernate.annotations.NaturalId";
|
||||
public static final String TRANSIENT = "jakarta.persistence.Transient";
|
||||
public static final String BASIC = "jakarta.persistence.Basic";
|
||||
public static final String ONE_TO_ONE = "jakarta.persistence.OneToOne";
|
||||
|
|
|
@ -2,10 +2,12 @@ package org.hibernate.jpamodelgen.test.dao;
|
|||
|
||||
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;
|
||||
@NaturalId String author;
|
||||
String text;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import jakarta.persistence.TypedQuery;
|
|||
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;
|
||||
|
||||
|
@ -15,6 +16,18 @@ public interface Dao {
|
|||
@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);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.hibernate.jpamodelgen.test.hqlsql;
|
||||
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import org.hibernate.annotations.processing.Find;
|
||||
import org.hibernate.annotations.processing.HQL;
|
||||
import org.hibernate.annotations.processing.SQL;
|
||||
import org.hibernate.query.Order;
|
||||
|
@ -10,6 +11,13 @@ import org.hibernate.query.SelectionQuery;
|
|||
import java.util.List;
|
||||
|
||||
public interface Dao {
|
||||
|
||||
@Find
|
||||
Book getBook(String isbn);
|
||||
|
||||
@Find
|
||||
Book getBook(String title, String isbn);
|
||||
|
||||
@HQL("from Book where title like ?1")
|
||||
TypedQuery<Book> findByTitle(String title);
|
||||
|
||||
|
|
Loading…
Reference in New Issue