Spring Data JPA - сбой инъекции - BeanCreationException: не удалось выполнить автоматическое подключение поля

Я следил за учебником, размещенным здесь получить базовое приложение для работы с Spring Data JPA. Теперь, как я понял, используя конфигурацию

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

должен привести к тому, что этот пакет будет просканирован Spring Data JPA на предмет интерфейсов, расширяющих JpaRepository, и создать его конкретный bean-компонент, чтобы его можно было использовать где угодно в моих классах обслуживания, используя простой Spring @Autowired. Но он терпит неудачу, говоря, что не может найти bean-компонент с className (это имя по умолчанию, которое bean-компонент получает при создании, просто используя имя ClassName без заглавных букв).

Однако, когда я настраиваю bean вручную в своем applicationContext следующим образом:

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

Весна способна найти боб. Тогда я, конечно, получаю сообщение об ошибке, потому что хочу создать bean-компонент из интерфейса, который, очевидно, не может работать. НО дело в том, что кажется, что «автоматическое создание bean-компонента» Spring Data JPA как-то не работает.

Я приложил соответствующий код, чтобы вы могли его увидеть. Кстати, я должен упомянуть, что я разрабатываю портлет, поэтому не удивляйтесь, почему у меня нет Spring-config. В настоящее время я использую только applicationConfig плюс MyPortlet-Portlet.xml для конфигураций портлетов (но это не должно иметь отношения к этой проблеме). Я добавил операторы импорта, чтобы убедиться, что я не использую неправильные аннотации / классы.

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

person omni    schedule 10.05.2012    source источник
comment
Вашему CustomerRepository не нужна аннотация Resource. Кроме того, findById уже должен быть предоставлен JpaRepository. Я предполагаю, что ваш entityManager создан без ошибок. С ‹jpa: repositories› должно хватить. Включите журнал отладки для классов Spring и проверьте, нет ли там ошибок.   -  person Luciano    schedule 13.05.2012
comment
Почему ваша служба CustomerService имеет аннотацию Repository вместо аннотации Service?   -  person Luciano    schedule 13.05.2012
comment
проверьте мои комментарии к ответу Райана Стюарта. в основном потому, что я следил за учебником, который кажется неполным.   -  person omni    schedule 16.05.2012


Ответы (1)


Проблема, скорее всего, в какой-то конфигурации, которую вы не показали. Также было бы хорошо, если бы вы опубликовали ошибку, которую получаете. Это может быть что-то другое, чем вы думаете.

В вашей конфигурации я заметил одну вещь: вы используете context:annotation-config вместо context:component-scan. Последний автоматически обнаружит и создаст bean-компоненты на основе семейства аннотаций @Component. Первый этого не делает.

Помимо этого, все, что вы опубликовали, похоже, должно работать, хотя есть несколько странных вещей, к которым я вернусь через мгновение. Я скопировал весь опубликованный вами код в образец проекта и заполнил несколько деталей, например maven pom, a persistence.xml и недостающие части applicationContext.xml. Я также добавил к сервису метод «create», чтобы он действительно что-то делал. С ними и с основным классом, который управляет всем этим, это работоспособный пример. Вы можете просмотреть код на 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

Теперь о странностях, которые я заметил. Сверху:

  • С указанным кодом нет необходимости в _ 5_, который вы добавили в applicationContext.xml. Это ничего не делает для вас. Конечно, вполне может быть другой код, которому он нужен, но который вы не показали.
  • Аннотация @Repository в вашей службе CustomerService предполагается использовать в Классы DAO или классы, которые взаимодействуют с базой данных. Соответствующая аннотация для службы - @Service .
  • Аннотация @Resource в вашем ICustomerRepository в основном используется для разметки полей и методов автопроводки. Я не уверен, что заставило вас подумать о том, чтобы поместить его в интерфейс вашего репозитория, но он там ничего не делает.
  • Ваш репозиторий не должен быть @Transactional. Он принадлежит к вашим услугам, и он у вас уже есть, так что ничего страшного. Обратите внимание, что он по-прежнему работает с @Transactional в репозитории, потому что он просто присоединяется к существующей транзакции, запущенной службой.
  • Стоит еще раз отметить, что вы не используете сканирование компонентов, даже если у вас есть аннотация, относящаяся к @Component (@Repository в вашем сервисе). Это может вызвать у вас проблемы. Вместо того, чтобы включать сканирование компонентов, я вручную создал служебный компонент, используя XML в примере проекта.

Итак ... если это вам что-то не объяснило, если вы дадите мне конкретную ошибку, я, вероятно, смогу объяснить, почему вы ее получаете, и сказать вам, что делать, чтобы исправить это.

