I am facing dependency cycle in my following design (taken from here).
I have 2 entities Post and PostLog. When Post is created, I want to persist it in PostLog as well. So created listener and applied it to "Post" entity. Both entities Post and PostLog are also using spring-boot "AuditingEntityListener", but for simplicity purpose I do not add that code here.
My Entities and Listener structure -
@Data
@EqualsAndHashCode(callSuper = false)
@Entity
@Table(name = "post")
@EntityListeners({AuditingEntityListener.class, PostLogListener.class})
public class Post extends Auditable<String> {
...
}
@Data
@EqualsAndHashCode(callSuper = false)
@Entity
@Table(name = "post_log")
@EntityListeners(AuditingEntityListener.class)
public class PostLog extends Auditable<String> {
...
}
@Component
@RequiredArgsConstructor
public class PostLogListener {
private final PostLogRepository repo;
@PostPersist
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logEvent(final Post post) {
PostLog log = createLog(post); // implementation is omitted here for keeping short
repo.save(log);
}
}
@Repository
public interface PostLogRepository extends CrudRepository<PostLog, Long> {}
Error I am getting -
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| entityManagerFactory defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]
↑ ↓
| com.example.listener.PostLogListener
↑ ↓
| postLogRepository defined in com.example.repository.PostLogRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration
↑ ↓
| (inner bean)#53c2dd3a
└─────┘
I have done some research but could not find the right solution.
Use lazy initialization to solve cyclic dependencies. For that, you need to create the constructor yourself to inject spring bean and use @Lazy (org.springframework.context.annotation.Lazy)
@Component
public class PostLogListener {
private final PostLogRepository repo;
public PostLogListener(@Lazy PostLogRepository repo) {
this.repo = repo;
}
@PostPersist
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logEvent(final Post post) {
PostLog log = createLog(post); // implementation is omitted here for keeping short
repo.save(log);
}
}
Note - This is required if any of the injected beans depends on the EntityManager. Spring Data repositories depend on EntityManager, so any bean having a repository or a direct entityManager will make a circle. Spring Dependency Injection into JPA entity listener