Spring Data - 如何在仓库片段中重用存储库?

3

更新: 以下内容使用的是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;
}

但是应用程序无法启动。我收到了一个空指针异常,但我认为这只是Spring在尝试解析这些依赖项时陷入循环。

我正在尝试的事情是否可能?还有其他方法可以做到这一点吗?


你好。请提供异常的堆栈跟踪信息吗?你的接口有 @Repository 注解吗?你在配置中启用了 JPA 存储库吗? - Mickael
嗨Mickael,堆栈跟踪太长了,不适合这个问题。我没有添加@Repository注释,也没有启用“JPA存储库”(我不知道那是什么意思)。请注意,一切都按预期工作,直到我尝试在片段内自动装配UserRepository的时候。 - Nikolaos Georgiou
你能解释一下什么是“正常工作”吗?你是指没有异常吗?还是说你实际上可以使用这些存储库? - Mickael
嗨,Mikael,当我说“工作”时,我的意思是数据访问按预期工作。我目前正在使用EntityManager,并在片段内手动创建Java Persistence Query Language查询。这是我想避免的部分,因为现在我正在使用EntityManager实现通常免费获得的方法(例如findByFirstNameAndLastName)。 - Nikolaos Georgiou
@NikolaosGeorgiou,你解决了这个问题吗? - Emerson Farrugia
@NikolaosGeorgiou 循环依赖堆栈跟踪与 NPE 非常不同。在这种情况下,应用程序甚至无法启动。我怀疑您没有循环依赖问题。我怀疑您的某个 bean 没有被初始化,并且在调用该未初始化 bean 上的某个方法时会抛出 NPE。使用调试器模式查看哪个变量为空。它必须是任何一个自动装配的 bean。 - Mukul Bansal
4个回答

4

我在最近的一个项目中遇到了完全相同的代码模式和问题,最终通过使用 @Lazy 进行惰性初始化 UserRepository 来解决了这个问题:

class UserExrtaLogicImpl implements UserExtraLogic {

    @Lazy
    @Autowired
    private UserRepository userRepository;

}

你是对的。循环依赖问题可以通过使用@Lazy来解决,但我非常怀疑@Nikolaos Georgiou有这个问题。他们描述的问题陈述中并没有循环依赖问题。 - Mukul Bansal

3

您可以使用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
getObject() being called too early. -> 所以您可以展示如何通过代码提前调用它。如果您像我的例子一样访问它,这不是问题。 - Mạnh Quyết Nguyễn

1

仓库启用

来源:文档

如果您正在使用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);
}

解释

来自 文档

使用 repositories 元素查找 Spring Data 仓库,如“创建仓库实例”所述。除此之外,它还为所有被注解为 @Repository 的 bean 激活了持久化异常转换功能,以将由 JPA 持久化提供程序引发的异常转换为 Spring 的 DataAccessException 层次结构。


嗨Mickael,感谢你的帮助。我意识到我忘了提到我的应用程序是在Spring Boot 2.1.0中。很抱歉我忘了提到它。我认为你提到的东西已经由Spring Boot为我提供了。 - Nikolaos Georgiou
这是不正确的。当尝试访问基本存储库中的派生查询方法时,我还看到来自存储库片段代码触发的NPE。 - Emerson Farrugia

0

在文档中要特别注意这一点。

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

当您扩展片段接口时,最终的存储库也将包括它。这就是它的好处。如果您想要访问原始存储库逻辑,我可以建议您3种方法。

  1. 在您的片段中添加查询,并将其扩展到存储库中。然后在您的服务方法中包含一个聚合方法。在该方法中编写您的逻辑。
  2. 在您的自定义接口中,包括您的存储库,但不要在存储库中扩展您的自定义接口。
  3. 使用装饰器类。将所有方法委派给存储库,并将自定义逻辑与存储库组合。

不要包含循环依赖项,因为这不是一个好习惯。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接