mysql - EntityManager merge() 导致重复行

标签 mysql hibernate jpa entitymanager

我在使用 EntityManager.merge() 时遇到了一些非常奇怪的行为。这就是正在发生的事情。我有一个使用 Spring MVC、Hibernate 和 JPA 的 REST API 设置,用于从 MySQL 数据库插入、更新和删除项目。插入和更新函数都使用 EntityManager.merge(),并且它们在 99% 的情况下都能正常工作。

但是,在某个时刻,这似乎是第一次调用更新记录,它会在数据库中创建该记录的副本,然后返回到原始记录并按应有的方式对其进行更改。

我真的不知道为什么会发生这种情况。您可能会认为,如果它创建了重复记录,它会编辑该记录,但不,在更新时,它会创建重复记录,放弃它,然后返回到第一条记录进行更改。

我认为这是因为我在实体 bean 中设置了 hashcode 和 equals,这可能是这样。尽管我没有发现它们有什么问题,因为我是基于唯一键而不是 ID 来构建它们的。

我的代码如下。任何关于正在发生的事情的想法都会有很大的帮助。

这是实体:

@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() {
        if(daysDateString == null) {
            DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
            return formatter.format(daysDate);
        }
        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;
        if (!getDaysDateString().equals(dayItem.getDaysDateString())) return false;
        if (getFoodId() != null ? !getFoodId().equals(dayItem.getFoodId()) : dayItem.getFoodId() != null) return false;
        return getMealId() != null ? getMealId().equals(dayItem.getMealId()) : dayItem.getMealId() == null;

    }

    @Override
    public int hashCode() {
        int result = getEmail().hashCode();
        result = 31 * result + getModId().hashCode();
        result = 31 * result + getFmOrder().hashCode();
        result = 31 * result + getDaysDateString().hashCode();
        result = 31 * result + (getFoodId() != null ? getFoodId().hashCode() : 0);
        result = 31 * result + (getMealId() != null ? getMealId().hashCode() : 0);
        return result;
    }
}

这是执行初始创建的代码:

@Override
public List<DayItem> saveDayItem(DayItem item) throws Exception {
    EntityManager manager = entityManagerFactory.createEntityManager();
    EntityTransaction tx = manager.getTransaction();
    List<DayItem> results = null;
    try {
        tx.begin();
        manager.merge(item);
        manager.flush();
        manager.clear();
        results = findUserDay(item.getDaysDate(), item.getEmail(), manager);
        tx.commit();
    } catch (Exception e) {
        tx.rollback();
        throw e;
    } finally {
        manager.close();
        return results;
    }
}

这是执行更新的代码:

public <T extends GtEntity> T store(T entity) throws Exception {
    T managedEntity = null;
    EntityManager manager = entityManagerFactory.createEntityManager();
    EntityTransaction tx = manager.getTransaction();
    try{
        tx.begin();
        managedEntity = manager.merge(entity);
        tx.commit();
    }catch(RuntimeException e){
        tx.rollback();
        throw e;
    }finally {
        manager.close();
        return managedEntity;
    }
}

最佳答案

由于您实现了 equalshasCode,Hibernate 将使用这些方法来查找要更新的实体。假设分离的对象已更改,equalshasCode 将永远不会返回与附加实体相同的值,因此 Hibernate 将假定它是一个新实例。

您应该阅读有关 equals and hasCode 的 hibernate 文档.

关于mysql - EntityManager merge() 导致重复行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38173457/

相关文章:

java - Hibernate JPA @Inheritance TABLE_PER_CLASS JpaRepository 子实体联合选择所有表

sql - 加入 3 个表 Hibernate & JPA

java - JPA 查找方法持久化数据

php - BEFORE UPDATE 触发器会影响 mysqli_insert_id 结果吗?

php - 如何使用php将单选按钮的数据更新到mysql数据库?

java - 如何映射已在 ORACLE 中创建但在 Hibernate 实体中创建的 "CREATE UNIQUE INDEX"

java - 如何使用 JPA 创建 UNIQUE 列?

php - ajax成功但不发送发布数据

mysql - 连接不同表中的值时使用 GROUP_CONCAT 和 CONCAT

java - Hibernate:asm jar 在一个工作区中工作时出错,而不是在另一个工作区中工作