How to get Repository Interface object back from SimpleJpaRepository<T,ID> object with proxy resolution

Viewed 905

Repository Class

@Repository
public interface AllFileRepository extends JpaRepository<AllFile, Long> {
    @Query("SELECT a From AllFile a where a.guid = :guid")
    AllFile findByGuid(@Param("guid") String guid);
}

Service Class

@Service
@Data
public class PipelineService implements Serializable {
    /**
     *
     */
    private static final long serialVersionUID = 1L;

    @Autowired
    AllFileRepository allFileRepository;
}

Creating object SimpleJpaRepository<AllFile, Long>

PipelineService pipelineService = new PipelineService();

void methodX() {
    AnnotationConfigApplicationContext applicationContext = (AnnotationConfigApplicationContext) 
    ApplicationContextUtils.getApplicationContext();
    AutowireCapableBeanFactory autowireCapableBeanFactory = 
        applicationContext.getAutowireCapableBeanFactory();
    autowireCapableBeanFactory.autowireBean(pipelineService);
    JpaRepository<AllFile, Long> jpaRepository = (SimpleJpaRepository<AllFile, Long>) 
        AopProxyUtils.getSingletonTarget(pipelineService.getAllFileRepository());

    // (AllFileRepository) jpaRepository casting causes ClassCastException
 }

Exception

java.lang.ClassCastException: org.springframework.data.jpa.repository.support.SimpleJpaRepository cannot be cast to ....repository.AllFileRepository

How to get an object of AllFileRepository interface? where I can access findByGuid("");

UPDATE

I am able to get the object of SimpleJpaRepository<AllFile, Long> belonging to interface AllFileRepository which inturn extends JpaRepository

public void initialize() {
        AnnotationConfigApplicationContext applicationContext = (AnnotationConfigApplicationContext) ApplicationContextUtils
                .getApplicationContext();
        EntityManager em = applicationContext.getBean(EntityManager.class);
        AllFileRepository allFileRepository = new JpaRepositoryFactory(em).getRepository(AllFileRepository.class);
        allFileRepository.findByGuid(""); // Method is accessible here but the object is proxy
        SimpleJpaRepository<AllFile, Long> simpleJpaRepository = (SimpleJpaRepository<AllFile, Long>) (AopProxyUtils
                .getSingletonTarget(allFileRepository)); // But proxy resolution yeilds SimpleJpaRepository cannot
                                                         // explicitly cast to AllFileRepository
        ((AllFileRepository) simpleJpaRepository).findByGuid(""); // class
                                                                  // org.springframework.data.jpa.repository.support.SimpleJpaRepository
                                                                  // cannot be cast to class
                                                                  // com.example.spingaoprepository.repository.AllFileRepository
                                                                  // (org.springframework.data.jpa.repository.support.SimpleJpaRepository
                                                                  // and
                                                                  // com.example.spingaoprepository.repository.AllFileRepository
                                                                  // are in unnamed module of loader 'app'

    }

Here is a link to sample program

3 Answers

There is no use in trying to cast incompatible types into each other. This is not a Spring problem, it is Java basics. I already told you how to solve that in Spring, too: You need to provide a class actually implementing AllFileRepository and make sure Spring Data JPA uses it as a repository instead of the interface. In order to do that, you need to

  • change the interface annotation from @Repository to @NoRepositoryBean,
  • create class @Repository AllFileRepositoryImpl and there provide an implementation of AllFile findByGuid(String guid) doing something meaningful.

Then you can easily cast the way you want because your proxy's target object will be an AllFileRepositoryImpl instance.

I sent you a pull request which you just need to accept. The classes changed in the relevant commit @5705cbb look as follows:

package com.example.spingaoprepository.repository;

import com.example.spingaoprepository.model.AllFile;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.data.repository.NoRepositoryBean;

@NoRepositoryBean
public interface AllFileRepository extends JpaRepository<AllFile, Long> {
    @Query("SELECT a From AllFile a where a.guid = :guid")
    AllFile findByGuid(@Param("guid") String guid);
}
package com.example.spingaoprepository.repository;

import com.example.spingaoprepository.model.AllFile;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.stereotype.Repository;

import javax.persistence.EntityManager;

@Repository
public class AllFileRepositoryImpl extends SimpleJpaRepository<AllFile, Long> implements AllFileRepository {
  public AllFileRepositoryImpl(EntityManager em) {
    super(AllFile.class, em);
  }

  @Override
  public AllFile findByGuid(String guid) {
    System.out.println("Finding AllFile by GUID " + guid);
    return null;
  }
}
package com.example.spingaoprepository.serializable;

import com.example.spingaoprepository.repository.AllFileRepository;
import com.example.spingaoprepository.service.PipelineService;
import com.example.spingaoprepository.utils.ApplicationContextUtils;

import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;

public class PipelineConfigurer {
    private ApplicationContext applicationContext = ApplicationContextUtils.getApplicationContext();
    private PipelineService pipelineService = applicationContext.getBean(PipelineService.class);

    public void initialize() {
        AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
        autowireCapableBeanFactory.autowireBean(pipelineService);
        AllFileRepository allFileRepository = (AllFileRepository) AopProxyUtils
                .getSingletonTarget(pipelineService.getAllFileRepository());
        allFileRepository.findByGuid("XY-123");
    }

}

You are instantiating your PipelineService using the new keyword and then manually getting the Spring context in your methodX().

In principle, of course you 'could' do it all manually without Spring. But given both your repository and service are Spring beans, then it's better to let Spring handle the bean lifecyle. It's certainly easier anyway.

As PipelineService is a Spring bean, you should really make any class that uses it Spring aware too. So just annotate the class that with a Spring annotation like @Component, @Service, or perhaps @RestController if it's a controller.

If you insist on creating the bean manually then try -

 ApplicationContext context = new AnnotationConfigApplicationContext();
 PipelineService pipelineService = context.getBean(PipelineService.class);

Using @Autowired you are injecting an object implementing AllFileRepository. This singleton was created at startup hence you can just use is with allFileRepository.findByGuid("");

Related