Spring Data:覆盖保存方法

时间:2012-10-23 17:39:57

标签: java spring-data

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

12 个答案:

答案 0 :(得分:26)

只需像往常一样创建自定义界面,并使用与CrudRepository(或JpaRepository等)公开的方案相同的签名声明您想要ovverride的方法。假设您有一个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将调用save的{​​{1}}方法,而不是默认实现。 至少这对我来说适用于Spring Data JPA 1.7.2中的MyEntityRepositoryImpl方法。

答案 1 :(得分:8)

我猜你扩展了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**"/>

希望它有所帮助。

答案 2 :(得分:7)

要提供对默认生成的save方法的覆盖,您需要在自己的自定义存储库实现中使用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;

答案 3 :(得分:6)

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

答案 4 :(得分:1)

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

答案 5 :(得分:1)

如果您要重用原始方法,这可能会有所帮助。只需在实现类中注入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);
        }
    }
}

答案 6 :(得分:1)

如果仅使用接口,则可以使用默认方法对CrudRepositoryJpaRepository进行简单的覆盖:


public interface MyCustomRepository extends CrudRepository<T, ID> {

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

答案 7 :(得分:1)

我正在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(实际上是CrudRepository扩展的JpaRepository)覆盖save()来调用我们的自定义方法。编译器会警告您未经检查的转换,因此如果您想使用@SuppressWarnings将其静音,则由您决定。

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

public class MyEntityRepositoryCustomSaveActionImpl implements 
  MyEntityRepositoryCustomSaveAction {

    @PersistenceContext
    private EntityManager entityManager;

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

}

答案 8 :(得分: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);
        }
    }
}

答案 9 :(得分:0)

为了正确地覆盖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> {}

答案 10 :(得分:0)

最适合我的方法(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
    }
}

答案 11 :(得分:0)

我正在将应用程序从 Spring Boot 1.5 更新到 Spring Boot 2.0,我发现我们的保存方法中的一些自定义行为突然不再起作用,结果我们不得不更新我们存储库上的 save 方法可以工作。请注意,我必须在函数上添加泛型类参数和泛型参数,才能在 Eclipse 内部和通过 CLI (gradle) 进行构建。

所以我像这样改变了我的自定义存储库界面:

interface ScoreRepositoryCustom {
  Score save(Score score);
}

到此(以匹配 CrudRepository 中的签名):

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