【问题标题】:Spring Data JPA - injection fails - BeanCreationException: Could not autowire fieldSpring Data JPA - 注入失败 - BeanCreationException:无法自动装配字段
【发布时间】:2012-05-19 08:17:15
【问题描述】:

我按照here 发布的教程获得了一个与 Spring Data JPA 一起使用的基础应用程序。现在,我如何理解,使用配置

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

应该导致 Spring Data JPA 扫描该包以查找扩展 JpaRepository 的接口,并为其创建一个 concreate bean,以便可以使用简单的 Spring @Autowired 在我的服务类中的任何地方使用它。但它失败了,说它找不到带有 className 的 bean(这是 bean 在创建时获得的默认名称,只需使用去大写的 ClassName)。

但是,当我像这样在 applicationContext 中手动配置 bean 时:

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

Spring 能够找到 bean。然后我当然会得到一个错误,因为我想从一个接口创建一个 bean,这显然是行不通的。但关键是 Spring Data JPA“自动 bean 创建”似乎以某种方式失败了。

我附上了相关代码,你可以看看。顺便说一句,我应该提到我正在开发一个 portlet,所以不要想知道为什么我没有 spring-config。我目前只使用一个 applicationConfig 加上一个 MyPortlet-Portlet.xml 用于 portlet 配置(但这应该与这个问题无关)。我添加了 import 语句只是为了确保我没有使用错误的注解/类。

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 - 只是 CustomerService 的一个接口

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

CustomerService - 我的应用程序逻辑用来获取/设置 ORM 数据的类

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 - 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);
}

客户 - 我的示例实体

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

我刚从 WebSphere 的类路径地狱(该死,多么糟糕的产品),现在我在这里。希望有人可以帮助我。

对究竟出了什么问题进行基本解释,也许可以更好地理解弹簧自动装配注射功能。我已经阅读了 spring 文档,但说实话:有很多方法可以配置某些东西,而我不太清楚选择其中一种配置样式时真正需要什么。

编辑

尝试更新项目后,我仍然收到错误消息。按照这里的要求提供更多细节(跟踪):

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)

编辑 #2 按要求添加完整的 applicationContext.xml(包括我根据正在进行的讨论所做的更改)

<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" />

【问题讨论】:

  • 您的 CustomerRepository 不需要 Resource 注释。此外,findById 应该已经由 JpaRepository 提供。我假设您的 entityManager 创建良好,没有错误。使用 就足够了。打开 Spring 类的调试日志并检查那里是否有错误。
  • 为什么您的 CustomerService 有 Repository 注释而不是 Service 注释?
  • 在 Ryan Stewart 的回答中查看我的 cmets。基本上是因为我遵循了似乎不完整的教程。

标签: java spring annotations spring-data spring-data-jpa


【解决方案1】:

问题很可能出在您未显示的某些配置中。如果您发布了您遇到的错误,那也会很好。它可能与你想象的不一样。

我注意到关于您的配置的一件事是您使用的是context:annotation-config 而不是context:component-scan。后者将根据@Component 系列注释自动检测和创建bean。前者不这样做。

除此之外,您发布的所有内容看起来都应该可以正常工作,尽管有一些奇怪的事情,我稍后会谈到。我将您发布的所有代码复制到一个示例项目中并填写了一些细节,例如maven pompersistence.xml,以及the applicationContext.xml 的缺失部分。我还在服务中添加了一个“创建”方法,因此它实际上会做一些事情。有了这些到位和一个主要的类来驱动这一切,这是一个可运行的例子。你可以browse the code on github,或者你可以克隆并运行它:

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

现在是我注意到的奇怪之处。从上到下:

  • 使用给定的代码,就不需要您添加到applicationContext.xmlPersistenceAnnotationBeanPostProcessor。它没有为你做任何事情。当然,很可能还有其他代码需要它而您没有展示。
  • CustomerService 上的@Repository 注释是supposed to be used on DAO classes,或与数据库交互的类。服务的适当注解是@Service
  • ICustomerRepository 上的@Resource 注释主要是used for marking fields and methods for autowiring。我不确定是什么让你想把它放在你的存储库界面上,但它在那里没有做任何事情。
  • 您的存储库不应为@Transactional。这属于你的服务,你已经得到了它,所以没关系。请注意,它仍然可以与存储库上的 @Transactional 一起使用,因为它只是加入了由服务启动的现有事务。
  • 再次值得注意的是,您没有使用component scanning,即使您有与@Component 相关的注释(您的服务上的@Repository)。这可能会给您带来一些问题。我没有打开组件扫描,而是在示例项目中使用 XML 手动创建了服务 bean。

所以...如果这还没有向你解释什么,如果你给我一个具体的错误,我可能会解释你为什么会得到它,并告诉你如何做才能使它正确。

【讨论】:

  • 我会检查你的代码并看看。到目前为止您的问题:-对,看来我不需要 PersistenceAnnotationBeanPostProcessor。对于调试,我选择了“尝试更多并在它工作后删除你不需要的东西” - 我知道存储库的东西很奇怪。但是我发布的教程(我的问题中的第一句话)指出这是正确的,因为该类是一个存储库,即使它的命名为 Service - 我从 IFace 中删除了 Resource 注释 - 教程中也说明了 Transcational 注释 - 我没有不知道我应该使用 anno conf 中的组件扫描
  • component-scan 标签is a superset of annotation-config。它会这样做,而且它会扫描由任何@Component 相关注释注释的类。
  • 您关注的博客文章认为“服务”是一个存储库,因为它注入了一个 EntityManager,使其成为传统的“存储库”。它还提到了稍后引入一个真正的存储库的意图,尽管它似乎没有解决这个问题。您的 CustomerService 被注入了一个 CustomerRepository,所以很明显一个是 @Service,另一个是 @Repository
  • hmm 那么我的注释结构应该是什么样子?因为在您的示例中,您也使用了 @Repository 注释。我尝试根据您的测试项目修改我的项目(以为我还没有时间验证您的项目是否可以在服务器上运行 - 接下来会这样做)。但到目前为止,我仍然收到错误消息。请查看我已编辑的初始帖子以获取更多跟踪信息。
  • 我尽可能少地更改您的代码以制作一个有效的示例。正确的注释将是您的 UserService 上的 @Service 和您的 UserRepository 上的 @Repository。至于您获得的 NPE,这与您的交易设置有关。我假设您使用的是JTATransactionManager,而不是我使用的休眠模式。我从来没有使用过容器管理的事务,所以我可能帮不上什么忙。你的 tx mgr 配置是什么样的?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-17
  • 2013-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-19
相关资源
最近更新 更多