簡體   English   中英

Spring Data:覆蓋保存方法

[英]Spring Data: Override save method

我正在考慮一個項目的彈簧數據。 是否可以覆蓋每個默認生成的保存方法? 如果是,如何?

只需像往常一樣創建您的自定義接口,並在那里聲明您想要覆蓋的方法,其簽名與CrudRepository (或JpaRepository等)公開的方法相同。 假設你有一個MyEntity實體和MyEntityRepository庫,並要覆蓋默認的自動生成的save方法MyEntityRepository這需要一個唯一的實體實例,然后定義:

public interface MyEntityRepositoryCustom {
  <S extends MyEntity> S save(S entity);
}

並像往常一樣在MyEntityRepositoryImpl實現您喜歡的方法:

@Transactional
public class MyEntityRepositoryImpl implements MyEntityRepositoryCustom {
  public <S extends MyEntity> S save(S entity) {
    // your implementation
  }
}

然后,像往常一樣,讓MyEntityRepository擴展MyEntityRepositoryCustom

這樣做,Spring Data JPA 將調用MyEntityRepositoryImplsave方法而不是默認實現。 至少這對我有用 Spring Data JPA 1.7.2 中的delete方法。


“模棱兩可的參考”錯誤

正如一些評論者所報告的那樣,可能是從某個 Spring Data JPA 版本或 javac 版本開始(我不能說它什么時候開始失敗,但我確信它以前有效)javac 編譯器開始進行編譯覆蓋方法上的錯誤:“不明確的引用”。 Eclipse JDT 不會返回此錯誤並且代碼在運行時有效,實際上我為此打開了錯誤 463770 :要么是 javac 錯誤,要么是不符合 javac 的 JDT 錯誤。 話雖如此,盧卡斯已經在下面發布了解決方法,乍一看似乎與上述方法相同。 實際上,區別在於MyEntityRepositoryCustom ,聲明必須包含泛型類型<S> ,即使它顯然沒有用。 因此,如果您遇到此錯誤,請將自定義接口聲明更改為:

public interface MyEntityRepositoryCustom<S> {
  <S extends MyEntity> S save(S entity);
}

並讓標准存儲庫接口實現MyEntityRepositoryCustom<MyEntity>而不僅僅是MyEntityRepositoryCustom

但是,如果您不想泛化您的自定義界面,您可以為save方法2提取一個單獨的片段界面。 然后解決方案如下:

public interface MyEntityRepositoryCustom {
  // custom methods
}

public interface CustomizedSaveRepository<T> {
  <S extends T> S save(S entity);
}

@Transactional
public class MyEntityRepositoryImpl implements MyEntityRepositoryCustom,
 CustomizedSaveRepository<MyEntity> {
  
  @Override
  public MyEntity save(MyEntity entity) {
    // your implementation for CustomizedSaveRepository#save
  }

  // implementations of MyEntityRepositoryCustom
}

要提供默認生成的保存方法的覆蓋,您需要在您自己的自定義存儲庫實現中使用 Spring Data 存儲庫實現的聚合。

倉庫接口:

public interface UserRepository extends CrudRepository<User, String>{

}

您的存儲庫實現:

@Repository("customUserRepository")
public class CustomUserRepository implements UserRepository {

    @Autowired
    @Qualifier("userRepository") // inject Spring implementation here
    private UserRepository userRepository;

    public User save(User user) {
        User user = userRepository.save(entity);
        // Your custom code goes here
        return user;
    }

    // Delegate other methods here ...

    @Override
    public User findOne(String s) {
        return userRepository.findOne(s);
    }
}

然后在您的服務中使用您的自定義實現:

@Autowired
@Qualifier("customUserRepository")
private UserRepository userRepository;

沒有讓它很好地工作,所以我把我需要的邏輯放到一個服務類中,並保持存儲庫保存方法不變。

我猜你擴展 SimpleJpaRepository :

