JFinal源码走读_3_ActiveRecord初始化

      • ActiveRecord初始化
        • activerecord实例化
          • c3po实例化
          • activerecordplugin实例化
        • c3po插件的初始化
        • activerecord插件的初始化
          • TableBuilderbuildtableList config解析
            • doBuildtable conn config解析
      • ActiveRecord初始化小结

ActiveRecord初始化

activerecord是作为插件来支持jfinal webapp的,所以其初始化入口就在start(),此方法是在initPlugin()后startPlugin()时被调用的
startPlugins方法详情

    private static void startPlugins() {
        // 获取配置的plugins集合
        List<IPlugin> pluginList = plugins.getPluginList();
        if (pluginList == null)
            return;
        // IPlugin只有两个方法分别控制插件的开始和停止,此处应用了观察者模式来统一管理plugin的生命周期
        for (IPlugin plugin : pluginList) {
            try {
                // process ActiveRecordPlugin devMode
                if (plugin instanceof com.jfinal.plugin.activerecord.ActiveRecordPlugin) {
                    com.jfinal.plugin.activerecord.ActiveRecordPlugin arp = (com.jfinal.plugin.activerecord.ActiveRecordPlugin) plugin;
                    if (arp.getDevMode() == null)
                        arp.setDevMode(constants.getDevMode());
                }
                // 对于具体plugin的初始化,暂时不做深究
                if (plugin.start() == false) {
                    String message = "Plugin start error: "
                            + plugin.getClass().getName();
                    log.error(message);
                    throw new RuntimeException(message);
                }
            } catch (Exception e) {
                String message = "Plugin start error: "
                        + plugin.getClass().getName() + ". \n" + e.getMessage();
                log.error(message, e);
                throw new RuntimeException(message, e);
            }
        }
    }

activerecord实例化

// process ActiveRecordPlugin devMode
                if (plugin instanceof com.jfinal.plugin.activerecord.ActiveRecordPlugin) {
                    com.jfinal.plugin.activerecord.ActiveRecordPlugin arp = (com.jfinal.plugin.activerecord.ActiveRecordPlugin) plugin;
                    if (arp.getDevMode() == null)
                        arp.setDevMode(constants.getDevMode());
                }
      /** * 配置插件 */
    public void configPlugin(Plugins me) {
        // 配置C3p0数据库连接池插件
        C3p0Plugin c3p0Plugin = new C3p0Plugin(getProperty("jdbcUrl"),
                getProperty("user"), getProperty("password").trim());
        me.add(c3p0Plugin);

        // 配置ActiveRecord插件
        ActiveRecordPlugin arp = new ActiveRecordPlugin(c3p0Plugin);
        me.add(arp);
        arp.addMapping("blog", Blog.class); // 映射blog 表到 Blog模型
    }

上面两段分别来自startPlugins方法和demo中的DemoConfig的插件配置部分,欲观activerecord还需先看c3po插件的初始化

c3po实例化
public C3p0Plugin(String jdbcUrl, String user, String password) {
        this.jdbcUrl = jdbcUrl;
        this.user = user;
        this.password = password;
    }
activerecordplugin实例化
public ActiveRecordPlugin(IDataSourceProvider dataSourceProvider) {
        this(DbKit.MAIN_CONFIG_NAME, dataSourceProvider);
    }

由于activerecord支持多数据源,所以这个配置就是把当前的数据源配置为主数据源

c3po插件的初始化

由于插件初始化是按照添加顺序依次初始化,所以作为activerecordplugin的依赖插件,数据源插件先初始化是必然的

public boolean start() {
        // 实例化dataSource对象并为止填充必要的属性
        dataSource = new ComboPooledDataSource();
        dataSource.setJdbcUrl(jdbcUrl);
        dataSource.setUser(user);
        dataSource.setPassword(password);
        try {
            dataSource.setDriverClass(driverClass);
        } catch (PropertyVetoException e) {
            dataSource = null;
            System.err.println("C3p0Plugin start error");
            throw new RuntimeException(e);
        }
        dataSource.setMaxPoolSize(maxPoolSize);
        dataSource.setMinPoolSize(minPoolSize);
        dataSource.setInitialPoolSize(initialPoolSize);
        dataSource.setMaxIdleTime(maxIdleTime);
        dataSource.setAcquireIncrement(acquireIncrement);

        return true;
    }

没什么好说的,构建datasource,仅此而已

activerecord插件的初始化

public boolean start() {
        // 防止重复初始化
        if (isStarted)
            return true;
        // 从provider得到dataSource
        if (dataSourceProvider != null)
            dataSource = dataSourceProvider.getDataSource();
        if (dataSource == null)
            throw new RuntimeException("ActiveRecord start error: ActiveRecordPlugin need DataSource or DataSourceProvider");

        // 生成新的config对象或使用现有的config
        if (config == null)
            // Config内部默认初始化了一些值
            config = new Config(configName, dataSource, dialect, showSql, devMode, transactionLevel, containerFactory, cache);

        // 数据库工具箱添加当前配置
        DbKit.addConfig(config);

        // 重点来了,延后分析,实现了java世界与数据库世界的映射
        boolean succeed = TableBuilder.build(tableList, config);

        //tool box init
        if (succeed) {
            Db.init();
            isStarted = true;
        }

        return succeed;
    }
