jfinal me.add 的c list add第一个个参数可以随便写吗

私信发送成功
Fork 该项目?
使用 fork 功能将在后台会为你创建一个与该项目内容一样的同名项目,你可以在这个新项目里自由的修改内容。
建议只在有意向参与改进该项目时使用 fork 功能。
JFinal整合Druid(监控平台),Redis(数据缓存),Quartz(任务定时调度)
Loading...
#JFinal后端开发指南
一、JFinal初识
1MVC架构,设计精巧,使用简单
2遵循COC原则,零配置,无xml
3独创Db + Record模式,灵活便利
4ActiveRecord支持,使数据库开发极致快速
5自动加载修改后的java文件,开发过程中无需重启web server
6AOP支持,拦截器配置灵活,功能强大
7Plugin体系结构,扩展性强
8多视图支持,支持FreeMarker、JSP、Velocity
9强大的Validator后端校验功能
10功能齐全,拥有struts2的绝大部分功能
11体积小仅218K,且无第三方依赖
二、Jfinal开发核心
1.web.xml:
&filter&
 &filter-name&jfinal&/filter-name&
 &filter-class&com.jfinal.core.JFinalFilter&/filter-class&
 &init-param&

&param-name&configClass&/param-name&

&param-value&com.demo.jfinal.config.CoreConfig&/param-value&
 &/init-param&
&/filter&

&filter-mapping&
 &filter-name&jfinal&/filter-name&
 &url-pattern&/*&/url-pattern&
&/filter-mapping&

2.CoreConfig
/** 常量配置 **/
@Override
public void configConstant(Constants me) {
 loadPropertyFile("system_config_info.txt");
 // 设置开发模式
 me.setDevMode(getPropertyToBoolean("devMode", true));
 me.setUrlParaSeparator(Const.SYMBOLAMPERSAND);
}

/** 处理器配置 接管所有web请求,可在此层做功能性的拓展 **/
@Override
public void configHandler(Handlers me) {
 DruidStatViewHandler dvh =
new DruidStatViewHandler("/druid");
 me.add(dvh);
 
}

/** 拦截器配置 类似与struts2拦截器,处理action **/
@Override
public void configInterceptor(Interceptors me) {

 me.add(new TxByMethods("find","update","delete","save")); //声明式事物管理
 
}

/** 插件配置 JFinal集成了很多插件:redis,druid,quartz... **/
@Override
public void configPlugin(Plugins me) {

 /** 数据库监控druid **/
 DruidPlugin dp = new DruidPlugin(getProperty("jdbcUrl"),

getProperty("user"), getProperty("password"));
 dp.addFilter(new StatFilter()); //sql监控
 dp.addFilter(new WallFilter()); //防止sql注入
 WallFilter wall = new WallFilter();
 wall.setDbType("mysql");
//mysql
 dp.addFilter(wall);
 me.add(dp);
 
 ActiveRecordPlugin arp = new ActiveRecordPlugin(dp);
 me.add(arp);
 
 //表映射关系
 arp.addMapping(BlogConst.BLOGTABLE, "id", Blog.class);
 
 /** redis缓存支持根据不同模块使用缓存,目前我创建一个关于blog的缓存块 **/
 RedisPlugin blogRedis = new RedisPlugin(BlogConst.BLOGTABLE,

"localhost");
 me.add(blogRedis);

 /** 定时任务插件 目前配置了一个每5秒钟执行一次的定时脚本**/
 QuartzPlugin quartzPlugin =
new QuartzPlugin("job.properies");
 me.add(quartzPlugin);

}

/** 路由配置:1.如下配置;2.写个类如:BlogRoute继承Routes,配置,me.add(new BlogRoute());也可 **/
/** 路径设置:1.第三个参数;2.可通过注解@ActionKey("/index")方式 **/
@Override
public void configRoute(Routes me) {

 me.add("/", IndexController.class, "/index");
 /** controller配置路径 **/
 me.add("/blog", BlogController.class);

}

/*插件关闭之前【方法可选择性使用】**/ 
public void beforeJFinalStop(){
 System.out.println("插件关闭");
};

/*插件加载完后【方法可选择性使用】**/
public void afterJFinalStart(){
 System.out.println("加载完毕");
};

三.Jfinal MVC思想

Model:

