文档章节

Play源码深入之六:数据库与事务管理

奋斗到天明
 奋斗到天明
发布于 2015/08/27 18:12
字数 769
阅读 769
收藏 0

由前面的文章大致知道,Play的事务由过滤器中处理,这里理一下Play框架与数据库相关的部分。 

主要是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。

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中,处理逻辑较多。主要集中在下面四个方法:

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之中。 

enhance方法,为基于play.db.jpa.JPABase的子Model织入相应辅助代码。 

onApplicationStart方法,根据数据配置实例工厂。 

getFilter方法,获取过滤器,这里就是事务相关的过滤器。 

在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用户所期望的。

© 著作权归作者所有

共有 人打赏支持
奋斗到天明
粉丝 18
博文 112
码字总数 82707
作品 0
昌平
程序员
Play源码解析计划

最近有想法看看Play的源码,以提高自己的编码水平。之前都是东看看,西看看。最后看来去却好像无所大成。有人说过,伤敌十指,不如断敌一指,于是我有开始了学习之路。 原计划是采用1.2.3版本...

刀狂剑痴
2015/08/27
143
0
Play源码深入之五:Job模块的原理

先看play.jobs.JobsPlugin。 public void onApplicationStart() { int core = Integer.parseInt(Play.configuration.getProperty("play.jobs.pool", "10")); executor = new ScheduledThread......

刀狂剑痴
2015/08/27
810
0
Play 1.x框架学习之一:功能测试 (functional test in play framework)

Play框架中已经集成了junit框架,大家可以非常方便的进行功能测试,这里我展现一个测试新增的例子,其他的大家可以照这个例子深入。 首先需要在app/modules包中定义一个Beat类,app/control...

奋斗到天明
2015/08/27
0
0
Play 1.x框架学习之七:多数据库切换与源码修改 ( Databases Switch And Modify Source Code)

在单数据源(单个ip)下的多库,可以使用use xxdb 命令进行切换,但是如果多个数据源的情况下,use命令就不行了。在play框架中,提供了多数据源多库的切换。本文不提供完全的例子,只提供部分...

奋斗到天明
2015/08/27
0
0
spring mvc 数据库方面如何处理并发的?

看了ThreadLocal,了解其应用的场景多在数据库连接,事务管理方面,并在spring,hibernate的源码中有广泛运用。所以,这里在没有看源码的基础上,有个疑问:spirng mvc项目在连接数据库时都是根...

安静的敲代码
2016/05/26
1K
8

没有更多内容

加载失败,请刷新页面

加载更多

下一页

谈谈神秘的ES6——(五)解构赋值【对象篇】

上一节课我们了解了有关数组的解构赋值相关内容,这节课,我们接着,来讲讲对象的解构赋值。 解构不仅可以用于数组,还可以用于对象。 let { foo, bar } = { foo: "aaa", bar: "bbb" };fo...

JandenMa
19分钟前
0
0
OSChina 周一乱弹 —— 有人要给本汪介绍妹子啦

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @莱布妮子 :分享水木年华的单曲《中学时代》@小小编辑 手机党少年们想听歌,请使劲儿戳(这里) @须臾时光:夏天还在做最后的挣扎,但是晚上...

小小编辑
25分钟前
5
1
centos7安装redis及开机启动

配置编译环境: sudo yum install gcc-c++ 下载源码: wget http://download.redis.io/releases/redis-3.2.8.tar.gz 解压源码: tar -zxvf redis-3.2.8.tar.gz 进入到解压目录: cd redis-3......

hotsmile
47分钟前
0
0
PopupWindow总结和通用PopupWindow

PopupWindow 注意点: 设置PopupWindow的宽高,容易忘记,没有设置宽高在某些情况下会不展示。 基础设置少不了 pop.setBackgroundDrawable(new BitmapDrawable());pop.setFoucusable(true)...

DaSheng丶
56分钟前
0
0
Confluence 6 数据库和临时目录

数据库 所有的其他数据库,包括有页面,内容都存储在数据库中。如果你安装的 Confluence 是用于评估或者你选择使用的是 Embedded H2 Database 数据库。数据库有关的文件将会存储在 database...

honeymose
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部