19

i followed the tutorial posted here to get a basis application to work with Spring Data JPA. Now, how i understood, using the configuration

<jpa:repositories base-package="my.package.to.scan" />

should result in that package beeing scanned by Spring Data JPA for interfaces extending JpaRepository and create a concreate bean of it so it can be used anywhere in my service classes using simple Spring @Autowired. But it fails, saying it can't find a bean with className (which is the default name the bean gets when created, simply using the de-capitalized ClassName).

However, when i configure the bean manualy in my applicationContext like this:

<bean id="ClassName" class="my.package.to.scan.ClassName"/>

Spring is able to find the bean. I then of course get an error because i want to create a bean from an interface, which obviously can't work. BUT the point is that it seems like the Spring Data JPA "automatic bean creation" seems to fail somehow.

I attached the relevant code so you can look at it. Btw, i should mention that i'm developing a portlet, so don't wonder why i dont have a spring-config. I'm currently using a applicationConfig only plus an MyPortlet-Portlet.xml for portlet configurations (but that should be not relevent for this problem). I added the import statements just to make sure i'm not using the wrong annotions / classes.

applicationContext.xml

<beans *** ALL MY XMLN's and XSI's *** />
<context:annotation-config />
<jpa:repositories base-package="model.repositories" />

// JPA specific configuration here: dataSource, persistenceUnitManager exceptionTranslator, entityManagerFactory, SessionFactory, transactionManager - should not be relevant for this problem, tell me if i'm wrong

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

ICustomerService - just a interface for the CustomerService

import model.entities.Customer;
public interface ICustomerService {
        // example method
    public Customer getCustomer(Long customerId);   
}

CustomerService - the class used by my application logic to get / set ORM data

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import model.entities.Customer;
import model.repositories.CustomerRepository;
import model.service.interfaces.ICustomerService;
@Repository
@Transactional(readOnly = true)
public class CustomerService implements ICustomerService{
    @Autowired
    private CustomerRepository repository;

    // example method
    @Override
    public Customer getCustomer(Long customerId){
        return repository.findById(customerId);
    }

CustomerRepository - the repository for Spring Data JPA

import javax.annotation.Resource;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.transaction.annotation.Transactional;
import model.entities.Customer;
@Resource
@Transactional(readOnly = true)
public interface CustomerRepository extends JpaRepository<Customer, Long>{

    public Customer findById(Long id);
}

Customer - my sample entity

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "Customers")
public class Customer{

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "ID_CUSTOMER")
    private Long    id;

    @Column(name = "dbfirstname")
    private String  firstName;

    @Column(name = "dbname")
    private String  lastName;

    public Long getId(){
        return id;
    }

    public String getFirstName(){
        return firstName;
    }

    public void setFirstName(String firstName){
        this.firstName = firstName;
    }

    public String getLastName(){
        return lastName;
    }

    public void setLastName(String lastName){
        this.lastName = lastName;
    }
}

i just came from classpath hell with WebSphere (damn, what a fu**ed up product) and now i'm here. hope somebody can help me with this.

A basic explanation of what exacly goes wrong and maybe providing a better understanding of springs autowired injection feature would be great. I've read the spring documentation, but to say the truth: there are so many ways to configure something and it's not quite visible to me WHAT is really needed when choosing one of the config styles.

EDIT

After trying to update the project i'm still getting the error. as requested here a little more details (trace):

Exception created : org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private model.repositories.CustomerRepository model.service.CustomerService.repository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerRepository': 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 com.ibm.ws.http.HttpConnection.run(HttpConnection.java:522)
    at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1563)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private model.repositories.CustomerRepository model.service.CustomerService.repository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerRepository': FactoryBean threw exception on object creation; nested exception is java.lang.NullPointerException
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:506)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:284)
    ... 96 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerRepository': 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:848)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:790)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:707)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:478)
    ... 98 more