public class Blog extends Model {
private static final long serialVersionUID = -6235483L;

// 方便于访问数据库,不是必须
public static final Blog dao = new Blog();

/**
 * 分页查询数据,jfinal使用原生sql处理数据,会节省解析性能
 * @param pageNumber
 * @param pageSize
 * @return
 */
public Page&Blog& paginate(int pageNumber, int pageSize) {
 return paginate(pageNumber, pageSize, "select * ","from blog order by id asc");
}

public Blog getLastInsertBlog(){
 return findFirst("select * from blog order by id desc"); //降序
}

2.Controller:
/** 常量配置 **/
@Override
public void configConstant(Constants me) {
 loadPropertyFile("system_config_info.txt");
 // 设置开发模式
 me.setDevMode(getPropertyToBoolean("devMode", true));
 me.setUrlParaSeparator(Const.SYMBOLAMPERSAND);
}

/** 处理器配置 接管所有web请求,可在此层做功能性的拓展 **/
@Override
public void configHandler(Handlers me) {
 DruidStatViewHandler dvh =
new DruidStatViewHandler("/druid");
 me.add(dvh);
 
}

/** 拦截器配置 类似与struts2拦截器,处理action **/
@Override
public void configInterceptor(Interceptors me) {

 me.add(new TxByMethods("find","update","delete","save")); //声明式事物管理
 
}

/** 插件配置 JFinal集成了很多插件:redis,druid,quartz... **/
@Override
public void configPlugin(Plugins me) {

 /** 数据库监控druid **/
 DruidPlugin dp = new DruidPlugin(getProperty("jdbcUrl"),

getProperty("user"), getProperty("password"));
 dp.addFilter(new StatFilter()); //sql监控
 dp.addFilter(new WallFilter()); //防止sql注入
 WallFilter wall = new WallFilter();
 wall.setDbType("mysql");
//mysql
 dp.addFilter(wall);
 me.add(dp);
 
 ActiveRecordPlugin arp = new ActiveRecordPlugin(dp);
 me.add(arp);
 
 //表映射关系
 arp.addMapping(BlogConst.BLOGTABLE, "id", Blog.class);
 
 /** redis缓存支持根据不同模块使用缓存,目前我创建一个关于blog的缓存块 **/
 RedisPlugin blogRedis = new RedisPlugin(BlogConst.BLOGTABLE,

"localhost");
 me.add(blogRedis);

 /** 定时任务插件 目前配置了一个每5秒钟执行一次的定时脚本**/
 QuartzPlugin quartzPlugin =
new QuartzPlugin("job.properies");
 me.add(quartzPlugin);

}

/** 路由配置:1.如下配置;2.写个类如:BlogRoute继承Routes,配置,me.add(new BlogRoute());也可 **/
/** 路径设置:1.第三个参数;2.可通过注解@ActionKey("/index")方式 **/
@Override
public void configRoute(Routes me) {

 me.add("/", IndexController.class, "/index");
 /** controller配置路径 **/
 me.add("/blog", BlogController.class);

 }

 /*插件关闭之前【方法可选择性使用】**/ 
 public void beforeJFinalStop(){

System.out.println("插件关闭");
 };

 /*插件加载完后【方法可选择性使用】**/
 public void afterJFinalStart(){

System.out.println("加载完毕");
 };

3.View:
Jfinal支持FreeMarker、JSP、Velocity
四.Jfinal 组合插件:
1.Druid:
配置插件:
/** 数据库监控druid **/

DruidPlugin dp = new DruidPlugin(getProperty("jdbcUrl"),

getProperty("user"), getProperty("password"));

dp.addFilter(new StatFilter()); //sql监控

dp.addFilter(new WallFilter()); //防止sql注入

WallFilter wall = new WallFilter();

wall.setDbType("mysql");
//mysql

dp.addFilter(wall);

me.add(dp);

配置Handler:
DruidStatViewHandler dvh =
new DruidStatViewHandler("/druid");

me.add(dvh);

访问路径:

localhost:8080/Jfinal/druid/index.html

2.Redis:
配置插件:
 /** redis缓存支持根据不同模块使用缓存,目前我创建一个关于blog的缓存块 **/
 RedisPlugin blogRedis = new RedisPlugin(BlogConst.BLOGTABLE,"localhost");
 me.add(blogRedis);

Redis非web环境下测试:
 public static void main(String[] args) {
 
 RedisPlugin redisPlugin = new RedisPlugin("test", "localhost");
 redisPlugin.start();
 
 Redis.use("test").set("testDemo", "tdd");
 System.out.println(Redis.use("test").get("testDemo"));
 }

3.Quartz:
配置插件:
 QuartzPlugin quartzPlugin =
new QuartzPlugin("job.properies");
 me.add(quartzPlugin);

配置job.properies:
a.job=com.demo.jfinal.job.JobDemo
a.cron=*/5 * * * * ?
a.enable=true
JobDemo类:
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
 System.out.println("JobDemo开始执行啦" + System.currentTimeMillis());
}

4.log4j
log4j.properties:
log4j.rootLogger=INFO,appender1,appender2