public class **CustomSimpleJpaRepository** extends SimpleJpaRepository {

@Transactional
public <S extends T> S save(S entity) { //do what you want instead }
}

然后通過擴展確保使用它而不是默認的 SimpleJpaRepository :

public class CustomJpaRepositoryFactory extends JpaRepositoryFactory {

    protected <T, ID extends Serializable> JpaRepository<?, ?> getTargetRepository(RepositoryMetadata metadata, EntityManager entityManager) {

      Class<?> repositoryInterface = metadata.getRepositoryInterface();
      JpaEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainType());

      SimpleJpaRepository<?, ?> repo = isQueryDslExecutor(repositoryInterface) ? new QueryDslJpaRepository(
            entityInformation, entityManager) : new CustomSimpleJpaRepository(entityInformation, entityManager);
    repo.setLockMetadataProvider(lockModePostProcessor.getLockMetadataProvider());

      return repo;
  }
}

還沒有完成,我們還需要有自己的工廠 bean 才能在 config xml 中使用它:

public class CustomRepositoryFactoryBean <T extends JpaRepository<S, ID>, S, ID extends Serializable> extends JpaRepositoryFactoryBean<T, S, ID> {

protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
    return new **CustomJpaRepositoryFactory**(entityManager);
}

}

配置:

<jpa:repositories base-package="bla.bla.dao" factory-class="xxxx.**CustomRepositoryFactoryBean**"/>

希望能幫助到你。

為了正確覆蓋 save 方法,您必須創建一個接口,該接口具有在 CrudRepository 上聲明的原始方法的正確簽名,包括泛型

public interface MyCustomRepository<T> {
    <S extends T> S save(S entity);
}

然后,創建您的實現(后綴 Impl 在類名中很重要)

public class MyCustomRepositoryImpl implements MyCustomRepository<MyBean> {

    @Autowired
    private EntityManager entityManager;


    @Override
    public <S extends MyBean> S save(S entity) {
       /**
         your custom implementation comes here ...
         i think the default one is just        
        return this.entityManager.persist(entity);
       */
    }

}

最后,使用之前創建的界面擴展您的存儲庫

@RepositoryRestResource
@Repository
public interface MyBeanRepository extends PagingAndSortingRepository<MyBean, Long>, MyCustomRepository<MyBean> {}

我在 OpenJDK 11 上使用 Spring Boot 2.1.4 並且不斷從編譯器中得到ambiguous reference錯誤(盡管我的 IDE 使用的 Eclipse JDT 編譯器沒有問題,所以我沒有發現這個問題,直到我嘗試在我的 IDE 之外構建它)。

我基本上最終在我的擴展接口中定義了一個具有不同名稱的方法,然后在我的主存儲庫接口中使用default覆蓋在調用普通save()時調用它。

下面是一個例子:

像往常一樣定義自定義邏輯的接口:

public interface MyEntityRepositoryCustomSaveAction {
    public MyEntity saveSafely(MyEntity entity);
}

使您的存儲庫擴展該接口:

public interface MyEntityRepository extends JpaRepository<MyEntity, MyEntityId>,
  MyEntityRepositoryCustomSaveAction {

    @Override
    @SuppressWarnings("unchecked")
    default MyEntity save(MyEntity entity)
    {
        return saveSafely(entity);
    }
}

請注意,我們已經從JpaRepository (嗯,實際上是JpaRepository擴展的CrudRepository )覆蓋了 save() 來調用我們的自定義方法。 編譯器會警告未經檢查的轉換,所以如果您想使用@SuppressWarnings使其靜音, @SuppressWarnings@SuppressWarnings

使用自定義邏輯遵循 Impl 類的約定

public class MyEntityRepositoryCustomSaveActionImpl implements 
  MyEntityRepositoryCustomSaveAction {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public MyEntity saveSafely(MyEntity entity) {
       //whatever custom logic you need
    }

}

如果您僅使用接口,則可以使用默認方法對CrudRepositoryJpaRepository進行簡單覆蓋:


