jfinal心得

来源:互联网 发布:怎样分析天然气数据 编辑:程序博客网 时间:2024/05/26 09:53

JFinal基本配置

内置Jetty启动项目:

Jfinal推荐使用WebRoot\WEB-INF\classes放class文件, 
于是创建项目的时候Default output folder位置改成上面,然后Content directory要与此对应。,当然也可以用tomcat来使用,跟SSH的新建dynamic Web Project一样新建出来就可;

添加JFinal的Controller过滤器

<filter><filter-name>jfinal</filter-name><filter-class>com.jfinal.core.JFinalFilter</filter-class><init-param><param-name>configClass</param-name><param-value>demo.DemoConfig</param-value></init-param></filter><filter-mapping><filter-name>jfinal</filter-name><url-pattern>/*</url-pattern></filter-mapping>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

创建配置文件,是个java文件

package demo;import com.jfinal.config.*;import com.jfinal.plugin.activerecord.ActiveRecordPlugin;import com.jfinal.plugin.c3p0.C3p0Plugin;import com.jfinal.render.ViewType;public class DemoConfig extends JFinalConfig{    /**      此方法用来配置 JFinal 常量值, 如开发模式常量 devMode 的配置, 默认视图类型 ViewType的配置     JFinal 会对每次请求输出报告,如输出本次请求的 Controller、Method 以及请求所携带的参数。JFinal 支持 JSP、FreeMarker、Velocity 三种常用视图。     */    @Override    public void configConstant(Constants me) {        me.setDevMode(true);        //设置运行在开发模式下的默认视图类型为FREE_MARKER        me.setViewType(ViewType.FREE_MARKER);    }    /**     * 此方法用来配置 JFinal 访问路由(路径),如下代码配置了将”/hello”映射到 HelloController 这个控制 器 , 通 过 以 下 的 配 置 , http://localhost/hello 将 访 问 HelloController.index() 方 法 , 而http://localhost/hello/methodName 将访问到 HelloController.methodName()方法。     *      */    @Override    public void configRoute(Routes me) {        //add表示添加了一个控制器,路径为/,进入该控制器将默认访问该控制器的index方法,HelloControl为处理该请求路径的类。        //加入要访问该控制器下的其他请求,那么在输入http输入/hello/test,就访问到该控制器中的test方法        me.add("/",HelloControl.class);//      me.add("/hello/test",HelloControl.class);    }    /**     * 此方法用来配置 JFinal 的 Plugin, 如下代码配置了 C3p0 数据库连接池插件与 ActiveRecord数据库访问插件。通过以下的配置,可以在应用中使用 ActiveRecord 非常方便地操作数据库     *      */    @Override    public void configPlugin(Plugins me) {        loadPropertyFile("config.txt");        C3p0Plugin c3p0Plugin = new C3p0Plugin(getProperty("jdbcURL"),                getProperty("user"),getProperty("password")                );        me.add(c3p0Plugin);        ActiveRecordPlugin arp = new ActiveRecordPlugin(c3p0Plugin);        //显示sql语句        arp.setShowSql(true)        me.add(arp);//      //添加数据库与Model的关系映射,User需要继承Model<>类  arp.addMapping("user", User.class);    }    @Override    public void configInterceptor(Interceptors me) {}    /**     *      * 此方法用来配置JFinal的Handler, 如下代码配置了名为ResourceHandler的处理器, Handler可以接管所有 web 请求,并对应用拥有完全的控制权,可以很方便地实现更高层的功能性扩展      */    @Override    public void configHandler(Handlers me) {    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82

HelloControl控制器

package demo;import com.jfinal.core.Controller;public class HelloControl extends Controller {    public void index(){        String msg = "Welcome To JFinal World";        //将信息传递到页面,页面中读取代码为:        //<!--为什么有!'',意思是如果读取到的参数为null,使用''代替,        //因为Freemarker需要安全输出,页面中如果有Null会报错-->        //<h1>${(helloworld)!''}</h1>        setAttr("helloworld", msg);        //使用FreeMarker渲染页面        renderFreeMarker("helloworld.html");    }    //该注解是直接映射,意思测试的时候使用localhost/test就会访问到该路径,当然,服务器端口使用的80        @ActionKey("test")      public void test(){        renderFreeMarker("/helloworld.html");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

有个小技巧,在html页面中,可以使用 
<#include “xxx.html”> 
可以将xxx.html里面的东西包含到你需要的页面,例如修改和删除都使用同一个页面的情况下,就可以用的到。

Jfinal MVC及参数的接收,以及数据源配置

数据源配置:

数据源配置即配置数据库连接,在这里使用的mysql,以及使用外置文件配置相关连接参数,url,username等; 
在jfinal中,这种配置类似一种插件,可插拔,所以放在了配置未见中的 
configPlugin(Plugins me方法中)

    /**     * 此方法用来配置 JFinal 的 Plugin, 如下代码配置了 C3p0 数据库连接池插件与 ActiveRecord数据库访问插件。通过以下的配置,可以在应用中使用 ActiveRecord 非常方便地操作数据库     *      */    @Override    public void configPlugin(Plugins me) {        //加载配置文件        loadPropertyFile("config.txt");        //C3P0数据源的初始化        C3p0Plugin c3p0Plugin = new C3p0Plugin(getProperty("jdbcURL"),                getProperty("user"),getProperty("password")                );        me.add(c3p0Plugin);        ActiveRecordPlugin arp = new ActiveRecordPlugin(c3p0Plugin);        //可插拔,添加数据库和对象之间的数据源        me.add(arp);        //映射数据库,user是表名,表默认主键是id,后面参数是实体,将实体和表映射,注意,User继承了Jfinal的Model<User(M)>类,是个泛型类        arp.addMapping("user", User.class);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

关于Jfinal中Model的配置;

Model只需要继承一个类,不用设置属性,属性是从数据库里面映射过来,数据库的列名是什么,这个model对应的属性就是什么

package demo;import com.jfinal.plugin.activerecord.Model;public class User extends Model<User> {    /**     *      */    private static final long serialVersionUID = 1L;    public static User dao = new User();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

表单参数的接收

如果使用get方法的RESTFul风格,那么有可能出现问题,在使用这样的 getParaToInt(“id”)的时候 
Jfinal中接收表单参数的方法有几种,一种是getPara(“表单name”),一种是使用对象来接收参数,类似Spring MVC的控制器,

在控制器中某表单提交去向对应的方法,我这给的是test,

    public void test(){    //这个是最简单的方法,通过getPara    //  getPara("表单名");        //接受传过来的参数,刚好是某个对象的属性,使用getModel来取得表单参数,getModel有2种重载,一种是单个参数,一种是两个参数,单个参数的如下,在表单域里面输入框的name,例如我这使用的是User.class,那么在那里就写user.attribute,对象.属性的方法来作为name,在表单页面的输入框name为User,首字母小写就对应这个对象,        User user = getModel(User.class);        //第二种重载,getModel(User.class,"u"),就是起个别名,在表单里面的name就为u.attribute,就可以接收参数了。        User user1 = getModel(User.class,"u");        //取出user的某个属性,还有getInt,getDate等方法来获取对应类型的属性        System.out.println(user.getStr("name"));    //这个方法是渲染视图,上面讨论过。如何传数据从控制器到页面呢?使用setAttr("参数名",参数值)这个方法来将参数放到request域里面传递到页面        renderFreeMarker("/helloworld.html");    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

JFinal之CRUD

Model书写:

对象的属性是从数据库直接获取的,假如数据库列名是name,那么当我们在取name这个属性的时候,在页面,就直接${user.name}

package demo;import com.jfinal.plugin.activerecord.Model;public class User extends Model<User> {    private static final long serialVersionUID = 1L;    public static User dao = new User();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

查询

使用freemaker渲染页面,html页面如下书写,接收服务器传过来的数据(未分页版本)

页面书写:

其中 userList是从服务器返回来的数据,是一个集合,这里做的是遍历。list表示遍历这个集合<!--假如是分页查询,那么应该这么写<#list userList.list as user> --><#list userList as user>        <tr>            <td>            ${(user.id)!''}            </td>            <td>            ${(user.name)!''}            </td>            <td>            ${(user.address)!''}            </td>            <td>            ${(user.phone)!''}            </td>            <td>            这个是查询单条的访问路径            <a href="/userid/${(user.id)!''}">查看</a>            </td>        </tr>    </#list>分页查询输出当前页码,总记录数,总页数;总共${userList.totalRow}条记录,当前第${pageNum}页,总共${userList.totalPage}页<#--一个简单的分页控制器,顺便用了下freemaker的if使用,freemaker要使用#才能注释,注意,条件判断之后不要有空格,因为不会自动过滤空格--> <p><a href="/userPage/1">首页</a>  <a href="/userPage/<#if pageNum == 1>1   <#else>${pageNum-1}  </#if>">上一页</a> &ebsp;  <a href="/userPage/<#if pageNum == userList.totalPage>${userList.totalPage}  <#else>${pageNum+1}  </#if>">下一页</a>    <a href="/userPage/${userList.totalPage}">末页</a></p> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

控制器书写:

    /**    查询所有    */public void user(){        String sql = "select * from user order by id desc";        setAttr("userList",User.dao.find(sql));        renderFreeMarker("/helloworld.html");    }    /**     * 查询单条记录     */public void userid(){        //截取id,注意,这种是RESTFul风格的获取get方式传参的手段,假如使用getParaToInt("id"),url为userid/1,的时候,获取不到值,那么这里也支持问号传参,userid?id=xxx,这样就可以使用getParaToInt("id")来取得id        int id = getParaToInt(0);           //根据id查询用户        User user = User.dao.findById(id);        //还可以这样写,下面参数第二个开始是一个Object... params,不定参数        User user = User.dao.findFirst("select * from user where id = ? ", id);        //显示查询到的用户信息        System.out.println(user);}/**     * 分页查询,Jfinal提供了方法     * paginate(pageNumber, pageSize, 需要查的映射(select *), sql语句,从from开始, 用以替换?的参数)     * 最后一个参数可要可不要,因为是不定参数。     */    public void userPage(){        String sql = "from user";        int pageNum = getParaToInt("pageNo",1);        setAttr("pageNum", pageNum);        setAttr("userList", User.dao.paginate(pageNum, 2, "select *", sql));        renderFreeMarker("helloworld.html");    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

增加

页面书写:

    <form action="addUser">        <input type="text" name="user.name">        <input type="text" name="user.phone">        <input type="submit" value="提交">    </form>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

控制器书写:

    public void addUser() {        //从页面获取user的属性        User user = getModel(User.class);        //save()方法放返回一个布尔值,如果添加成功返回true,在这里我们添加成功的话就重定向到user列表页面,失败的话就渲染一个纯文本页面,renderText        boolean flag = user.save();        if (flag) {            redirect("/user/");        } else {            renderText("Sorry,有异常,插入失败");        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

删除

控制器书写:

public void deleteUser() {        //取得例如deleteUser/1-2-3中第一个参数1,加入使用getParaToInt(1);则取到的是第二个参数2               int id = getParaToInt(0);        boolean flag = User.dao.deleteById(id);        if (flag) {            redirect("/user/");        } else {            renderText("Sorry,有异常,删除失败");        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

页面书写:

<#list userList as user>        <tr>            <td>            ${(user.id)!''}            </td>            <td>            ${(user.name)!''}            </td>            <td>            ${(user.address)!''}            </td>            <td>            ${(user.phone)!''}            </td>            <td>            <a href="/deleteUser/${(user.id)!''}">删除</a>            </td>        </tr>    </#list>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

修改

页面书写:

页面其实跟添加页面差不多,只是action的时候带过去id,在这里是做了一个隐藏域,当然也可以在表单的action中updateUser/${(user.id)!”}这样来传递ID

    <form action="updateUser">        用户名<input type="text" name="user.name"><br>        电话号码<input type="text" name="user.phone"><br>        地址<input type="text" name="user.address"><br>        id<input type="hidden" name="user.id"><br>        <input type="submit" value="提交">    </form>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

控制器书写

/**修改用户*/public void updateUser() {        User user = getModel(User.class);        boolean flag = user.update();        if (flag) {            redirect("/user/");        } else {            renderText("Sorry,有异常,修改失败");        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

Interceptor拦截器

拦截器:JFinal AOP的实现方式,拦截器并非线程安全,县城安全拦截器需要继承PrototypeInteceptor来实现

拦截器有三个级别,Global,Controller,Action,Global对所有Action进行拦截;Controller就是一整个控制器,包含了多个Action级, Action级就是具体到某个Action的拦截 
Interceptor通过注解来实现,支持的注解为@Before(xxxInterceptor.class) 
这个xxxInterceptor需要实现Interceptor接口

public class DemoActionInterceptor implements Interceptor {    @Override    public void intercept(Invocation inv) {        Controller c = inv.getController();        System.out.println("这个是拦截器在业务方法前执行的代码");        inv.invoke();//让被拦截的方法继续执行。        System.out.println("这个是拦截器在业务方法后执行的代码部分");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

全局Interceptor的配置:在Config里面的configInterceptor中配置,而不使用注解,代码如下。

    public void configInterceptor(Interceptors me) {        me.add(new DemoInterceptor());//定义一个全局拦截器    }
  • 1
  • 2
  • 3
  • 4

Controller级别Interceptor的配置:其实就是将Before放在类上,

@Before({DemoInterceptor.class,OtherInteceptor.class})public class HelloControl extends Controller {    public void index() {}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Action级别的拦截器:其实就是放在Action上面,即Controller里面的某个方法

权限验证拦截器

public class AuthInterceptor implements Interceptor {  public void intercept(ActionInvocation ai) {    Controller controller = ai.getController();    User loginUser = controller.getSessionAttr("loginUser");    if (loginUser != null && loginUser.canVisit(ai.getActionKey()))      ai.invoke();    else      controller.redirect("/login.html");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

思考Jfinal中Service层及AOP实现

研究了3-4个小时,一直在纠结Interceptor和AOP,SSH中的AOP是放在了Service中的方法上,于是我按照同样的思路想在jfinal中搞个service层,然后给其加上AOP,后来发现不可行,报了这么个错误;

This method can only be used for action interception 
意思这个方法只能用于Action拦截,深层意思就是只能用在控制器Controller上,而不能用在自己定义的Service上。

意味着jfinal的AOP都是实现在控制器级别的,例如这样:

    @Before(DemoActionInterceptor.class)    public void test1(){        //这个enhance就是返回一个带了事务(@Before(Tx.class))的Service对象        UserService userService = enhance(UserService.class);//还可以上第二个参数,Tx.class,Inject拦截注入一个事务给service,就可以不用在service里面来Before了        User user = userService.login("老大","asd");        if(user!=null){        System.out.println("业务实体返回了一个DTO:" + user);        renderText("asd");        }else{            renderText("用户名或密码错误");        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

业务层代码:

//@Before(DemoActionInterceptor.class如果加了这句话会报运行时错误。public class UserService {    @Before(Tx.class)    public User login(String username){        //调用dao,jfinal中model其实就是一个dao        User u = User.dao.findFirst("select * from user where name=?",username);        if(u != null && password.equals(u.getStr("password"))){        return u;}        else            return null;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

实践表明,Interceptor不能单独的应用在S层,必须应用在C层,为什么这样呢,看了下Jfinal大大对于这个问题给的回复,恍然大悟,。以下是大大的设计思路 
- AOP 本来就是独立出来的,业务层不需要知道 AOP 在哪里,AOP紧靠service或controller在本质上并无差别,如果要找差别的话:==紧靠service层做事务 AOP会让事务开启的时间稍晚一些,带来略微的性能提升,其实controller中的代码是简单的控制代码所耗性能对于业务层来说可以忽略不计,所以在 controller 上做声明式事务是jfinal权衡后最佳的选择。== 
AOP 希望贴近 service 来做是理论化、学术化的诉求,通常软件开发是工程性的活动,理论化与学术化不经济也不实用。

So,我们的S层仅仅只需要事务支持,(事务支持其实可以简单理解成就是数据库操作的多次操作,然后事务提交,提交失败回滚),而不做AOP,AOP放在了Controller来进行操作,假如需要的话。

业务逻辑层放哪里?

这个问题也是思考了半天,最后咨询得出,有两个方案,一个方案是放在Model里面,一个方案也是单独抽一个Service层出来,SSH的思路是S层调用dao对象,所以可以在Service中使用Model.dao来调用dao对象(Model.dao是一个静态的已经初始化号的Model对象,当然这个Model继承了Jfinal提供的Model<xxxx>

PS:没想到这么快就接触到了领域模型,充血模型,,我还以为我要在事务脚本摸爬滚打许久;

表关联映射

Jfinal中表关联映射有两种,一种是通过sql语句的联表查询来进行关联,什么inner join,left join之类的,跟sql查询差不多。

public void relation() {String sql = "select b.*, u.user_name from blog b inner join user u on b.user_id=u.id where b.id=?";Blog blog = Blog.dao.findFirst(sql, 123);String name = blog.getStr("user_name");}
  • 1
  • 2
  • 3
  • 4
  • 5

另外一种就是Jfinal特有的 
理解思路:加入有两个对象,一个User,一个Blog,一对多关系,一个User有多个Blog,一个Blog只有一个User。 
思路即:查询方法直接写好,getUser(),传入Blog中的表示User的外键user_id来查询Blog对应的User; 
在User中,用Blog.dao来查询,传入User的id来查询该User拥有的Blog集合

public class Blog extends Model<Blog>{public static final Blog dao = new Blog();public User getUser() {return User.dao.findById(get("user_id"));}}public class User extends Model<User>{public static final User dao = new User();public List<Blog> getBlogs() {return Blog.dao.find("select * from blog where user_id=?",get("id"));}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

ActiveRecord

这个部分包含了Model以及Db+Record的模式来替代Model;

两者用法区别不大,唯一的区别,在于不需要建立实体类,例如,之前使用dao的时候会建立一个继承Model的model,然后内部实例化该对象来当dao使用,那么在使用Db+Record的时候就可以采用输入表名的手段来区别实体对象,就不用单独建立实体类;代码区别示例

这个是Model//创建name属性为James,age属性为25的User对象并添加到数据库new User().set("name", "James").set("age", 25).save();//删除id值为25的UserUser.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.findById(25, "name, age");//获取user的name属性String userName = user.getStr("name");//获取user的age属性Integer userAge = user.getInt("age");//查询所有年龄大于18岁的userList<User> users = User.dao.find("select * from user where age>18");// 分页查询年龄大于18的user,当前页号为1,每页10个user    Page<User> userPage = User.dao.paginate(1, 10, "select *", "from user where age > ?", 18);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
这个是DB+Record// 创建name属性为James,age属性为25的record对象并添加到数据库Record user = new Record().set("name", "James").set("age", 25);Db.save("user", user);// 删除id值为25的user表中的记录Db.deleteById("user", 25);// 查询id值为25的Record将其name属性改为James并更新到数据库user = Db.findById("user", 25).set("name", "James");Db.update("user", user);// 查询id值为25的user, 且仅仅取name与age两个字段的值user = Db.findById("user", 25, "name, age");// 获取user的name属性String userName = user.getStr("name");// 获取user的age属性Integer userAge = user.getInt("age");// 查询所有年龄大于18岁的userList<Record> users = Db.find("select * from user where age > 18");// 分页查询年龄大于18的user,当前页号为1,每页10个userPage<Record> userPage = Db.paginate(1, 10, "select *", "from user where age > ?", 18);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

关于Record的事务

说到事务,其实底层就是把 
jdbc的Connection conn.autoCommit(false)设置为false, 
然后进行jdbc几个操作一起进行,捆绑进行,要不都完成成功,要不都失败,然后开始 
Connection conn.commit()开始提交 
,提交成功就都成功,失败就回滚,一般来个try catch,抓到异常后进行事务回滚, 
session.rollback();事务回滚

编程式事务:

boolean succeed = Db.tx(new IAtom(){    public boolean run() throws SQLException {    //数据操作1    int count = Db.update("update account set cash = cash - ? where id = ?", 100, 123);    //数据操作2    int count2 = Db.update("update account set cash = cash + ? where id = ?", 100, 456);    return count == 1 && count2 == 1;}});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

声名式事务: 
其实跟之前的事务差不多,来个注解

// 本例仅为示例, 并未严格考虑账户状态等业务逻辑@Before(Tx.class)public void trans_demo() {// 获取转账金额Integer transAmount = getParaToInt("transAmount");// 获取转出账户idInteger fromAccountId = getParaToInt("fromAccountId");// 获取转入账户idInteger toAccountId = getParaToInt("toAccountId");// 转出操作Db.update("update account set cash = cash - ? where id = ?",transAmount, fromAccountId);// 转入操作Db.update("update account set cash = cash + ? where id = ?",transAmount, toAccountId);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

Cache缓存

缓存就是什么,就是你经常要查的东西来放到缓存里,因为开启数据库是个代价比较高昂的操作,所以应用缓存来减少数据库开和关的次数来提升性能,类似连接池,空间换时间; 
一般缓存里放的东西都是经常查但又不经常修改的数据,因为缓存可以设置缓存内容失效时间,可以相对也比较灵活。

在Jfinal中默认使用的Cache是EhCache,使用方法有几个地方 
1:添加相关jar报,包括 
- ehcache-core 
- log4j 
- slf4j-api 
- slf4j-log4j12

2:在Config类中

public void configPlugin(Plugins me){    me.add(new EhCachePlugin())}
  • 1
  • 2
  • 3

3:拷贝ehcache.xml文件推荐放到src下或者WEB-INF下(其实放哪里都可以,名字叫这个就行);

4:对Action进行注解

@Before(CacheInterceptor.class)//jfinal框架带的//  @CacheName("userPage")自己定义使用缓存的目录,如果没有这个注解的话,默认actionKey为缓存Action    public void userPage(){        System.out.println("这个是主体方法");        String sql = "from user";        int pageNum = getParaToInt("pageNo",1);        setAttr("pageNum", pageNum);        setAttr("userList", User.dao.paginate(pageNum, 2, "select *", sql));        renderFreeMarker("helloworld.html");    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

5:以上代码运行还会报错,还需要在ehcache中配置,如下:配置cachename与java文件中的cachename或者actionKey一致,注意,包括路径映射路径,假如我使用了如下/hello/test,那么我的cacheName就应该为/hello/test/userPage
config中的映射

    @Override    public void configRoute(Routes me) {        //add表示添加了一个控制器,路径为/hello,HelloControl为处理该请求路径的类。        //加入要访问该控制器下的其他请求,那么在输入http输入/hello/test,就访问到该控制器中的test方法        me.add("/",HelloControl.class);//      me.add("/hello/test",HelloControl.class);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

XML配置,主要是<cache name>部分,顺便添加个配置说明

<?xml version="1.0" encoding="UTF-8"?><ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:noNamespaceSchemaLocation="ehcache.xsd"         updateCheck="false" monitoring="autodetect"         dynamicConfig="true">    磁盘缓存位置    <diskStore path="java.io.tmpdir"/>    默认缓存配置    <defaultCache           maxEntriesLocalHeap="10000"           eternal="false"           overflowToDisk="true"           timeToIdleSeconds="20"           timeToLiveSeconds="60">    </defaultCache> <cache name="/userPage"           堆内存中最大缓存对象书,0没有限制           maxEntriesLocalHeap="10000"           磁盘中的最大对象书,默认为0不限制           maxEntriesLocalDisk="1000"           是否永久有效,如果为truetimeouts被忽略,永不过期           eternal="false"           当调用flush()是否清除缓存,默认是           overflowToDisk="true"           磁盘缓存的缓存区大小,每个Cache都有自己的一个缓冲区           diskSpoolBufferSizeMB="20"           失效前的空闲秒数,当eternalfalse时,这个属性才有效           timeToIdleSeconds="300"           失效前的存活秒数,创建时间到失效时间的时间间隔为存活时间,           timeToLiveSeconds="600"           内存回收策略:使用频率最低:LFU,Less Frequently Used,最近最少使用LRU,Least Recently Used,先进先出,FIFO,Firsh in First Out           memoryStoreEvictionPolicy="LFU"           transactionalMode="off"           是否缓存虚拟机重启期数据            diskPersistent="true"           磁盘失效线程运行时间间隔,默认为120diskExpiryThreadIntervalSeconds="1"            /></ehcache>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

EvictInterceptor

用来修改了数据库信息后实时更新信息;用法如下: 
在使用了缓存的查询方法上

    @Before(CacheInterceptor.class)    @CacheName("/userPage")    public void userPage(){        System.out.println("这个是主体方法");        String sql = "from user";        int pageNum = getParaToInt("pageNo",1);        setAttr("pageNum", pageNum);        setAttr("userList", User.dao.paginate(pageNum, 5, "select *", sql));        renderFreeMarker("helloworld.html");    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

假如现在添加了一个用户,不清楚缓存则我们看到的数据不能显示新添加的这个用户,那么需要如下操作

@Before({LoginValidator.class,EvictInterceptor.class})    @CacheName("/userPage")    public void addUser() {        User user = getModel(User.class);        boolean flag = user.save();        if (flag) {            redirect("/userPage/");        } else {            renderText("Sorry,有异常,插入失败");        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

Validator,验证器

Validator实际上就是一个Inteceptor,配置地方差不多,在使用的时候,建立一个类,然后继承Validator,里面有两个方法,一个是校验方法,一个是出错后的处理方法

public class LoginValidator extends Validator {/**     * 用于参数校验的方法     */    @Override    protected void validate(Controller c) {        /*        field:需要校验的字段,例如页面输入框的name是user.name,那么这里就是user.name        errorKey:出错了的时候返回错误信息,在页面取错误信息所用的键        errorMessage:错误的具体信息        */        //validateRequiredString("field", "errorKey", errorMessage);        validateRequiredString("name", "nameMsg", "请输入用户名");        validateRequiredString("pass", "passMsg", "请输入密码");    }    /**     * 用于参数校验未通过产生错误的时候处理错误的方法     */    @Override    protected void handleError(Controller c) {        //取得actionKey之后用以判断是添加出错还是更新出错        String actionKey = getActionKey();        String view = null;        c.keepModel(User.class);//保存上一次提交的表单记录        //失败后返回        System.out.println("actionkey:" + actionKey);        if("/addUser".equals(actionKey)){            System.out.println("这个是添加");            view = "/addUser.html";        }        if("/doUpdateUser".equals(actionKey)){            System.out.println("这个是更新");            view = "/updateUser.html";        }        c.renderFreeMarker(view);    }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

在控制器中,主要就是添加了一个Validator注解:

@Before({LoginValidator.class,EvictInterceptor.class})    @CacheName("/userPage")    public void addUser() {        User user = getModel(User.class);        boolean flag = user.save();        if (flag) {            redirect("/userPage/");        } else {            renderText("Sorry,有异常,插入失败");        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在页面中,主要就是需要来接收服务器传递过来的错误信息

用户名<input type="text" name="user.name" value="${(user.name)!''}">${(nameMsg)!''}<br>电话号码<input type="text" name="user.phone" value="${(user.phone)!''}">${(phoneMsg)!''}<br>地址<input type="text" name="user.address" value="${(user.address)!''}"><br><input type="hidden" name="user.id" value="${(user.id)!''}"><br><input type="submit" value="保存">
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

文件上传

tomcat重新更新,启动的时候会重新部署项目,那么上传的文件可能丢失

文件上传需要一个jar包支持,当然在jfinal里面集成了 
- cos-26Dec2008.jar

文件上传的思路其实就是表单提交的时候添加enctype=”multipart/form-data”属性,method为POST;控制器方面使用getFile(“fileName”)来提取上传的文件,然后jfinal默认文件上传到WebRoot(WebContent根目录下)的upload目录中,当然可以修改。在config中; 
代码如下:

页面

    <form action="upload/doUpload" method="POST" enctype="multipart/form-data" >        <input type="text" name="title"><br><br>        <input type="file" name="filename"><br>        <input type="submit" value="上传">    </form>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

控制器

package demo;import java.io.File;import java.util.UUID;import com.jfinal.core.Controller;import com.jfinal.upload.UploadFile;public class UploadController extends Controller {    /**     * 去向文件上传页面     */    public void index(){        render("/upload.html");    }    /**     * 执行文件上传操作,加了个try catch是为了 方便调试,不然出错都不知道出错在哪里,实际使用是不加,注意,如果是tomcat服务器启动的项目,文件会上传到eclipse中tomcat的部署目录去eclipse中是看不到的,但是服务器能访问到,如果是jetty的话,就能直接在eclipse中看到     * 或者来个全局Inteceptor     */public void doUpload(){        try{        //如何获取带有文件上传的表单中的非文件元素数据                //String title = getPara("title");//      这个方法不可行了,那么必须要先得到getFile,才能得到相关数据,因为multipart请求要求先解析        UploadFile file = getFile("filename");        //取得文件扩展名        String ext = file.getFileName().substring(file.getFileName().lastIndexOf("."));        //文件重命名        file.getFile().renameTo(new File(file.getSaveDirectory()+UUID.randomUUID()+ext));//      System.out.println(file.getSaveDirectory());        String title = getPara("title");                System.out.println(title);        renderText("success");        }        catch(Exception e){            e.printStackTrace();        }        catch(Error s){            s.printStackTrace();        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

配置文件

@Override    public void configConstant(Constants me) {        me.setDevMode(true);        //设置运行在开发模式下的默认视图类型为JSP        me.setViewType(ViewType.FREE_MARKER);        me.setMaxPostSize(10*1024*1024); //单位是字节,1*1024代表1KB,*1024代表1m        //设置文件上传路径        me.setUploadedFileSaveDirectory("jfinalDemoUpload");//可以写绝对路径:当然\分隔符是\\    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
原创粉丝点击