文档章节

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

奋斗到天明
 奋斗到天明
发布于 2015/08/27 18:12
字数 769
阅读 803
收藏 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

没有更多内容

加载失败,请刷新页面

加载更多

Android安全防护防护———加密算法

摘要 这篇文章本来早就应该写了,但是由于项目一直开发新的需求,就拖后了。现在有时间了,必须得写了。现在Android应用程序对安全防范这方面要求越来越高了。特别是金融行业,如果金融app没...

北辰丨丶
27分钟前
1
0
MongoDB ObjectId详解及使用

MongoDB ObjectId详解及使用 2017年09月13日 14:25:18 universsky2015 阅读数:11802 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/universsky2015/article...

linjin200
31分钟前
2
0
Caffe在CentOS和Ubuntu上安装

1 CentOS上安装中需要配置第三方源 #sudo yum install epel-release 如果不安装第三方软件源,很多依赖包都不能通过yum安装 Makefile blas = open 2 Unbuntu PYTHON_INCLUDES=/usr/local/li...

shengjuntu
32分钟前
1
0
一线 IT 公司开发转管理,我是怎么从 0 到 1 的?

在某一线互联网公司的任职生涯马上就要结束了,回想起来,从 16 年校招加入,到今年年初离职,在这快三年的时间里,公司在飞速地发展和变化,我也从一个刚入职场的初级后台开发成长为带着十来...

编辑部的故事
33分钟前
1
0
Hibernate 自动建表默认编码格式改为UTF-8

Hibernate 自动建表时报编码错误: Caused by: java.sql.SQLException: Incorrect string value: '\...' for column 'cust_name' at row 1 一般情况我们使用的mysql方言为:org.hibernate.d......

gwl_
35分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部