slyak / spring-data-jpa-extra

spring data jpa with template dynamic query (eg: freemarker,velocity etc.) like mybatis. The master branch already support springboot2+ spring5+
Apache License 2.0
398 stars 115 forks source link

Invalid derived query! No property XXX found for type YYY! #26

Open bladethirst opened 6 years ago

bladethirst commented 6 years ago

你好!我的开发工具为Spring Source Tool Suite。

  1. 我在项目中按说明使用了@TemplateQuery,在REPO接口中,会提示查找的对象没有属性。当我的代码是这样 public interface ApmObjThresSnapshotRepo extends BaseDao<ApmObjThresSnapshot, String> { @TemplateQuery public Page<Map<String, Object>> findMgedobjThresSnapshot(ApmObjThresSnapshot snap, Pageable pageable);

    @TemplateQuery public List<Map<String, Object>> findMgedobjThresSnapshot(); } 时,启动会提示 Caused by: java.lang.IllegalArgumentException: Failed to create query for method public abstract org.springframework.data.domain.Page com.set.me.repo.apm.ApmObjThresSnapshotRepo.findMgedobjThresSnapshot(com.set.me.model.apm.ApmObjThresSnapshot,org.springframework.data.domain.Pageable)! No property findMgedobjThresSnapshot found for type ApmObjThresSnapshot!

  2. 我直接clone您的spring-data-jpa-extra到本地, 运行Test代码,会提示 11:11:52.378 [main] ERROR org.springframework.test.context.TestContextManager - Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@1a04f701] to prepare test instance [com.slyak.spring.jpa.JpaTest@63021689] java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83) at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117) at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83) at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206) Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in com.slyak.spring.jpa.AppConfig: Invocation of init method failed; nested exception is java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1080) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:857) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543) at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:128) at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60) at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:106) at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:249) at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98) at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116) ... 25 common frames omitted Caused by: java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy at sun.reflect.annotation.AnnotationParser.parseClassArray(AnnotationParser.java:724) at sun.reflect.annotation.AnnotationParser.parseArray(AnnotationParser.java:531) at sun.reflect.annotation.AnnotationParser.parseMemberValue(AnnotationParser.java:355) at sun.reflect.annotation.AnnotationParser.parseAnnotation2(AnnotationParser.java:286) at sun.reflect.annotation.AnnotationParser.parseAnnotations2(AnnotationParser.java:120) at sun.reflect.annotation.AnnotationParser.parseAnnotations(AnnotationParser.java:72) at java.lang.Class.createAnnotationData(Class.java:3521) at java.lang.Class.annotationData(Class.java:3510) at java.lang.Class.getAnnotation(Class.java:3415) at java.lang.reflect.AnnotatedElement.isAnnotationPresent(AnnotatedElement.java:258) at java.lang.Class.isAnnotationPresent(Class.java:3425) at org.hibernate.annotations.common.reflection.java.JavaAnnotationReader.isAnnotationPresent(JavaAnnotationReader.java:50) at org.hibernate.annotations.common.reflection.java.JavaXAnnotatedElement.isAnnotationPresent(JavaXAnnotatedElement.java:60) at org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.categorizeAnnotatedClass(AnnotationMetadataSourceProcessorImpl.java:115) at org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.(AnnotationMetadataSourceProcessorImpl.java:104) at org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.(MetadataBuildingProcess.java:147) at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:141) at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:847) at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:874) at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60) at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:360) at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:382) at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:371) at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:336) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624) ... 40 common frames omitted 11:11:52.390 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - After test class: context [DefaultTestContext@6f96c77 testClass = JpaTest, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@be64738 testClass = JpaTest, locations = '{}', classes = '{class com.slyak.spring.jpa.AppConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]], class annotated with @DirtiesContext [false] with mode [null].

stormning commented 6 years ago

你好: 第一点,你的Repo应该继承GenericJpaRepository 第二点,你clone的是master分支,master分支是一个正在开发的新版本,可能会有跑不通的情况,请选择其他的branch

谢谢你的来信,如果有问题,再沟通

在 2018年6月15日,上午11:19,bladethirst notifications@github.com 写道:

org.springframework.test.context.TestContextManager

bladethirst commented 6 years ago

@stormning 感谢你的超快速答复! 目前我的项目用的是spring-boot 2.0.2版本,多数据源形式。 关于上面两个问题,第2个问题,我用您的2.1.2.RELEASE分支,可以测试通过。 第1个问题,我maven依赖上面的2.1.2.RELEASE的jar包,之前我用的是继承BaseDao,BaseDao继承了您的GenericJpaRepository,和另外的PagingAndSortingRepository、JpaSpecificationExecutor。测试过了还是报同样的spring data jpa找不到实体属性的错。单独写一个Repo只继承GenericJpaRepository,也是报同样的错。 堆栈如下: Caused by: java.lang.IllegalArgumentException: Failed to create query for method public abstract org.springframework.data.domain.Page com.set.me.repo.apm.ApmObjThresSnapshotNativeRepo.findMgedobjThresSnapshot(com.set.me.model.apm.ApmObjThresSnapshot,org.springframework.data.domain.Pageable)! No property findMgedobjThresSnapshot found for type ApmObjThresSnapshot! at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.(PartTreeJpaQuery.java:82) ~[spring-data-jpa-2.0.7.RELEASE.jar:2.0.7.RELEASE] at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:103) ~[spring-data-jpa-2.0.7.RELEASE.jar:2.0.7.RELEASE] at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:208) ~[spring-data-jpa-2.0.7.RELEASE.jar:2.0.7.RELEASE] at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:79) ~[spring-data-jpa-2.0.7.RELEASE.jar:2.0.7.RELEASE] at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lookupQuery(RepositoryFactorySupport.java:553) ~[spring-data-commons-2.0.7.RELEASE.jar:2.0.7.RELEASE] at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$mapMethodsToQuery$1(RepositoryFactorySupport.java:546) ~[spring-data-commons-2.0.7.RELEASE.jar:2.0.7.RELEASE] at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[na:1.8.0_162]

