JPA实体状态&联级操作
JPA的实体具有四种状态,如图所示:
- 新实体(new)。Entity由应用产生,和EJB3 Persistence运行环境没有联系,也没有唯一的标示符(Identity)。
- 持久化实体(managed)。新实体和EJB3 Persistence运行环境产生关联(通过persist(), merge()等方法),在EJB3 Persistence运行环境中存在和被管理,标志是在EJB3 Persistence运行环境中有一个唯一的标示(Identity)。
- 分离的实体(detached)。Entity有唯一标示符,但它的标示符不被EJB3 Persistence运行环境管理, 同样的该Entity也不被EJB3 Persistence运行环境管理。
- 删除的实体(removed)。Entity被remove()方法删除,对应的纪录将会在当前事务提交的时候从数据库中删除。
接下来动手测试下实体状态:
- 构建实体类:Parent和Child
@Entity
public class Parent {
@Id @GeneratedValue
private int id;
//定义了5中不同的联级类型,all表示包含四种类型
@OneToOne(cascade=CascadeType.PERSIST)
private Child persist;
@OneToOne(cascade=CascadeType.REMOVE)
private Child remove;
@OneToOne(cascade=CascadeType.MERGE)
private Child merge;
@OneToOne(cascade=CascadeType.REFRESH)
private Child refresh;
@OneToOne(cascade=CascadeType.ALL)
private Child all;
// getter and setter
}
@Entity
public class Child {
@Id @GeneratedValue
private int id;
private String name;
public Child(String name) {
this.name = name;
}
public Child() {}
// getter and setter
}
- 测试
public class Test_CascadeType {
private static EntityManagerFactory fac;
private EntityManager manager;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
fac = Persistence.createEntityManagerFactory("test");
}
@Before
public void setUp() throws Exception {
manager = fac.createEntityManager();
}
@Test
public void persist() {
Parent parent = new Parent();
Child persist = new Child("persist");
Child remove = new Child("remove");
Child merge = new Child("merge");
Child refresh = new Child("refresh");
Child all = new Child("all");
parent.setAll(all);
parent.setMerge(merge);
parent.setPersist(persist);
parent.setRefresh(refresh);
parent.setRemove(remove);
/*
// 这部分代码愿意是直接持久化parent对象,提交时会抛出RollbackException,但当提交第二个事务时则会抛出异常
// org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
// 据说解决方式是设置Parent的主键为非自增,暂时留在,请大牛们指教
manager.getTransaction().begin();
try {
// 抛出异常,因为Child对象merge、remove、refresh的CascadeType未声明为persist类型,只能手动持久化
manager.persist(parent);
manager.getTransaction().commit();
Assert.assertTrue(false);
} catch (Exception e) {
if(manager.getTransaction().isActive())
manager.getTransaction().rollback();
Assert.assertTrue(e instanceof RollbackException);
}
Assert.assertTrue(manager.contains(parent));
*/
manager.getTransaction().begin();
// insert顺序是merge,refresh,remove,all,persist,parent,如果先持久化parent,则先insert parent,最后在update parent的外键值
manager.persist(parent.getMerge());
manager.persist(parent.getRefresh());
manager.persist(parent.getRemove());
manager.persist(parent);
manager.getTransaction().commit();
Query query = manager.createQuery("select o from Child o");
List list = query.getResultList();
Assert.assertEquals(5, list.size());
}
@Test
public void merge() {
Parent parent = manager.find(Parent.class, 1);
Assert.assertEquals("persist", parent.getPersist().getName());// 其余同理
// Clear the persistence context, causing all managed entities to become detached.
// 将Entity能脱离EntityManager,避免长时间保持EntityManager打开占用资源并可以在不同的JVM之间传递Entity。
manager.clear();
parent.getMerge().setName("new merge");
parent.getPersist().setName("new persist");
// Merge the state of the given entity into the current persistence context.
// return the instance that the state was merged to.
Parent mp = manager.merge(parent);
Assert.assertNotSame(mp, parent);
Assert.assertEquals("new persist", parent.getPersist().getName());// 同refresh、remove
Assert.assertEquals("persist", mp.getPersist().getName());
Assert.assertEquals("new merge", parent.getMerge().getName()); // 同all
Assert.assertEquals("new merge", mp.getMerge().getName());
manager.getTransaction().begin();
// manager.persist(parent); // 将抛出PersistenceObjectException:detached entity passed to persist
manager.persist(mp); //或者manager.merge(parent);必须commit才更新到数据库,此处persist等child对象已存在数据库中,故而无需再持久化
manager.getTransaction().commit();
}
@Test
public void refresh() {
// 从数据库中获得实体
Parent p1 = manager.find(Parent.class, 1);
// 其他程序修改了数据库中的值
modifyBYOther();
// 缓存中的何真正要处理的值有所出入,是'脏'数据
Assert.assertEquals("persist", p1.getPersist().getName());
Assert.assertEquals("refresh", p1.getRefresh().getName());
// 用refresh从数据库中获取最新的实体
manager.refresh(p1);
// 验证
Assert.assertEquals("persist", p1.getPersist().getName());
Assert.assertEquals("new", p1.getRefresh().getName());
}
@Test
public void remove() {
Parent parent = manager.find(Parent.class, 1);
manager.getTransaction().begin();
manager.remove(parent);
manager.getTransaction().commit();
Assert.assertNotNull(parent);
Assert.assertNull(manager.find(Parent.class, 1));
Query query = manager.createQuery("select o from Child o");
List list = query.getResultList();
Assert.assertEquals(3, list.size());
}
private void modifyBYOther() {
manager.getTransaction().begin();
Query query = manager.createQuery("update Child o set o.name = 'new'");
query.executeUpdate();
manager.getTransaction().commit();
}
@After
public void tearDown() throws Exception {
manager.close();
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
fac.close();
}
}