person Ryan Stewart    schedule 13.05.2012
comment
Я просмотрю ваш код и посмотрю. пока что на ваши вопросы: - да, похоже, мне не нужен PersistenceAnnotationBeanPostProcessor. Для отладки я попробовал еще и удалил то, что вам не нужно после того, как он заработал - вещь с репозиторием странная, я знаю. но в опубликованном мной руководстве (первое предложение в моем вопросе) говорится, что это правильно, поскольку класс ЯВЛЯЕТСЯ репозиторием, даже если его именованная служба - я удалил аннотацию ресурса из IFace - аннотация Transcational также указана в учебнике - я не я знаю, что я должен использовать сканирование компонентов в соответствии с anno conf - person omni; 13.05.2012
comment
Тег component-scan является расширенным набором annotation-config. Он делает это, а также сканирует классы, помеченные любыми аннотациями, относящимися к @Component. - person Ryan Stewart; 14.05.2012
comment
В сообщении в блоге, за которым вы следили, утверждалось, что служба является репозиторием, потому что в нее был внедрен EntityManager, что делает его традиционным репозиторием. Он также упомянул намерение представить настоящий репозиторий позже, хотя, похоже, до этого не доходит. В ваш CustomerService добавлен CustomerRepository, поэтому очевидно, что один - это @Service, а другой - @Repository. - person Ryan Stewart; 14.05.2012
comment
хм, так как же должна выглядеть моя структура аннотаций? поскольку в вашем примере вы также используете аннотацию @Repository. Я попытался изменить свой проект в соответствии с вашим тестовым проектом (подумал, что у меня еще не было времени проверить, работает ли ваш проект на сервере - сделаю это дальше). но до сих пор я все еще получаю сообщение об ошибке. Пожалуйста, проверьте мою отредактированную исходную публикацию для получения дополнительной информации о трассировке. - person omni; 16.05.2012
comment
Я изменил как можно меньше вашего кода, чтобы создать рабочий пример. Правильные аннотации: @Service в вашем UserService и @Repository в UserRepository. Что касается NPE, который вы получаете, это связано с настройкой вашей транзакции. Я предполагаю, что вы используете JTATransactionManager вместо того, который я использовал в спящем режиме. Я никогда не использовал транзакции, управляемые контейнером, так что, возможно, я не сильно здесь помогу. Как выглядит ваша конфигурация tx mgr? - person Ryan Stewart; 16.05.2012
comment
Я использую спящий режим. Я приложил немного измененный (исходя из ваших предложений) applicationContext - person omni; 17.05.2012
comment
Этот файл контекста не работает. Вы не можете установить EntityManagerFactory для HibernateTransactionManager. Другая проблема заключается в том, что вы настраиваете Hibernate дважды: один раз с LocalSessionFactoryBean и один раз с LocalContainerEntityManagerFactoryBean. Одно это, вероятно, не вызовет сбоя во время выполнения, но это совершенно неправильно. Вам нужно выбрать один из способов настройки Hibernate. Кроме того, последний из этих двух, вероятно, является источником вашей первоначальной проблемы. Обратите внимание на Контейнер в его названии? Вероятно, поэтому он ищет транзакцию, управляемую контейнером. - person Ryan Stewart; 17.05.2012
comment
Ваш вопрос касался Spring Data JPA, но ваши проблемы не связаны с этим. Вы просто не понимаете, как настроить Hibernate и / или JPA в первую очередь. Во-первых, узнайте, как собрать базовое приложение Spring + JPA. Тогда вы можете беспокоиться о добавлении сахара. Найдите книгу или статью, которая поможет вам разобраться в основах. - person Ryan Stewart; 17.05.2012
comment
возможно ты прав. Дело в том, что я уже проверил так много источников, и все делают это по-своему. Меньше всего писателей тратят время на то, чтобы объяснить, ПОЧЕМУ они делают то, что делают. даже весна в действии не погружается в глубину того, что на самом деле происходит. так что новичку в EE и Spring очень сложно понять, что происходит. в любом случае я дам вам награду, раз уж вы ее действительно заслужили. спасибо за вашу поддержку, по крайней мере, я знаю, что это (скорее всего) не неправильный код, а мой applicationContext.xml. - person omni; 18.05.2012
comment
хм, кстати, глядя на ваш рабочий пример: вы использовали JPATransactionManager вместо упомянутого вами Hibernate. Сейчас я просматриваю официальное руководство Spring, чтобы узнать, смогу ли я найти что-то, что мне не хватает. - person omni; 18.05.2012
comment
Ты прав. Виноват. Я начал с диспетчера Hibernate tx, но потом понял, что мне понадобится JPA-модуль, чтобы запустить spring-data-jpa. По крайней мере, я считаю, что это так. Я не изучал это так глубоко. Кроме того, вы можете найти полезным просмотреть другие проекты в том же репозитории, где я разместил ваш пример. Это большой тестовый проект с множеством базовых конфигураций приложений. - person Ryan Stewart; 18.05.2012