【问题标题】:How can I access the repository from the entity in Spring Boot?如何从 Spring Boot 中的实体访问存储库?
【发布时间】:2018-02-15 23:27:09
【问题描述】:

在 Spring Boot 项目中,我有一个 JPA 实体,如下所示:

@Entity
public class Account {
}

然后我有了查询数据库的存储库:

public interface AccountRepository extends JpaRepository<Account, UUID> {

}

在应用程序和测试中,通过以下方式获取存储库很容易:

@Autowired
private AccountRepository accountRepository;

如何在 Account 类的方法中获取存储库?我试过@Autowired,但没用。

对于那些争论设计的人,我的问题与设计无关。我已经在 Ruby 和 Rails 以及 Clojure 中使用 HugSQL 编写了这段代码,它是独立且简洁的:创建新记录时,我还会生成一个紧凑的唯一字母数字 ID。在 Ruby on Rails 中,我将其作为库发布:https://github.com/pupeno/random_unique_id

【问题讨论】:

  • 您确定没有比从实体访问存储库更好的解决方案吗?
  • @davioooh:是的,我很确定。实体在保存前需要生成一些数据,这些数据依赖于现有记录。
  • 好的...我认为实体应该不知道存储库。我个人更喜欢在服务层实现额外的逻辑......但如果确定,那就去吧......
  • @Pablo 通常你会将额外的逻辑放在应用程序的服务层中。但如果你真的需要这个,我想你可以试试@EntityListeners@PrePersist concretepage.com/java/jpa/…

标签: java spring jpa spring-boot


【解决方案1】:

您可以从静态方法访问Spring Application Context,并使用此静态方法将您的存储库bean 加载到您的@Entity 类中,而不是自动装配它。

您需要创建以下类(找到here):

ApplicationContextProvider

@Component
public class ApplicationContextProvider implements ApplicationContextAware {

    private static ApplicationContext context;

    public ApplicationContext getApplicationContext() {
        return context;
    }

    @Override
    public void setApplicationContext(ApplicationContext ctx) {
        context = ctx;
    }
}

SpringConfiguration

@Configuration
public class SpringConfiguration {

    @Bean
    public static ApplicationContextProvider contextProvider() {
        return new ApplicationContextProvider();
    }

}

然后

@Entity
public class Account {
    //your code

    public void doAccountRepositoryStuff() {
        AccountRepository accountRepository = (AccountRepository) SpringConfiguration.contextProvider().getApplicationContext().getBean("accountRepository");
        // Do your own stuff with accountRepository here...
    }
}

【讨论】:

    【解决方案2】:

    我有同样的问题,发现this Github repository

    以下是部分代码:

    ...
    import de.ck35.example.ddd.jpa.SpringEntityListener;
    ...
    
    @Entity
    @Table(name="bookshelf")
    @EntityListeners(SpringEntityListener.class)
    public class BookshelfEntity implements Bookshelf {
    
        ...
    
    
        private String category;
    
        @Autowired transient BookRepository bookRepository;
        @Autowired transient BookshelfSpaceRepository bookshelfSpaceRepository;
    

    de.ck35.example.ddd.jpa.SpringEntityListener 的用途在Javadoc 中描述:

    /**
     * Entity listener which allows dependency injection inside entities.
     * The listener can be registered via {@link EntityListeners} annotation.
     * 
     * Dependency injection annotations like {@link Autowired} are supported.
     * 
     * @author Christian Kaspari
     * @since 1.0.0
     */
    public class SpringEntityListener {
    
        ...
    
        @PostLoad
        @PostPersist
        public void inject(Object object) {
            AutowireCapableBeanFactory beanFactory = get().getBeanFactory();
            if(beanFactory == null) {
                LOG.warn("Bean Factory not set! Depdendencies will not be injected into: '{}'", object);
                return;
            }
            LOG.debug("Injecting dependencies into entity: '{}'.", object);
            beanFactory.autowireBean(object);
        }
    

    还有一个配置可以启用嵌套在类中的存储库定义(在本例中为实体):

    @Configuration
    @EnableJpaRepositories(basePackages="de.ck35.example.ddd.jpa", considerNestedRepositories=true)
    @ComponentScan("de.ck35.example.ddd.jpa")
    public class JpaConfiguration {
    

    感谢Christian Kaspari,他是此存储库的作者。

    【讨论】:

      【解决方案3】:

      我猜你正在尝试在 Hibernate 中实现类似 Active Record pattern 的东西。这很不寻常,但您可以使用@Configurable 注释您的实体,这样您就可以在内部使用@Autowired 工作(同时确保实体包在您的@ComponentScan 范围内)

      【讨论】:

      • 我不确定我是否正在尝试实现活动记录,我只是想生成一个id。
      【解决方案4】:

      您必须使用@GenericGenerator@GeneratedValue 参见示例here。请注意,在IdentifierGenerator 实现中,您可以访问存储库。

      如果这是针对非 id 字段,则解决方法是创建一个新实体 GeneralSequenceNumber,其 ID 是所需生成的 ID,然后在 GeneralSequenceNumber 和您的 Account 之间进行一对一映射实体(如提到的here

      @Entity
      public class GeneralSequenceNumber {
       @Id
       @GenericGenerator(name = "sequence_dep_id", strategy = "com.xyz.ids.DepartmentIdGenerator")
       @GeneratedValue(generator = "sequence_dep_id")  
       @Column(name="Department_Id")
       private String deptId;
      }
      
      @Entity 
      public class Account {
        @Id ..
        private Long id;
      
        @OneToOne(...)
        private GeneralSequnceNumber myVal;
      } 
      

      【讨论】:

      • 策略可以是一个类吗?我错过了。让我检查一下。
      • 这很有希望,但似乎没有调用生成器。您确定它适用于非(主)id 列吗?
      • 您要求提供“唯一的字母数字 ID”。所以我假设您想为 ID 字段执行此操作。
      【解决方案5】:

      我在一个项目中看到了下面的方法,因为那个项目没有服务层。老实说,我建议添加服务层而不是这些解决方案,但如果您真的想/必须使用它,解决方案如下。

      创建存储库接口

      public interface BookRepository extends JpaRepository<Book, String> {
      }
      

      创建一个组件

      @Component
      public class Registry {
      
      private static BookRepository bookRepository;
      
      static BookRepository bookRepo() {
          return bookRepository;
      }
      

      }

      通过在组件中使用静态函数实现 repo

      import static com.x.Registry.bookRepo;
      @Entity
      class Book {
          public static List<Book> all() {
              return bookRepo().findAll();
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2020-05-05
        • 2017-01-12
        • 2011-09-28
        • 2016-11-30
        • 1970-01-01
        • 1970-01-01
        • 2019-03-09
        • 2019-07-28
        • 1970-01-01
        相关资源
        最近更新 更多