JFinal是基于Java的极速WEB+ORM框架,其核心设计目标是开发迅速、代码量少、学习简单、功能强大、轻量级、易扩展、Restful。
官网:http://www.jfinal.com/
特点:轻量级开发框架 类似springboot,mvc架构,零配置 无xml,Db+Record模式,ActiveRecord支持,AOP支持,Enjoy 模板引擎
第一步:在pom.xml文件中配置jfinal的相关依赖
第二步:创建MyConfig配置类(启动类,需要继承JfinalConfig)
第三步:web.xml中配置jfinal过滤器,MyConfig的路径
第四步:建立Controller,继承jfinal的controller
第五步:启动MyConfig中的main,打开浏览器访问localhost(默认80端口)
可以在官网中下载JBolt插件,十分方便构建Jfinal项目
基于JFinal的web需要创建一个继承JfinalCongfig的类,该类用于对整个项目进行配置。MyConfig需要实现六个抽象方法
1>configConstant:配置jfinal的常量值
me.setDevMode(true);将项目配置成开发者模式(每次修改自动加载,在控制台显示请求的详细信息)
setViewType(ViewType.JSP)(默认jfinal自己的视图Templat)设置视图类型//可以不用配置,render渲染时直接指定视图类型即可
2>configRoute:路由配置
me.setBaseViewPath("/view");设置视图的访问路径(适合模块开发)
me.add("/hello", HelloController.class);拦截到/hello请求后进入HelloController类(默认执行index方法),如果要访问其他方法的请求路径为/hello/methodName
me.add("/hello", HelloController.class,“/view”);相比于前一个方法,当controller返回的是一个视图(a.html)时,a.html需要在weapp下的view文件夹中
路由分包技法:前端路由配置都放在FrontRoutes类中,后端路由配置都放在AdminRoutes类中
me.add(new FrontRoutes());前端路由(需要继承Routes,然后重写其中的config方法,在方法中add("/hello",HelloController.class)即可),可以在方法中使用setBaseViewPath("/view");设置视图的访问路径。同时也能够配置路由级别的拦截器,addInterceptor(new myInterceptor)(myInterceptor需要实现Interceptor,然后一定要执行放行方法为ininvoke(),可以在此方法前执行我们想要的代码)
me.add(new AdminRoutes()); 后端路由,同上
finalView = baseViewPath + viewPath + view
访问路径 = setBaseViewPath设置的路径 + add中设置路径 + render渲染路径
注意:当view以 “/” 字符打头时表示绝对路径,baseViewPath 与 viewPath 将被忽略。
3>configEngine:模板引擎配置
4>configPlugin:插件配置
配置数据库连接池插件,数据库表的映射,读取项目的中.sql文件
5>configInterceptor:全局拦截器配置
6>configHandler:处理程序配置
me.add(new ContextPathHandler(“ctx”))配置一个项目全局的上下文路径,在前端的html页面中使用action=“#(ctx)/doadd”
>两个系统回调方法
void afterJFinalStart():系统启动后执行
void beforeJFinalStop():系统关闭前执行
>PropKit工具类
用来读取外部键值对配置文件,PropKit可以极度方便地在系统任意时空使用,配置文件的格式如下:PropKit.use("config.txt");
Controller是JFinal核心类之一,该类作为MVC模式中的控制器。一个请求就是一个action,一个Controller可以包含多个Action。Controller是线程安全的。
1>Action:请求处理类
Action方法必须在Controller中定义,且必须是public可见性,返回值必须是void
值的传递:
getPara(“name”,“defaultVal”)获取单个值,defaultVal是默认值(建议设置)
getparaValues(“name“)获取多个值,返回的是一个数组
获取model对象:
getModel(model.class,“设置别名”)获取model对象数据(设置别名之后前台数据就可以是别名.name),使用的attrName必须与数据表字段名完全一样,前台数据name必须是(blog.name 注意此处的blog首字母小写)
如果希望传参时避免使用modelName前缀,可以使用空串作为modelName来实现:getModel(Blog,class,"");
getBean(Blog.class) 获取model对象数据,支持传统Java Bean,包括支持使用jfinal生成器生成了getter、setter方法的Model,前台数据name必须是(blog.name)
setAttr(“name“,”value“)将值放入域中,可以在前端获取
参数注入:
public void index(Project project) {} Action 参数注入可以代替 getPara、getBean、getModel 系列方法获取参数,使用 File、UploadFile 参数时可以代替 getFile 方法实现文件上传。
原先通过 getBean(User.class, "") 获取时第二个参数为空字符串或null,那么与之等价的形参注入只需要用一下 @Para("") 注解即可:
session操作:
setSessionAttr(key,value)向session域中放入值
getSessionAttr(key)从session域中取出值
getSession()得到session对象,从而操作全面的sessionApi
removeSessionAttr(key)移除指定的session对象(cookie也有对应的方法)
文件上传:
getFile(fileName)接受前台文件的上传(收到的文件会自动保存)
renderFile()文件下载
注意:在有文件上传的表达操作中,必须要先执行getFile系列方法后再执行getPara(),上传文件中文件默认上传到根目录下的upload文件夹中(文件下载默认文件夹为download,其他下同),可以在configConstant(Constants me)方法中通过me.setBaseUploadPath(baseUploadPath) 设置文件上传基础路径,该路径参数接受以”/”打头或者以windows磁盘盘符打头的绝对路径,
render系列方法:
JFinal目前支持的视图类型有:JFinal Template、FreeMarker、JSP、Velocity、JSON、File、Text、Html、QrCode 二维码 等等。可以在configConstan方法中设置视图类型,setViewType(ViewType.JSP)(默认jfinal自己的视图Templat),可以render直接指定渲染的视图类型
RenderText(“text“)返回一个text文本到前端
renderJson(key,value); 返回一个json文本到前端
RenderTemplate(“viewName“)渲染一个jfinal默认的视图.html
renderHtml(“viewName”)渲染一个html页面
renderFreeMarker(“viewName”)渲染一个FreeMarke页面
redirect("/user");到一个请求
forwardAction(url)内部转发到一个请求
….还有更多的方法
在调用render系列的方法后程序并不会立即返回,如果需要立即返回需要使用return语句。在一个action中多次调用render方法只有最后一次有效。
依赖注入:
第一步:在configConstant打开开关
me.setInjectDependency(true); //设置启用依赖注入
第二步:使用注解@Inject
@Inject
UserService userService;
Method Interceptor:方法级别,注解实现
Class Interceptor:类级别,对当前类中所有的方法都有效,注解实现
Router Interceptor:路由级别,对当前路由中所有的方法都有效吗,参路由分包技法
Global Interceptor:全局拦截,对web请求中所有的请求方法进行拦截,需要在configInterceptor中me.add(new myGlobalInterceptor)
Inject Interceptor:业务注入拦截器,对指定需要被注入的方法有效,可以整个业务类,也可是某个方法
拦截器栈:多个Interceptor进行组合形成的拦截器栈,@ Befor([多个拦截器Class])执行顺序从左到右
常用注解:
@Befor(MyInterceptor.class)、
@Clear:清除只针对Clear本身所处层的向上所有层,本层与下层不清除,不带参数时清除所有拦截器,带参时清除参数指定的拦截器(也可以是数组,清除多个拦截器)
执行顺序:全局拦截 > 业务注入拦截器 > 路由级别 >类级别 > 方法级别
MyInterceptor需要实现Interceptor接口,并重写其中的方法(in.invoke方法放行)
ActiveRecord模式的核心是:一个 Model 对象唯一对应数据库表中的一条记录,而对应关系依靠的是数据库表的主键值。ActiveRecord使用时需要在JFinalConfig中配置ActiveRecordPlugin。
因此,ActiveRecord 模式要求数据库表必须要有主键。当数据库表没有主键时,只能使用 Db + Record 模式来操作数据库。基于ActiveRecord的Model无需定义属性,无需定义getter、setter方法,无需XML配置,无需Annotation配置
// 创建name属性为James,age属性为25的User对象并添加到数据库
new User().set("name", "James").set("age", 25).save();
// 删除id值为25的User
User.dao.deleteById(25);
// 查询id值为25的User将其name属性改为James并更新到数据库
User.dao.findById(25).set("name", "James").update();
// 查询id值为25的user, 且仅仅取name与age两个字段的值
User user = User.dao.findByIdLoadColumns(25, "name, age");
// 获取user的name属性
String userName = user.getStr("name");
// 获取user的age属性
Integer userAge = user.getInt("age");
// 查询所有年龄大于18岁的user
List users = User.dao.find("select * from user where age>18");
// 分页查询年龄大于18的user,当前页号为1,每页10个user
Page userPage = User.dao.paginate(1, 10, "select *", "from user where age > ?", 18);
//自定义的批量sql语句操作
Db.delete("delete from user where id = ?",id);
注意:User中定义的public static final User dao对象是全局共享的,只能用于数据库查询,如果需要接收查询的数据new User
小结:
1>user.save可以直接用于接收前台数据的user来进行保存或者更新操作
2>或者private User Dao = new User().dao.();创建一个dao,然后用dao来进行CRUD操作
3>Record user = new Record().set("name", "James").set("age", 25);然后使用Db.save("user", user); Record就是一个泛型的Model对象(可以对任何模拟所有实体类)
事务操作:
1>直接在service类加速@Before(Tx.class)的事务注解
2>在Db.tx中操作
boolean succeed = Db.tx(new IAtom(){
public boolean run() throws SQLException {
int count = Db.update("update account set cash = cash - ? where id = ?", 100, 123);
int count2 = Db.update("update account set cash = cash + ? where id = ?", 100, 456);
return count == 1 && count2 == 1;
}});
Sql的管理与生成:将sql从service或dao层分离出来
1>读取sql文件:在configPlugin读取.sql文件
//数据库文件在resource下
arp.getEngine().setSourceFactory(new ClassPathSourceFactory());//二选一
//数据库文件在webapp/WEB-INF下
arp.setbaseSqlTemlatePath(PathKit.getWebRootPath+”/WEB-INF”);//二选一:设置sql文件的起始文件夹
//将配置读取
arp.addSqlTemplate("/all.sql");//可以多使用多个(加载多个sql文件),resource下不需要“/”
2>sql文件的内容:#sql,#para,#namespace指令
#sql(“blogList”)
Select * from blog order by id desc
#end
3>使用sql语句
Blog.dao.getSql(key)//或String sql = Db.getSql(key);,不同点(为多数据源分别配置了sql模板的场景)
Db.find(sql, 16, 23);//
注意:占位符需要按顺序来
#para指令:用于生成sql中的问号占位符以及问号占位符所对应的参数值
两种用法:
1传入int常量类型(select * from girl where age > #para(0) and weight < #para(1))
使用(SqlPara sqlPara = Db.getSqlPara("findGirl", 18, 50); Db.find(sqlPara);)
2传入任意类型参数(select * from girl where age > #para(age) and weight < #para(weight))
使用(Kv cond = Kv.by("age", 18).set("weight", 50);
SqlPara sp = Db.getSqlPara("findGirl", cond);)
注意:前者传入的是Object… paras参数,后者是Map data参数。
#namespace指令:为sql的key添加一个包,需要#end结束,#sql之前使用#namespace,调用时的key:namespace.key
Like查询:select * from article where title like concat('%', #para(title), '%');
分页查询:dao.paginate(1, 10, "select *", "from girl where age > ? and weight < ?", 18, 50);
表关联查询
ActiveRecord 天然支持表关联操作,两种方式解决表关联(一是直接使用sql得到关联数据;二是在Model中添加获取关联数据的方法。)
1.直接使用sql关联:
select b.*, u.user_name from blog b inner join user u on b.user_id=u.id where b.id=?
2.修改model,在model中添加获取关联数据的方法:
Blog的model:1的一方
public User getUser() {
return User.dao.findById(get("user_id"));
}
User的model:多的一方
public List getBlogs() {
return Blog.dao.find("select * from blog where user_id=?", get("id"));
}
Ehcache缓存
默认的缓存实现是 ehcache,使用时需要引入 ehcache 的 jar 包及其配置文件
List blogList = Blog.dao.findByCache("cacheName", "key", "select * from blog");
cacheName需要在ehcache.xml中配置如: