persistence@glassfish.java.net

Problems with shared cache

From: Jon Miller <jemiller_at_uchicago.edu>
Date: Mon, 12 Feb 2007 18:21:36 -0600

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);
        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();
    }
}