TableBuilder.build(tableList, config)解析
static boolean build(List<Table> tableList, Config config) {
        // 准备工作
        Table temp = null;
        Connection conn = null;
        try {
            // 获取数据库连接
            conn = config.dataSource.getConnection();

            // TableMapping 又一个单例类
            TableMapping tableMapping = TableMapping.me();

            // 此处tableList的值来自于为activeRecord添加的映射
            // 如 arp.addMapping("blog", Blog.class); // 映射blog 表到 Blog模型
            for (Table table : tableList) {
                temp = table;

                // 重点在此,延后分析
                doBuild(table, conn, config);

                // 将构建成功的table添加到tableMapping中
                tableMapping.putTable(table);

                // 暂时不明此DbKit作用,遇到了再分析
                DbKit.addModelToConfigMapping(table.getModelClass(), config);
            }

            // 至此,Java世界与数据库世界有了联系
            return true;
        } catch (Exception e) {
            if (temp != null)
                System.err.println("Can not create Table object, maybe the table " + temp.getName() + " is not exists.");
            throw new ActiveRecordException(e);
        }
        finally {
            config.close(conn);
        }
    }
doBuild(table, conn, config);解析
private static void doBuild(Table table, Connection conn, Config config)
            throws SQLException {
        // 准备工作
        table.setColumnTypeMap(config.containerFactory.getAttrsMap());

        if (table.getPrimaryKey() == null)
            // 如果主键没有被设置,则设置为默认主键 "ID"
            table.setPrimaryKey(config.dialect.getDefaultPrimaryKey());

        // 以demo为例 获取的sql为 "select * from `blog` where 1 = 2"
        // 此处确实高能,此语句被执行后ResultSet为空,但是会以此得到该表的所有列的名称和属性
        String sql = config.dialect.forTableBuilderDoBuild(table.getName());

        // 查询结果
        Statement stm = conn.createStatement();
        ResultSet rs = stm.executeQuery(sql);

        // rs.getMetaData() 获取此 ResultSet 对象的列的编号、类型和属性。
        //ResultSetMetaData 可用于获取关于 ResultSet 对象中列的类型和属性信息的对象
        ResultSetMetaData rsmd = rs.getMetaData();

        // 循环处理数据库类型和java类型的对应关系,并添加到数据库表对应的Table对象的columnType中
        for (int i = 1; i <= rsmd.getColumnCount(); i++) {
            String colName = rsmd.getColumnName(i);
            String colClassName = rsmd.getColumnClassName(i);
            if ("java.lang.String".equals(colClassName)) {
                // varchar, char, enum, set, text, tinytext, mediumtext,
                // longtext
                table.setColumnType(colName, java.lang.String.class);
            } else if ("java.lang.Integer".equals(colClassName)) {
                // int, integer, tinyint, smallint, mediumint
                table.setColumnType(colName, java.lang.Integer.class);
            } else if ("java.lang.Long".equals(colClassName)) {
                // bigint
                table.setColumnType(colName, java.lang.Long.class);
            }
            // else if ("java.util.Date".equals(colClassName)) { //
            // java.util.Data can not be returned
            // java.sql.Date, java.sql.Time, java.sql.Timestamp all extends
            // java.util.Data so getDate can return the three types data
            // result.addInfo(colName, java.util.Date.class);
            // }
            else if ("java.sql.Date".equals(colClassName)) {
                // date, year
                table.setColumnType(colName, java.sql.Date.class);
            } else if ("java.lang.Double".equals(colClassName)) {
                // real, double
                table.setColumnType(colName, java.lang.Double.class);
            } else if ("java.lang.Float".equals(colClassName)) {
                // float
                table.setColumnType(colName, java.lang.Float.class);
            } else if ("java.lang.Boolean".equals(colClassName)) {
                // bit
                table.setColumnType(colName, java.lang.Boolean.class);
            } else if ("java.sql.Time".equals(colClassName)) {
                // time
                table.setColumnType(colName, java.sql.Time.class);
            } else if ("java.sql.Timestamp".equals(colClassName)) {
                // timestamp, datetime
                table.setColumnType(colName, java.sql.Timestamp.class);
            } else if ("java.math.BigDecimal".equals(colClassName)) {
                // decimal, numeric
                table.setColumnType(colName, java.math.BigDecimal.class);
            } else if ("[B".equals(colClassName)) {
                // binary, varbinary, tinyblob, blob, mediumblob, longblob
                // qjd project: print_info.content varbinary(61800);
                table.setColumnType(colName, byte[].class);
            } else {
                int type = rsmd.getColumnType(i);
                if (type == Types.BLOB) {
                    table.setColumnType(colName, byte[].class);
                } else if (type == Types.CLOB || type == Types.NCLOB) {
                    table.setColumnType(colName, String.class);
                } else {
                    table.setColumnType(colName, String.class);
                }
                // core.TypeConverter
                // throw new
                // RuntimeException("You've got new type to mapping. Please add code in "
                // + TableBuilder.class.getName() +
                // ". The ColumnClassName can't be mapped: " + colClassName);
            }
        }

        rs.close();
        stm.close();
    }

ActiveRecord初始化小结

至此,activeRecord的初始化完成,将数据库中的表与具体的Model进行了映射,实现了精巧的orm

你可能感兴趣的:(java,javaweb,源码分析,jFinal,基础架构)