Caused by: java.lang.NullPointerException
    at org.hibernate.engine.transaction.internal.jta.JtaStatusHelper.getStatus(JtaStatusHelper.java:73)
    at org.hibernate.engine.transaction.internal.jta.JtaStatusHelper.isActive(JtaStatusHelper.java:115)
    at org.hibernate.engine.transaction.internal.jta.CMTTransaction.join(CMTTransaction.java:149)
    at org.hibernate.ejb.AbstractEntityManagerImpl.joinTransaction(AbstractEntityManagerImpl.java:1215)
    at org.hibernate.ejb.AbstractEntityManagerImpl.postInit(AbstractEntityManagerImpl.java:177)
    at org.hibernate.ejb.EntityManagerImpl.<init>(EntityManagerImpl.java:89)
    at org.hibernate.ejb.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:179)
    at org.hibernate.ejb.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:174)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:48)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:600)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.invokeProxyMethod(AbstractEntityManagerFactoryBean.java:376)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean$ManagedEntityManagerFactoryInvocationHandler.invoke(AbstractEntityManagerFactoryBean.java:517)
    at $Proxy325.createEntityManager(Unknown Source)

    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:234)
    at $Proxy328.createNamedQuery(Unknown Source)
    at org.springframework.data.jpa.repository.query.NamedQuery.<init>(NamedQuery.java:74)
    at org.springframework.data.jpa.repository.query.NamedQuery.lookupFrom(NamedQuery.java:96)
    at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$DeclaredQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:128)
    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:71)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.<init>(RepositoryFactorySupport.java:303)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:157)
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:120)
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:39)
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:142)

EDIT #2 compleate applicationContext.xml (includeing the changes i made based on the ongoing discussion) added as requested

<context:annotation-config />

<jpa:repositories base-package="model.repositories" />

<context:component-scan base-package="model,model.repositories,model.service,controller" />

<bean class="model.service.CustomerService"/>
<bean class="model.service.OrderService"/>
<bean class="model.repositories.CustomerRepository"/>
<bean class="model.repositories.OrderRepository"/>


<bean id="myExceptionTranslator" class="org.springframework.orm.hibernate4.HibernateExceptionTranslator" /> 

<jee:jndi-lookup id="dataSource" jndi-name="jdbc/mydata"
    resource-ref="true" cache="true" />


<bean id="pum"
    class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
    <property name="persistenceXmlLocations">
        <list>
            <value>classpath*:META-INF/OverridePersistence.xml</value>
        </list>
    </property>
    <property name="defaultDataSource" ref="dataSource" />
</bean>


<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="generateDdl" value="true" />
            <property name="database" value="MYSQL" />
        </bean>
    </property>
    <property name="persistenceUnitManager" ref="pum" />
    <property name="persistenceUnitName" value="default" />
</bean>

<bean id="mySessionFactory"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="packagesToScan" value="model"/>
    <property name="hibernateProperties">
        <value>hibernate.dialect=org.hibernate.dialect.MySQLDialect</value>
    </property>
</bean>

<bean id="transactionManager"
    class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
    <property name="sessionFactory" ref="mySessionFactory" />
</bean>

<tx:annotation-driven />

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
omni
  • 4,104
  • 8
  • 48
  • 67
  • Your CustomerRepository does not need the Resource annotation. Also, findById should already be provided by JpaRepository. I'm assuming your entityManager is created fine without errors. With should be enough. Turn on your debug log for Spring classes and check if there is some error there. – Luciano May 13 '12 at 01:54
  • Why your CustomerService has the Repository annotation instead of the Service annotation? – Luciano May 13 '12 at 01:58
  • check my comments on Ryan Stewart's answer. basicly because i followed the tutorial which seems to be incompleate. – omni May 16 '12 at 12:13

1 Answers1

21

The problem is very probably in some of the configuration you haven't shown. It would also be good if you posted the error you're getting. It might be something different than what you think it is.

One thing I notice about your config is that you're using context:annotation-config instead of context:component-scan. The latter will auto-detect and create beans based on the @Component family of annotations. The former doesn't do that.

Other than that, everything you posted looks like it should work, though there are several odd things, which I'll come to in a moment. I copied all your posted code into a sample project and filled in a few details, like a maven pom, a persistence.xml, and the missing pieces of the applicationContext.xml. I also added a "create" method to the service so it would actually do something. With those in place and a main class to drive it all, it's a runnable example. You can browse the code on github, or you can clone and run it with:

git clone git://github.com/zzantozz/testbed tmp
cd tmp/stackoverflow/10539417-basic-spring-data-jpa
mvn -q compile exec:java -Dexec.mainClass=rds.testbed.springDataJpa.SpringDataJp

Now for the oddities that I noticed. From the top:

  • With the code as given, there's no need for the PersistenceAnnotationBeanPostProcessor that you've added to the applicationContext.xml. It's not doing anything for you. Of course, there may well be other code that needs it that you haven't shown.
  • The @Repository annotation on your CustomerService is supposed to be used on DAO classes, or classes that interact with a database. The appropriate annotation for a service is @Service.
  • The @Resource annotation on your ICustomerRepository is mostly used for marking fields and methods for autowiring. I'm not sure what made you think to put it on your repository interface, but it's not doing anything there.
  • Your repository shouldn't be @Transactional. That belongs at your service, and you've already got it there, so that's fine. Note that it still works with the @Transactional on the repository because it just joins the existing transaction started by the service.
  • It's worth noting again that you're not using component scanning, even though you have a @Component-related annotation (the @Repository on your service). That might be causing you some problems. Instead of turning on component scanning, I manually created the service bean using XML in the sample project.

