休眠事实:了解刷新操作顺序很重要
Hibernate將開發人員的思維方式從思考SQL轉變為思考對象狀態轉換。 根據Hibernate Docs,實體可能處于以下狀態之一:
- new / transient:實體不與持久性上下文關聯,因為它是數據庫不知道的新創建的對象。
- 持久性:實體與持久性上下文相關聯(位于第一級緩存中),并且存在一個表示該實體的數據庫行。
- 分離:實體以前與持久性上下文相關聯,但是持久性上下文已關閉,或者手動撤消了該實體。
- 已刪除:實體被標記為已刪除,并且持久性上下文將在刷新時從數據庫中將其刪除。
通過調用EntityManager方法來將對象從一種狀態移動到另一種狀態,例如:
- 堅持()
- 合并()
- 去掉()
級聯允許將給定事件從父級傳播到子級,還簡化了實體關系管理的管理。
在刷新期間,Hibernate會將當前持久性上下文記錄的更改轉換為SQL查詢。
現在,考慮一下以下代碼中發生了什么(為簡潔起見,將其簡化):
@Entity public class Product {@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "product", orphanRemoval = true)@OrderBy("index")private Set images = new LinkedHashSet();public Set getImages() {return images;}public void addImage(Image image) {images.add(image);image.setProduct(this);}public void removeImage(Image image) {images.remove(image);image.setProduct(null);} }@Entity public class Image {@Column(unique = true)private int index;@ManyToOneprivate Product product;public int getIndex() {return index;}public void setIndex(int index) {this.index = index;}public Product getProduct() {return product;}public void setProduct(Product product) {this.product = product;} }final Long productId = transactionTemplate.execute(new TransactionCallback() {@Overridepublic Long doInTransaction(TransactionStatus transactionStatus) {Product product = new Product();Image frontImage = new Image();frontImage.setIndex(0);Image sideImage = new Image();sideImage.setIndex(1);product.addImage(frontImage);product.addImage(sideImage);entityManager.persist(product);return product.getId();} });try {transactionTemplate.execute(new TransactionCallback() {@Overridepublic Void doInTransaction(TransactionStatus transactionStatus) {Product product = entityManager.find(Product.class, productId);assertEquals(2, product.getImages().size());Iterator imageIterator = product.getImages().iterator();Image frontImage = imageIterator.next();assertEquals(0, frontImage.getIndex());Image sideImage = imageIterator.next();assertEquals(1, sideImage.getIndex());Image backImage = new Image();sideImage.setName("back image");sideImage.setIndex(1);product.removeImage(sideImage);product.addImage(backImage);entityManager.flush();return null;} });fail("Expected ConstraintViolationException"); } catch (PersistenceException expected) {assertEquals(ConstraintViolationException.class, expected.getCause().getClass()); }由于存在Image.index唯一約束,因此在刷新期間會收到ConstraintviolationException。
您可能想知道為什么會發生這種情況,因為在添加具有相同索引的backImage之前,我們為sideImage調用remove,并且答案是沖洗操作順序。
根據Hibernate JavaDocs ,SQL操作順序為:
- 插入
- 更新
- 集合元素的刪除
- 集合元素的插入
- 刪除
因為我們的圖像集合是“ mappedBy”,所以圖像將控制關聯,因此“ backImage”插入發生在“ sideImage”刪除之前。
select product0_.id as id1_5_0_, product0_.name as name2_5_0_ from Product product0_ where product0_.id=? select images0_.product_id as product_4_5_1_, images0_.id as id1_1_1_, images0_.id as id1_1_0_, images0_.index as index2_1_0_, images0_.name as name3_1_0_, images0_.product_id as product_4_1_0_ from Image images0_ where images0_.product_id=? order by images0_.index insert into Image (id, index, name, product_id) values (default, ?, ?, ?) ERROR: integrity constraint violation: unique constraint or index violation; UK_OQBG3YIU5I1E17SL0FEAWT8PE table: IMAGE要解決此問題,您必須在除去操作之后手動刷新持久性上下文:
transactionTemplate.execute(new TransactionCallback<Void>() {@Overridepublic Void doInTransaction(TransactionStatus transactionStatus) {Product product = entityManager.find(Product.class, productId);assertEquals(2, product.getImages().size());Iterator<Image> imageIterator = product.getImages().iterator();Image frontImage = imageIterator.next();assertEquals(0, frontImage.getIndex());Image sideImage = imageIterator.next();assertEquals(1, sideImage.getIndex());Image backImage = new Image();backImage.setIndex(1);product.removeImage(sideImage);entityManager.flush();product.addImage(backImage);entityManager.flush();return null;} });這將輸出所需的行為:
select versions0_.image_id as image_id3_1_1_, versions0_.id as id1_8_1_, versions0_.id as id1_8_0_, versions0_.image_id as image_id3_8_0_, versions0_.type as type2_8_0_ from Version versions0_ where versions0_.image_id=? order by versions0_.type delete from Image where id=? insert into Image (id, index, name, product_id) values (default, ?, ?, ?)- 源代碼在這里 。
翻譯自: https://www.javacodegeeks.com/2013/11/hibernate-facts-knowing-flush-operations-order-matters.html
總結
以上是生活随笔為你收集整理的休眠事实:了解刷新操作顺序很重要的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑怎样设置wifi密码(电脑怎样设置w
- 下一篇: 使用序列化查找对象中的脏字段