Question

I'm using javax.persistence and Spring and I have a simple entity class and a simple CrudRepository that are working well until I try and add my own findByXXX() method.

My entity class is as follows:

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

You'll notice I'm using a naming convention of an 'm' prefix on instance properties. This works fine with my repository until I add my own findByXXX() methods. So I can save and retrieve a Node from a data store using the standard CRUD methods just fine.

When I try and add my own query method, thus:

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

my system breaks. Specifically the auto-wiring of my repository fails (see stack trace at end of this post). More by luck than design, I found that if I modified my node class to no longer use the 'm' prefix on properties, the problem went away. That is, the following code works:

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

It would seem that when Spring is building the findByActivity() method it searches the Node bean by the property name (eg. activity), rather than the property methods (eg. getActivity(), setActivity()).

Does anyone know if this is indeed the case? If so, is there a way around it so I can keep my naming convention? I'm not wedded it to it, it's just something I'm used to.

PS: I'm using Spring 3.2.x and Java 7

EDITED to add stack trace - program is running from within 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
Was it helpful?

Solution

See here for documatation on how queries are resolved.

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

so short of naming your method:

public List<Node> findByMActivity(String activity);

or moving your mappings to the Methods rather than the fields, the only obvious way I can see is to actually annotate this method with @Query or define a named query somewhere else that this method would refer to.

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

To do it in a more Generic way I guess you would be looking at overriding or passing a custom QueryResolver to:

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

Anyway, all seems like a lot of hassle just to have your fields prefixed with an 'm'.

OTHER TIPS

Although I like @Alan Hay's answer, another option is to add a "services" layer to your solution. I've done this in the past, in order to create an interface to the CRUD operations in my models layer.

You can create a services class with the name of the getter method you want, i.e. findByActivity, calling the repository class' findByMActivity(... method.

This also allows you to have a models layer with all the available CRUD methods, and the services object would only use those CRUD operations pertinent to that particular service, i.e. ActivityService. In essence, you can create multiple interfaces for the same data, depending on the actions being performed on that data.

Then your service layer class can have whatever method names would make sense to the user, i.e. findByActivity vs findByMActivity. Martin Fowler describes the services layer well.

Your solution may not call for an additional layer, but it may be an extra abstraction that can resolve interface issues like this, where it would be more costly to change a domain variable name.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top