Hibernate:有了 save,为什么还需要 persist?
背景
万物皆自然,每个 API 的设计,无论是否正确,都有其意图。因此,在学习某些框架的时候,我们需要经常思考:这个 API 的设计意图是啥?
本文来探讨一下 Session 中 persist 的设计意图。
官方注释
save
1 /** 2 * Persist the given transient instance, first assigning a generated identifier. (Or 3 * using the current value of the identifier property if the <tt>assigned</tt> 4 * generator is used.) This operation cascades to associated instances if the 5 * association is mapped with {@code cascade="save-update"} 6 * 7 * @param object a transient instance of a persistent class 8 * 9 * @return the generated identifier 10 */ 11 public Serializable save(Object object);
persist
1 /** 2 * Make a transient instance persistent. This operation cascades to associated 3 * instances if the association is mapped with {@code cascade="persist"} 4 * <p/> 5 * The semantics of this method are defined by JSR-220. 6 * 7 * @param object a transient instance to be made persistent 8 */ 9 public void persist(Object object);
解释
save 因为需要返回一个主键值,因此会立即执行 insert 语句,而 persist 在事务外部调用时则不会立即执行 insert 语句,在事务内调用还是会立即执行 insert 语句的。
看 persist 的注释会觉得其不会立即执行 insert 语句,为何 在事务内调用会立即执行 insert 语句,后面再分析。
测试
SessionHelper
1 package demo; 2 3 import org.hibernate.*; 4 import org.hibernate.cfg.*; 5 6 public final class SessionHelper { 7 private static SessionFactory factory; 8 9 public static void execute(SessionAction action) { 10 execute(action, false); 11 } 12 13 public static void execute(SessionAction action, boolean beforeTransaction) { 14 Session session = openSession(); 15 try { 16 if (beforeTransaction) { 17 System.out.println("action"); 18 action.action(session); 19 } 20 21 System.out.println("begin transaction"); 22 session.beginTransaction(); 23 if (!beforeTransaction) { 24 System.out.println("action"); 25 action.action(session); 26 } 27 28 System.out.println("flush and commit"); 29 session.flush(); 30 session.getTransaction().commit(); 31 } catch (Exception ex) { 32 session.getTransaction().rollback(); 33 } finally { 34 session.close(); 35 } 36 } 37 38 @SuppressWarnings("deprecation") 39 public static Session openSession() { 40 if (factory == null) { 41 factory = new Configuration().configure().buildSessionFactory(); 42 } 43 44 return factory.openSession(); 45 } 46 }
save
1 package demo; 2 3 import model.*; 4 5 import org.hibernate.*; 6 /* 7 * save 会导致 insert 语句的立即执行。 8 */ 9 public class SaveDemo implements Demo { 10 11 @Override 12 public void run() { 13 SessionHelper.execute(new SessionAction() { 14 15 @Override 16 public void action(Session session) { 17 User user = new User(); 18 user.setUsername("woshishui"); 19 user.setPassword("123456"); 20 21 session.save(user); 22 } 23 24 }); 25 } 26 27 }
输出结果
1 begin transaction 2 action 3 Hibernate: 4 /* insert model.User 5 */ insert 6 into 7 USERS 8 (USERNAME, PASSWORD) 9 values 10 (?, ?) 11 flush and commit
persis in transactiont
1 package demo; 2 3 import model.*; 4 5 import org.hibernate.*; 6 7 /* 8 * persist 在事务中执行,会导致 insert 语句的立即执行。 9 */ 10 public class PersisInTransactiontDemo implements Demo { 11 12 @Override 13 public void run() { 14 SessionHelper.execute(new SessionAction() { 15 16 @Override 17 public void action(Session session) { 18 User user = new User(); 19 user.setUsername("woshishui"); 20 user.setPassword("123456"); 21 22 session.persist(user); 23 } 24 25 }); 26 } 27 28 }
输出结果
1 begin transaction 2 action 3 Hibernate: 4 /* insert model.User 5 */ insert 6 into 7 USERS 8 (USERNAME, PASSWORD) 9 values 10 (?, ?) 11 flush and commit
persis before transactiont
1 package demo; 2 3 import model.*; 4 5 import org.hibernate.*; 6 7 /* 8 * persist 不在事务中执行,不会导致 insert 语句的立即执行,而是在 flush 时执行 insert 语句。 9 */ 10 public class PersisBeforeTransactiontDemo implements Demo { 11 12 @Override 13 public void run() { 14 SessionHelper.execute(new SessionAction() { 15 16 @Override 17 public void action(Session session) { 18 User user = new User(); 19 user.setUsername("woshishui"); 20 user.setPassword("123456"); 21 22 session.persist(user); 23 } 24 25 }, true); 26 } 27 28 }
输出结果
1 action 2 begin transaction 3 flush and commit 4 Hibernate: 5 /* insert model.User 6 */ insert 7 into 8 USERS 9 (USERNAME, PASSWORD) 10 values 11 (?, ?)
分析
为何 persist 在事务内和事务外表现的行为不同呢?为何这样设计?目前还没有查到官方的第一手资料(刚学习 Java 和 Hibernate),我的猜测是:事务外的 persist 是为了应对长事务,事务内的 persist 是为了和 save 保持一个语义。
备注
学习 Hibernate 的过程也相当于从新学习了一遍 EntityFramework,换个视角总有好处。