我的自定义存储库实现中的 CrudRepository

Posted

技术标签:

【中文标题】我的自定义存储库实现中的 CrudRepository【英文标题】:CrudRepository inside my custom repository implementation 【发布时间】:2013-02-14 00:26:27 【问题描述】:

我正在尝试获取对我的存储库接口 (UserRepository) 的引用,该接口在我的自定义实现 (UserRepositoryExtensionImpl) 中扩展了 CrudRepository,以便访问 Spring JPA 提供的所有方法。

Crud 扩展:

@Repository
public interface UserRepository extends CrudRepository<User, String>, UserRepositoryExtension<RosterUser> 
    ...any custom spring JPA methods...

扩展接口:

@Repository
public interface UserRepositoryExtension <T> 
   public T put(T entity);

自定义实现:

public class UserRepositoryExtensionImpl implements UserRepositoryExtension<User> 

    UserRepository userRepository;

    @Autowired
    public UserRepositoryExtensionImpl(UserRepository userRepository) 
        this.userRepository = userRepository;
    

    @Override
    public User put(User user) 
        System.out.println(user + "was put");
        // ...put logic here
        return null;
    ...

但是,我无法注入UserRepository,因为存在循环依赖(假设UserRepository 扩展了我的UserRepositoryImpl 实现的接口)。我收到以下错误:

org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name '    userRepositoryImpl': Requested bean is currently in creation: Is there an unresolvable circular     reference?

一种可能但不太理想的解决方案是将EntityManager 注入UserRepositoryImp,但在这种情况下,我无法访问CrudRepository 提供的任何Spring JPA 方法,或任何其他方法我可能在 UserRepository 中创建的方法。

关于如何解决这个问题的任何建议?

任何帮助将不胜感激。

编辑:正如@shelley 的回答中提到的,我可以通过进行 3 处更改来解决这个问题:

UserRepositoryExtensionImpl 中删除@RepositoryUserRepositoryExtensionImpl 重命名为UserRepositoryImpl。显然,这使 Spring 意识到实现的存在。见Spring Doc 删除我的构造函数并将 @Autowired 移动到 userRepository 字段

成功!

【问题讨论】:

如果没有任何实际的循环依赖(即:Spring 只是感到困惑,因为它认为您有许多类似类型的 bean)。添加额外的限定符,例如 bean id,以便 Spring 知道要自动装配哪个 bean。 给我们你的 Spring 应用上下文 @gerrytan - 我可能是错的,但我确实认为存在循环依赖。仅供参考:同样的问题被引用here 【参考方案1】:

您应该关注well defined way to create custom repository implementations in Spring Data JPA。基本上,您需要扩展 CrudRepository,这样您就不必在自定义实现中注入它的实例。

【讨论】:

我认为 OP 正在遵循这种方法,但希望在自定义存储库实现中使用标准存储库 bean 中的方法。 @zagyi,雪莱是正确的。我正在尝试遵循 Spring 建议的相同方法,这就是我发现自己所处位置的方式。我将在问题中添加更多细节以使问题更加清晰。 @shelley, @Julian:抱歉,在仔细阅读问题后,很明显我严重误解了它。然而,我认为朱利安试图做的事情很奇怪。通常它是一个服务类,您将在其中注入存储库实例并实现调用一个或多个存储库操作的更高级别的方法。检查将put() 的逻辑上一层移到服务接口/类中是否更有意义。 @zagyi - 我明白你的意思。因为它是一个 PUT(一个实际的 HTTP 方法),我觉得 repo 是这个逻辑最合适的地方。此外,如果我要这样做,这个类将是我的存储库的包装器(如果我错了,请纠正我)因此我必须为所有 spring 提供的方法编写包装器(恕我直言,这不是理想的)。但这将是一个可能的解决方案。实际上,我是按照雪莱的建议和一个更微妙的方式让它发挥作用的。 如果 put() 真的是一个 HTTP 请求处理程序,那么我认为您在任何情况下都不应该将它放在存储库中。根据Spring webapp的标准分层,它属于repository之上的第二层(controller->service->repository)。【参考方案2】:

