在项目中,我们会需要使用到数据库,那么JFinal中是如何使用数据库的呢?
要使用数据库,我们首先需要建立数据库连接,JFinal给我们提供了两种现成的JDBC连接池,分别是C3p0 和 Druid,对应的相应的类是 C3p0Plugin 和 DruidPlugin,这两个类都实现了IDataSourceProvider接口,接口中定义了 getDataSource() 获取数据源的方法。
那么JFinal是如何通过连接池类,来操作数据库的呢?
这里,就要引入插件的概念–Plugin。
在JFinal中,插件会继承IPlugin接口,其中有 start() 和 stop() 两个方法。C3p0Plugin 和 DruidPlugin 也都实现了这个接口。具体如下:
其中,ActiveRecordPlugin就是进行数据库映射、配置的类。
下面,是操作数据库的流程,这里以保存用户为例:
1.首先建立数据库
CREATE DATABASE IF NOT EXISTS `db_jfinal`
USE `db_jfinal`;
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
2.创建Model
public class User extends Model<User>{
public static final User dao = new User();
}
3.在配置类中添加代码:
@Override
public void configPlugin(Plugins me) {
C3p0Plugin cp = new C3p0Plugin("jdbc:mysql://localhost:3306/db_jfinal",
"root", "admin");
me.add(cp);
ActiveRecordPlugin arp = new ActiveRecordPlugin(cp);
me.add(arp);
arp.addMapping("t_user", User.class);
}
4.在Controller中执行保存用户操作
new User().set("name", "zorro").save();
在Jfinal中操作数据库就如上面这么简单,接下来,我们来看下具体是如何实现数据库保存的:
1.首先,在过滤器 JFinalFilter中的init方法中,静态变量jfinal会执行init方法,在这个方法中,会将配置类中的设置的插件取出,并启动。
com.jfinal.core.Config类中获取并启动插件:
static void configJFinal(JFinalConfig jfinalConfig) {
jfinalConfig.configConstant(constants); initLogFactory();
jfinalConfig.configRoute(routes);
jfinalConfig.configPlugin(plugins); startPlugins(); // very important!!!
jfinalConfig.configInterceptor(interceptors);
jfinalConfig.configHandler(handlers);
}
2.由于在配置类中配置了两个插件,一个是C3p0Plugin,一个是ActiveRecordPlugin。
我们先看下C3p0Plugin的start方法:
public boolean start() {
if (isStarted)
return true;
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);
isStarted = true;
return true;
}
看代码知道,C3p0的启动,主要是实例化了一个数据源 dataSource,并给数据源设置了一些属性。
3.在看ActiveRecordPlugin,由于我们在配置类中实例化ActiveRecordPlugin的时候,将C3p0Plugin的实例传给了它,相当于ActiveRecordPlugin持有了数据源。
4.ActiveRecordPlugin的start() 方法如下:
public boolean start() {
if (isStarted) {
return true;
}
if (configName == null) {
configName = DbKit.MAIN_CONFIG_NAME;
}
if (dataSource == null && dataSourceProvider != null) {
dataSource = dataSourceProvider.getDataSource();
}
if (dataSource == null) {
throw new RuntimeException("ActiveRecord start error: ActiveRecordPlugin need DataSource or DataSourceProvider");
}
if (config == null) {
config = new Config(configName, dataSource);
}
if (dialect != null) {
config.dialect = dialect;
}
if (showSql != null) {
config.showSql = showSql;
}
if (devMode != null) {
config.devMode = devMode;
}
if (transactionLevel != null) {
config.transactionLevel = transactionLevel;
}
if (containerFactory != null) {
config.containerFactory = containerFactory;
}
if (cache != null) {
config.cache = cache;
}
new TableBuilder().build(tableList, config);
DbKit.addConfig(config);
Db.init();
isStarted = true;
return true;
}
这里会实例化一个com.jfinal.plugin.activerecord.Config类,该类将持有数据源dataSource,之后DbKit.addConfig(config),会将config实例赋值给DbKit类下的静态属性,这使用了单例模式,以后再要使用数据源来获取连接,就不需要再实例化了,只要DbKit.getConfig() 就可以。
再看上面的
new TableBuilder().build(tableList, config);
这个操作是实现数据库的映射,通过查询各个表,将表中各字段记录到Table类中。并将table 保存到TableMapping中。
void build(List<Table> tableList, Config config) {
if (tableList.size() == 0) {
return ;
}
Table temp = null;
Connection conn = null;
try {
conn = config.dataSource.getConnection();
TableMapping tableMapping = TableMapping.me();
for (Table table : tableList) {
temp = table;
doBuild(table, conn, config);
tableMapping.putTable(table);
DbKit.addModelToConfigMapping(table.getModelClass(), config);
}
} 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);
}
}
5.最后是保存数据。
实体类User 继承了com.jfinal.plugin.activerecord.Model类,这个Model类中,就包含的对数据库操作方法。
Model中有属性Map
public boolean save() {
Config config = getConfig();
Table table = getTable();
StringBuilder sql = new StringBuilder();
List<Object> paras = new ArrayList<Object>();
config.dialect.forModelSave(table, attrs, sql, paras);
// if (paras.size() == 0) return false; // The sql "insert into tableName() values()" works fine, so delete this line
// --------
Connection conn = null;
PreparedStatement pst = null;
int result = 0;
try {
conn = config.getConnection();
if (config.dialect.isOracle())
pst = conn.prepareStatement(sql.toString(), table.getPrimaryKey());
else
pst = conn.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);
config.dialect.fillStatement(pst, paras);
result = pst.executeUpdate();
getGeneratedKey(pst, table, config);
getModifyFlag().clear();
return result >= 1;
} catch (Exception e) {
throw new ActiveRecordException(e);
} finally {
config.close(pst, conn);
}
}
仔细看下,其实就是普通的JDBC操作。至此,整个操作数据库的流程就完成了。