[英]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 將調用MyEntityRepositoryImpl
的save
方法而不是默認實現。 至少這對我有用 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
}
}
如果您僅使用接口,則可以使用默認方法對CrudRepository
或JpaRepository
進行簡單覆蓋:
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.