Génération de référentiel Spring par rapport à la convention de dénomination des propriétés

StackOverflow https://stackoverflow.com//questions/20038306

Question

j'utilise javax.persistence et Spring et moi avons une classe d'entité simple et un simple CrudRepository qui fonctionnent bien jusqu'à ce que j'essaye d'ajouter le mien findByXXX() méthode.

Ma classe d'entité est la suivante :

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

Vous remarquerez que j'utilise une convention de dénomination d'un préfixe « m » sur les propriétés d'instance.Cela fonctionne bien avec mon référentiel jusqu'à ce que j'ajoute le mien findByXXX() méthodes.Je peux donc sauvegarder et récupérer un Node à partir d'un magasin de données en utilisant très bien les méthodes CRUD standard.

Lorsque j'essaie d'ajouter ma propre méthode de requête, ainsi :

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

mon système tombe en panne.Plus précisément, le câblage automatique de mon référentiel échoue (voir trace de pile à la fin de cet article).Plus par chance que par conception, j'ai découvert que si je modifiais ma classe de nœuds pour ne plus utiliser le préfixe « m » sur les propriétés, le problème disparaissait.Autrement dit, le code suivant fonctionne :

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

Il semblerait que lorsque Spring construit le findByActivity() méthode avec laquelle il recherche le Node bean par le nom de la propriété (par ex. activity), plutôt que les méthodes de propriété (par ex. getActivity(), setActivity()).

Est-ce que quelqu'un sait si c'est effectivement le cas ?Si oui, existe-t-il un moyen de contourner ce problème pour que je puisse conserver ma convention de dénomination ?Je n'y suis pas attaché, c'est juste quelque chose auquel je suis habitué.

PS :J'utilise Spring 3.2.x et Java 7

MODIFIÉ pour ajouter une trace de pile - le programme s'exécute de l'intérieur 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
Était-ce utile?

La solution

Voir ici pour la documentation sur la façon dont les requêtes sont résolues.

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

si peu de nommer votre méthode:

public List<Node> findByMActivity(String activity);

ou en déplaçant vos mappages vers les méthodes plutôt que vers les champs, le seul moyen évident que je puisse voir est d'annoter cette méthode avec @Query ou de définir une requête nommée ailleurs à laquelle cette méthode ferait référence.

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

Pour le faire de manière plus générique, je suppose que vous envisageriez de remplacer ou de transmettre un QueryResolver personnalisé à :

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

Quoi qu'il en soit, tout cela semble très compliqué simplement d'avoir vos champs préfixés par un «m».

Autres conseils

Bien que j'aime la réponse de @Alan Hay, une autre option consiste à ajouter une couche « services » à votre solution.Je l'ai fait dans le passé, afin de créer une interface pour les opérations CRUD dans ma couche de modèles.

Vous pouvez créer une classe de services avec le nom de la méthode getter souhaitée, c'est-à-dire findByActivity, appelant la classe du référentiel' findByMActivity(... méthode.

Cela vous permet également d'avoir une couche de modèles avec toutes les méthodes CRUD disponibles, et l'objet services n'utiliserait que les opérations CRUD pertinentes pour ce service particulier, c'est-à-direActivitéService.Essentiellement, vous pouvez créer plusieurs interfaces pour les mêmes données, en fonction des actions effectuées sur ces données.

Ensuite, votre classe de couche de service peut avoir tous les noms de méthode qui auraient du sens pour l'utilisateur, c'est-à-direfindByActivity contre findByMActivity.Martin Fowler décrit couche de services bien.

Votre solution ne nécessite peut-être pas de couche supplémentaire, mais il peut s'agir d'une abstraction supplémentaire qui peut résoudre des problèmes d'interface comme celui-ci, où il serait plus coûteux de modifier le nom d'une variable de domaine.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top