Spring Data - как повторно использовать репозиторий во фрагменте репозитория?

Обновлять: ниже используется Spring Boot 2.1.0

У меня есть репозиторий Spring Data, и я пытаюсь предоставить ему некоторые настраиваемые функции, следуя пример фрагментов из документации.

Итак, я добавил в репозиторий дополнительный интерфейс:

public interface UserRepository extends JpaRepository<User, Long>, UserExtraLogic {
    User findByFirstNameAndLastName(String firstName, String lastName);
}

с этим настраиваемым интерфейсом:

interface UserExtraLogic {
    void ensureHasAccess();
}

и его реализация:

class UserExtraLogicImpl implements UserExtraLogic {
    public void ensureHasAccess() {
    }
}

Проблема в том, что я хотел бы иметь возможность использовать свои репозитории внутри UserExtraLogicImpl, чтобы я мог повторно использовать методы запросов, такие как findByFirstNameAndLastName, без необходимости писать их самостоятельно с помощью EntityManager. Итак, я попробовал это:

class UserExrtaLogicImpl implements UserExtraLogic {
    @Autowired
    private UserRepository userRepository;
}

Но тогда приложение не запускается. Я получаю исключение NullPointerException, но я думаю, что Spring просто входит в цикл, пытаясь разрешить эти зависимости.

Возможно ли то, что я пытаюсь сделать? Есть другой способ сделать это?

Привет. Не могли бы вы предоставить трассировку стека исключения? Есть ли в вашем интерфейсе аннотация @Repository? Включили ли вы репозитории JPA в своей конфигурации?

Mickael 15.11.2018 16:17

Привет, Микаэль, трассировка стека слишком длинная и не соответствует поставленному вопросу. Я не добавил аннотацию @Repository и не включил «репозитории JPA» (я не знаю, что это значит). Обратите внимание, что все работает должным образом, пока я не попытаюсь автоматически подключить UserRepository внутри фрагмента.

Nikolaos Georgiou 15.11.2018 16:33

Не могли бы вы объяснить, что «работает»? Вы имеете в виду, что у вас нет исключений? Или вы имеете в виду, что действительно можете использовать репозитории?

Mickael 15.11.2018 16:56

Привет, Микаэль, когда я говорю «работает», я имею в виду, что доступ к данным работает, как ожидалось. В настоящее время я использую EntityManager и вручную создаю запросы с использованием языка запросов Java Persistence Query Language внутри фрагментов. Этой части я бы хотел избежать, потому что теперь я реализую с EntityManager метод, который обычно получаю бесплатно (например, findByFirstNameAndLastName).

Nikolaos Georgiou 15.11.2018 17:04

@NikolaosGeorgiou, ты решил это?

Emerson Farrugia 29.05.2019 21:42

@NikolaosGeorgiou Круговая трассировка стека зависимостей сильно отличается от NPE. И в этом случае приложение даже не запускалось. Я сомневаюсь, что у вас нет проблемы циклической зависимости. Я подозреваю, что один из ваших bean-компонентов не инициализируется и выдает NPE, когда вы вызываете какой-либо метод для этого неинициализированного bean-компонента. Используйте режим отладчика, чтобы узнать, какая переменная имеет значение NULL. Это должен быть какой-либо из автосоединенных бобов.

Mukul Bansal 31.05.2019 12:51
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
6
1 888
4

Ответы 4

Включение репозиториев

От документация

Если вы используете конфигурацию Spring XML, у вас должно быть следующее:

<?xml version = "1.0" encoding = "UTF-8"?>
<beans xmlns = "http://www.springframework.org/schema/beans"
  xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
  xmlns:jpa = "http://www.springframework.org/schema/data/jpa"
  xsi:schemaLocation = "http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/jpa
    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

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

</beans>

Если вместо этого вы используете конфигурацию Java, у вас должно быть следующее:

@Configuration
@EnableJpaRepositories("com.acme.repositories")
class ApplicationConfiguration {

  @Bean
  EntityManagerFactory entityManagerFactory() {
    // …
  }
}

Конфигурация репозиториев

Также вам необходимо добавить аннотацию @Repository в свои репозитории:

@Repository
public interface UserRepository extends JpaRepository<User, Long>, UserExtraLogic {
    User findByFirstNameAndLastName(String firstName, String lastName);
}

Объяснение

От документация

Using the repositories element looks up Spring Data repositories as described in “Creating Repository Instances”. Beyond that, it activates persistence exception translation for all beans annotated with @Repository, to let exceptions being thrown by the JPA persistence providers be converted into Spring’s DataAccessException hierarchy.

Привет, Микаэль, спасибо за помощь. Я понимаю, что забыл упомянуть, что мое приложение находится в Spring Boot 2.1.0. Мне жаль, что я забыл об этом упомянуть. Я думаю, что то, о чем вы говорите, уже предоставлено мне Spring Boot.

Nikolaos Georgiou 15.11.2018 17:25

Это неверно. Я также вижу, что NPE запускаются из кода фрагмента репозитория при попытке доступа к производным методам запроса в базовом репозитории.

Emerson Farrugia 30.05.2019 20:16

Вы можете лениво загрузить свой репозиторий с помощью ObjectFactory<T> (концепция Spring) или Provider<T> (стандартный java api).

class UserExrtaLogicImpl implements UserExtraLogic {
    @Autowired
    private ObjectFactory<UserRepository> userRepository;

    public void soSomething() {
         userRepository().getObject().findById(xxx);
    }
}

@ MạnhQuyếtNguyễn Я пробовал аналогичный подход, который не сработал из-за слишком раннего вызова getObject(). Это действительно работает. При этом он не такой идиоматичный, как @Lazy, и определенно не идентичен, поэтому отрицательный голос не оправдан.

Emerson Farrugia 31.05.2019 11:05
getObject() being called too early. -> Итак, вы можете показать, как вы вызываете это на раннем этапе кода. Если вы получите доступ к нему, как в моем примере, это не проблема.
Mạnh Quyết Nguyễn 31.05.2019 11:08

У меня точно такие же шаблоны кода, и я столкнулся с той же проблемой в недавнем проекте, и я, наконец, решил ее, используя @Lazy для ленивой инициализации UserRepository:

class UserExrtaLogicImpl implements UserExtraLogic {

    @Lazy
    @Autowired
    private UserRepository userRepository;

}

Ты прав. Проблема круговой зависимости может быть решена с помощью @Lazy, но я очень сомневаюсь, что у @Nikolaos Georgiou есть такая проблема. Описанная ими постановка проблемы не имеет проблемы циклической зависимости.

Mukul Bansal 31.05.2019 12:49

Обратите внимание на это в документации.

Extending the fragment interface with your repository interface combines the CRUD and custom functionality and makes it available to clients.

Когда вы расширяете интерфейс фрагментов, ваш последний репозиторий также будет включать его. Это его благодать. Если вы хотите получить доступ к исходной логике репозитория, я могу предложить вам 3 метода.

  1. Добавьте свой запрос в свой фрагмент и расширите его в своем репозитории. Затем в свой метод обслуживания включите агрегированный метод. В этом методе напишите свою логику.
  2. В свой пользовательский интерфейс включите свой репозиторий и не расширяйте свой пользовательский интерфейс в репозитории.
  3. Используйте класс декоратора. Делегируйте все методы в репозиторий и объедините настраиваемую логику с репозиторием.

Не включайте циклические зависимости, поскольку это не очень хорошая практика.

Другие вопросы по теме