需要更改一些小事情才能使其正常工作:

从自定义存储库接口 (UserRepositoryExtension) 中删除 @Repository 注释。

自定义存储库实现实际上应该命名为“&lt;StandardRepository&gt;Impl”而不是“&lt;CustomRepository&gt;Impl”。在您的代码示例中,这应该是 UserRepositoryImpl 而不是 UserRepositoryExtensionImpl

【讨论】:

谢谢@shelley!您的建议,除了将 @Autowired 从构造函数移动到 userRepository 字段外,还解决了问题。我现在可以在我的自定义实现中引用UserRepository。 :)【参考方案3】:

我通过注入ApplicationContext 并使用applicationContext.getBean(UserRepository.class) 以惰性方式获取bean 解决了问题。 它是这样工作的。

【讨论】:

【参考方案4】:

正如雪莱所指出的,命名对于使自动连线工作非常重要。在下面的示例中,我为我的自定义接口及其实现遵循了正确的命名标准。但是我扩展 JpaRepository 的接口被命名为“ItemDao”而不是“ItemRepository”,这导致 spring 完全忽略了我的自定义实现......

OBS!!!应该是“ItemRepository”

@Repository
public interface ItemDao extends JpaRepository<Item, Long>, ItemRepositoryCustom 

我的界面

interface ItemRepositoryCustom ...

我的实现类

class ItemRepositoryImpl implements ItemRepositoryCustom ...

如果有人遇到类似问题,请按照以下链接中的 spring 文档中使用的命名标准开始。

http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations

【讨论】:

我希望更明确地提及这些细节......但是,是的,命名似乎非常重要。很高兴你也能弄清楚! 我,当你在没有事先了解 spring 知识的情况下阅读文档的一部分时,无法弄清楚命名规则有多严格。【参考方案5】:

我找到了不需要@Autowire的方法:

public interface UserRepository extends 
    UserRepositoryBasic,
    UserRepositoryExtension 
 


public interface UserRepositoryBasic extends
    JpaRepository<User, String>

    // standard Spring Data methods, like findByLogin


public interface UserRepositoryExtension 

    public void customMethod();


public class UserRepositoryExtensionImpl implements
    UserRepositoryExtension 

    private final UserRepositoryBasic userRepositoryBasic;

    // constructor-based injection
    public UserRepositoryExtensionImpl(
        UserRepositoryBasic userRepositoryBasic)
    
        this.userRepositoryBasic = userRepositoryBasic;
    

    public void customMethod() 
    
        // we can call all basic Spring Data methods using
        // userRepositoryBasic
    

【讨论】:

【参考方案6】:

在这种情况下,我建议使用@Lazy 注释。

public class MyCustomRepositoryImpl implements MyCustomRepository 

    @Lazy
    @Autowired
    private MyRepository myRepository;

    @Override
    public boolean customMethod() 
       return myRepository.count() > 0;
    


使用构造函数参数,Spring 尝试创建需要您自定义存储库的“基本”存储库类,而该自定义存储库需要您使用“基本”存储库 - 具有循环依赖的典型案例。

没有@Lazy 但只有@Autowired 也无法工作(基本回购的工厂bean 会有问题)。

我认为在这种情况下@Lazy 是最优雅的解决方案。

【讨论】:

以上是关于我的自定义存储库实现中的 CrudRepository的主要内容,如果未能解决你的问题,请参考以下文章

将相机图像保存在手机记忆库中的自定义位置

如何从自定义实现中引用“正常”弹簧数据存储库?

Mercurial中的自定义修订属性?

java - 如何在spring boot java中编写一个函数来处理JPA存储库中的自定义查询?

维护第三方代码的自定义补丁

无法清除 OnPaint 方法中的自定义控件