How to add custom method to Spring Data JPA

Posted on

How to add custom method to Spring Data JPAError is an illegal operation performed by the user which results in the abnormal working of the program. By now, you’ve probably seen a few errors, either when compiling or running your code like How to add custom method to Spring Data JPA. It can be frustrating, but they can also give you a lot of information about exactly how you can fix the problems in your code about java and spring-data. In this post covers the types of errors you’ll see when programming in Java, and how to fix them. Don’t pay any attention to the number of errors. Just read the first error message and work on fixing that error.

Problem :

I am looking into Spring Data JPA. Consider the below example where I will get all the crud and finder functionality working by default and if I want to customize a finder then that can be also done easily in the interface itself.

@Transactional(readOnly = true)
public interface AccountRepository extends JpaRepository<Account, Long> {

  @Query("<JPQ statement here>")
  List<Account> findByCustomer(Customer customer);

I would like to know how can I add a complete custom method with its implementation for the above AccountRepository? Since its an Interface I cannot implement the method there.

Solution :

You need to create a separate interface for your custom methods:

public interface AccountRepository 
    extends JpaRepository<Account, Long>, AccountRepositoryCustom { ... }

public interface AccountRepositoryCustom {
    public void customMethod();

and provide an implementation class for that interface:

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    AccountRepository accountRepository;  /* Optional - if you need it */

    public void customMethod() { ... }

See also:

In addition to axtavt’s answer, don’t forget you can inject Entity Manager in your custom implementation if you need it to build your queries:

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    private EntityManager em;

