由前面的文章大体知道,Play的事务由过滤器中处理,这里理一下Play框架与数据库相关的部分。 java
主要是play.db包中的DBPlugin/DB类,与play.db.jpa包中的JPAPlugin/JPA类有关,前者管理数据源,后者管理JPA。另外由于play是基于ActiveRecord模型,在play.db.jpa.JPAEnhancer类中,play织入了许多辅助方法。 数据库
DBPlugin/JPAPlugin类属于PlayPlugin子类,在play发出的事件中处理相关操做。由于DBPlugin的index比JPAPlugin的大,因此DBPlugin的事件处理会在JPAPlugin的前面。这里须要注意play处理顺序,在前半部分请求是从0到100000,后半部分响应时是从100000到0处理,因此整个处理过程是0、100、1000、...1000、100、0。app
0:play.CorePlugin 100:play.data.parsing.TempFilePlugin 200:play.data.validation.ValidationPlugin 300:play.db.DBPlugin 400:play.db.jpa.JPAPlugin 450:play.db.Evolutions 500:play.i18n.MessagesPlugin 600:play.libs.WS 700:play.jobs.JobsPlugin 100000:play.plugins.ConfigurablePluginDisablingPlugin
从配置文件中加载数据源主要发生在onApplicationStart,并可配置多个数据源。在配置数据源的时候,play会试探性链接,用来判断数据源是否可用。框架
public class DBPlugin extends PlayPlugin { @Override public void onApplicationStart() { if (changed()) { String dbName = ""; try { ... Set dbNames = Configuration.getDbNames(); Iterator it = dbNames.iterator(); while(it.hasNext()) { dbName = it.next(); ... if (isJndiDatasource || datasourceName.startsWith("java:")) { ... } else { ... url = ds.getJdbcUrl(); Connection c = null; try { c = ds.getConnection(); } finally { if (c != null) { c.close(); } } Logger.info("Connected to %s for %s", ds.getJdbcUrl(), dbName); DB.datasources.put(dbName, extDs); } } } catch (Exception e) { ... } } } }
以前写过一篇基于play 1.2.3的多数据库文章: 多数据库切换。 JPAPlugin中,处理逻辑较多。主要集中在下面四个方法:ide
public class JPAPlugin extends PlayPlugin { @Override public Object bind(RootParamNode rootParamNode, String name, Class clazz, java.lang.reflect.Type type, Annotation[] annotations) {....} @Override public void enhance(ApplicationClass applicationClass) throws Exception {...} @Override public void onApplicationStart() {...} @Override public Filter getFilter() {...} }
bind方法,将请求中域对象加入HibernateSession之中。 this
enhance方法,为基于play.db.jpa.JPABase的子Model织入相应辅助代码。 url
onApplicationStart方法,根据数据配置实例工厂。 .net
getFilter方法,获取过滤器,这里就是事务相关的过滤器。 code
在play.db.jpa.JPA:withTransaction()方法中,能够看出,play已经能够同时管理多个数据库的事务,在1.2.3版本这是没有见过。对象
public class JPA { public static T withTransaction(String dbName, boolean readOnly, F.Function0 block) throws Throwable { if (isEnabled()) { boolean closeEm = true; // For each existing persisence unit try { // we are starting a transaction for all known persistent unit // this is probably not the best, but there is no way we can know where to go from // at this stage for (String name : emfs.keySet()) { EntityManager localEm = JPA.newEntityManager(name); JPA.bindForCurrentThread(name, localEm, readOnly); if (!readOnly) { localEm.getTransaction().begin(); } } T result = block.apply(); boolean rollbackAll = false; // Get back our entity managers // Because people might have mess up with the current entity managers for (JPAContext jpaContext : get().values()) { EntityManager m = jpaContext.entityManager; EntityTransaction localTx = m.getTransaction(); // The resource transaction must be in progress in order to determine if it has been marked for rollback if (localTx.isActive() && localTx.getRollbackOnly()) { rollbackAll = true; } } for (JPAContext jpaContext : get().values()) { EntityManager m = jpaContext.entityManager; boolean ro = jpaContext.readonly; EntityTransaction localTx = m.getTransaction(); // transaction must be active to make some rollback or commit if (localTx.isActive()) { if (rollbackAll || ro) { localTx.rollback(); } else { localTx.commit(); } } } return result; } catch (...) { } finally { ... } } else { return block.apply(); } } }
对于每一个数据库实例逐一开启事务,处理用户代码以后,检查每一个实例,若是有一个回滚,则全部事务都回滚,若是没有则逐一提交。
虽然Play2.x已经横在前方,可是Play1.x还在演进,我想这是全部Play1.x用户所指望的。