文档章节

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

奋斗到天明
 奋斗到天明
发布于 2015/08/27 18:12
字数 769
阅读 797
收藏 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
spring mvc 数据库方面如何处理并发的?

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

安静的敲代码
2016/05/26
1K
8
Spring的事务管理实现原理初探

这里主要是通过分析部分源码进行剖析Spring事务管理的实现原理。 再分析源码前,现从理论上大概分析哈: 纯JDBC操作数据库的基本步骤: 1. 获取连接 Connection conn = DriverManager.getCon...

rockypeng
2014/01/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Deepin 安装wireshark抓包工具

一、关于deepin和wireshark deepin目前已经发展到15.8了,开发Android毫无压力,在四个月的使用时间里,已经非常习惯了。目前想处理一些网络问题,因此尝试在deepin上安装一个抓包工具。dee...

IamOkay
13分钟前
0
0
Docker镜像仓库服务-Nexus

建立云原生集群系统,建立自己的私有Docker镜像仓库必不可少。一方面可以加快多节点部署容器镜像的下载速度,另一方面是为了安全(容器里存储有系统所有的信息、包括密码、数据库等等,切记不...

openthings
25分钟前
1
0
127.0.0.1 和 0.0.0.0 地址的区别

1. IP地址分类 1.1 IP地址表示 IP地址由两个部分组成,net-id和host-id,即网络号和主机号。 net-id:表示ip地址所在的网络号。 host-id:表示ip地址所在网络中的某个主机号码。 即: IP-a...

华山猛男
今天
19
0
解决Unknown host 'd29vzk4ow07wi7.cloudfront.net'. You may need to adjust the proxy settings in Gradle.

把 总项目 下的 build.gradle 中的 两个 jcenter() 用 maven{ url ‘http://maven.aliyun.com/nexus/content/groups/public/’} 代替。...

lanyu96
今天
4
0
基于redis的分布式锁

redisson提供了基于redis的分布式锁实现方式,本文就尝试了下锁的使用方式。Redisson同时还为分布式锁提供了异步执行的相关方法,第二节执行介绍。 一、可重入锁验证 同一个jvm里面同一线程的...

noob_chr
今天
14
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部