So... if this hasn't explained something to you, if you give me a specific error, I can probably explain why you're getting it and tell you what to do to make it right.

Ryan Stewart
  • 126,015
  • 21
  • 180
  • 199
  • i'll go through your code and have a look. so far to your questions: - right, it seems i dont need the PersistenceAnnotationBeanPostProcessor. For debugging i went with "try more and remove what you dont need after it works" - the Repository thing is strange i know. but the tutorial i posted (first sentence in my question) states that it's right since the class IS a repository even if its named Service - I removed the Resource annotation from the IFace - The Transcational annotation is also stated in the tutorial - i didn't know i'm supposed to use component scanning insted of the anno conf – omni May 13 '12 at 08:50
  • The `component-scan` tag [is a superset of `annotation-config`](http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-java-combining-xml-centric-component-scan). It does that, plus it scans for classes annotated by any `@Component`-related annotation. – Ryan Stewart May 13 '12 at 23:45
  • The blog post you were following argued that the "service" was a repository because it had an EntityManager injected into it, making it a traditional "repository". It also mentioned the intention to introduce a true repository later on, though it doesn't seem to get around to it. Your CustomerService is injected with a CustomerRepository, so clearly the one is a `@Service` and the other is a `@Repository`. – Ryan Stewart May 13 '12 at 23:49
  • hmm so how should my annotation structure look like? since in your example you're using the @Repository annotation too. I tried to modify my project acording to your test project (thought i had no time yet to verify if your project works on the server - will do that next). but so far i'm still getting the error. Please check my Edited initial post for more trace informations. – omni May 16 '12 at 09:40
  • I changed as little of your code as possible to make a working example. The correct annotations would be `@Service` on your UserService and `@Repository` on your UserRepository. As for the NPE you're getting, that's related to your transaction setup. I assume you're using a [JTATransactionManager](http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/transaction/jta/JtaTransactionManager.html), instead of the hibernate one that I used. I've never used container-managed transactions, so I may not be of much help there. What does your tx mgr config look like? – Ryan Stewart May 16 '12 at 15:02
  • I'm using the hibernate one. I attached the slightly changed (based on the suggestions you made) applicationContext – omni May 17 '12 at 15:45
  • There's no way that context file is working. You can't set an EntityManagerFactory on a HibernateTransactionManager. Another problem is that you're configuring Hibernate twice: once with a LocalSessionFactoryBean and once with a LocalContainerEntityManagerFactoryBean. That alone probably won't cause a failure at runtime, but it's totally wrong. You need to pick one way of configuring Hibernate. Further, the latter of those two is probably where your initial problem came from. Notice the "Container" in its name? That's probably why it's looking for a container-managed transaction. – Ryan Stewart May 17 '12 at 18:21
  • Your question was about Spring Data JPA, but your problems are unrelated to it. You just don't seem to understand how to configure Hibernate and/or JPA in the first place. First, learn how to put together a basic Spring+JPA application. Then you can worry about adding in the sugar. Find a book or article to guide you through the basics. – Ryan Stewart May 17 '12 at 18:24
  • you're probably right. the thing is i already checked so many sources and everybody is doing it in a different way. least of the writers take the time to explain WHY they are doing what they are doing. even "spring in action" dosn't dive into the deep of what is actually happening. so as a EE and Spring beginner it's realy hard to understand what's going on. anyways i'll give you the bounty since you realy deserved it. thanks for your support, atleast i know that it's (most probably) not the code thats wrong but my applicationContext.xml. – omni May 18 '12 at 12:21
  • hmm btw, looking at your working example: you used the JPATransactionManager instead of the Hibernate one you mentioned. I'm going through the official Spring guide now to see if i can find something i'm missing. – omni May 18 '12 at 16:50
  • You're right. My mistake. I started off with the Hibernate tx mgr, but then I realized I'd need the JPA one to get spring-data-jpa up and running. At least I believe that's the case. I haven't looked into it that deeply. Also, you might find it educational to look through the other projects in that same repository where I put your example. It's a [large testbed project](https://github.com/zzantozz/testbed) with lots of basic application configs in it. – Ryan Stewart May 18 '12 at 19:46