Hi Jon,
In general Toplink (and the JPA spec) requires that relationships are
maintained on both sides of a bidirectional relationship. The symptoms
you are mentioning smells like some path in your code is not doing it
correctly. Can you please re-check your code. Please see inline for a
comment
Jon Miller wrote:
> Hi all,
>
> I've been having some problems when I don't turn off the shared cache
> (i.e. use the default). I was hoping to come up with a unit test that
> demonstrates the problem. However, the one time that I want the test
> to fail, it works. Basically, I have a bidirectional relationship.
> Item.reservations <-> Reservation.items. I have some other more
> complicated code which does basically the same thing that you see
> below. It perssists a User, then an Item, and then a Reservation. The
> problem happens when I do a query for the Item. The Item's
> reservations property is size() == 0 instead of 1. I've ran into this
> problem in another app too. If I disable the shared cache, it works
> fine. I'm wondering if it's possible to corrupt the shared cache and
> if that's what I'm doing? The way I have my app setup, I'm mostly
> working with detached entities.
>
> public void testPersist() {
> String s = "testPersist";
> EntityManagerFactory emf = Persistence.createEntityManagerFactory(
> "Reservations");
> EntityManager em = emf.createEntityManager();
> em.getTransaction().begin();
> Item i = new Item(s);
> em.persist(i);
> em.getTransaction().commit();
> em.close();
>
> em = emf.createEntityManager();
> em.getTransaction().begin();
> User u = new User(s);
> em.persist(u);
> em.getTransaction().commit();
> em.close();
>
> em = emf.createEntityManager();
> em.getTransaction().begin();
> Reservation r = new Reservation();
> r.setStartTime(new GregorianCalendar(1969, 1, 1, 1, 0).getTime());
> r.setEndTime(new GregorianCalendar(1969, 1, 1, 2, 0).getTime());
> r.setUser(u);
i needs to be managed above. can you try calling i = em.merge(i) at this
point.
Regards,
Mitesh
> r.getItems().add(i);
> i.getReservations().add(r);
> em.persist(r);
> em.getTransaction().commit();
> em.close();
>
> em = emf.createEntityManager();
> Query q = em.createQuery(
> "SELECT i FROM Item i LEFT JOIN FETCH i.reservations
> WHERE i.name = :name");
> q.setParameter("name", s);
> List l = q.getResultList();
> Item i2 = (Item)l.get(0);
> assertTrue(i2.getReservations().size() == 1);
>
> em.close();
>
> emf.close();
> }
>
> // Reservation.java
> package edu.uchicago.at.reservations.persistence.entity;
>
> import edu.uchicago.at.common.persistence.entity.AuditObject;
> import edu.uchicago.at.common.persistence.entity.User;
> import java.util.ArrayList;
> import java.util.Date;
> import java.util.List;
> import javax.persistence.Column;
> import javax.persistence.Entity;
> import javax.persistence.GeneratedValue;
> import javax.persistence.GenerationType;
> import javax.persistence.Id;
> import javax.persistence.JoinColumn;
> import javax.persistence.JoinTable;
> import javax.persistence.ManyToMany;
> import javax.persistence.ManyToOne;
> import javax.persistence.OrderBy;
> import javax.persistence.Table;
> import javax.persistence.Temporal;
> import javax.persistence.TemporalType;
>
> @Entity
> @Table(name="Reservation")
> public class Reservation extends AuditObject implements
> Comparable<Reservation>
> {
> private Date checkInTime;
> private Date checkOutTime;
> private String description;
> private Date endTime;
> private Integer id;
> private List<Item> items = new ArrayList<Item>();
> private Date startTime;
> private User user;
>
> public Reservation() {
> }
>
> public Reservation(Date startTime, Date endTime, User user, Item
> item) {
> setStartTime(startTime);
> setEndTime(endTime);
> setUser(user);
> items.add(item);
> }
>
> public int compareTo(Reservation reservation) {
> int i = 0;
> if(startTime == null && reservation.getStartTime() != null) {
> return -1;
> }
> if(startTime != null && reservation.getStartTime() == null) {
> return 1;
> }
> if(startTime == null && reservation.getStartTime() == null) {
> i = 0;
> }
> else {
> i = startTime.compareTo(reservation.getStartTime());
> }
> if(i == 0) {
> if(user == null && reservation.getUser() != null) {
> return -1;
> }
> if(user != null && reservation.getUser() == null) {
> return 1;
> }
> if(user == null && reservation.getUser() == null) {
> i = 0;
> }
> else {
> i = user.compareTo(reservation.getUser());
> }
> }
> return i;
> }
>
> @Column(name="CheckInTime")
> @Temporal(TemporalType.TIMESTAMP)
> public Date getCheckInTime() {
> return checkInTime;
> }
>
> public void setCheckInTime(Date checkInTime) {
> this.checkInTime = checkInTime;
> }
>
> @Column(name="CheckOutTime")
> @Temporal(TemporalType.TIMESTAMP)
> public Date getCheckOutTime() {
> return checkOutTime;
> }
>
> public void setCheckOutTime(Date checkOutTime) {
> this.checkOutTime = checkOutTime;
> }
>
> @Column(name="Description"/*, columnDefinition="nvarchar(max)"*/)
> public String getDescription() {
> return description;
> }
>
> public void setDescription(String description) {
> this.description = description;
> }
>
> @Column(name="EndTime", nullable=false)
> @Temporal(TemporalType.TIMESTAMP)
> public Date getEndTime() {
> return endTime;
> }
>
> public void setEndTime(Date endTime) {
> this.endTime = endTime;
> }
>
> @Id
> @GeneratedValue(strategy = GenerationType.IDENTITY)
> @Column(name="Id")
> public Integer getId() {
> return id;
> }
>
> public void setId(Integer id) {
> this.id = id;
> }
>
> @Column(name="StartTime", nullable=false)
> @Temporal(TemporalType.TIMESTAMP)
> public Date getStartTime() {
> return startTime;
> }
>
> public void setStartTime(Date startTime) {
> this.startTime = startTime;
> }
>
> @ManyToOne
> @JoinColumn(name="UserId", nullable=false)
> public User getUser() {
> return user;
> }
>
> public void setUser(User user) {
> this.user = user;
> }
>
> @ManyToMany
> @JoinTable(name="ReservationItem",
> joinColumns={_at_JoinColumn(name="ReservationId")},
> inverseJoinColumns={_at_JoinColumn(name="ItemId")})
> @OrderBy("name")
> public List<Item> getItems() {
> return items;
> }
>
> public void setItems(List<Item> items) {
> this.items = items;
> }
>
> public String toString() {
> StringBuilder sb = new StringBuilder();
> sb.append("[");
> sb.append(String.format("id = %d", id));
> sb.append(String.format(", startTime = %s", startTime));
> sb.append(String.format(", endTime = %s", endTime));
> sb.append(String.format(", user = %s", user));
> sb.append(String.format(", checkOutTime = %s", checkOutTime));
> sb.append(String.format(", checkInTime = %s", checkInTime));
> sb.append(String.format(", description = \"%s\"", description));
> sb.append(String.format(", items = %s", items));
> sb.append("]");
> return sb.toString();
> }
> }
>
> // Item.java
> package edu.uchicago.at.reservations.persistence.entity;
>
> import edu.uchicago.at.common.persistence.entity.AuditObject;
> import java.util.ArrayList;
> import java.util.List;
> import javax.persistence.Column;
> import javax.persistence.Entity;
> import javax.persistence.GeneratedValue;
> import javax.persistence.GenerationType;
> import javax.persistence.Id;
> import javax.persistence.JoinColumn;
> import javax.persistence.JoinTable;
> import javax.persistence.ManyToMany;
> import javax.persistence.OrderBy;
> import javax.persistence.Table;
>
> @Entity
> @Table(name="Item")
> public class Item extends AuditObject implements Comparable<Item> {
> private String description;
> private Integer id;
> private String name;
> private List<Reservation> reservations = new ArrayList<Reservation>();
> private List<Item> subItems = new ArrayList<Item>();
> private List<Item> superItems = new ArrayList<Item>();
>
> public Item() {
> }
>
> public Item(String name) {
> setName(name);
> }
>
> public int compareTo(Item item) {
> if(name == null && item.getName() == null) {
> return 0;
> }
> if(name == null) {
> return -1;
> }
> if(item.getName() == null) {
> return 1;
> }
> return name.compareTo(item.getName());
> }
>
> public boolean contains(Item item) {
> for(Item i : subItems) {
> if (i.id == item.id) {
> return true;
> }
> if (i.contains(item)) {
> return true;
> }
> }
> return false;
> }
>
> @Column(name="Description"/*, columnDefinition="nvarchar(max)"*/)
> public String getDescription() {
> return description;
> }
>
> public void setDescription(String description) {
> this.description = description;
> }
>
> @Id
> @GeneratedValue(strategy = GenerationType.IDENTITY)
> @Column(name="Id")
> public Integer getId() {
> return id;
> }
>
> public void setId(Integer id) {
> this.id = id;
> }
>
> @Column(name="Name"/*, columnDefinition="nvarchar(255)"*/)
> public String getName() {
> return name;
> }
>
> public void setName(String name) {
> this.name = name;
> }
>
> @ManyToMany(mappedBy="items")
> @OrderBy("startTime")
> public List<Reservation> getReservations() {
> return reservations;
> }
>
> public void setReservations(List<Reservation> reservations) {
> this.reservations = reservations;
> }
>
> @ManyToMany
> @JoinTable(name="ItemItem", joinColumns={_at_JoinColumn(name="ItemId")},
> inverseJoinColumns={_at_JoinColumn(name="SubItemId")})
> @OrderBy("name")
> public List<Item> getSubItems() {
> return subItems;
> }
>
> public void setSubItems(List<Item> subItems) {
> this.subItems = subItems;
> }
>
> @ManyToMany(mappedBy="subItems")
> @OrderBy("name")
> public List<Item> getSuperItems() {
> return superItems;
> }
>
> public void setSuperItems(List<Item> superItems) {
> this.superItems = superItems;
> }
>
> public String toString() {
> StringBuilder sb = new StringBuilder();
> sb.append("[");
> sb.append(String.format("id = %d", id));
> sb.append(String.format(", name = \"%s\"", name));
> sb.append(String.format(", subItems = %s", subItems));
> sb.append(String.format(", description = \"%s\"", description));
> sb.append("]");
> return sb.toString();
> }
> }