JFinal开发框架一

一、JFinal简介

JFinal是基于Java的极速WEB+ORM框架,其核心设计目标是开发迅速、代码量少、学习简单、功能强大、轻量级、易扩展、Restful。

官网:http://www.jfinal.com/

特点:轻量级开发框架 类似springboot,mvc架构,零配置 无xml,Db+Record模式,ActiveRecord支持,AOP支持,Enjoy 模板引擎

 

二、JFinal项目的构建流程(eclipse+maven)

第一步:在pom.xml文件中配置jfinal的相关依赖

第二步:创建MyConfig配置类(启动类,需要继承JfinalConfig)

第三步:web.xml中配置jfinal过滤器,MyConfig的路径

第四步:建立Controller,继承jfinal的controller

第五步:启动MyConfig中的main,打开浏览器访问localhost(默认80端口)

可以在官网中下载JBolt插件,十分方便构建Jfinal项目

 

三、JfinalConfig核心配置文件(MyConfig启动类)

基于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 将被忽略。

JFinal开发框架一_第1张图片

 

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控制器

Controller是JFinal核心类之一,该类作为MVC模式中的控制器。一个请求就是一个action,一个Controller可以包含多个Action。Controller是线程安全的。

1>Action:请求处理类

Action方法必须在Controller中定义,且必须是public可见性,返回值必须是void

值的传递:

getPara(“name”,“defaultVal”)获取单个值,defaultVal是默认值(建议设置)

getparaValues(“name“)获取多个值,返回的是一个数组

JFinal开发框架一_第2张图片

 

获取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;

 

五、Interceptor拦截器:常用于权限检查、日志等

Method   Interceptor:方法级别,注解实现

Class        Interceptor:类级别,对当前类中所有的方法都有效,注解实现

Router     Interceptor:路由级别,对当前路由中所有的方法都有效吗,参路由分包技法

Global      Interceptor:全局拦截,对web请求中所有的请求方法进行拦截,需要在configInterceptorme.addnew  myGlobalInterceptor

Inject       Interceptor:业务注入拦截器,对指定需要被注入的方法有效,可以整个业务类,也可是某个方法

拦截器栈:多个Interceptor进行组合形成的拦截器栈,@ Befor([多个拦截器Class])执行顺序从左到右

常用注解:

@Befor(MyInterceptor.class)、

@Clear:清除只针对Clear本身所处层的向上所有层,本层与下层不清除,不带参数时清除所有拦截器,带参时清除参数指定的拦截器(也可以是数组,清除多个拦截器)

 

执行顺序:全局拦截 > 业务注入拦截器 > 路由级别 >类级别 > 方法级别

MyInterceptor需要实现Interceptor接口,并重写其中的方法(in.invoke方法放行)

 

六、ActiveRecord数据库操作

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中配置如:

 

 

 

你可能感兴趣的:(java)