2013-11-18 3 views
0

Я использую 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; } 
    ... 
} 

Вы заметите, я использую именование в «м» приставку на свойствах экземпляра. Это отлично работает с моим хранилищем, пока я не добавлю свои собственные методы findByXXX(). Поэтому я могу сохранить и получить Node из хранилища данных, используя стандартные методы CRUD.

Когда я пытаюсь добавить свой собственный метод запроса, таким образом:

public interface NodeRepository extends CrudRepository<Node, Integer> 
{ 
    public List<Node> findByActivity(String activity); 
} 

моя система ломается. В частности, автоматическая проводка моего репозитория выходит из строя (см. Трассировку стека в конце этого сообщения). Более удачлив, чем дизайн, я обнаружил, что если бы я изменил свой класс узлов, чтобы больше не использовать префикс 'm' для свойств, проблема исчезла. То есть, следующий код работает: (., Например, activity).

@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; } 
    ... 
} 

Казалось бы, что, когда весна строит метод findByActivity() он ищет Node боб по имени свойства, а не методы свойств (например getActivity(), setActivity()).

Кто-нибудь знает, действительно ли это так? Если да, есть ли способ обойти это, чтобы я мог сохранить свое соглашение об именах? Я не женился на нем, это просто то, к чему я привык.

PS: Я использую Spring 3.2.x и Java 7

отредактирован добавить трассировки стека - программа работает внутри SpringJUnit4ClassRunner:

INFO: Pre-instantiating singletons in org.s[email protected]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.springframewor[email protected]57b3fe3c] to prepare test instance [[email protected]] 
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 
+0

Можете ли вы опубликовать полную трассировку стека исключений для автоматической проводки? –

+0

След стека добавлен. – dave

ответ

0

Смотрите здесь documatation о том, как запросы будут решены.

http://docs.spring.io/spring-data/jpa/docs/1.4.2.RELEASE/reference/html/repositories.html#repositories.query-methods

так мало именовании метода:

public List<Node> findByMActivity(String activity); 

или двигая отображения на методы, а не на полях, единственный очевидный способ, которым я могу видеть это на самом деле аннотирования этот метод с @Query или определить именованный запрос где-то еще, на что ссылается этот метод.

@Query("my custom query") 
public List<Node> findByActivity(String activity); 

Чтобы сделать это в более общем плане, я думаю, вы будете смотреть на переопределение или передачи пользовательских QueryResolver в:

org.springframework.data.jpa.repository.query.JpaQueryMethod

В любом случае, все кажется очень сложным, если у вас есть поля с префиксом «m».

+0

Как я уже сказал, я не привязан к соглашению об именах, это просто то, к чему я привык. Я всего лишь избегаю хлопот :) Спасибо за ответ и ссылку. Я несколько раз читал справочный документ и упускал весенний фокус на «имя свойства». Для меня это несовместимо с определением Java Bean, где соглашение об именах указывает имена имен методов доступа. Следовательно, мое соглашение об именах может использоваться в компоненте. К сожалению, это похоже на непостоянство к весне. Например, findOne() отлично работает с mID. Во всяком случае, я думаю, что я буду останавливать «m» для простоты. – dave

0

Хотя мне нравится ответ @Alan Hay, еще один вариант - добавить слой «услуги» к вашему решению.Я сделал это в прошлом, чтобы создать интерфейс для операций CRUD в моем слое моделей.

Вы можете создать класс услуг с именем метода getter, которым вы хотите, то есть findByActivity, вызывая метод репозитория 'findByMActivity(....

Это также позволяет вам иметь слой моделей со всеми доступными методами CRUD, а объект службы будет использовать только те операции CRUD, которые относятся к этой конкретной службе, то есть ActivityService. По сути, вы можете создавать несколько интерфейсов для одних и тех же данных, в зависимости от действий, выполняемых над этими данными.

Тогда ваш класс уровня обслуживания может иметь любые имена методов, которые будут иметь смысл для пользователя, то есть findByActivity vs findByMActivity. Мартин Фаулер описывает services layer well.

Ваше решение не может требовать дополнительного слоя, но это может быть дополнительная абстракция, которая может разрешать такие проблемы интерфейса, как это, где было бы более дорогостоящим изменить имя переменной домена.

Смежные вопросы