public interface MyCustomRepository extends CrudRepository<T, ID> {

  @Override
  default <S extends T> S save(S entity)
  {
    throw new UnsupportedOperationException("writes not allowed");
  }
}

如果您要重用原始方法,這可能會有所幫助。 只需在實現類中注入EntityManager

public interface MyEntityRepositoryCustom {
  <S extends MyEntity> S save(S entity);
}

public class MyEntityRepositoryImpl implements MyEntityRepositoryCustom {

    // optionally specify unitName, if there are more than one
    @PersistenceContext(unitName = PRIMARY_ENTITY_MANAGER_FACTORY)
    private EntityManager entityManager;

    /**
     * @see org.springframework.data.jpa.repository.support.SimpleJpaRepository
     */
    @Transactional
    public <S extends MyEntity> S save(S entity) {
        // do your logic here
        JpaEntityInformation<MyEntity, ?> entityInformation = JpaEntityInformationSupport.getMetadata(MyEntity.class, entityManager);
        if (entityInformation.isNew(entity)) {
            entityManager.persist(entity);
            return entity;
        } else {
            return entityManager.merge(entity);
        }
    }
}

使用 JPA 事件偵聽器,如 @PrePersist、@PreUpdate。 如果底層 JPA 提供程序支持此功能,這將起作用。 這是 JPA 2 特性,所以最新的 Hibernate、EclipseLink 等應該支持它。

@ytterrr 的解決方案有效,但對於較舊的 Spring Data 版本,至少對於Spring Data 2.1 ,這是一種不僅可以覆蓋任何存儲庫方法而且還可以訪問底層功能(訪問實體管理器以進行持久化、刪除、查找)的方法...):

public interface MyEntityRepositoryCustom {
  <S extends MyEntity> S save(S entity);
}

public class MyEntityRepositoryImpl implements MyEntityRepositoryCustom {

    final JpaEntityInformation<MyEntity, ?> entityInformation;
    EntityManager em;

    public MyEntityRepositoryImpl(EntityManager entityManager) {
        this.entityInformation = JpaEntityInformationSupport.getEntityInformation(MyEntity.class, entityManager);
        this.em = entityManager;
    }

    /**
     * @see org.springframework.data.jpa.repository.support.SimpleJpaRepository
     */
    @Transactional
    public <S extends MyEntity> S save(S entity) {

        // do your logic here

        if (entityInformation.isNew(entity)) {
            em.persist(entity);
            return entity;
        } else {
            return em.merge(entity);
        }
    }
}

什么對我有用(Spring boot 2.x Java 11),即使不是很干凈。 它使用 IDE、Maven 和 Gradle 進行編譯。 Lucas 的上述解決方案不適用於 JpaRepository。

public interface MyRepo extends JpaRepository<MyType, Long>, MyRepoCustom{

   //Implemented in MyRepoCustom
   public MyType save(MyType mytypeEntity);
}

自定義接口(重復聲明,不好):

public interface MyRepoCustom{
    public MyType save(MyType mytypeEntity);
}

自定義Impl:

@Repository
public class MyRepoImpl implements MyRepoCustom{
    @PersistenceContext
    private EntityManager em;

    @Transactional
    public MyType save(MyType mytypeEntity) {
       //type safe implementation
    }
}

我正在將應用程序從 Spring Boot 1.5 更新到 Spring Boot 2.0,我發現我們的保存方法中的一些自定義行為突然不再起作用,結果我們不得不更新保存方法的簽名在我們的存儲庫上工作。 請注意,我必須在函數上添加泛型類參數和泛型參數,才能在 Eclipse 內部和通過 CLI (gradle) 進行構建。

所以我改變了我的自定義存儲庫界面,如下所示:

interface ScoreRepositoryCustom {
  Score save(Score score);
}

為此(以匹配 CrudRepository 中的簽名):

interface ScoreRepositoryCustom<T> {
  <S extends T> S save(S to);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM