为什么Spring Data JPA存储库仅强制使用接口?

时间:2019-04-11 12:47:41

标签: spring-boot spring-data-jpa spring-data

我正在建立一个具有以下结构的多模块spring-boot项目:

    • 核心(具有类TestingHandler,注入TestingRepository以保存数据)
    • persistence-api(具有自定义存储库接口TestingRepository)
    • 持久性内存(具有MemoryTestingRepository抽象类,扩展了TestingRepository和JpaRepository)
    • main(具有应用程序的入口点和一个运行程序,该运行程序调用TestingHandler来保存数据)

我正在尝试构建一个简单的spring boot应用程序,该应用程序会将示例记录保留在其h2 in-mem数据库中。但我不断收到以下错误:

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'com.****.***.services.testing.persistence.memory.MemoryTestingRepository' in your configuration.

我想念什么?

我尝试了以下方法:

  • 不使用存储库层次结构。仅使用扩展了JpaRepository的单个接口即可,但是由于自定义方法,我必须具有接口层次结构,并且将来需要支持多个数据库实现。
  • 尝试直接在TestingHandler中使用MemoryRepository,但无效。如果我将MemoryRepository转换为接口(并摆脱了自定义方法),那么它将起作用。但是就像我提到的那样,我需要有一个自定义方法,并且必须支持接口层次结构以支持遗留代码。
  • 尝试使用诸如@ EnableJpaRepositories,@ ComponentScan等注释之类的东西玩很多,没有帮助。
  • Testing.java是带有@Entity和@Id批注的非常标准的POJO,非常简单。

这是Handler类:

package com.***.**.services.testing.core;

@Service
public class TestingHandler {

    @Autowired
    private TestingRepository testingRepository;

    @NotNull
    public Testing create(@NotNull final TestingDto testingDto) {
        Testing testing = new Testing("123", testingDto.getName(), testingDto.getDescription(), testingDto.Key(), testingDto.getVersion());
        testingRepository.create(testing);
        return testing;
    }
}

请注意,它正在注入接口TestingRepository。

这是TestingRepository界面(简单明了):

package com.***.**.services.testing.persistence.api;

@Repository
public interface TestingRepository {
  void create();
  void findByCustomAttr();
}

这是TestingRepository的impl类:

package com.***.**.services.testing.persistence.memory;

@Repository
public abstract class MemoryTestingRepository implements JpaRepository<Testing, Integer>, TestingRepository {
 @Override
    public void create(@NotNull Testing testing) {
        save(testing); //the real jpa method called.
    }

@Override
public void findByCustomAttr(){
//some impl....
}

}

最后,主类如下所示:

package com.***.**.services.testing.main;
@SpringBootApplication(scanBasePackages = "com.***.**.services.testing")
@EnableJpaRepositories("com.***.**.services.testing.persistence")
@EntityScan(basePackages = {"com.***.**.services.testing.persistence"})
public class TestingApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestingApplication.class, args);
    }

}

我也有一个运行程序类,它调用处理程序方法:

package com.***.**.services.testing.main;

@Component
public class TestingService implements CommandLineRunner {

    @Autowired
    private TestingHandler testingHandler;

    @Override

    public void run(final String... args) throws Exception {
        test();
    }


    public void test() {
        testingHandler.create(new TestingDto("name", "desc", "some_mapping_id", Version.create()));
    }

}

有指针吗?

2 个答案:

答案 0 :(得分:0)

在我看来,不仅您的MemoryTestingRepository没有实现TestingRepository接口,而且还是抽象的,这让我有些困惑。那是实际代码还是您在发布时犯了一些编辑错误? 如果是,那么答案可能是Spring可能无法从抽象类创建bean。

查看文档https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.single-repository-behavior 解释了如何设置自定义存储库实现

编辑: 根据您的评论和更新,我现在了解您的问题。

您想要的是使您的具体存储库扩展SimplaJpaRepository,以便您的实现可以访问JpaRepository的save方法。

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.customize-base-repository

中查看示例

您的存储库看起来像这样

package com.***.**.services.testing.persistence.memory;

@Repository
public class MemoryTestingRepository extends SimpleJpaRepository<Testing, Integer> implements TestingRepository {
 @Override
    public void create(@NotNull Testing testing) {
        save(testing); //the real jpa method called.
    }

@Override
public void findByCustomAttr(){
//some impl....
}

}

答案 1 :(得分:0)

您的MemoryTestingRepository是抽象的,因此Spring不能实例化它。另外,存储库接口也由Spring Data Jpa管理,您不应在核心模块中具有该依赖性。

根据我在多模块项目中的经验(我很少接受),我目前在核心模块中为每个数据提供者请求创建SAM接口。然后在我的数据提供者模块中的具体数据提供者类中实现这些接口,并注入所需的JpaRepository。

对于您而言,这意味着:

核心模块:

@Service
public class TestingHandler {

    @Autowired
    private TestingCreator testingCreator;

    @NotNull
    public Testing create(@NotNull final TestingDto testingDto) {
        Testing testing = ...;
        testingCreator.createTesting(testing);
        return testing;
    }

}
public interface TestingCreator {

    void createTesting(Testing testing);

}

持久性模块

@Repository
public class TestingDataprovider implements TestingCreator /*, ...other interfaces from core module*/ {

    @Autowired
    private TestingRepository testingRepository;

    @Override
    public void createTesting(Testing testing) {
        testingRepository.save(testing);
    }

    // other methods like findByCustomAttr

}
public interface TestingRepository extends JpaRepository<Testing, Integer> {
    // custom methods
}

通过这种方式,您还可以将核心实体与持久性实体分离。


作为参考,我发现最有用的指南是this github repository,它具有出色的自述文件以及其他有用的链接。