根据此文档,我们可以将基于类的投影传递给jpa api:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections.dtos
因此,我正在尝试使用BeanGenerator创建动态的jpa投影,但似乎无法正常工作...有人知道如何在spring数据jpa中生成动态投影吗?
final Map<String, Class<?>> properties =
new HashMap<String, Class<?>>();
properties.put("name", String.class);
properties.put("phone", String.class);
final Class<?> beanClass = PojoGenerator.generate("com.logicbig.example.EmployeeName", properties);
List list3 = employeeRepository.findByDept("IT", beanClass);
import javassist.*;
import java.io.Serializable;
import java.util.Map;
import java.util.Map.Entry;
/**
* copy from https://blog.javaforge.net/post/31913732423/howto-create-java-pojo-at-runtime-with-javassist
*
* @author blog.javaforge.net
*/
public class PojoGenerator {
public static Class generate(String className, Map<String, Class<?>> properties) throws NotFoundException,
CannotCompileException {
ClassPool pool = ClassPool.getDefault();
CtClass declaringClass = pool.makeClass(className);
// add this to define a super class to extend
// cc.setSuperclass(resolveCtClass(MySuperClass.class));
// add this to define an interface to implement
declaringClass.addInterface(resolveCtClass(Serializable.class));
for (Entry<String, Class<?>> entry : properties.entrySet()) {
String fieldName = entry.getKey();
Class<?> fieldClass = entry.getValue();
declaringClass.addField(new CtField(resolveCtClass(fieldClass), fieldName, declaringClass));
// add getter
declaringClass.addMethod(generateGetter(declaringClass, fieldName, fieldClass));
// add setter
declaringClass.addMethod(generateSetter(declaringClass, fieldName, fieldClass));
}
// add constructor
generateConstructor(declaringClass, properties);
return declaringClass.toClass();
}
private static CtMethod generateGetter(CtClass declaringClass, String fieldName, Class fieldClass)
throws CannotCompileException {
String getterName = "get" + fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1);
StringBuffer sb = new StringBuffer();
sb.append("public ").append(fieldClass.getName()).append(" ")
.append(getterName).append("(){").append("return this.")
.append(fieldName).append(";").append("}");
return CtMethod.make(sb.toString(), declaringClass);
}
private static CtMethod generateSetter(CtClass declaringClass, String fieldName, Class fieldClass)
throws CannotCompileException {
String setterName = "set" + fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1);
StringBuffer sb = new StringBuffer();
sb.append("public void ").append(setterName).append("(")
.append(fieldClass.getName()).append(" ").append(fieldName)
.append(")").append("{").append("this.").append(fieldName)
.append("=").append(fieldName).append(";").append("}");
return CtMethod.make(sb.toString(), declaringClass);
}
private static void generateConstructor(CtClass declaringClass, Map<String, Class<?>> properties)
throws CannotCompileException {
String constructorMethodName = declaringClass.getSimpleName();
StringBuffer constructorMethod = new StringBuffer();
StringBuffer constructorBody = new StringBuffer();
constructorMethod.append("public ").append(constructorMethodName).append("(");
for (Entry<String, Class<?>> entry : properties.entrySet()) {
String fieldName = entry.getKey();
Class<?> fieldClass = entry.getValue();
// add constructor
constructorMethod.append(fieldClass.getName()).append(" ").append(fieldName).append(",");
constructorBody.append("this.").append(fieldName)
.append("=").append(fieldName).append(";");
}
constructorMethod.deleteCharAt(constructorMethod.lastIndexOf(","));
constructorMethod.append(") {");
constructorMethod.append(constructorBody);
constructorMethod.append("}");
System.out.println(constructorMethod);
CtConstructor constructor = CtNewConstructor.make(constructorMethod.toString(), declaringClass);
declaringClass.addConstructor(constructor);
CtConstructor defaultConstructor = CtNewConstructor.make("public " + constructorMethodName + "() {}", declaringClass);
declaringClass.addConstructor(defaultConstructor);
}
private static CtClass resolveCtClass(Class clazz) throws NotFoundException {
ClassPool pool = ClassPool.getDefault();
return pool.get(clazz.getName());
}
}
====== finding beanClass for IT dept =====
13:50:12.495 [main] DEBUG org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler - Creating new EntityManager for shared EntityManager invocation
13:50:12.495 [main] DEBUG org.hibernate.query.criteria.internal.CriteriaQueryImpl - Rendered criteria query -> select generatedAlias0 from Employee as generatedAlias0 where generatedAlias0.dept=:param0
13:50:12.496 [main] DEBUG org.hibernate.hql.internal.ast.QueryTranslatorImpl - parse() - HQL: select generatedAlias0 from com.logicbig.example.Employee as generatedAlias0 where generatedAlias0.dept=:param0
13:50:12.497 [main] DEBUG org.hibernate.hql.internal.ast.ErrorTracker - throwQueryException() : no errors
13:50:12.498 [main] DEBUG org.hibernate.hql.internal.ast.QueryTranslatorImpl - --- HQL AST ---
\-[QUERY] Node: 'query'
+-[SELECT_FROM] Node: 'SELECT_FROM'
| +-[FROM] Node: 'from'
| | \-[RANGE] Node: 'RANGE'
| | +-[DOT] Node: '.'
| | | +-[DOT] Node: '.'
| | | | +-[DOT] Node: '.'
| | | | | +-[IDENT] Node: 'com'
| | | | | \-[IDENT] Node: 'logicbig'
| | | | \-[IDENT] Node: 'example'
| | | \-[IDENT] Node: 'Employee'
| | \-[ALIAS] Node: 'generatedAlias0'
| \-[SELECT] Node: 'select'
| \-[IDENT] Node: 'generatedAlias0'
\-[WHERE] Node: 'where'
\-[EQ] Node: '='
+-[DOT] Node: '.'
| +-[IDENT] Node: 'generatedAlias0'
| \-[IDENT] Node: 'dept'
\-[COLON] Node: ':'
\-[IDENT] Node: 'param0'
13:50:12.498 [main] DEBUG org.hibernate.hql.internal.antlr.HqlSqlBaseWalker - select << begin [level=1, statement=select]
13:50:12.499 [main] DEBUG org.hibernate.hql.internal.ast.tree.FromElement - FromClause{level=1} : com.logicbig.example.Employee (generatedAlias0) -> employee0_
13:50:12.499 [main] DEBUG org.hibernate.hql.internal.ast.tree.FromReferenceNode - Resolved : generatedAlias0 -> employee0_.id
13:50:12.500 [main] DEBUG org.hibernate.hql.internal.ast.tree.FromReferenceNode - Resolved : generatedAlias0 -> employee0_.id
13:50:12.500 [main] DEBUG org.hibernate.hql.internal.ast.tree.DotNode - getDataType() : dept -> org.hibernate.type.StringType@6b63d445
13:50:12.500 [main] DEBUG org.hibernate.hql.internal.ast.tree.FromReferenceNode - Resolved : generatedAlias0.dept -> employee0_.dept
13:50:12.500 [main] DEBUG org.hibernate.hql.internal.antlr.HqlSqlBaseWalker - select : finishing up [level=1, statement=select]
13:50:12.500 [main] DEBUG org.hibernate.hql.internal.ast.HqlSqlWalker - processQuery() : ( SELECT ( {select clause} employee0_.id ) ( FromClause{level=1} Employee employee0_ ) ( where ( = ( employee0_.dept employee0_.id dept ) ? ) ) )
13:50:12.509 [main] DEBUG org.hibernate.hql.internal.ast.util.JoinProcessor - Using FROM fragment [Employee employee0_]
13:50:12.509 [main] DEBUG org.hibernate.hql.internal.antlr.HqlSqlBaseWalker - select >> end [level=1, statement=select]
13:50:12.510 [main] DEBUG org.hibernate.hql.internal.ast.QueryTranslatorImpl - --- SQL AST ---
\-[SELECT] QueryNode: 'SELECT' querySpaces (Employee)
+-[SELECT_CLAUSE] SelectClause: '{select clause}'
| +-[ALIAS_REF] IdentNode: 'employee0_.id as id1_0_' {alias=generatedAlias0, className=com.logicbig.example.Employee, tableAlias=employee0_}
| \-[SQL_TOKEN] SqlFragment: 'employee0_.dept as dept2_0_, employee0_.name as name3_0_, employee0_.phone as phone4_0_, employee0_.salary as salary5_0_'
+-[FROM] FromClause: 'from' FromClause{level=1, fromElementCounter=1, fromElements=1, fromElementByClassAlias=[generatedAlias0], fromElementByTableAlias=[employee0_], fromElementsByPath=[], collectionJoinFromElementsByPath=[], impliedElements=[]}
| \-[FROM_FRAGMENT] FromElement: 'Employee employee0_' FromElement{explicit,not a collection join,not a fetch join,fetch non-lazy properties,classAlias=generatedAlias0,role=null,tableName=Employee,tableAlias=employee0_,origin=null,columns={,className=com.logicbig.example.Employee}}
\-[WHERE] SqlNode: 'where'
\-[EQ] BinaryLogicOperatorNode: '='
+-[DOT] DotNode: 'employee0_.dept' {propertyName=dept,dereferenceType=PRIMITIVE,getPropertyPath=dept,path=generatedAlias0.dept,tableAlias=employee0_,className=com.logicbig.example.Employee,classAlias=generatedAlias0}
| +-[ALIAS_REF] IdentNode: 'employee0_.id' {alias=generatedAlias0, className=com.logicbig.example.Employee, tableAlias=employee0_}
| \-[IDENT] IdentNode: 'dept' {originalText=dept}
\-[NAMED_PARAM] ParameterNode: '?' {name=param0, expectedType=org.hibernate.type.StringType@6b63d445}
13:50:12.510 [main] DEBUG org.hibernate.hql.internal.ast.ErrorTracker - throwQueryException() : no errors
13:50:12.510 [main] DEBUG org.hibernate.hql.internal.ast.QueryTranslatorImpl - HQL: select generatedAlias0 from com.logicbig.example.Employee as generatedAlias0 where generatedAlias0.dept=:param0
13:50:12.510 [main] DEBUG org.hibernate.hql.internal.ast.QueryTranslatorImpl - SQL: select employee0_.id as id1_0_, employee0_.dept as dept2_0_, employee0_.name as name3_0_, employee0_.phone as phone4_0_, employee0_.salary as salary5_0_ from Employee employee0_ where employee0_.dept=?
13:50:12.510 [main] DEBUG org.hibernate.hql.internal.ast.ErrorTracker - throwQueryException() : no errors
13:50:12.511 [main] DEBUG org.hibernate.SQL - select employee0_.id as id1_0_, employee0_.dept as dept2_0_, employee0_.name as name3_0_, employee0_.phone as phone4_0_, employee0_.salary as salary5_0_ from Employee employee0_ where employee0_.dept=?
13:50:12.512 [main] DEBUG org.hibernate.loader.Loader - Result set row: 0
13:50:12.515 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[com.logicbig.example.Employee#2]
13:50:12.520 [main] DEBUG org.hibernate.loader.Loader - Result set row: 1
13:50:12.521 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[com.logicbig.example.Employee#5]
13:50:12.521 [main] DEBUG org.hibernate.loader.Loader - Result set row: 2
13:50:12.521 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[com.logicbig.example.Employee#6]
13:50:12.521 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Resolving associations for [com.logicbig.example.Employee#2]
13:50:12.522 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Done materializing entity [com.logicbig.example.Employee#2]
13:50:12.522 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Resolving associations for [com.logicbig.example.Employee#5]
13:50:12.522 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Done materializing entity [com.logicbig.example.Employee#5]
13:50:12.522 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Resolving associations for [com.logicbig.example.Employee#6]
13:50:12.522 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Done materializing entity [com.logicbig.example.Employee#6]
13:50:12.522 [main] DEBUG org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl - Initiating JDBC connection release from afterTransaction
13:50:12.523 [main] DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager
Exception in thread "main" org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [com.logicbig.example.Employee] to type [com.logicbig.example.EmployeeName]
at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:321)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:194)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:174)
at org.springframework.data.repository.query.ResultProcessor$ProjectingConverter.convert(ResultProcessor.java:293)
at org.springframework.data.repository.query.ResultProcessor$ChainingConverter.lambda$and$0(ResultProcessor.java:213)
at org.springframework.data.repository.query.ResultProcessor$ChainingConverter.convert(ResultProcessor.java:224)
at org.springframework.data.repository.query.ResultProcessor.processResult(ResultProcessor.java:152)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:141)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:125)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:590)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:578)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:135)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy34.findByDept(Unknown Source)
at com.logicbig.example.ExampleClient.run(ExampleClient.java:74)
at com.logicbig.example.ExampleMain.main(ExampleMain.java:12)
13:50:26.941 [pool-1-thread-1] DEBUG org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl - Connection pool now considered primed; min-size will be maintained
假设BeanGenerator
生成典型的Java Bean,则该类可能缺少具有所有必填字段的构造函数:
如果商店通过限制要加载的字段来优化查询执行,则要加载的字段由公开的构造函数的参数名称确定。
引号来自https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections.dtos