JPA Interface extending JpaRepository only, with use of EntityManager in default methods

Viewed 189

First of all, for those who wanted to know how to use an EntityManager in an interface extending a JpaRepository. Here is a potential solution. But !!! I'm aware this is maybe a baaaad practice, and experienced people in Spring and JPA will maybe try to kill me ;) apologizes...

So, after this BIG warning, here is my question :

I would like to use only interfaces without a Custom implementation and this is what I did :

@Repository
@Transactional
public interface BookingDao extends JpaRepository<Booking, Long> {

    Booking findByCodeId(String codeId);

    void deleteByCodeId(String codeId);

    default Integer findCountOfBookingOldDay() {
        EntityManager entityManager = ApplicationContextProvider.getEntityManager();
        Query query = entityManager.createNativeQuery("XXXX");
        Number result = (Number) query.getSingleResult();
        return result.intValue();
    }
}
    @Component(value = "applicationContextProvider")
    public class ApplicationContextProvider implements ApplicationContextAware {
        private static ApplicationContext context;
        public static ApplicationContext getApplicationContext() {
            return context;
        }
        public static EntityManager getEntityManager() {
            return EntityManagerFactoryUtils.getTransactionalEntityManager(context.getBean(LocalContainerEntityManagerFactoryBean.class).getObject());
            return context.getBean(EntityManager.class);
        }
        @Override
        public void setApplicationContext(ApplicationContext ac) throws BeansException {
            context = ac;
        }
}

I'm aware this is maybe not the best practice, and the best way would be to use @PersistenceContext (takes care to create a unique EntityManager for every thread) in a class... But what would be in my case the best alternative :

this :

return EntityManagerFactoryUtils.getTransactionalEntityManager(context.getBean(LocalContainerEntityManagerFactoryBean.class).getObject());

or this :

return context.getBean(EntityManager.class)

or maybe another better way, still by using only interface.

Thank you for your remarks/advices.

PS: "EntityManagerFactoryUtils" is not doing the same as PersistenceContext ?

1 Answers

For what you want to do I'd annotate a method in the interface by @Query("<your native select counting what you need>", native=true).

When I need any fancy logic, I usually create a service, which is using the repository like this:

@Service
@Transactional
public class MyService {

    private final MyRepository repository;

    @Autowired
    public MyService (MyRepository repository) {
        this.repository = repository;
    }

    public int myMethod(){
        //code using repository
    }

}
Related