I'm having a problem with JPA and Hibernate where EntityManager.find()
or even EntityManager.createQuery()
will not return an entity in my database with the corresponding ID. The former returns null
and the latter throws a No entity found for query
exception. I know for a fact that the entity with that ID exists in the database because I'm staring at the entry as the code is run.
There are a few steps I take to get to where it fails. First, I add two items to my DayItem table and return them to the client. I change their values on the client and use a POST to send them back to the server. Once received by the server I create a new EntityManager
and attempt to delete the entities by ID by getting their class and ID and passing these values to EntityManager.find(Class, ID)
.
This finds the entity in the database with its original values, not the changed ones sent over from the client. EntityManager.find(Class, ID)
is also exactly where the problem is happening. Once the entity from the database is found, I call EntityManager.remove()
on the entity to remove it.
The reason that I don't use EntityManager.merge()
directly on the entities right when they are received by the server is because their unique keys were swapped, and this would cause a constraint violation unless I delete them from the database before running EntityManager.merge()
.
Finally, after the entities from the database are removed, I add them back with their updated values by calling EntityManager.merge()
on them.
Please take a look at my code below, any help with why EntityManager.find()
returns null and why the query throws the No entity found for query
exception would be a big help.
Here is the unique key on the DayItem table:
ALTER TABLE DAY_ITEM
ADD CONSTRAINT DAY_ITEM_UK
UNIQUE (EMAIL, MOD_ID, FM_ORDER, DAYS_DATE);
Here is the DayItem table entity:
@Entity
@Table(name = "DAY_ITEM")
public class DayItem implements GtEntity, Serializable{
private Long id;
private String email;
private String name;
private BigDecimal amount;
private Integer modId;
private Integer fmOrder;
private String modName;
private Date daysDate;
private String daysDateString;
private Long foodId;
private Long mealId;
private FoodItem foodItem;
private MealItem mealItem;
private ArrayList<FoodItem> foodItems;
private ArrayList<MealItem> mealItems;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Basic
@Column(name = "EMAIL", nullable = false, length = 50)
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Basic
@Column(name = "NAME", nullable = true, length = 100)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Basic
@Column(name = "AMOUNT", nullable = true)
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
@Basic
@Column(name = "MOD_ID", nullable = false)
public Integer getModId() {
return modId;
}
public void setModId(Integer modId) {
this.modId = modId;
}
@Basic
@Column(name = "FM_ORDER", nullable = false)
public Integer getFmOrder() {
return fmOrder;
}
public void setFmOrder(Integer fmOrder) {
this.fmOrder = fmOrder;
}
@Basic
@Column(name = "MOD_NAME", nullable = true, length = 50)
public String getModName() {
return modName;
}
public void setModName(String modName) {
this.modName = modName;
}
@Basic
@Column(name = "DAYS_DATE", nullable = false)
public Date getDaysDate() {
return daysDate;
}
public void setDaysDate(Date daysDate) {
this.daysDate = daysDate;
}
@Transient
public String getDaysDateString() {
return daysDateString;
}
public void setDaysDateString(String daysDateString) {
this.daysDateString = daysDateString;
}
@Basic
@Column(name = "FOOD_ID", nullable = true)
public Long getFoodId() {
return foodId;
}
public void setFoodId(Long foodId) {
this.foodId = foodId;
}
@Basic
@Column(name = "MEAL_ID", nullable = true)
public Long getMealId() {
return mealId;
}
public void setMealId(Long mealId) {
this.mealId = mealId;
}
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "FOOD_ID", referencedColumnName = "ID",
updatable = false, insertable = false)
public FoodItem getFoodItem() {
return foodItem;
}
public void setFoodItem(FoodItem foodItem) {
this.foodItem = foodItem;
}
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "MEAL_ID", referencedColumnName = "ID",
updatable = false, insertable = false)
public MealItem getMealItem() {
return mealItem;
}
public void setMealItem(MealItem mealItem) {
this.mealItem = mealItem;
}
@Transient
public ArrayList<FoodItem> getFoodItems() {
return foodItems;
}
public void setFoodItems(ArrayList<FoodItem> foodItems) {
this.foodItems = foodItems;
}
@Transient
public ArrayList<MealItem> getMealItems() {
return mealItems;
}
public void setMealItems(ArrayList<MealItem> mealItems) {
this.mealItems = mealItems;
}
public void makeDaysDate() throws ParseException{
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
this.daysDate = new Date(formatter.parse(this.daysDateString).getTime());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof DayItem)) return false;
DayItem dayItem = (DayItem) o;
if (!getEmail().equals(dayItem.getEmail())) return false;
if (!getModId().equals(dayItem.getModId())) return false;
if (!getFmOrder().equals(dayItem.getFmOrder())) return false;
return getDaysDate().equals(dayItem.getDaysDate());
}
@Override
public int hashCode() {
int result = getEmail().hashCode();
result = 31 * result + getModId().hashCode();
result = 31 * result + getFmOrder().hashCode();
result = 31 * result + getDaysDate().hashCode();
return result;
}
}
Here's the code that adds a DayItem:
@Override
public List<DayItem> saveDayItem(DayItem item) throws Exception {
List<DayItem> results = null;
EntityManager manager = createEntityManager();
EntityTransaction tx = manager.getTransaction();
try {
tx.begin();
DayItem managedItem = manager.merge(item);
manager.flush();
manager.clear();
tx.commit();
results = findUserDay(managedItem.getDaysDate(), managedItem.getEmail(), manager);
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
manager.close();
}
return results;
}
Here's the code that deletes the DayItems:
public <T extends GtEntity> void batchDelete(List<T> entities) throws Exception {
if (!entities.isEmpty()) {
EntityManager manager = createEntityManager();
EntityTransaction tx = manager.getTransaction();
try {
tx.begin();
for (int i = 0; i < entities.size(); i++) {
if (i % this.jdbcBatchSize == 0) {
manager.flush();
manager.clear();
}
T del = entities.get(i);
T managed = manager.find((Class<T>)del.getClass(), del.getId());
//The line above is where the problem happens
manager.remove(managed);
}
manager.flush();
manager.clear();
tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
}
}
}