Hibernate实战(第二版)笔记----第三章--域模型与元数据 今天药忘吃喽~ 2022-06-13 09:12 199阅读 0赞 本章主要讲了域模型和元数据,以一个CaveatEmptor在线拍卖系统为例讲解了从项目架构分层,分析业务域,域模型,域模型的持久化类和持久化层之间的关注点完全分离,持久化类不清楚-且不依赖-持久化机制,然后讲解了域模型域元数据的关系,以及在运行时的方法方式。 域模型:被创建的实体类与属性被称为域模型. 元数据:即告知Hibernate持久化类及其属性与数据库表和列关联起来的方式。 JPA提供了两个元数据选项: 1、注解 2、XML描述符 JPA提供的访问持久化元模型API: 1、JAVA持久化中的动态元模型API 2、使用一个静态元模型 CaveatEmptor示例 CaveatEmptor是一个揭示ORM技术和Hibernate功能的在线拍卖应用,其中域模型为: ![20170703220612026][] 所对应的实体类为: ![20170703222034505][] /model/src/main/java/org/jpwh/model/simple/Item.java package org.jpwh.model.simple; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToOne; import javax.persistence.Transient; import javax.persistence.Version; import javax.validation.constraints.Future; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import java.math.BigDecimal; import java.util.Date; import java.util.HashSet; import java.util.Set; @Entity public class Item { @Id @GeneratedValue(generator = "ID_GENERATOR") protected Long id; public Long getId() { // 可选但有用 return id; } @Version protected long version; @NotNull @Size( min = 2, max = 255, message = "Name is required, maximum 255 characters." ) protected String name; @Future protected Date auctionEnd; public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getAuctionEnd() { return auctionEnd; } public void setAuctionEnd(Date auctionEnd) { this.auctionEnd = auctionEnd; } protected BigDecimal buyNowPrice; public BigDecimal getBuyNowPrice() { return buyNowPrice; } public void setBuyNowPrice(BigDecimal buyNowPrice) { this.buyNowPrice = buyNowPrice; } @Transient protected Set<Bid> bids = new HashSet<Bid>(); public Set<Bid> getBids() { return bids; } public void setBids(Set<Bid> bids) { this.bids = bids; } @ManyToOne(fetch = FetchType.LAZY) protected Category category; public Category getCategory() { return category; } public void setCategory(Category category) { this.category = category; } public void addBid(Bid bid) { // Be defensive if (bid == null) throw new NullPointerException("Can't add null Bid"); if (bid.getItem() != null) throw new IllegalStateException("Bid is already assigned to an Item"); getBids().add(bid); bid.setItem(this); } public Bid placeBid(Bid currentHighestBid, BigDecimal bidAmount) { if (currentHighestBid == null || bidAmount.compareTo(currentHighestBid.getAmount()) > 0) { return new Bid(bidAmount, this); } return null; } } /model/src/main/java/org/jpwh/model/simple/Item\_.java package org.jpwh.model.simple; import java.util.Date; import javax.persistence.metamodel.SingularAttribute; @javax.persistence.metamodel.StaticMetamodel(Item.class) public abstract class Item_ { public static volatile SingularAttribute<Item,Long> id; public static volatile SingularAttribute<Item,String> name; public static volatile SingularAttribute<Item,Date> auctionEnd; } /model/src/main/java/org/jpwh/model/simple/Bid.java package org.jpwh.model.simple; import org.jpwh.model.Constants; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.validation.constraints.NotNull; import java.math.BigDecimal; @Entity public class Bid { @Id @GeneratedValue(generator = Constants.ID_GENERATOR) protected Long id; public Long getId() { // 可选的但是有用的 return id; } public Bid() { } @NotNull protected BigDecimal amount; public Bid(BigDecimal amount, Item item) { this.amount = amount; this.item = item; } public BigDecimal getAmount() { return amount; } public void setAmount(BigDecimal amount) { this.amount = amount; } @ManyToOne(optional = false, fetch = FetchType.LAZY) // NOT NULL @JoinColumn(name = "ITEM_ID") // 实际上默认名称 protected Item item; public Bid(Item item) { this.item = item; item.getBids().add(this); // 双向 } public Item getItem() { return item; } public void setItem(Item item) { this.item = item; } } /model/src/main/java/org/jpwh/model/simple/Address.java package org.jpwh.model.simple; import javax.persistence.Column; import javax.persistence.Embeddable; import javax.validation.constraints.NotNull; /** * *而不是<code> @Entity </code>,该组件POJO标有<code> @Embeddable </code>。 它 *没有标识符属性。 */ @Embeddable public class Address { @NotNull // 忽略了DDL生成! @Column(nullable = false) // 用于DDL生成! protected String street; @NotNull @Column(nullable = false, length = 5) // Override VARCHAR(255) protected String zipcode; @NotNull @Column(nullable = false) protected String city; /** *Hibernate会调用这个无参数的构造函数来创建一个实例,然后 *直接填充字段。 */ protected Address() { } /** *为方便起见,您可以拥有其他(public)构造函数。 **/ public Address(String street, String zipcode, String city) { this.street = street; this.zipcode = zipcode; this.city = city; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public String getZipcode() { return zipcode; } public void setZipcode(String zipcode) { this.zipcode = zipcode; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } } /model/src/main/java/org/jpwh/model/simple/Category.java package org.jpwh.model.simple; import org.jpwh.model.Constants; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Category { @Id @GeneratedValue(generator = Constants.ID_GENERATOR) protected Long id; public Long getId() { return id; } protected String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } 提供的一些测试类: ![20170703222508104][] /examples/src/test/java/org/jpwh/test/simple/AccessJPAMetamodel.java package org.jpwh.test.simple; import org.jpwh.env.JPATest; import org.jpwh.model.simple.Item; import org.jpwh.model.simple.Item_; import org.testng.annotations.Test; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Path; import javax.persistence.criteria.Root; import javax.persistence.metamodel.Attribute; import javax.persistence.metamodel.ManagedType; import javax.persistence.metamodel.Metamodel; import javax.persistence.metamodel.SingularAttribute; import javax.persistence.metamodel.Type; import javax.transaction.UserTransaction; import java.util.Date; import java.util.List; import java.util.Set; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; //JPA访问元模型 public class AccessJPAMetamodel extends JPATest { @Override public void configurePersistenceUnit() throws Exception { configurePersistenceUnit("SimpleXMLCompletePU"); } @Test public void accessDynamicMetamodel() throws Exception { EntityManagerFactory entityManagerFactory = JPA.getEntityManagerFactory(); Metamodel mm = entityManagerFactory.getMetamodel(); Set<ManagedType<?>> managedTypes = mm.getManagedTypes(); assertEquals(managedTypes.size(), 1); ManagedType itemType = managedTypes.iterator().next(); assertEquals( itemType.getPersistenceType(), Type.PersistenceType.ENTITY ); SingularAttribute nameAttribute = itemType.getSingularAttribute("name"); assertEquals( nameAttribute.getJavaType(), String.class ); assertEquals( nameAttribute.getPersistentAttributeType(), Attribute.PersistentAttributeType.BASIC ); assertFalse( nameAttribute.isOptional() // NOT NULL ); SingularAttribute auctionEndAttribute = itemType.getSingularAttribute("auctionEnd"); assertEquals( auctionEndAttribute.getJavaType(), Date.class ); assertFalse( auctionEndAttribute.isCollection() ); assertFalse( auctionEndAttribute.isAssociation() ); } @Test public void accessStaticMetamodel() throws Exception { SingularAttribute nameAttribute = Item_.name; assertEquals( nameAttribute.getJavaType(), String.class ); } @Test public void queryStaticMetamodel() throws Exception { UserTransaction tx = TM.getUserTransaction(); try { tx.begin(); EntityManager entityManager = JPA.createEntityManager(); Item itemOne = new Item(); itemOne.setName("This is some item"); itemOne.setAuctionEnd(new Date(System.currentTimeMillis() + 100000)); entityManager.persist(itemOne); Item itemTwo = new Item(); itemTwo.setName("Another item"); itemTwo.setAuctionEnd(new Date(System.currentTimeMillis() + 100000)); entityManager.persist(itemTwo); tx.commit(); entityManager.close(); entityManager = JPA.createEntityManager(); tx.begin(); CriteriaBuilder cb = entityManager.getCriteriaBuilder(); // This query is the equivalent of "select i from Item i" CriteriaQuery<Item> query = cb.createQuery(Item.class); Root<Item> fromItem = query.from(Item.class); query.select(fromItem); List<Item> items = entityManager.createQuery(query) .getResultList(); assertEquals(items.size(), 2); // "where i.name like :pattern" Path<String> namePath = fromItem.get("name"); query.where( cb.like( namePath, // Has to be a Path<String> for like() operator! cb.parameter(String.class, "pattern") ) ); items = entityManager.createQuery(query) .setParameter("pattern", "%some item%") // Wildcards! .getResultList(); assertEquals(items.size(), 1); assertEquals(items.iterator().next().getName(), "This is some item"); query.where( cb.like( fromItem.get(Item_.name), // Static Item_ metamodel! cb.parameter(String.class, "pattern") ) ); items = entityManager.createQuery(query) .setParameter("pattern", "%some item%") // Wildcard! .getResultList(); assertEquals(items.size(), 1); assertEquals(items.iterator().next().getName(), "This is some item"); tx.commit(); entityManager.close(); } finally { TM.rollback(); } } } /examples/src/test/java/org/jpwh/test/simple/CRUD.java package org.jpwh.test.simple; import org.jpwh.env.JPATest; import org.jpwh.model.simple.Item; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import javax.persistence.EntityManager; import javax.persistence.Query; import javax.transaction.UserTransaction; import java.util.Date; import java.util.List; import static org.testng.Assert.assertEquals; public class CRUD extends JPATest { @Override public void configurePersistenceUnit() throws Exception { configurePersistenceUnit("SimplePU"); } @Test public void storeAndQueryItems() throws Exception { storeAndQueryItems("findItems"); } public void storeAndQueryItems(String queryName) throws Exception { UserTransaction tx = TM.getUserTransaction(); try { tx.begin(); EntityManager em = JPA.createEntityManager(); Item itemOne = new Item(); itemOne.setName("Item One"); itemOne.setAuctionEnd(new Date(System.currentTimeMillis() + 100000)); em.persist(itemOne); Item itemTwo = new Item(); itemTwo.setName("Item Two"); itemTwo.setAuctionEnd(new Date(System.currentTimeMillis() + 100000)); em.persist(itemTwo); tx.commit(); em.close(); tx.begin(); em = JPA.createEntityManager(); Query q = em.createNamedQuery(queryName); List<Item> items = q.getResultList(); assertEquals(items.size(), 2); tx.commit(); em.close(); } finally { TM.rollback(); } } } /examples/src/test/java/org/jpwh/test/simple/CRUDMetadataHBMXML.java package org.jpwh.test.simple; import org.testng.annotations.Test; //CRUD HBM XML元数据 public class CRUDMetadataHBMXML extends CRUD { @Override public void configurePersistenceUnit() throws Exception { configurePersistenceUnit("SimpleXMLHibernatePU", "simple/Native.hbm.xml"); } @Test @Override public void storeAndQueryItems() throws Exception { super.storeAndQueryItems("findItemsHibernate"); } } /examples/src/test/java/org/jpwh/test/simple/CRUDMetadataOverrideXML.java package org.jpwh.test.simple; import org.hibernate.SessionFactory; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; //CRUD XML元数据覆盖 public class CRUDMetadataOverrideXML extends CRUD { @Override public void configurePersistenceUnit() throws Exception { configurePersistenceUnit("SimpleXMLOverridePU"); } @Test @Override public void storeAndQueryItems() throws Exception { super.storeAndQueryItems(); } @Test public void checkMetadataOverride() throws Exception { //使用Hibernate元数据API来查找覆盖SQL列的名称,JPA不 //支持访问SQL详细信息 // TODO:不知道如何使用Hibernate 5 API访问SQL映射详细信息... /* Property nameProperty = itemClass.getProperty("name"); Column nameColumn = (Column) nameProperty.getColumnIterator().next(); // 列的名称来自XML描述符 assertEquals(nameColumn.getName(), "ITEM_NAME"); // 但是Bean验证注释仍然被识别! assertEquals(nameColumn.isNullable(), false); assertEquals(nameColumn.getLength(), 255); */ } } /examples/src/test/java/org/jpwh/test/simple/CRUDMetadataXML.java package org.jpwh.test.simple; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; //CRUD XML元数据 public class CRUDMetadataXML extends CRUD { @Override public void configurePersistenceUnit() throws Exception { configurePersistenceUnit("SimpleXMLCompletePU"); } @Test @Override public void storeAndQueryItems() throws Exception { super.storeAndQueryItems(); } } /examples/src/test/java/org/jpwh/test/simple/MappingEmbeddables.java package org.jpwh.test.simple; import org.jpwh.env.JPATest; import org.jpwh.model.simple.Address; import org.jpwh.model.simple.User; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import javax.persistence.EntityManager; import javax.persistence.PersistenceException; import javax.transaction.UserTransaction; import java.util.logging.Level; import java.util.logging.Logger; import static org.testng.Assert.assertEquals; //映射可嵌入 public class MappingEmbeddables extends JPATest { private static final Logger LOG = Logger.getLogger(MappingEmbeddables.class.getName()); @Override public void configurePersistenceUnit() throws Exception { configurePersistenceUnit("SimplePU"); } @Test public void storeAndLoadUsers() throws Exception { UserTransaction tx = TM.getUserTransaction(); try { tx.begin(); EntityManager em = JPA.createEntityManager(); User user = new User(); user.setUsername("johndoe"); Address homeAddress = new Address("Some Street 123", "12345", "Some City"); user.setHomeAddress(homeAddress); em.persist(user); tx.commit(); em.close(); tx.begin(); em = JPA.createEntityManager(); User u = em.find(User.class, user.getId()); assertEquals(u.getUsername(), "johndoe"); assertEquals(u.getHomeAddress().getStreet(), "Some Street 123"); tx.commit(); em.close(); } finally { TM.rollback(); } } @Test(expectedExceptions = org.hibernate.exception.ConstraintViolationException.class) public void storeAndLoadInvalidUsers() throws Throwable { UserTransaction tx = TM.getUserTransaction(); try { tx.begin(); EntityManager em = JPA.createEntityManager(); User user = new User(); user.setUsername("johndoe"); Address homeAddress = new Address("Some Street 123", "12345", null); // NULL city! user.setHomeAddress(homeAddress); em.persist(user); try { // Hibernate尝试插入但失败 em.flush(); //注意:如果你尝试用tx.commit()和一个冲洗的副作用,你就不会 //得到ConstraintViolationException。Hibernate会在内部捕获它 //简单地将事务标记为回滚。 } catch (Exception ex) { throw unwrapCauseOfType(ex, org.hibernate.exception.ConstraintViolationException.class); } } finally { TM.rollback(); } } } /examples/src/test/java/org/jpwh/test/simple/MappingManyToOne.java package org.jpwh.test.simple; import org.jpwh.env.JPATest; import org.jpwh.model.simple.Bid; import org.jpwh.model.simple.Item; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import javax.persistence.EntityManager; import javax.transaction.UserTransaction; import java.math.BigDecimal; import static org.testng.Assert.assertEquals; //映射多对一 public class MappingManyToOne extends JPATest { @Override public void configurePersistenceUnit() throws Exception { configurePersistenceUnit("SimplePU"); } @Test public void storeAndLoadBids() throws Exception { UserTransaction tx = TM.getUserTransaction(); try { tx.begin(); EntityManager em = JPA.createEntityManager(); //存储在一个持久性上下文(事务) Item anItem = new Item(); anItem.setName("Example Item"); Bid firstBid = new Bid(new BigDecimal("123.00"), anItem); Bid secondBid = new Bid(new BigDecimal("456.00"), anItem); // 订单在这里很重要,Hibernate不够聪明了! em.persist(anItem); em.persist(firstBid); em.persist(secondBid); tx.commit(); em.close(); tx.begin(); em = JPA.createEntityManager(); Long BID_ID = firstBid.getId(); // 加载另一个持久性上下文 Bid someBid = em.find(Bid.class, BID_ID); // SQL SELECT //初始化Item代理,因为我们调用getId(),它是 //未映射为标识符属性(the field is!) assertEquals(someBid.getItem().getId(), anItem.getId()); // SQL SELECT tx.commit(); em.close(); } finally { TM.rollback(); } } } /examples/src/test/java/org/jpwh/test/simple/ModelOperations.java package org.jpwh.test.simple; import org.jpwh.model.simple.Bid; import org.jpwh.model.simple.Item; import org.testng.annotations.Test; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; import java.util.Date; import java.util.Locale; import java.util.Set; import static org.testng.Assert.*; //模式操作 public class ModelOperations { @Test public void linkBidAndItem() { Item anItem = new Item(); Bid aBid = new Bid(); anItem.getBids().add(aBid); aBid.setItem(anItem); assertEquals(anItem.getBids().size(), 1); assertTrue(anItem.getBids().contains(aBid)); assertEquals(aBid.getItem(), anItem); // 再次用方便的方法 Bid secondBid = new Bid(); anItem.addBid(secondBid); assertEquals(2, anItem.getBids().size()); assertTrue(anItem.getBids().contains(secondBid)); assertEquals(anItem, secondBid.getItem()); } @Test public void validateItem() { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); Validator validator = factory.getValidator(); Item item = new Item(); item.setName("Some Item"); item.setAuctionEnd(new Date()); Set<ConstraintViolation<Item>> violations = validator.validate(item); // 我们有一个验证错误,拍卖结束日期不在将来! assertEquals(1, violations.size()); ConstraintViolation<Item> violation = violations.iterator().next(); String failedPropertyName = violation.getPropertyPath().iterator().next().getName(); assertEquals(failedPropertyName, "auctionEnd"); if (Locale.getDefault().getLanguage().equals("en")) assertEquals(violation.getMessage(), "must be in the future"); } } /examples/src/test/java/org/jpwh/test/simple/SimpleTransitions.java package org.jpwh.test.simple; import org.hibernate.Session; import org.hibernate.jdbc.Work; import org.jpwh.env.JPATest; import org.jpwh.model.simple.Address; import org.jpwh.model.simple.Item; import org.jpwh.model.simple.User; import org.testng.annotations.Test; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.FlushModeType; import javax.persistence.PersistenceUnitUtil; import javax.transaction.Status; import javax.transaction.UserTransaction; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.HashSet; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.Executors; import static org.testng.Assert.*; import static org.testng.Assert.assertEquals; //简单的过渡 public class SimpleTransitions extends JPATest { @Override public void configurePersistenceUnit() throws Exception { configurePersistenceUnit("SimplePU"); } @Test public void basicUOW() { EntityManager em = null; UserTransaction tx = TM.getUserTransaction(); try { tx.begin(); em = JPA.createEntityManager(); // 应用程序管理 Item someItem = new Item(); someItem.setName("Some Item"); em.persist(someItem); tx.commit(); // 同步/刷新持久性上下文 } catch (Exception ex) { // 事务回滚,异常处理 try { if (tx.getStatus() == Status.STATUS_ACTIVE || tx.getStatus() == Status.STATUS_MARKED_ROLLBACK) tx.rollback(); } catch (Exception rbEx) { System.err.println("Rollback of transaction failed, trace follows!"); rbEx.printStackTrace(System.err); } throw new RuntimeException(ex); } finally { if (em != null && em.isOpen()) em.close(); // 你创建它,你关闭它! } } @Test public void makePersistent() throws Exception { UserTransaction tx = TM.getUserTransaction(); try { EntityManager em; tx.begin(); em = JPA.createEntityManager(); Item item = new Item(); item.setName("Some Item"); // Item#name is NOT NULL! em.persist(item); Long ITEM_ID = item.getId(); // Has been assigned tx.commit(); em.close(); tx.begin(); em = JPA.createEntityManager(); assertEquals(em.find(Item.class, ITEM_ID).getName(), "Some Item"); tx.commit(); em.close(); } finally { TM.rollback(); } } @Test public void retrievePersistent() throws Exception { UserTransaction tx = TM.getUserTransaction(); try { tx.begin(); EntityManager em = JPA.createEntityManager(); Item someItem = new Item(); someItem.setName("Some Item"); em.persist(someItem); tx.commit(); em.close(); long ITEM_ID = someItem.getId(); { tx.begin(); em = JPA.createEntityManager(); // Hit the database if not already in persistence context Item item = em.find(Item.class, ITEM_ID); if (item != null) item.setName("New Name"); // Modify tx.commit(); // Flush: Dirty check and SQL UPDATE em.close(); } { tx.begin(); em = JPA.createEntityManager(); Item itemA = em.find(Item.class, ITEM_ID); Item itemB = em.find(Item.class, ITEM_ID); // Repeatable read assertTrue(itemA == itemB); assertTrue(itemA.equals(itemB)); assertTrue(itemA.getId().equals(itemB.getId())); tx.commit(); // Flush: Dirty check and SQL UPDATE em.close(); } tx.begin(); em = JPA.createEntityManager(); assertEquals(em.find(Item.class, ITEM_ID).getName(), "New Name"); tx.commit(); em.close(); } finally { TM.rollback(); } } @Test(expectedExceptions = org.hibernate.LazyInitializationException.class) public void retrievePersistentReference() throws Exception { UserTransaction tx = TM.getUserTransaction(); try { tx.begin(); EntityManager em = JPA.createEntityManager(); Item someItem = new Item(); someItem.setName("Some Item"); em.persist(someItem); tx.commit(); em.close(); long ITEM_ID = someItem.getId(); tx.begin(); em = JPA.createEntityManager(); /* 如果持久性上下文已经包含具有给定标识符的<code> Item </code> <code> Item </code>实例由<code> getReference()</code>返回,而不会触发数据库。 此外,如果目前正在管理具有该标识符的持久化实例<em>no</em>,那么一个空 占位符将由Hibernate代理生成。 这意味着<code> getReference()</code>不会 访问数据库,它不返回<code> null </code>,与<code> find()</code>不同。 */ Item item = em.getReference(Item.class, ITEM_ID); /* JPA提供<code> PersistenceUnitUtil </code>辅助方法,例如<code> isLoaded()</code> 检测您是否使用未初始化的代理。 */ PersistenceUnitUtil persistenceUtil = JPA.getEntityManagerFactory().getPersistenceUnitUtil(); assertFalse(persistenceUtil.isLoaded(item)); /* 一旦您在代理上调用任何方法,例如<code> Item#getName()</code>, 执行<code> SELECT </code>以完全初始化占位符。 这个规则的例外是 一种映射的数据库标识符getter方法,如<code> getId()</code>。 代理人 可能看起来像真实的东西,但它只是一个携带标识符值的占位符 它代表的实体实例。 如果代理服务器上不存在数据库记录 被初始化,将抛出一个<code> EntityNotFoundException </code>。 */ // assertEquals(item.getName(), "Some Item"); /* Hibernate有一个方便的静态<code> initialize()</code>方法,加载代理的数据。 */ // Hibernate.initialize(item); tx.commit(); em.close(); /* 在持久化上下文关闭之后,<code>item</code>处于分离状态。 如果你这样做在持久化上下文仍然打开时,不初始化代理,如果你访问代理,您将得到一个 <code> LazyInitializationException </code>。 你不能加载,一旦持久化上下文关闭,就按需数据。 解决方案很简单:加载 关闭持久化上下文之前的数据。 */ assertEquals(item.getName(), "Some Item"); } finally { TM.rollback(); } } @Test public void makeTransient() throws Exception { UserTransaction tx = TM.getUserTransaction(); try { tx.begin(); EntityManager em = JPA.createEntityManager(); Item someItem = new Item(); someItem.setName("Some Item"); em.persist(someItem); tx.commit(); em.close(); long ITEM_ID = someItem.getId(); tx.begin(); em = JPA.createEntityManager(); /* 如果调用<code>find()</code>,Hibernate将执行<code>SELECT</code> 加载<code>Item</code>。 如果你调用<code> getReference()</code>,Hibernate 将尝试避免<code>SELECT</code>并返回代理。 */ Item item = em.find(Item.class, ITEM_ID); //Item item = em.getReference(Item.class, ITEM_ID); /* 调用<code>remove()</code>将队列化实体删除 工作单位完成,现在已被删除<em>removed</em>。如果<code>remove()</code> 在代理上调用,Hibernate将执行<code>SELECT</code>来加载数据。 实体实例必须在生命周期过渡期间完全初始化。 你可能 是否启用了生命周期回调方法或实体侦听器 (请参阅<a href="#EventListenersInterceptors"/>),实例必须通过这些 拦截器完成其整个生命周期。 */ em.remove(item); /* 被删除状态的实体不再处于持久状态,这可以是 使用<code> contains()</code>操作进行检查。 */ assertFalse(em.contains(item)); /* 您可以使删除的实例再次持久化,取消删除。 */ // em.persist(item); // hibernate.use_identifier_rollback已启用,它现在看起来像一个暂时的实例 assertNull(item.getId()); /* 当事务提交时,Hibernate会同步状态转换 数据库并执行SQL <code> DELETE </code>。 JVM垃圾收集器检测到 <code> item </code>不再被任何人引用,最后删除最后一个跟踪 数据。 */ tx.commit(); em.close(); tx.begin(); em = JPA.createEntityManager(); item = em.find(Item.class, ITEM_ID); assertNull(item); tx.commit(); em.close(); } finally { TM.rollback(); } } @Test public void refresh() throws Exception { UserTransaction tx = TM.getUserTransaction(); try { tx.begin(); EntityManager em = JPA.createEntityManager(); Item someItem = new Item(); someItem.setName("Some Item"); em.persist(someItem); tx.commit(); em.close(); final long ITEM_ID = someItem.getId(); tx.begin(); em = JPA.createEntityManager(); Item item = em.find(Item.class, ITEM_ID); item.setName("Some Name"); // 有人更新数据库中的这一行! Executors.newSingleThreadExecutor().submit(new Callable<Object>() { @Override public Object call() throws Exception { UserTransaction tx = TM.getUserTransaction(); try { tx.begin(); EntityManager em = JPA.createEntityManager(); Session session = em.unwrap(Session.class); session.doWork(new Work() { @Override public void execute(Connection con) throws SQLException { PreparedStatement ps = con.prepareStatement("update ITEM set name = ? where ID = ?"); ps.setString(1, "Concurrent Update Name"); ps.setLong(2, ITEM_ID); /* 替代方法:在刷新时收到EntityNotFoundException PreparedStatement ps = con.prepareStatement("delete from ITEM where ID = ?"); ps.setLong(1, ITEM_ID); */ if (ps.executeUpdate() != 1) throw new SQLException("ITEM row was not updated"); } }); tx.commit(); em.close(); } catch (Exception ex) { TM.rollback(); throw new RuntimeException("Concurrent operation failure: " + ex, ex); } return null; } }).get(); String oldName = item.getName(); em.refresh(item); assertNotEquals(item.getName(), oldName); assertEquals(item.getName(), "Concurrent Update Name"); tx.commit(); // 刷新:脏检查和SQL UPDATE em.close(); } finally { TM.rollback(); } } @Test(groups = {"H2", "POSTGRESQL", "ORACLE"}) public void replicate() throws Exception { Long ITEM_ID; try { UserTransaction tx = TM.getUserTransaction(); tx.begin(); EntityManager em = JPA.createEntityManager(); Item someItem = new Item(); someItem.setName("Some Item"); em.persist(someItem); tx.commit(); em.close(); ITEM_ID = someItem.getId(); } finally { TM.rollback(); } UserTransaction tx = TM.getUserTransaction(); try { tx.begin(); EntityManager emA = getDatabaseA().createEntityManager(); Item item = emA.find(Item.class, ITEM_ID); EntityManager emB = getDatabaseB().createEntityManager(); emB.unwrap(Session.class) .replicate(item, org.hibernate.ReplicationMode.LATEST_VERSION); tx.commit(); emA.close(); emB.close(); } finally { TM.rollback(); } } protected EntityManagerFactory getDatabaseA() { return JPA.getEntityManagerFactory(); } protected EntityManagerFactory getDatabaseB() { // TODO:失败,因为我们不能在同一事务中招募两个非XA连接 //在MySQL上 XA在MySQL中破坏,所以我们必须使用Bitronix XA包装器,它可以 //每个事务只处理一个非XA资源。 请参阅DatabaseProduct.java return JPA.getEntityManagerFactory(); } @Test public void flushModeType() throws Exception { UserTransaction tx = TM.getUserTransaction(); Long ITEM_ID; try { tx.begin(); EntityManager em = JPA.createEntityManager(); Item someItem = new Item(); someItem.setName("Original Name"); em.persist(someItem); tx.commit(); em.close(); ITEM_ID = someItem.getId(); } finally { TM.rollback(); } try { tx.begin(); EntityManager em = JPA.createEntityManager(); Item item = em.find(Item.class, ITEM_ID); item.setName("New Name"); // 禁用刷新之前查询: em.setFlushMode(FlushModeType.COMMIT); assertEquals( em.createQuery("select i.name from Item i where i.id = :id") .setParameter("id", ITEM_ID).getSingleResult(), "Original Name" ); tx.commit(); // Flush! em.close(); } finally { TM.rollback(); } } @Test public void scopeOfIdentity() throws Exception { UserTransaction tx = TM.getUserTransaction(); try { tx.begin(); EntityManager em = JPA.createEntityManager(); Item someItem = new Item(); someItem.setName("Some Item"); em.persist(someItem); tx.commit(); em.close(); long ITEM_ID = someItem.getId(); tx.begin(); em = JPA.createEntityManager(); Item a = em.find(Item.class, ITEM_ID); Item b = em.find(Item.class, ITEM_ID); assertTrue(a == b); assertTrue(a.equals(b)); assertEquals(a.getId(), b.getId()); tx.commit(); em.close(); // PC is gone, 'a' and 'b' are now references to instances in detached state! tx.begin(); em = JPA.createEntityManager(); Item c = em.find(Item.class, ITEM_ID); assertTrue(a != c); // The 'a' reference is still detached! assertFalse(a.equals(c)); assertEquals(a.getId(), c.getId()); tx.commit(); em.close(); Set<Item> allItems = new HashSet<>(); allItems.add(a); allItems.add(b); allItems.add(c); assertEquals(allItems.size(), 2); // 这似乎是错的和任意的! } finally { TM.rollback(); } } @Test public void detach() throws Exception { UserTransaction tx = TM.getUserTransaction(); try { tx.begin(); EntityManager em = JPA.createEntityManager(); User someUser = new User(); someUser.setUsername("johndoe"); someUser.setHomeAddress(new Address("Some Street", "1234", "Some City")); em.persist(someUser); tx.commit(); em.close(); long USER_ID = someUser.getId(); tx.begin(); em = JPA.createEntityManager(); User user = em.find(User.class, USER_ID); em.detach(user); assertFalse(em.contains(user)); tx.commit(); em.close(); } finally { TM.rollback(); } } @Test public void mergeDetached() throws Exception { UserTransaction tx = TM.getUserTransaction(); try { tx.begin(); EntityManager em = JPA.createEntityManager(); User detachedUser = new User(); detachedUser.setUsername("foo"); detachedUser.setHomeAddress(new Address("Some Street", "1234", "Some City")); em.persist(detachedUser); tx.commit(); em.close(); long USER_ID = detachedUser.getId(); detachedUser.setUsername("johndoe"); tx.begin(); em = JPA.createEntityManager(); User mergedUser = em.merge(detachedUser); // 合并后丢弃'detachUser'引用! // 'mergedUser'处于持久状态 mergedUser.setUsername("doejohn"); tx.commit(); // UPDATE在数据库中 em.close(); tx.begin(); em = JPA.createEntityManager(); User user = em.find(User.class, USER_ID); assertEquals(user.getUsername(), "doejohn"); tx.commit(); em.close(); } finally { TM.rollback(); } } } [20170703220612026]: /images/20220613/4ff1438b9239463e993146be0acc3836.png [20170703222034505]: /images/20220613/210bccff71ff4540acdde80638285f1d.png [20170703222508104]: /images/20220613/cad913acce7f4c4ea83cefb6fa945618.png
还没有评论,来说两句吧...