Spring Data: Override save method

I'm considering spring data for a project. Is it possible to override the per default generated save method? And if yes, how?


Simply create your custom interface as usual and declare there the methods you want to ovverride with the same signature of the one exposed by CrudRepository (or JpaRepository, etc.). Suppose you have a MyEntity entity and a MyEntityRepository repository and you want to override the default auto-generated save method of MyEntityRepository which takes an only entity instance, then define:

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

and implement this method as you like in your MyEntityRepositoryImpl, as usual:

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

And then, as usual, let MyEntityRepository extend MyEntityRepositoryCustom.

Doing this, Spring Data JPA will call the save method of your MyEntityRepositoryImpl instead of the default implementation. At least this works for me with the delete method in Spring Data JPA 1.7.2.


"ambiguous reference" error

As reported by some of the commenters, probably starting from a certain Spring Data JPA version or javac version (I can't say when it started to fail, but I know for sure that it worked before) the javac compiler began to give a compilation error on the overridden method: "ambiguous reference". Eclipse JDT does not return this error and code works at runtime, in fact I opened Bug 463770 for this reason: either it's a javac bug or a JDT bug that does not conform to javac. This said, Lucas has posted the workaround below, which at a first sight might seem to be identical to the one described above. Actually, the difference stands on the MyEntityRepositoryCustom, declaration which must include the generic type <S>, even if it's apparently useless. So, if you encounter this error change the custom interface declaration as:

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

and let the standard repository interface implement MyEntityRepositoryCustom<MyEntity> rather than just MyEntityRepositoryCustom.

However, if you don't want to generify your custom interface, you can extract a separate fragment interface particulary for the save method 2. Then the solution looks as follows:

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
}

To provide override of default generated save method you need to use aggregation of Spring Data repository implementation inside your own custom repository implementation.

Repository interface:

public interface UserRepository extends CrudRepository<User, String>{

}

Your repository implementation:

@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);
    }
}

Then use your custom implementation in your service:

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