JavaSpring存储库生成与属性命名约定
我正在使用javax.persistence
和Spring,我有一个简单的实体类和一个简单的CrudRepository
,在我尝试添加我自己的findByXXX()
方法之前,它们工作得很好
我的实体类如下所示:
@Entity
public class Node
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column (name = "ID")
private Integer mID;
/** The node's activity. */
@Column (name = "Activity", nullable = false, length = 200)
private String mActivity;
// ... other properties.
public Integer getId() { return mID; }
public void setId(Integer id) { mID = id; }
public String getActivity() { return mActivity; }
public void setActivity(String activity) { mActivity = activity; }
...
}
您会注意到我在实例属性上使用了“m”前缀的命名约定。在我添加自己的findByXXX()
方法之前,这对我的存储库很有效。因此,我可以使用标准的CRUD方法从数据存储中保存和检索Node
当我尝试添加自己的查询方法时,因此:
public interface NodeRepository extends CrudRepository<Node, Integer>
{
public List<Node> findByActivity(String activity);
}
我的系统坏了。具体来说,我的存储库的自动连接失败了(请参阅本文末尾的堆栈跟踪)。幸运的是,我发现如果我修改了我的节点类,不再在属性上使用'm'前缀,那么问题就消失了。也就是说,以下代码起作用:
@Entity
public class Node
{
...
/** The node's activity. */
@Column (name = "Activity", nullable = false, length = 200)
private String activity;
// ... other properties.
public String getActivity() { return activity; }
public void setActivity(final String activity) { this.activity = activity; }
...
}
当Spring构建findByActivity()
方法时,它似乎是通过属性名(例如activity
)而不是属性方法(例如getActivity(), setActivity()
)来搜索Node
bean
有人知道情况是否如此吗?如果是这样的话,有没有办法让我保持我的命名惯例?我没有和它结婚,这只是我习惯了的事
PS:我用的是Spring3.2。x和Java 7
编辑以添加堆栈跟踪-程序正在SpringJUnit4ClassRunner:
内运行
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@32f2e8ca: defining beans [nodeRepository,org.springframework.data.repository.core.support.RepositoryInterfaceAwareBeanPostProcessor#0,org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor#0,dataSource,entityManagerFactory,transactionManager,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
Nov 18, 2013 6:59:10 PM org.springframework.test.context.TestContextManager prepareTestInstance
SEVERE: Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@57b3fe3c] to prepare test instance [com.example.persistence.JPAXMLCfgTest@69f8421f]
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.example.persistence.JPAXMLCfgTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.example.persistence.NodeRepository com.example.persistence.JPAXMLCfgTest.mRepo; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'nodeRepository': FactoryBean threw exception on object creation; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:287)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1106)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:374)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:110)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:312)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:288)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:284)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.example.persistence.NodeRepository com.example.persistence.JPAXMLCfgTest.mRepo; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'nodeRepository': FactoryBean threw exception on object creation; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:513)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:92)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:284)
... 32 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'nodeRepository': FactoryBean threw exception on object creation; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:149)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:102)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1442)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:248)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:871)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:813)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:730)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:485)
... 34 more
Caused by: java.lang.NullPointerException
at org.springframework.data.jpa.repository.query.QueryUtils.isEntityPath(QueryUtils.java:462)
at org.springframework.data.jpa.repository.query.QueryUtils.toExpressionRecursively(QueryUtils.java:445)
at org.springframework.data.jpa.repository.query.JpaQueryCreator$PredicateBuilder.build(JpaQueryCreator.java:197)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.toPredicate(JpaQueryCreator.java:144)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.create(JpaQueryCreator.java:86)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.create(JpaQueryCreator.java:44)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createCriteria(AbstractQueryCreator.java:109)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:88)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:73)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.<init>(PartTreeJpaQuery.java:98)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$CountQueryPreparer.<init>(PartTreeJpaQuery.java:166)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.<init>(PartTreeJpaQuery.java:60)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:90)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:162)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:68)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.<init>(RepositoryFactorySupport.java:290)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:158)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:162)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:44)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:142)
... 42 more
# 1 楼答案
虽然我喜欢@Alan Hay的答案,但另一个选择是在解决方案中添加“服务”层。我在过去做过这件事,是为了在我的模型层中创建CRUD操作的接口
您可以使用所需的getter方法的名称创建服务类,即
findByActivity
,调用存储库类findByMActivity(...
方法这还允许您拥有一个包含所有可用CRUD方法的模型层,并且服务对象将只使用与该特定服务相关的CRUD操作,即ActivityService。本质上,您可以为同一数据创建多个接口,具体取决于对该数据执行的操作
然后,您的服务层类可以具有任何对用户有意义的方法名称,即findByActivity与findByActivity。Martin Fowler描述了services layer well
您的解决方案可能不需要额外的层,但它可能是一个额外的抽象,可以解决这样的接口问题,在这种情况下,更改域变量名的成本会更高
# 2 楼答案
有关如何解决查询的文档,请参见此处
只需简单地命名您的方法:
或者将映射移动到方法而不是字段,我能看到的唯一明显的方法是使用@Query实际注释这个方法,或者在这个方法将引用的其他地方定义一个命名查询
要以更通用的方式执行此操作,我想您应该考虑将自定义QueryResolver重写或传递给:
不管怎么说,仅仅在字段前加上“m”似乎很麻烦