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);
}
}
}
// 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插件的初始化
public C3p0Plugin(String jdbcUrl, String user, String password) {
this.jdbcUrl = jdbcUrl;
this.user = user;
this.password = password;
}
public ActiveRecordPlugin(IDataSourceProvider dataSourceProvider) {
this(DbKit.MAIN_CONFIG_NAME, dataSourceProvider);
}
由于activerecord支持多数据源,所以这个配置就是把当前的数据源配置为主数据源
由于插件初始化是按照添加顺序依次初始化,所以作为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,仅此而已
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;
}
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);
}
}
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的初始化完成,将数据库中的表与具体的Model进行了映射,实现了精巧的orm