3

I use Hibernate 5.1.0.Final. My GenericDAO class main methods:

public T save(T entity) {
    entityManager.getTransaction().begin();
    entityManager.persist(entity);
    entityManager.getTransaction().commit();
}

public T find(Long entityId) {
    T e = (T) entityManager.find(type, entityId);
    entityManager.refresh(e);
    return e;
}

I have Item entity which contains List<Image> images.

@Entity
@Table(name="item")
public class Item extends GenericEntity {

    @Column(name="title")
    String title;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "item", cascade = {CascadeType.ALL}, orphanRemoval = true)
    @NotFound(action = NotFoundAction.IGNORE)
    List<Image> images;

    @Version
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name="version")
    Date version;

}


@Entity 
@Table(name="image")
public class Image extends GenericEntity {

    @ManyToOne
    @JoinColumn(name="item")
    Item item;

    @Column(name="image_name")
    String imageName;

}

First, I load Item with genericDAO.find(id) method - it contains up-to-date list of images. Then I load Item with the same ID in another REST method which removes old images and adds new ones, changes title. Later, if try to reload Item with genericDAO.find(id) in the first REST method, I get outdated images - they aren't selected again while Item title is retrieved correctly.

How to get completely refreshed entity with its childs from a database?

Justinas Jakavonis
  • 8,220
  • 10
  • 69
  • 114
  • Are you sure that your update the `images` field ? – davidxxx Aug 16 '16 at 12:32
  • Yes, the changes are visible if I query directly from the DB. – Justinas Jakavonis Aug 16 '16 at 12:39
  • so you use a cache somewhere. do you use a level 2 cache with Hibernate ? Or any cache mechanisms in your application ? – davidxxx Aug 16 '16 at 12:39
  • My Hibernate properties contain: properties.put("hibernate.cache.use.query_cache", "false"); properties.put("hibernate.cache.use_second_level_cache", "false"); Also I expect that entityManager.refresh(e); should get the newest data. – Justinas Jakavonis Aug 16 '16 at 12:42
  • 1
    Indeed, it should. Do you use the same session for the two calls ? if it is the same, the problem is there. You can check it with an session. evict() of the object – davidxxx Aug 16 '16 at 12:44
  • I use EntityManager instead of Session, it is reused for the two calls. It is provided from ThreadLocal. I know that it may be problem but guessed that entityManager.refresh(e) will solve the issue. Now I get No row with the given identifier exists: [com.example.Image#484] when I try to entityManager.refresh(item) . – Justinas Jakavonis Aug 16 '16 at 13:33

2 Answers2

2

Your way of caching your entityManagers is correct since in the request scope but it has side-effects as you can notice.
You use the level one cache of Hibernate.
Why don't use a distinct entityManager instance by transaction ?
It should not be expensive to instantiate a EntityManager by user request.

If you want to cache the entityManager, entityManager.refres‌​h(e) seems not enough since it doesn't seem to clear the level one cache. So, if you want to clear the first level cache, you should try to clear the persistenceContext. No guarantee but you can try : entityManager.clear()

Personally, I prefer the solution using a unique entityManager instance by transaction. It's cheap and a clear design.

davidxxx
  • 125,838
  • 23
  • 214
  • 215
0

1st/2nd level cache of Hibernate has nothing to do with visibility when there are simultaneous changes to DB. You need to look at isolation levels, REPEATABLE_READ is most common and should be source of your problems.

You could "lock" entity to prevent modifications:

@PersistenceContext
private EntityManager em;

@Transactional
@Test
public void lock() {
    final Session session = em.unwrap(Session.class);
    final Book book = session.get(Book.class, 1L, LockMode.PESSIMISTIC_WRITE);
}

Depending on DB / Hibernate adapter it might resulted in SELECT .... FOR UPDATE.

gavenkoa
  • 45,285
  • 19
  • 251
  • 303