Search code examples
javaspringspring-bootspring-data-jpacircular-dependency

Circular dependency while injecting JPA repository to implementation


I was studying Spring Data JPA recently, and I faced a weird circular dependency error.

Previously, I often used MyBatis as shown below.

// I often declare mapper first, and inject it to my persistence.

@Mapper
public interface MyBatisMapper {/* ... */}

/* ------------ */

@Repository
public class MyBatisRepo {
  private final MyBatisMapper mapper;

  public MyBatisRepo(MyBatisMapper mapper) {
    this.mapper = mapper;
  }
  
  /* ... */

}

So I tried JPA on this way, and faced a circular dependency.

Here's the code on JPA


  1. TestEntity
// entity just for test
@Entity
public class TestEntity {

    @Id
    private Long id;
}
  1. Repository interface (contracts)
// Persistence contracts
public interface TestRepo {

}
  1. JPA DAO
// 
public interface JPATestRepo extends JpaRepository<TestEntity, Long> {

}
  1. Repository implements
@Repository
public class JPATestRepoImpl implements TestRepo {

    private final JPATestRepo jpaRepo;

    public JPATestRepoImpl(JPATestRepo jpaRepo) {
        this.jpaRepo = jpaRepo;
    }
}
  1. Errors
2024-10-19T19:46:00.760+09:00  WARN 66384 --- [testing] [           main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'JPATestRepoImpl' defined in file [/~~~/Desktop/Coding/testing/build/classes/java/main/core/testing/JPATestRepoImpl.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'JPATestRepoImpl': Requested bean is currently in creation: Is there an unresolvable circular reference?
2024-10-19T19:46:00.761+09:00  INFO 66384 --- [testing] [           main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2024-10-19T19:46:00.762+09:00  INFO 66384 --- [testing] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2024-10-19T19:46:00.807+09:00  INFO 66384 --- [testing] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
2024-10-19T19:46:00.811+09:00  INFO 66384 --- [testing] [           main] .s.b.a.l.ConditionEvaluationReportLogger : 

Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-10-19T19:46:00.825+09:00 ERROR 66384 --- [testing] [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

┌──->──┐
|  JPATestRepoImpl defined in file [/~~~/Desktop/Coding/testing/build/classes/java/main/core/testing/JPATestRepoImpl.class]
└──<-──┘


Action:

Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.


Process finished with exit code 1

In console, it says JPATestRepoImpl has a cycle to itself.

But in JPATestRepoImpl on above, it doesn't. (at least on my thought)

I've treid restart, claen & rebuild application, but it's all the same.

HOWEVER, if I rename JPATestRepoImpl to another (like TestRepoJPAImpl), the circular dependency disapper like magic...

It seems like Spring or JPA conflicts its' bean names to each other, but I have no idea why it has.

So I wanted to ask whether there is really a circular dependency in my source code or where it comes from if there isn't. If there is any policy coming from JPA, is there any documentation on it?

You can see full source code on here


Solution

  • The default repositoryImplementationPostfix is Impl so Spring is assuming that your JPATestRepoImpl is a custom implementation of JPATestRepo.

    If you want to use Impl for another purpose, you can change the repositoryImplementationPostfix with the following setting:

    @EnableJpaRepositories(repositoryImplementationPostfix = "CustomImpl")
    

    link to the repositoryImplementationPostfix documentation