    public void customMethod() { 

There’s a slightly modified solution that does not require additional interfaces.

As specificed in the documented functionality, the Impl suffix allows us to have such clean solution:

  • Define in you regular @Repository interface, say MyEntityRepository the custom methods (in addition to your Spring Data methods)
  • Create a class MyEntityRepositoryImpl (the Impl suffix is the magic) anywhere (doesn’t even need to be in the same package) that implements the custom methods only and annotate such class with @Component** (@Repository will not work).
    • This class can even inject MyEntityRepository via @Autowired for use in the custom methods.


Entity class (for completeness):

package myapp.domain.myentity;
public class MyEntity {
    @Id     private Long id;
    @Column private String comment;

Repository interface:

package myapp.domain.myentity;

public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {

    List<MyEntity> findByCommentEndsWith(String x);

    List<MyEntity> doSomeHql(Long id);   // custom method, code at *Impl class below

    List<MyEntity> useTheRepo(Long id);  // custom method, code at *Impl class below


Custom methods implementation bean:

package myapp.infrastructure.myentity;

@Component // Must be @Component !!
public class MyEntityRepositoryImpl { // must have the exact repo name + Impl !!

    private EntityManager entityManager;

    private MyEntityRepository myEntityRepository;

    public List<MyEntity> doSomeHql(Long id) {
        String hql = "SELECT eFROM MyEntity e WHERE = :id";
        TypedQuery<MyEntity> query = entityManager.createQuery(hql, MyEntity.class);
        query.setParameter("id", id);
        return query.getResultList();

    public List<MyEntity> useTheRepo(Long id) {
        List<MyEntity> es = doSomeHql(id);
        return es;



// You just autowire the the MyEntityRepository as usual
// (the Impl class is just impl detail, the clients don't even know about it)
public class SomeService {
    private MyEntityRepository myEntityRepository;

    public void someMethod(String x, long y) {
        // call any method as usual

And that’s all, no need for any interfaces other than the Spring Data repo one you already have.

The only possible drawbacks I identified are:

  • The custom methods in the Impl class are marked as unused by the compiler, thus the @SuppressWarnings("unused") suggestion.
  • You have a limit of one Impl class. (Whereas in the regular fragment interfaces implementation the docs suggest you could have many.)
  • If you place the Impl class at a different package and your test uses only @DataJpaTest, you have to add @ComponentScan("package.of.the.impl.clazz") to your test, so Spring loads it.

The accepted answer works, but has three problems:

  • It uses an undocumented Spring Data feature when naming the custom implementation as AccountRepositoryImpl. The documentation clearly states that it has to be called AccountRepositoryCustomImpl, the custom interface name plus Impl
  • You cannot use constructor injection, only @Autowired, that are considered bad practice
  • You have a circular dependency inside of the custom implementation (that’s why you cannot use constructor injection).

I found a way to make it perfect, though not without using another undocumented Spring Data feature:

public interface AccountRepository extends AccountRepositoryBasic,

public interface AccountRepositoryBasic extends JpaRepository<Account, Long>
    // standard Spring Data methods, like findByLogin

public interface AccountRepositoryCustom 
    public void customMethod();

public class AccountRepositoryCustomImpl implements AccountRepositoryCustom 
    private final AccountRepositoryBasic accountRepositoryBasic;

    // constructor-based injection
    public AccountRepositoryCustomImpl(
        AccountRepositoryBasic accountRepositoryBasic)
        this.accountRepositoryBasic = accountRepositoryBasic;

    public void customMethod() 
        // we can call all basic Spring Data methods using
        // accountRepositoryBasic

This is limited in usage, but for simple custom methods you can use default interface methods like:

import demo.database.Customer;

public interface CustomerService extends CrudRepository<Customer, Long> {

    default void addSomeCustomers() {
        Customer[] customers = {
            new Customer("Józef", "Nowak", "", 679856885, "Rzeszów", "Podkarpackie", "35-061", "Zamknięta 12"),
            new Customer("Adrian", "Mularczyk", "", 867569344, "Krosno", "Podkarpackie", "32-442", "Hynka 3/16"),
            new Customer("Kazimierz", "Dejna", "", 996435876, "Jarosław", "Podkarpackie", "25-122", "Korotyńskiego 11"),
            new Customer("Celina", "Dykiel", "", 947845734, "Żywiec", "Śląskie", "54-333", "Polna 29")

        for (Customer customer : customers) {


In this spring tutorial it is written:

Spring Data JPA also allows you to define other query methods by
simply declaring their method signature.

So it is even possible to just declare method like:

Customer findByHobby(Hobby personHobby);

and if object Hobby is a property of Customer then Spring will automatically define method for you.

Im using the following code in order to access generated find methods from my custom implementation. Getting the implementation through the bean factory prevents circular bean creation problems.

public class MyRepositoryImpl implements MyRepositoryExtensions, BeanFactoryAware {

    private BrandRepository myRepository;

    public MyBean findOne(int first, int second) {
        return myRepository.findOne(new Id(first, second));

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        myRepository = beanFactory.getBean(MyRepository.class);

Considering your code snippet, please note that you can only pass Native objects to the findBy### method, lets say you want to load a list of accounts that belongs certain costumers, one solution is to do this,

@Query("Select a from Account a where a."#nameoffield"=?1")
List<Account> findByCustomer(String "#nameoffield");

Make sue the name of the table to be queried is thesame as the Entity class.
For further implementations please take a look at this

If you want to be able to do more sophisticated operations you might need access to Spring Data’s internals, in which case the following works (as my interim solution to DATAJPA-422):

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    private EntityManager entityManager;

    private JpaEntityInformation<Account, ?> entityInformation;

    public void postConstruct() {
        this.entityInformation = JpaEntityInformationSupport.getMetadata(Account.class, entityManager);

    public Account saveWithReferenceToOrganisation(Account entity, long referralId) {
        entity.setOrganisation(entityManager.getReference(Organisation.class, organisationId));
        return save(entity);

    private Account save(Account entity) {
        // save in same way as SimpleJpaRepository
        if (entityInformation.isNew(entity)) {
            return entity;
        } else {
            return entityManager.merge(entity);


There is another issue to be considered here. Some people expect that adding custom method to your repository will automatically expose them as REST services under ‘/search’ link. This is unfortunately not the case. Spring doesn’t support that currently.

This is ‘by design’ feature, spring data rest explicitly checks if method is a custom method and doesn’t expose it as a REST search link:

private boolean isQueryMethodCandidate(Method method) {    
  return isQueryAnnotationPresentOn(method) || !isCustomMethod(method) && !isBaseClassMethod(method);

This is a qoute of Oliver Gierke:

This is by design. Custom repository methods are no query methods as
they can effectively implement any behavior. Thus, it’s currently
impossible for us to decide about the HTTP method to expose the method
under. POST would be the safest option but that’s not in line with the
generic query methods (which receive GET).

For more details see this issue:

I liked Danila’s solution and started using it but nobody else on the team liked having to create 4 classes for each repository. Danila’s solution is the only one here that let’s you use the Spring Data methods in the Impl class. However, I found a way to do it with just a single class:

public interface UserRepository extends MongoAccess, PagingAndSortingRepository<User> {

    List<User> getByUsername(String username);

    default List<User> getByUsernameCustom(String username) {
        // Can call Spring Data methods!

        // Can write your own!
        MongoOperations operations = getMongoOperations();
        return operations.find(new Query(Criteria.where("username").is(username)), User.class);

You just need some way of getting access to your db bean (in this example, MongoOperations). MongoAccess provides that access to all of your repositories by retrieving the bean directly:

public interface MongoAccess {
    default MongoOperations getMongoOperations() {
        return BeanAccessor.getSingleton(MongoOperations.class);

Where BeanAccessor is:

public class BeanAccessor implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    public static <T> T getSingleton(Class<T> clazz){
        return applicationContext.getBean(clazz);

    public static <T> T getSingleton(String beanName, Class<T> clazz){
        return applicationContext.getBean(beanName, clazz);

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        BeanAccessor.applicationContext = applicationContext;


Unfortunately, you can’t @Autowire in an interface. You could autowire the bean into a MongoAccessImpl and provide a method in the interface to access it, but Spring Data blows up. I don’t think it expects to see an Impl associated even indirectly with PagingAndSortingRepository.

public interface UserCustomRepository {
 List<User> findAllUsersBySomeCriteria(UserCriteriaRequest criteriaRequest);

I extends the SimpleJpaRepository:

public class ExtendedRepositoryImpl<T extends EntityBean> extends SimpleJpaRepository<T, Long>
    implements ExtendedRepository<T> {

    private final JpaEntityInformation<T, ?> entityInformation;

    private final EntityManager em;

    public ExtendedRepositoryImpl(final JpaEntityInformation<T, ?> entityInformation,
                                                      final EntityManager entityManager) {
       super(entityInformation, entityManager);
       this.entityInformation = entityInformation;
       this.em = entityManager;

and adds this class to @EnableJpaRepositoryries repositoryBaseClass.

I use SimpleJpaRepository as the base class of repository implementation and add custom method in the interface,eg:

public interface UserRepository  {
    User FindOrInsert(int userId);

public class UserRepositoryImpl extends SimpleJpaRepository implements UserRepository {

    private RedisClient redisClient;

    public UserRepositoryImpl(RedisClient redisClient, EntityManager em) {
        super(User.class, em);
        this.redisClient = redisClient;

public User FindOrInsert(int userId) {

    User u = redisClient.getOrSet("test key.. User.class, () -> {
        Optional<User> ou = this.findById(Integer.valueOf(userId));
        return ou.get();
    return u;

Leave a Reply

Your email address will not be published. Required fields are marked *