50

我正在考虑一个项目的弹簧数据。是否可以覆盖每个默认生成的保存方法?如果是,如何?

4

12 回答 12

39

只需像往常一样创建您的自定义接口,并在那里声明您想要覆盖的方法,并使用与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
  }
}

然后,像往常一样,让MyEntityRepositoryextend MyEntityRepositoryCustom

这样做,Spring Data JPA 将调用save您的方法MyEntityRepositoryImpl而不是默认实现。至少这对我来说适用delete于 Spring Data JPA 1.7.2 中的方法。


“不明确的参考”错误

正如一些评论者所报告的,可能是从某个 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
}
于 2015-03-12T10:05:37.583 回答
20

要覆盖默认生成的保存方法,您需要在您自己的自定义存储库实现中使用 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;
于 2016-01-15T15:21:04.897 回答
16

没有让它很好地工作,所以我把我需要的逻辑放到一个服务类中,并保持存储库保存方法不变。

于 2012-12-10T09:26:44.177 回答
10

我猜你扩展 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 才能在配置 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**"/>

希望能帮助到你。

于 2013-02-09T22:02:24.750 回答
7

为了正确覆盖保存方法,您必须创建一个接口,该接口具有在 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> {}
于 2019-07-22T16:00:08.467 回答
6

我在 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);
    }
}

请注意,我们已经覆盖了 save() from JpaRepository(好吧,真的CrudRepositoryJpaRepository扩展)来调用我们的自定义方法。编译器会警告未经检查的转换,所以如果你想用@SuppressWarnings.

使用您的自定义逻辑遵循 Impl 类的约定

public class MyEntityRepositoryCustomSaveActionImpl implements 
  MyEntityRepositoryCustomSaveAction {

    @PersistenceContext
    private EntityManager entityManager;

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

}
于 2019-05-15T21:29:49.447 回答
5

如果您要重用原始方法,这可能会有所帮助。只需注入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);
        }
    }
}
于 2018-01-20T11:28:29.223 回答
5

如果您只使用接口,则可以使用默认方法对CrudRepositoryor进行简单的覆盖JpaRepository


public interface MyCustomRepository extends CrudRepository<T, ID> {

  @Override
  default <S extends T> S save(S entity)
  {
    throw new UnsupportedOperationException("writes not allowed");
  }
}
于 2019-03-20T08:03:58.113 回答
1

使用 JPA 事件侦听器,例如 @PrePersist、@PreUpdate。如果底层 JPA 提供程序支持此功能,这将起作用。这是 JPA 2 的特性,所以最新的 Hibernate、EclipseLink 等应该支持它。

于 2013-07-13T06:36:28.193 回答
1

什么对我有用(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
    }
}
于 2020-09-28T08:21:54.260 回答
0

@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);
        }
    }
}
于 2019-07-05T20:21:33.393 回答
0

我正在将应用程序从 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);
}
于 2021-01-30T18:06:40.933 回答