简体   繁体   中英

Keeping Hibernate EntityManager within a Runnable

I am having a bean within which I create a new Thread with Runnable:

@Component
public class MyBean {

    private final Task task = new Task();

    @PersistenceContext
    EntityManager em;

    @PostConstruct
    public void init() {
        task.setEntityManager(em);
        new Thread(task).start();
    }

    public static class Task implements Runnable {

        @Setter
        private EntityManager em;

        @Override
        public void run() {
            while (true) {
                // working with EntityManager
                Thing t = em.findById(...); // Fetching a Thing from repo
                t.getSomethingList(); // LazyInit exception
                wait();
            }
        }
    }
}

Withing the init method, new Thread is created with instance of EntityManager. When I try to load something from the repository the session is instantly closed and getting any lazy field results in failed to lazily initialize a collection of role: Something, no session or session was closed exception from Hibernate.

I tried all the @Transactional annotations with no effect. I need to achieve something like OpenEntityManagerInView, but with the difference that this is not within view.

Thanks

EDIT1:

  1. According to comments - I tried using em.getTransaction().begin(); but this is getting me Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT .

  2. skirsch suggested that I should invoke Transactional method on some other bean. That is what I actually do - exactly as you suggested. I wanted to make things simpler and I did not realize the difference, so I demostrated the problem directly in the class Task . So to summarize, I have it exactly like skirsch suggested, but the problem persists.

As Spring is not managing your Runnable , annotating it won't have the desired effect. So you either need to use an annotated (and Spring-managed) bean from within your Runnable or you need to take care of the txn management manually.

Use Spring transaction management

You define some kind of service

@Service
public class MyService {
    @PersistenceContext
    EntityManager em;

    @Transactional
    public void doSomething() {
        Thing t = em.findById(...);
        t.getSomethingList();
    }
}

And then your bean would look like this:

@Component
public class MyBean {

    private final Task task = new Task();

    @Autowired
    MyService service;

    @PostConstruct
    public void init() {
        task.setService(service);
        new Thread(task).start();
    }

    public static class Task implements Runnable {

        @Setter
        private MyService service;

        @Override
        public void run() {
            while (true) {
                service.doSomething();
                wait();
            }
        }
    }
}

Manual transaction management

In case you set up JPA Resource Local Transactions, here you go:

// working with EntityManager
em.getTransaction().begin()
try {
    Thing t = em.findById(...);
    t.getSomethingList();
} finally {
    em.getTransaction().rollback()
}
wait();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM