views:

438

answers:

2

Hi!

I have two entities like the following:

@Entity
public class Trip {

    @OneToMany(mappedBy = "trip", fetch = FetchType.LAZY)
    private Set<Job> jobs = new HashSet<Job>();

}

@Entity
public class Job {

    @ManyToOne(fetch = FetchType.LAZY)
    private Trip trip;

}

The problem is that the mappedBy relationship behaves differently in different circumstances. Here is an example

    EntityManager em1 = unit.createEntityManager();
    EntityManager em2 = unit.createEntityManager();

    // One EM starts transaction and stores a trip
    em1.getTransaction().begin();

    Trip trip = new Trip();
    em1.persist(trip);

    Long tripId = trip.getId();

    assertThat(tripId).isPositive();

    em1.getTransaction().commit();

    // Then em2 starts different transaction
    em2.getTransaction().begin();

    // Looking up for the trip through clean em (cache is empty)
    Trip em2Trip = em2.find(Trip.class, tripId);

    Job job = new Job();
    job.setTrip(em2Trip);

    em2.persist(job);

    // The em2Trip should not be updated

    assertThat(em2Trip.getJobs()).hasSize(1);

    em2.getTransaction().commit();

    em1.getTransaction().begin();

    Trip em1Trip = em1.find(Trip.class, tripId);

    // fails here
    assertThat(em1Trip.getJobs()).hasSize(1);

    em1.getTransaction().commit();

The code above shows that if an entity is already loaded in the entity manager's cache, the getter for the mappedBy relationship may return invalid results.

I have a proof it doesn't work under JBoss either. The following code behaves differently depending on which entity manager is being used. The result is unpredictable.

        Trip trip = em.find(Trip.class, tripId);
        if (trip.getJobs().size() == 0) ...

Does this mean that the mappedBy automatically makes application buggy as soon as it is introduced and used?

P.S. I am not trying to abuse hibernate. I only want to find out if someone faced such a problem and how did they cope with it

A: 

Using mappedBy (i.e. bidirectional associations) is not buggy or inconsistent. It is, however, tricky to configure, and you have to be very careful how you use it - it's not a magic bullet, and if you don't follow the programming guidelines, you can get your entities into an inconsistent state.

I tend to avoid them for this reason; I only use them if I really need bidirectional associations, in in my experience, that's very rare.

skaffman
+2  A: 

Behavior you're describing has absolutely nothing to do with associations; you would get the exact same results if you just tried to read / update simple POJO from two entity managers. Once your entity is associated with persistence context it will NOT be automatically refreshed from the database. This is a documented behavior - and in the vast majority of cases this is a DESIRED behavior.

As far as "keeping your application stable" goes:

  1. EntityManager instances directly correspond to Hibernate sessions and thus should not be held for prolonged periods of time. If you rewrite your code above to use a new EntityManager instance (em3) instead of reusing em1, your problem will go away.
  2. EntityManager has a refresh() method which you can invoke to reload entity state from the database.
  3. EntityManager has a clear() method which will clear the persistence context completely thus preventing this issue as well. Use it sparingly and cautiously, though - invoking clear() without flush() will throw away all pending updates.
ChssPly76
That's right, simple attributes do not get updated either! Thanks for pointing this out.
artemb