【问题标题】:EntityManager.find() does not find entityEntityManager.find() 找不到实体
【发布时间】:2016-07-08 03:20:57
【问题描述】:

我在使用 JPA 和 Hibernate 时遇到问题,其中 EntityManager.find() 甚至 EntityManager.createQuery() 不会在我的数据库中返回具有相应 ID 的实体。前者返回null,后者抛出No entity found for query 异常。我知道具有该 ID 的实体存在于数据库中,因为我在运行代码时盯着条目。

我采取了几个步骤来到达失败的地方。首先,我将两个项目添加到我的 DayItem 表并将它们返回给客户端。我在客户端更改它们的值并使用 POST 将它们发送回服务器。一旦被服务器接收到,我创建一个新的EntityManager 并尝试通过获取实体的类和 ID 并将这些值传递给EntityManager.find(Class, ID) 来按 ID 删除实体。

这会在数据库中找到具有原始值的实体,而不是从客户端发送过来的已更改的实体。 EntityManager.find(Class, ID) 也正是问题所在。找到数据库中的实体后,我会在实体上调用EntityManager.remove() 将其删除。

我没有在服务器接收到实体时直接在实体上使用EntityManager.merge() 的原因是它们的唯一键已交换,除非我在运行之前将它们从数据库中删除,否则这将导致违反约束EntityManager.merge().

最后,从数据库中删除实体后,我通过在它们上调用 EntityManager.merge() 将它们添加回更新值。

请看我下面的代码,任何关于为什么 EntityManager.find() 返回 null 以及为什么查询抛出 No entity found for query 异常的帮助都会有很大帮助。

这是 DayItem 表上的唯一键:

ALTER TABLE DAY_ITEM
ADD CONSTRAINT DAY_ITEM_UK
UNIQUE (EMAIL, MOD_ID, FM_ORDER, DAYS_DATE);

这里是 DayItem 表实体:

@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;
    }
}

这是添加 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;
}

这是删除 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;
        }
    }
}

【问题讨论】:

  • 为什么 find() 返回 null?当您查看调用的 SQL 时,也许您会找到答案?并查看数据库中的内容。也许对象尚未刷新?或 tx 隔离意味着它不可见
  • @NeilStockton 谢谢你的评论。这是 tx 隔离。在我的休眠配置文件中,我添加了hibernate.connection.isolation=1。它将事务隔离设置为TRANSACTION_READ_UNCOMMITTED。这似乎已经解决了它。有了这个,我想我也不再需要所有这些冲洗和清除了。
  • 您仍然需要flush(),因为这会将数据发送到数据库......隔离只是意味着您的查询现在可以看到新添加的数据。
  • @NeilStockton 我将我的EntityManager 设置为FlushMode.COMMIT,所以它应该在每次交易后刷新。我现在注意到 Id 混淆的问题,因此我将考虑添加反冲洗和清除作为可能的解决方案。

标签: hibernate jpa jdbc entitymanager hibernate-entitymanager


【解决方案1】:
    public Clan saveClan(String ime, String prezime, String adresa, Date datumRodjenja, Date datumUpisa,
                int idKategorije) {
            try {
                EntityManager em = JPAUtil.getEntityManager();
                em.getTransaction().begin();
    
                Kategorija k = em.find(Kategorija.class, idKategorije);
                Clan c = new Clan();
                c.setIme(ime);
                c.setPrezime(prezime);
                c.setAdresa(adresa);
                c.setDatumRodjenja(datumRodjenja);
                c.setDatumUpisa(datumUpisa);
                c.setKategorija(k);
                em.persist(c);
    
                em.getTransaction().commit();
                return c;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

public boolean razduzi(int id) {
        try {
            EntityManager em = JPAUtil.getEntityManager();
            em.getTransaction().begin();

            Zaduzenje z = em.find(Zaduzenje.class, id);
            z.setDatumVracanja(new Date());
            em.merge(z);

            em.getTransaction().commit();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

【讨论】:

  • 感谢您提供此代码 sn-p,它可能会提供一些有限的即时帮助。 proper explanation 将通过展示为什么这是解决问题的好方法,并使其对有其他类似问题的未来读者更有用,从而大大提高其长期价值。请edit您的回答添加一些解释,包括您所做的假设。
【解决方案2】:
    public boolean deleteClan(Integer clBroj) {
            try {
                EntityManager em = JPAUtil.getEntityManager();
                em.getTransaction().begin();
    
                Clan c = em.find(Clan.class, clBroj);
    
                List<Zaduzenje> zaduzenjaClana = em
                        .createQuery("" + "select z from Zaduzenje z where " + "z.clan.clanskibroj=:br", Zaduzenje.class)
                        .setParameter("br", clBroj).getResultList();
    
                for (Zaduzenje z : zaduzenjaClana) {
                    em.remove(z);
                }
                em.remove(c);
                em.getTransaction().commit();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

public List<Integer> dodajPrimerke(int idKnjige, int brojPrimeraka) {
        ArrayList<Integer> invBrojevi = new ArrayList<Integer>();
        try {
            EntityManager em = JPAUtil.getEntityManager();
            Knjiga k = em.find(Knjiga.class, idKnjige);
            if (k != null) {
                em.getTransaction().begin();
                for (int i = 0; i < brojPrimeraka; i++) {
                    Primerak p = new Primerak();
                    p.setKnjiga(k);
                    em.persist(p);
                    invBrojevi.add(p.getInvBroj());
                }
                em.getTransaction().commit();
            }
            return invBrojevi;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

【讨论】:

  • 感谢您提供此代码 sn-p,它可能会提供一些有限的即时帮助。 proper explanation 将通过展示为什么这是解决问题的好方法,并使其对有其他类似问题的未来读者更有用,从而大大提高其长期价值。请edit您的回答添加一些解释,包括您所做的假设。
【解决方案3】:

公共列表 getUlogeZaPredstavu(int idPredstave){ EntityManager em = JPAUtil.getEntityManager();

    List<Glumi> uloge = em.createQuery("select g from Glumi g where "
            + "g.uloga.predstava.idPredstava = :paramIdPredstava", Glumi.class)
            .setParameter("paramIdPredstava", idPredstave).getResultList();
    return uloge;
}

public Zanr getZanr(int id) {
    EntityManager em = JPAUtil.getEntityManager();
    Zanr z = em.find(Zanr.class, id);
    return z;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    • 2013-01-10
    • 2011-08-27
    • 2015-01-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多