Hibernate update child from parent, joined column is not specified in generated sql 查询条件

Hibernate update child from parent, joined column is not specified in generated sql query condition

parent为产品实体,child为按时间区分的库存实体(如酒店库存模型,每个预订日的库存应该不同,单独操作)

ProductEntity定义为:

@Entity
@Table(name = "product")
public class ProductEntity{
     ....
     private long productId;
     private List<StockEntity> stockEntityList;
     ....

     @Id
     @Column(name="productId")
     public long getProductId(){
         return this.productId;
     }

     @OneToMany(mappedBy="productEntity", fetch= FetchType.EAGER
                  cascade={CascadeType.ALL,CascadeType.PERSIST,CascadeType.MERGE},
                  orphanRemoval = true)
     public List<StockEntity> getStockEntityList(){
                  return this.stockEnityList;
     }

     public void setStockEntityList(List<StockEntity> stockEntityList){...}
     ....
}

StockEntity 定义为:

@Entity
@Table(name = "stock")
public class StockEntity{
    ...
    private ProductEntity productEntity;
    private long startTime;
    ...
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="productId")
    public ProductEntity getProductEntity(){
         return this.producntEntity;
    }

    @Id
    @Column(name="startTime")
    public long getStartTime(){
        return this.startTime;
    }

    ....
}

既然你知道,我使用 ProductEntity 的 productId 作为外键,startTime(长类型时间戳)作为主键。

然后我想通过以下方式更新产品的特定库存项目:

 public void consumeStockQuantity(long productId, long startTime, int count){
     Session session = HBSession.getSession();
     Transaction tx = session.beginTransaction();
     ProductEntity productEntity = session.get(productId, ProductEntity.class);
     for(StockEntity stockEntity: productEntity.getStockEntityList()){
         if(stockEntity.getStartTime() == startTime){
            stockEntity.setQuantity(stockEntity.getQuantity-count);
         }
     }
     try{
        session.update(productEntity);
        tx.commit();
     }catch(Exception e){
        e.printStackTrace();
        tx.rollback();
     }finally{
        session.close();
     }
 }

使用上面的 DAO 代码,我希望修改特定产品的特定库存项目(由 startTime 标识)的库存量。

但有时当存在具有相同 startTime 和不同产品 ID 的库存商品时,我会得到一个 update count mis-match with expected error,我触发休眠日志并发现,在 sql由休眠生成,更新 SQL 仅包含 startTime(显式为 @Id)但查询条件中没有 productId

Hibernate: 
    update
        test.stock 
    set
        marketPrice=?,
        productId=?,
        purchasePrice=?,
        quantity=?,
        sellPrice=? 
    where
        startTime=? //here missing productId which is a joinedColumn
HibernateLog --> 15:35:27 TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [FLOAT] - [1.2]
HibernateLog --> 15:35:27 TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [BIGINT] - [1075]
HibernateLog --> 15:35:27 TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [3] as [FLOAT] - [0.01]
HibernateLog --> 15:35:27 TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [4] as [BIGINT] - [1000]
HibernateLog --> 15:35:27 TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [5] as [FLOAT] - [0.01]
HibernateLog --> 15:35:27 TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [6] as [BIGINT] - [1438175312412]
Exception in thread "main" org.hibernate.jdbc.BatchedTooManyRowsAffectedException: Batch update returned unexpected row count from update [0]; actual row count: 2; expected: 1

那么,如何解决这个问题?

仅仅因为 productEntity 是一个连接列并不能使它成为 ID 的一部分。 您要么必须使用包含时间戳和产品 ID 的复合 ID(例如,请参阅 How to map a composite key with Hibernate?),但也许更容易的是首先不将时间戳定义为 ID,而是使用普通的 "autoincrement" 或类似的 ID,并将时间戳定义为普通的列。