我再查看了您项目的源码后,推测您的动态查询应该是由com.slyak.spring.jpa.TemplateQueryLookupStrategy这个类来解析的吧,对应上面错误堆栈中的org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy.AbstractQueryLookupStrategy,

请问除了GIT上列出的配置外,还需要其他配置吗?我觉得是添加了@TemplateQuery也没有使用TemplateQueryLookupStrategy来解析的原故。

bladethirst commented 6 years ago

@stormning 我把代码集成到项目中去,发现很多类已经不能用了,应该是和spring-data-jpa不兼容了。在spring-boot2.0.2中,spring-data-jpa已经是2.0.7.release版本,很多类的写法已经变了。 请问一下,spring-data-jpa有升级适配spring-data-jpa2.x的计划吗?非常期待您们的作品!

bladethirst commented 6 years ago

@stormning 大神你好!再打扰一下。 目前我集成到spring-boot.2.0.2版本后,通过修改spring-data-jpa的代码后已经可以在项目中正常运行了。目前还有一个小小的问题。 您的测试代码中com.slyak.spring.jpa.JpaTest.findByTemplateQueryWithTemplateQueryObject(),用到的对象是com.slyak.spring.jpa.SampleQuery,这里设置content的值,用的是 public void setContent(String content) { if (content != null) { this.content = "%" + content + "%"; } } 所以在.sftl中 --countContent SELECT count(*) FROM t_sample WHERE 1 = 1 <#if content??> AND content LIKE :content </#if> content是直接带有%%号的,但是在实际使用过程中,像content这样的内容字段,肯定是不会前后带%的吧。那么问题就来了,在正常的spring-data-jpa中,@query注解里面写like查询是可以用 where column like %:value%这样来写的,但是现在在.sftl中像这样写就会报错,提示ERROR: syntax error at or near "%",请问现在spring-data-jpa-extra不支持这样的写法吗??? 盼答复,甚为感谢!

stormning commented 6 years ago

正常的spring-data-jpa也是不能%:value%这样写的吧,是也:value,然后传入值的时候自己加%,因为:value会转成preparedStatement,如果是%:value%,最后的结果就变成了 where content like %‘x x x’% ,是错误的sql,所以只能填:value,%是自己传参数的时候加

bladethirst commented 6 years ago

你好!感谢答复。 我查了现在项目中的写法,spring-data-jpa是可以用%:value%这样的来查询的。 我的请求是:/amattribute/findAmAttrResource?resourcetype=Ac&sort=resourcetype&order=asc&offset=0& Controller层: @RequestMapping("findAmAttrResource") public @ResponseBody PageImpl<Map<String, String>> findAmAttr4Choose(AmAttribute attribute, HttpServletRequest req, HttpServletResponse res) { List<Map<String, String>> result = new ArrayList<Map<String, String>>(); Page page = null; Sort sort = new Sort(Direction.ASC, "resourcetype"); Pageable pageable = PropertyFilter.buildPageableFromHttpRequest(req, sort); page = service.findAmAttrResource(attribute.getResourcetype(), new BigDecimal(0), pageable); for (String resType : page.getContent()) { Map<String, String> m = new HashMap<String, String>(); m.put("resourcetype", resType); result.add(m); } PageImpl<Map<String, String>> newPage = new PageImpl<>(result, pageable, page.getTotalElements()); return newPage; }

Service层: public Page findAmAttrResource(String resourcetype, BigDecimal type, Pageable pageable) { if (StringUtils.isEmpty(resourcetype)) { return repo.findDistinctResourcetype(type, pageable); } else { return repo.findDistinctResourcetype(resourcetype, type, pageable); } }

Repo层: @Query(value = "select distinct resourcetype from AmAttribute a where resourcetype like %:resourcetype% and type=:type") public Page findDistinctResourcetype(@Param("resourcetype") String resourcetype, @Param("type") BigDecimal type, Pageable pageable);

当然这些都是在未发现您的项目之前,我的service层用了比较丑的代码,如果条件一多就晕掉了。 所以,很感谢你的项目,现在可以像Mybatis那样根据对象属性是否为空,来动态设置查询条件。

329009099 commented 5 years ago

666