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