log4j.appender.appender1=org.apache.log4j.ConsoleAppender

log4j.appender.appender1.layout=org.apache.log4j.PatternLayout

log4j.appender.appender1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss:SSS}[%p]: %m%n

log4j.appender.appender2=org.apache.log4j.jdbc.JDBCAppender

log4j.appender.appender2.driver=com.mysql.jdbc.Driver

log4j.appender.appender2.URL=jdbc:mysql://localhost:3306/jfinal_demo?useUnicode=true&characterEncoding=UTF-8

log4j.appender.appender2.user=root

log4j.appender.appender2.password=root

log4j.appender.appender2.sql=insert into log (create_time,log) VALUES ('%d{yyyy-MM-dd hh:mm:ss}', '%c %p %m %n')

log4j.appender.appender2.layout=org.apache.log4j.PatternLayout

Job表:
Create Table: CREATE TABLE `log` (


`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,


`create_time` datetime NOT NULL,


`log` varchar(200) NOT NULL,


PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=168 DEFAULT CHARSET=utf8

应用:
 public void find() {
 
 //先从缓存里面找数据
 Blog cacheBlog = BlogRedis.getBlogFromCache(getParaToInt(BlogConst.ID));

 if(!BeanUtil.isBeanEmpty(cacheBlog)){

("查找成功");

renderJson(ControllerCommon.ctrCommon.returnJsonToClient(cacheBlog));
 }else {

//缓存没有数据,从DB读

Blog blog = BlogService.blogService.findBlogById(getParaToInt(BlogConst.ID));

if (BeanUtil.isBeanEmpty(blog)) {

logger.error("查找失败");

ControllerCommon.errorMsg("数据为空");

("查找成功");

renderJson(ControllerCommon.ctrCommon.returnJsonToClient(blog));
 }
}

5..JFinal单元测试:
JFinalModelCase:
protected static DruidP
protected static ActiveRecordPlugin activeR

/**
 * 数据连接地址
 */
private static final String URL = "jdbc:mysql://127.0.0.1/jfinal_demo?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull";
private static final String USERNAME = "root";
private static final String PASSWORD = "root";
private static final String DRIVER = "com.mysql.jdbc.Driver";
private static final String DATABASE_TYPE = "mysql";

/**
 * @throws java.lang.Exception
 */
@BeforeClass
public static void setUpBeforeClass() throws Exception {
 dp = new DruidPlugin(URL, USERNAME, PASSWORD, DRIVER);
 dp.addFilter(new StatFilter());
 dp.setInitialSize(3);
 dp.setMinIdle(2);
 dp.setMaxActive(5);
 dp.setMaxWait(60000);
 dp.setTimeBetweenEvictionRunsMillis(120000);
 dp.setMinEvictableIdleTimeMillis(120000);

 WallFilter wall = new WallFilter();
 wall.setDbType(DATABASE_TYPE);
 dp.addFilter(wall);

 dp.getDataSource();
 dp.start();

 activeRecord = new ActiveRecordPlugin(dp);
 activeRecord.setDialect(new MysqlDialect()).setDevMode(true)

.setShowSql(true); // 是否打印sql语句

 // 映射数据库的表和继承与model的实体
 // 只有做完该映射后,才能进行junit测试
 activeRecord.addMapping(BlogConst.BLOGTABLE, Blog.class); // 测试其他Model可以继续添加配置
 activeRecord.start();
}

/**
 * @throws java.lang.Exception
 */
@After
public void tearDown() throws Exception {
 activeRecord.stop();
 dp.stop();
}

编写测试类(需继承JFinalModelCase)
@Test
public void testFind() {

 Blog blog = BlogService.blogService.findBlogById(1);
 System.out.println(blog);
}

@Test
public void testJson() {

 // HashMap&String, String& map = ControllerCommon.errorMsg("失败");
 // String string = JsonKit.toJson(map);

 String columns = BlogConst.CONTENT + Const.SYMBOLCOMMA

+ BlogConst.TITLE;
 Blog blog = Blog.dao.findById(1, columns);

 ControllerCommon.errorMsg("执行");

 JFinalTestUtil.print(JsonKit.toJson(ControllerCommon.ctrCommon

.returnJsonToClient(blog)));

}

@Test
public void testFindFirst() {

 Blog blog = Blog.dao.getLastInsertBlog();
 JFinalTestUtil.print(String.valueOf(blog.getInt("id")));
 
}

五、开发实例
index:

{
"result": {


"pageSize": 10,


"pageNumber": 1,


"list": [


{


"content": "JFinal Demo Content here",


"id": 1,


"title": "JFinal Demo Title here"


},


{


"content": "test 1",


"id": 2,


"title": "test 1"


},


{


"content": "test 2",


"id": 3,


"title": "test 2"


},


{


"content": "test 3",


"id": 4,


"title": "test 3"


},


{


"content": "test 4",


"id": 5,


"title": "test 4"


}


],


"totalRow": 5,


"totalPage": 1

},

"status": "200000",

"msg": "成功"

}
find:

{
"result": {


"content": "test 1",


"title": "test 1"

},

"status": "200000",

"msg": "成功"

}
delete:
http://localhost:8080/Jfinal/blog/delete?id=1

{
"result": true,

"status": "200000",

"msg": "成功"

}
save:
http://localhost:8080/Jfinal/blog/save?title=1&content=1

{
"result": "",

"status": "200000",

"msg": "成功"

}
update:

{
"result": true,

"status": "200000",

"msg": "成功"

}
六、Jfinal扩展


本demo中我建立了blog模块,其他模块建议以下架构风格:一个模块一个service,一个模块一个Redis,一个模块多个Model,一
个模块一个Controller


项目扩展:建议抽象出BaseModel,所有的model只需要集成它,减少代码的冗余


日志:实际开发过程中会使用到更多可用字段:userId,iP等;建议抽象log代码块,或者在filter中做相应处理


为了极速开发,中小型项目,可以不使用Service层,而且业务全部放入Model,称之为充血领域模型


正在加载...OSC上关于Jfinal的提问整理&二&
1.【问】:Db.tx(new IAtom())事务不起作用?
【jfinal答】:1:如果使用的mysql,确保引擎为 InnoDB
2:这行代码改一下Db.save(c3p0Plugin.getDataSource(), "tb_test", "PK_ID",
record);去掉 c3p0Plugin.getDataSource()这个参数。
2.【问】:JFinal使用el标签取元素属性报 does not have the property 错误。
我在Controller中往session中放了一个User对象,jsp页面el标签取对象属性的时候报错:
User' does not have the property 'nickName'.
是什么问题呢?
P.S1.Config中已经进行模型映射
arp.addMapping("user", User.class);
2.表中也有nickName这个字段
3.jsp页面表达式${sessionScope.curUserObj.nickName}
4.在页面 ${sessionScope.curUserObj}能输出user对象,并且也有nickName属性:
bean.User@4a952b12 {id:1, status:null, nickName:Romotc, email:, pswd:4e6327, lastLoginTime:, registerTime:2:56.0}
【jfinal答】:
JspRender对HttpServletRequest对象中的数据进行了处理,数据全被放入了Map之中,如果希望不被处理,可以通过在YourJFinalConfig中调用一下:JspRender.setSupportActiveRecord(false)。
由于JFinal为了Resful,session中的数据默认是不处理的,所以session中的数据无法通过${blog.title}获取到。这个问题本质上是JSTL数据读取能力太弱造成的,建议使用FreeMarker取代JSP
3.【问】:当from加上属性enctype="multipart/form-data"获取不到表单的参数,除掉没问题。
【jfinal答】:如果请求类型为"multipart/form-data",必须先调用任意一个 getFile 方法,因为 multipart 请求的解析是在 getFile 方法中完成的。调用完后就可以通过 getPara 方法来获取值了。
4.【问】:使用getPara获取值的时候的乱码问题如何解决?是在内置的jetty环境下。
【jfinal答】:getPara 获取值取到是乱码的问题可能与字符集设置有关,JFinal默认字符集为utf-8,可以通过 me.setEncoding(String)设置成别的字符集,注意页面字符集与jfinal字符集设置一致
5.【问】:在JFinal里,请问UrlPara与Parameter有什么区别?
【jfinal答】: urlPara 是指没有名字只有位置,并且是在url存在的参数。而常规 para 都有个名字,如表单中通过input设置的,以及在url中通过问号挂参设置的。
6.【问】jfinal怎么使用druid的监控呀!(集成jfinal)
【jfinal答】:JFinal 1.1.3 已经集成了,您无需在 web.xml 中添加配置就可以使用,要使用这个功能,只需分两步:
1:在 configPlugin中添加DruidPlugin
public void configPlugin(Plugins me) {
// DruidPlugin
DruidPlugin dp = new DruidPlugin(getProperty("jdbcUrl"), getProperty("user"), getProperty("password"));
dp.addFilter(new StatFilter());
WallFilter wall = new WallFilter();
wall.setDbType("mysql");
dp.addFilter(wall);
me.add(dp);
// ActiveRecordPlugin
ActiveRecordPlugin arp = new ActiveRecordPlugin(dp);
me.add(arp);
2:在configHander 中添加DruidHandler
public void configHandler(Handlers me) {
DruidStatViewHandler dvh =
new DruidStatViewHandler("/druid");
me.add(dvh);
/question/99
7.【问】:如何很好的设计jfinal的controller层的类?
【jfinal答】:JFinal 建议的项目结构:
1:总体划分规则:先分模块,然后在模块中分层。
2:模块划分:中小型项目,每个领域模型划分为一个模块,如 jfinal demo 给出的 blog就是一个模块。大型项目可以在模块下面划分子模块。
3:层次划分:中小型项目可以在模块下面直接以类文件命名来约定层次,如Controller层为BlogController,Model层为Blog,业务层为BlogService。大型项目可以为层分配子包,如在模块下面创建service、controller、model包,然后在包中再创建该层次的相关类文件。
4:为了极速开发,中小型项目,可以不使用Service层,而且业务全部放入Model,称之为充血领域模型。
OSC上关于Jfinal的提问整理(一)
Copyright (C) , All Rights Reserved.
版权所有 闽ICP备号
processed in 0.033 (s). 10 q(s)JFinal 源码超详细解析之DB+ActiveRecord-java-火龙果软件工程
您可以捐助,支持我们的公益事业。
每天15篇文章
不仅获得谋生技能
更可以追随信仰
源码超详细解析之DB+ActiveRecord
作者 李林锋,火龙果软件&&&
10141 次浏览
&&&&评价:
我记得以前有人跟我说,“面试的时候要看spring的源码,要看ioc、aop的源码&那为什么要看这些开源框架的源码呢,其实很多人都是&应急式&的去读,就像读一篇文章一下,用最快的速度把文章从头到尾读一遍,那结果就是当你读完它,你也不清楚它讲了一个什么故事,想表达什么。
一个优秀的架构的源码我认为就好像一本名著一样,你的“文学”水平越高,你就越能读出作者设计的精妙之处。一篇源码在你不同水平的时候,能读出不同的东西,因此,我觉得优秀的框架的源码是经久不衰的,反复读多少次都不嫌多,直到你能设计出预期并驾齐驱甚至超越它的优美的架构。
读源码起初是一件很痛苦的事儿,想赶紧把它像流水账一样的读完;慢慢实力增强后,会感觉到读源码能够不费力气的读通;再假以时日,就能看出这些精妙的设计模式的组合。我有一个朋友,典型的源码痴狂症,他跟我说他第一次看见spring的源码,感觉特别兴奋,读了一宿没睡觉.......好吧,我还有很长的路需要走~
话说多了,我们赶紧入正题:
JFinal的框架我24号的一篇博文写到过,它优秀的地方在精简代码上,那么有两处源码是我觉得是值得我们要好好解析一下,一处是初始化加载―servlet跳转,另一处是DB+ActiveRecord的映射。
那么DB映射相对比较简单,我们这次就先来看看。
首先我们看看代码,还是之前我写过的 dog与cat的故事。
// 采用DB+ActiveRecord模式
ActiveRecordPlugin arp = new ActiveRecordPlugin(c3p0Plugin);
me.add(arp);
// 进行DB映射
arp.addMapping(&animal&, AnimalModel.class);
这三行代码就是加载DB映射的关键,那么我们复习一下,JFinal的DB映射无需配置文件,无需与DB对应的POJO,只需要写一个类,继承Model&M
extends Model&即可。
第一步:为ActiveRecordPlugin的 private IDataSourceProvider
dataSourceProvider 赋值。
那么我们先来看看ActiveRecordPlugin的构造器。
public ActiveRecordPlugin(IDataSourceProvider dataSourceProvider) {
this(DbKit.MAIN_CONFIG_NAME, dataSourceProvider);
这里重要的是dataSourceProvider,IDataSourceProvider是一个接口,它的运行时类型是
public class C3p0Plugin implements IPlugin, IDataSourceProvider{...}
那么,可以看到
this(DbKit.MAIN_CONFIG_NAME, dataSourceProvider);
这段代码又继续读取另一个重载的构造器,然后调用了
public ActiveRecordPlugin(String configName, IDataSourceProvider dataSourceProvider, int transactionLevel) {
if (StrKit.isBlank(configName))
throw new IllegalArgumentException(&configName can not be blank&);
if (dataSourceProvider == null)
throw new IllegalArgumentException(&dataSourceProvider can not be null&);
this.configName = configName.trim();
this.dataSourceProvider = dataSourceP
this.setTransactionLevel(transactionLevel);
最重要的就是这行代码: this.dataSourceProvider
= dataSourceP
这时,ActiveRecordPlugin的static变量的dataSourceProvider就已经被赋为C3p0Plugin的实例了。
第二步:定义映射用POJO
public class AnimalModel extends Model&AnimalModel& {...}
这里Model的源码我们一会再看,现在不着急。
然后进行映射
// 进行DB映射
arp.addMapping(&animal&, AnimalModel.class);
这里我们又回到了ActiveRecordPlugin类里,它实际上有两个addMapping方法,只是参数不同。
public ActiveRecordPlugin addMapping(String tableName, String primaryKey, Class&? extends Model&?&& modelClass) {
tableList.add(new Table(tableName, primaryKey, modelClass));
public ActiveRecordPlugin addMapping(String tableName, Class&? extends Model&?&& modelClass) {
tableList.add(new Table(tableName, modelClass));
我们看到,第一个方法多了一个参数 String primaryKey,我的代码里用的是第二个方法。这两个方法实际上都调用了tableList.add(Table
tbl)方法,我们看看tableList是什么
private List&Table& tableList = new ArrayList&Table&();
它是ActiveRecordPlugin的一个成员变量,并且是private的,那我们可以猜到,tableList保存了所有的映射关系。(ActiveRecordPlugin真是强大,后面会越来越强大~)。
第三步:创建映射关系
new Table(tableName, primaryKey, modelClass)
new Table(tableName, modelClass)
我们进去看看
public Table(String name, Class&? extends Model&?&& modelClass) {
if (StrKit.isBlank(name))
throw new IllegalArgumentException(&Table name can not be blank.&);
if (modelClass == null)
throw new IllegalArgumentException(&Model class can not be null.&);
this.name = name.trim();
this.modelClass = modelC
public Table(String name, String primaryKey, Class&? extends Model&?&& modelClass) {
if (StrKit.isBlank(name))
throw new IllegalArgumentException(&Table name can not be blank.&);
if (StrKit.isBlank(primaryKey))
throw new IllegalArgumentException(&Primary key can not be blank.&);
if (modelClass == null)
throw new IllegalArgumentException(&Model class can not be null.&);
this.name = name.trim();
setPrimaryKey(primaryKey.trim());
// this.primaryKey = primaryKey.trim();
this.modelClass = modelC
这两个方法都是为Table里的成员变量赋值,第二个方法,也就是带primaryKey参数的那个多出一行,我们看看这一行干了什么
setPrimaryKey(primaryKey.trim());
// this.primaryKey = primaryKey.trim();
void setPrimaryKey(String primaryKey) {
String[] keyArr = primaryKey.split(&,&);
if (keyArr.length & 1) {
if (StrKit.isBlank(keyArr[0]) || StrKit.isBlank(keyArr[1]))
throw new IllegalArgumentException(&The composite primary key can not be blank.&);
this.primaryKey = keyArr[0].trim();
this.secondaryKey = keyArr[1].trim();
this.primaryKey = primaryK
这样的作用就是为Table下的primaryKey 和 secondaryKey赋值。
第四步:加载ActiveRecordPlugin
那么代码好像跟到这里就完事了,怎么回事?是不是跟丢了?
别忘了,ActiveRecordPlugin是在FinalConfig里的configPlugin方法加载的。那么又有谁来加载FinalConfig呢?
PS:(FinalConfig是我自己定义的类)
public class FinalConfig extends JFinalConfig
这儿涉及到初始化的加载了,我简单的讲一下。
整个JFinal的入口是web.xml的一段配置:
&filter-name&jfinal&/filter-name&
&filter-class&com.jfinal.core.JFinalFilter&/filter-class&
&init-param&
&param-name&configClass&/param-name&
&param-value&com.demo.config.FinalConfig&/param-value&
&/init-param&
接着我们看到了关键的累 JFinalFilter,还是点进去看看。
public final class JFinalFilter implements Filter
这个类实现了Filter接口,那就得实现方法init(),doFilter(),destroy()方法。
我们去看init()方法:
public void init(FilterConfig filterConfig) throws ServletException {
createJFinalConfig(filterConfig.getInitParameter(&configClass&));
if (jfinal.init(jfinalConfig, filterConfig.getServletContext()) == false)
throw new RuntimeException(&JFinal init error!&);
handler = jfinal.getHandler();
constants = Config.getConstants();
encoding = constants.getEncoding();
jfinalConfig.afterJFinalStart();
String contextPath = filterConfig.getServletContext().getContextPath();
contextPathLength = (contextPath == null || &/&.equals(contextPath) ? 0 : contextPath.length());
绕过其他的加载,直接看这行
if (jfinal.init(jfinalConfig, filterConfig.getServletContext()) == false)
我们看看jfinal的类型是 private static final
JFinal jfinal = JFinal.me();
那么我们去JFinal类里看看它的init方法。
boolean init(JFinalConfig jfinalConfig, ServletContext servletContext) {
this.servletContext = servletC
this.contextPath = servletContext.getContextPath();
initPathUtil();
Config.configJFinal(jfinalConfig);
// start plugin and init logger factory in this method
constants = Config.getConstants();
initActionMapping();
initHandler();
initRender();
initOreillyCos();
initI18n();
initTokenManager();
看这行,下面这行主要是通过Config来加载暴露给程序员的核心文件,JFinalConfig的子类FinalConfig。
Config.configJFinal(jfinalConfig);
// start plugin and init logger factory in this method
* Config order: constant, route, plugin, interceptor, handler */
tatic void configJFinal(JFinalConfig jfinalConfig) {
jfinalConfig.configConstant(constants);
initLoggerFactory();
jfinalConfig.configRoute(routes);
jfinalConfig.configPlugin(plugins);
startPlugins(); // very important!!!
jfinalConfig.configInterceptor(interceptors);
jfinalConfig.configHandler(handlers);
这段代码实际上有个地方特别坑!就是
jfinalConfig.configPlugin(plugins);
startPlugins(); // very important!!!
这行代码一共做了两件事,第一件事是jfinalConfig.configPlugin(plugins);来加载插件。还记得我们之前写的FinalConfig里的configPlugin(Plugins
me) 方法吗?
* Config plugin
* 配置插件
* JFinal有自己独创的 DB + ActiveRecord模式
* 此处需要导入ActiveRecord插件
public void configPlugin(Plugins me) {
// 读取db配置文件
loadPropertyFile(&db.properties&);
// 采用c3p0数据源
C3p0Plugin c3p0Plugin = new C3p0Plugin(getProperty(&jdbcUrl&),getProperty(&user&), getProperty(&password&));
me.add(c3p0Plugin);
// 采用DB+ActiveRecord模式
ActiveRecordPlugin arp = new ActiveRecordPlugin(c3p0Plugin);
me.add(arp);
// 进行DB映射
arp.addMapping(&animal&, AnimalModel.class);
它实际上就是通过me.add来加载插件,通过Config的 private
static final Plugins plugins = new Plugins(); 来装载。
第二件事就是 发现没有,后面的startPlugins()不是注释!是一个方法,这块实在太坑了,恰巧,这就是我们要找到的地方。
这个方法的代码有点长,但因为很重要,我不得不都贴出来。
private static void startPlugins() {
List&IPlugin& pluginList = plugins.getPluginList();
if (pluginList != null) {
for (IPlugin plugin : pluginList) {
// process ActiveRecordPlugin devMode
if (plugin instanceof com.jfinal.plugin.activerecord.ActiveRecordPlugin) {
com.jfinal.plugin.activerecord.ActiveRecordPlugin arp =
(com.jfinal.plugin.activerecord.ActiveRecordPlugin)
if (arp.getDevMode() == null)
arp.setDevMode(constants.getDevMode());
boolean success = plugin.start();
if (!success) {
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);
上面这个方法一共有两个地方要注意一下,
for (IPlugin plugin : pluginList) {
上面这行是循环所有的插件,并且启动插件的start()方法。
那么,我们中有一个插件记不记得是ActiveRecordPlugin的实例?那么
boolean success = plugin.start();
这行代码就会执行ActiveRecordPlugin下的start()代码。终于绕回来了!!红军二万五千里长征,为了证明这个调用,我写了多少字....
那么我们看ActiveRecordPlugin下的start()方法吧,实际上这个start()方法是因为实现了IPlugin接口里的start()方法。
public boolean start() {
if (isStarted)
if (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, dialect,
showSql, devMode, transactionLevel, containerFactory, cache);
DbKit.addConfig(config);
boolean succeed = TableBuilder.build(tableList, config);
if (succeed) {
Db.init();
isStarted =
我们直接看与DB映射有关的代码,首先是取得dataSource,dataSourceProvider这个忘了没,忘了就翻到最前面,第一步讲的。
config = new Config(configName, dataSource, dialect, showSql, devMode, transactionLevel, containerFactory, cache);
这行代码中的dataSource 在插件里配置的C3P0数据源。这里的Config与前面加载FinalConfig的可不是一个啊,千万别看错了,这个是DB的
com.jfinal.plugin.activerecord.Config。
第五步:TableBuilder
来自ActiveRecordPlugin.java
boolean succeed = TableBuilder.build(tableList, config);
static boolean build(List&Table& tableList, Config config) {
Table temp =
Connection conn =
conn = config.dataSource.getConnection();
TableMapping tableMapping = TableMapping.me();
for (Table table : tableList) {
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);
config.close(conn);
这里循环所有的tableList,对每个Table对象进行建表。那么我们先看看Table是用什么来存储数据库映射关系的,相信大家都能猜到是Map了。
public class Table {
private String primaryK
private String secondaryKey =
private Map&String, Class&?&& columnTypeM
// config.containerFactory.getAttrsMap();
private Class&? extends Model&?&& modelC
columnTypeMap是关键字段,暂且记下来。
下面我们还是回到TableBuilder里的doBuild(table,
conn, config);方法。
这个才是DB映射的关键,我其实直接讲这一个类就可以的......这个方法代码实在太多了,我贴部分代码做讲解吧。
那么第六步:doBuild详解。
这块有点类,我直接在代码里写注释吧:
@SuppressWarnings(&unchecked&)
private static void doBuild(Table table, Connection conn, Config config) throws SQLException {
// 初始化 Table 里的columnTypeMap字段。
table.setColumnTypeMap(config.containerFactory.getAttrsMap());
// 取得主键,如果取不到的话,默认设置&id&。
// 记不记得最开始的两个同名不同参的方法 addMapping(...),
在这才体现出后续处理的不同。
if (table.getPrimaryKey() == null)
table.setPrimaryKey(config.dialect.getDefaultPrimaryKey());
// 此处如果没有设置方言,则默认
Dialect dialect = new MysqlDialect(); Mysql的方言。
// sql为&select * from `& + tableName + &` where 1 = 2&;
String sql = config.dialect.forTableBuilderDoBuild(table.getName());
Statement stm = conn.createStatement();
ResultSet rs = stm.executeQuery(sql);
//取得个字段的信息
ResultSetMetaData rsmd = rs.getMetaData();
// 匹配映射
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, String.class);
else if (&java.lang.Integer&.equals(colClassName)) {
// int, integer, tinyint, smallint, mediumint
table.setColumnType(colName, Integer.class);
else if (&java.lang.Long&.equals(colClassName)) {
table.setColumnType(colName, 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, Double.class);
else if (&java.lang.Float&.equals(colClassName)) {
table.setColumnType(colName, Float.class);
else if (&java.lang.Boolean&.equals(colClassName)) {
table.setColumnType(colName, Boolean.class);
else if (&java.sql.Time&.equals(colClassName)) {
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);
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);
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();
这里巧妙的运用了 where 1=2的无检索条件结果,通过ResultSetMetaData
rsmd = rs.getMetaData(); 导出了DB模型,这招确实漂亮。之前我还冥思苦相,他是怎么做的呢,看着此处源码,茅塞顿开。
接着,把编辑好的Table实例,放到TableMapping的成员变量
Model&?&&, Table& modelToTableMap 里去,TableMapping是单例的。
private final Map&Class&? extends Model&?&&, Table& modelToTableMap=
new HashMap&Class&? extends Model&?&&, Table&();
public void putTable(Table table) {
modelToTableMap.put(table.getModelClass(), table);
这样,所有的映射关系就都存在TableMapping的modelToTableMap
tableMapping.putTable(table);
再将modelToConfig都放入DbKit.modelToConfig里。
DbKit.addModelToConfigMapping(table.getModelClass(), config);
第七步,使用
Model里的save方法举例:
* Save model.
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)
// The sql &insert into tableName() values()& works fine, so delete this line
// --------
Connection conn =
PreparedStatement pst =
int result = 0;
conn = config.getConnection();
if (config.dialect.isOracle())
pst = conn.prepareStatement(sql.toString(),
new String[]{table.getPrimaryKey()});
pst = conn.prepareStatement(sql.toString(),
Statement.RETURN_GENERATED_KEYS);
config.dialect.fillStatement(pst, paras);
result = pst.executeUpdate();
getGeneratedKey(pst, table);
getModifyFlag().clear();
return result &= 1;
} catch (Exception e) {
throw new ActiveRecordException(e);
} finally {
config.close(pst, conn);
Config config = getConfig();
上面这行就是调用DbKit的方法,取得DB配置。
public static Config getConfig(Class&? extends Model& modelClass) {
return modelToConfig.get(modelClass);
下面这段代码是去单例的TableMapping里取得表的具体信息。
Table table = getTable();
private Table getTable() {
return TableMapping.me().getTable(getClass());
以上,就是DB+ActiveRecord的核心调用流程,下次我会带来初始化流程,不过这是个大活,估计要分开三章来写吧。
更多课程...&&&
每天2个文档/视频
扫描微信二维码订阅
订阅技术月刊
获得每月300个技术资源
|&京ICP备号&京公海网安备号

我要回帖

更多关于 list add第一个 的文章

 

随机推荐