摘要
JFinal 是基于 Java 语言的极速 WEB + ORM 框架,其核心设计目标是开发迅速、代码量少、学习简单、功能强大、轻量级、易扩展、Restful。在拥有Java语言所有优势的同时再拥有ruby、python、php等动态语言的开发效率!为您节约更多时间,去陪恋人、家人和朋友 :)
JFinal有如下主要特点:
MVC架构,设计精巧,使用简单
遵循COC原则,零配置,无xml
独创Db + Record模式,灵活便利
ActiveRecord支持,使数据库开发极致快速
自动加载修改后的java文件,开发过程中无需重启web server
AOP支持,拦截器配置灵活,功能强大
Plugin体系结构,扩展性强
多视图支持,支持FreeMarker、JSP、Velocity
强大的Validator后端校验功能
功能齐全,拥有struts2的绝大部分功能
体积小仅339K,且无第三方依赖
JFinal开发环境搭建
该项目使用的IntelliJ IDEA进行开发,具体怎么搭建JFinal项目,请移步IntelliJ IDEA 14.1上JFinal开发环境搭建手册这篇博客介绍的很详细,这里就不在赘述了。该项目结构如下图:
链接数据库
1.导入Jar包
2.创建mysql数据库jfinal
新建表blog
id—-自动编号
title—-varchar
content—-text
pubtime—-datetim
3.创建数据库配置文件
右键src—new—file,输入config.properties
打开该文件,输入
jdbcUrl = jdbc:mysql://127.0.0.1/jfinal?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
user = root
password = root
devMode =true
修改IP地址,数据库名,账号密码等 devMode =true表示开发模式,在开发完毕正式运行环境下可以设置为false。
配置CommonConfig.java
在CommonConfig.java中配置数据源、常量等信息
1.在configConstant方法中配置常量
@Override
public void configConstant(Constants me) {
PropKit.use("config.properties"); //加载配置文件
// me.setViewType(ViewType.JSP); // Jsp 界面
me.setDevMode(PropKit.getBoolean("devMode", false));
}
2.在configPlugin方法中配置数据源连接
public static DruidPlugin createDruidPlugin() {
return new DruidPlugin(PropKit.get("jdbcUrl").trim(), PropKit.get("user").trim(), PropKit.get("password").trim());
}
@Override
public void configPlugin(Plugins me) {
// 配置C3p0数据库连接池插件
DruidPlugin druidPlugin = createDruidPlugin();
me.add(druidPlugin);
// 配置ActiveRecord插件
ActiveRecordPlugin arp = new ActiveRecordPlugin(druidPlugin);
// 所有映射在 MappingKit 中自动化搞定
MappingKit.mapping(arp);
me.add(arp);
}
在model包下新建MappingKit类,所有映射在 MappingKit 中自动化搞定。
数据库映射:数据库中的表跟java类的对应关系。
public class MappingKit {
public static void mapping(ActiveRecordPlugin arp) {
arp.addMapping("blog", "id", Blog.class);
}
}
3.在configRoute方法中配置路由
路由:指定浏览器里输入的/blog由哪个控制器来处理。
@Override
public void configRoute(Routes me) {
me.setBaseViewPath("WEB-INF/view"); // 设置默认试图路径
me.add("/", IndexController.class);
me.add("/hello", HelloController.class);
me.add("/blog", BlogController.class, "/blog"); // 由Jsp实现
me.add("/blog-html", BlogHtmlController.class, "/blog"); // 由Html实现
}
访问/blog由BlogController控制器处理,使用的试图路径为setBaseViewPath配置的路径下的blog目录。如果路径名称不是blog,可以设置第三个参数 me.add(“/blog”, BlogController.class, “blog”); 这样就是指定到WEB-INF/view/blog目录
创建Model实体类
在model包新建base包,在base包下创建BaseBlog.class
@SuppressWarnings({"serial", "unchecked"})
public abstract class BaseBlog<M extends BaseBlog<M>> extends Model<M> implements IBean {
public M setId(Integer id) {
set("id", id);
return (M) this;
}
public Integer getId() {
return get("id");
}
public M setTitle(String title) {
set("title", title);
return (M) this;
}
public String getTitle() {
return get("title");
}
public M setContent(String content) {
set("content", content);
return (M) this;
}
public String getContent() {
return get("content");
}
public M setPubTime(String pubtime) {
set("pubtime", pubtime);
return (M) this;
}
public String getPubTime() {
return get("pubtime");
}
}
在model包下新建Blog.class继承BaseBlog。
public class Blog extends BaseBlog<Blog> {
public static final Blog me = new Blog().dao();
/**
* 所有 sql 与业务逻辑写在 Service 中,在此放在 Model 中仅为示例,
* 不要写在 Controller 中,养成好习惯,有利于大型项目的开发与维护
*/
public Page<Blog> paginate(int pageNumber, int pageSize) {
return paginate(pageNumber, pageSize, "select *", "from blog order by id asc");
}
}
对数据库的增删查改以及分页
一、Jsp + jQuery
数据库增删查改
创建Controller
创建BlogController.class继承com.jfinal.core.Controller;包下Controller。
1.从数据库中查询数据,并显示。
public class BlogController extends Controller {
@Override
public void index() {
List<Blog> mBlogLst = Blog.me.find("select * from blog"); // 查询数据给ArrayList
for (Blog mItem : mBlogLst) {
if (mItem.getDate("pubtime") != null)
mItem.setPubTime(DateUtils.DateToString(mItem.getDate("pubtime"), "yyyy/MM/dd HH:mm:ss"));
}
String dataStr = JsonUtils.serialize(mBlogLst);
setAttr("data", dataStr);
render("index.jsp"); // 渲染视图文件
}
}
在WEB-INF/view目录下,创建blog文件夹,新建index.jsp。
body部分代码:
<div class="nav">
<a href="/blog/form" class="add">新增</a>
</div>
<div>
</div>
<div class="table_box">
<table class="list">
<tbody id="blog_lst">
</tbody>
</table>
</div>
js部分代码:
<script type="text/javascript">
$(document).ready(function () {
var mblogLst = ${data};
var str = "";
$("#blog_lst").empty();
for (var i = 0; i < mblogLst.length; i++) {
str += "<tr>";
str += "<td>" + mblogLst[i].id + "</td>";
str += "<td>" + mblogLst[i].title + "</td>";
str += "<td>" + mblogLst[i].content + "</td>";
str += "<td>" + mblogLst[i].pubTime + "</td>";
str += "<td>";
str += "<a href='/blog/delete/" + mblogLst[i].id + "'>" + "删除" + "</a>";
str += "<a href='/blog/form/" + mblogLst[i].id + "'>" + "修改" + "</a>";
str += "</td>";
str += "</tr>";
}
$("#blog_lst").html(str);
}
</script>
2.向数据库中添加数据和修改数据。
在界面点击新增按钮,执行BlogController 中form方法。在BlogController 中新增form方法(由于后面form界面会被改成新增和修改的公共界面,所以这里的form方法是修改后的公共方法):
public void form() {
Integer id = getParaToInt(0); //获取id参数
if (id != null && id > 0) { // 如果id不为空且大于0,执行编辑操作
setAttr("blog", Blog.me.findById(id));// 从数据库中找到该条记录放到request对象中
}
render("form.jsp");// 渲染页面
}
在WEB-INF/view目录下,创建blog文件夹,新建form.jsp。
body部分代码:
<form method="post" action="/blog/save">
<input type="hidden" name="blog.id" value="${blog.id}"/>
<div class="form_title">
<div>Title:</div>
<input type="text" name="blog.title" value="${blog.title}"/>
</div>
<div class="form_content">
<div>Content:</div>
<textarea name="blog.content">${blog.content}</textarea>
</div>
<input class="form_btn" type="submit" value=" 提交 "/>
</form>
点击提交后,会执行BlogController 中的save方法。在BlogController 添加save方法:
public void save() {
Blog blog = getModel(Blog.class, "blog");// 将表单中的数据转成blog对象
if (blog.get("id") == null) { // 新增
blog.set("pubtime", new Date()); // 添加发布时间
blog.save(); // 保存数据
} else if (blog.getInt("id") > 0) { // 修改
blog.update();
}
index();// 访问index方法跳转到显示列表页
}
3.删除数据库中的数据:
在界面点击删除按钮后,会执行BlogController 中的delete方法。在BlogController 添加delete方法:
/**
* 删除/blog/delete?id=4
*/
public void delete() {
Integer id = getParaToInt(0); //获取id参数
if (id != null && id > 0) {
boolean flag = Blog.me.deleteById(id); //执行删除操作
if (!flag) {
renderText("删除失败");
return; //结束,不再往下执行。
}
} else {
renderText("删除失败");
return; //结束,不再往下执行。
}
index(); //如果删除成功的话,跳转到显示列表页
}
分页
分页主要是使用jQuery完成数据分页功能,利用ajax发送请求,并就收后台返回的数据。所以index.jsp界面的js代码修改为:
<script type="text/javascript">
function showBlogLst(blogLst) {
var mblogLst = blogLst;
var str = "";
$("#blog_lst").empty();
for (var i = 0; i < mblogLst.length; i++) {
str += "<tr>";
str += "<td>" + mblogLst[i].id + "</td>";
str += "<td>" + mblogLst[i].title + "</td>";
str += "<td>" + mblogLst[i].content + "</td>";
str += "<td>" + mblogLst[i].pubTime + "</td>";
str += "<td>";
str += "<a href='/blog/delete/" + mblogLst[i].id + "'>" + "删除" + "</a>";
str += "<a href='/blog/form/" + mblogLst[i].id + "'>" + "修改" + "</a>";
str += "</td>";
str += "</tr>";
}
$("#blog_lst").html(str);
}
function PageFoot2(blogPage) {
var n = "";
var backword = "上一页";
var forword = "下一页";
var x = blogPage.pageNumber; //当前页
var back; //前一页
var next; //后一页
if (x == 1) { //对上一页的判断
back = 1;
} else {
back = x - 1;
}
if (x < blogPage.totalPage) { //对下一页判断
next = x + 1;
} else {
next = blogPage.totalPage;
}
n += "<li onclick=\"showPage2(" + back + ")\"><a>" + backword + "</a></li> ";
for (var a = 1; a <= blogPage.totalPage; a++) {
if (a == blogPage.pageNumber) {
n += "<li class=\"active\" onclick=\"showPage2(" + a + ")\"><a>" + a + "</a></li> ";
} else {
n += "<li onclick=\"showPage2(" + a + ")\"><a>" + a + "</a></li> ";
}
}
n += "<li onclick=\"showPage2(" + next + ")\"><a>" + forword + "</a></li> ";
$("#pagefoot").html(n); //把循环好的页码给替换掉
};
function showPage2(curr) {
$.ajax({
type: "get",
url: "/blog/paginate",
data: {"pageNumber": curr},
cache: false,
async: false,
dataType: "json",
success: function (data) {
showBlogLst(data.blogLst);
PageFoot2(data.blogPage);
return true;
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("请求失败!");
}
});
}
showPage2(1); //刚进入页面为第一页,列出第一页数据和页脚
</script>
在BlogController 添加paginate方法:
/**
* 分页
*/
public void paginate() {
Page<Blog> mBlogPage = Blog.me.paginate(getParaToInt("pageNumber", 1), 5);
List<Blog> mBlogLst = mBlogPage.getList();
for (Blog mItem : mBlogLst) {
if (mItem.getDate("pubtime") != null)
mItem.setPubTime(DateUtils.DateToString(mItem.getDate("pubtime"), "yyyy/MM/dd HH:mm:ss"));
}
HashMap<String, Object> map = new HashMap<>();
map.put("blogLst", mBlogLst);
map.put("blogPage", mBlogPage);
renderJson(map);
}
二、JFinal template engine
利用JFinal template主要是界面上的不同,Controller代码都差不多一样,就不多说了。单单说说Html的显示和分页。
在CommonConfig中的configEngine方法中添加需要添加的template:
@Override
public void configEngine(Engine me) {
me.addSharedFunction("WEB-INF/view/common/_layout.html");
me.addSharedFunction("WEB-INF/view/common/_paginate.html");
}
这两个template,在JFinal官网的demo实例就可找到。
form.html界面和form.jsp body部分的代码一样,这里只看看index.html部分:
<div class="nav">
<a href="/blog-html/form" class="add">新增</a>
</div>
<div>
</div>
<div class="table_box">
<table class="list">
<tbody id="blog_lst">
<tr>
<th>id</th>
<th>标题</th>
<th>内容</th>
<th>发布时间</th>
<th>操作</th>
</tr>
#for(x : blogPage.getList())
<tr>
<td>#(x.id)</td>
<td>#(x.title)</td>
<td>#(x.content)</td>
<td>#(x.pubtime)</td>
<td>
<a href="/blog-html/delete/#(x.id)">删除</a>
<a href="/blog-html/form/#(x.id)">修改</a>
</td>
</tr>
#end
</tbody>
</table>
</div>
BlogHtmlController中的index方法只需:
@Override
public void index() {
setAttr("blogPage", Blog.me.paginate(getParaToInt(0, 1), 10));
render("index.html"); // 渲染视图文件
}
就可完成对数据的展示。
分页
分页更加简单,只需在需要显示分页的地方加上:
#@paginate(blogPage.pageNumber, blogPage.totalPage, "/blog-html/")
最后index.html界面body部分代码:
<div class="nav">
<a href="/blog-html/form" class="add">新增</a>
</div>
<div>
</div>
<div class="table_box">
<table class="list">
<tbody id="blog_lst">
<tr>
<th>id</th>
<th>标题</th>
<th>内容</th>
<th>发布时间</th>
<th>操作</th>
</tr>
#for(x : blogPage.getList())
<tr>
<td>#(x.id)</td>
<td>#(x.title)</td>
<td>#(x.content)</td>
<td>#(x.pubtime)</td>
<td>
<a href="/blog-html/delete/#(x.id)">删除</a>
<a href="/blog-html/form/#(x.id)">修改</a>
</td>
</tr>
#end
</tbody>
</table>
</div>
#@paginate(blogPage.pageNumber, blogPage.totalPage, "/blog-html/")
结束
写这篇博客呢,主要是记一个笔记,有一些地方可能些的并不是很清楚,代码直接拷过去也有可能有问题,毕竟这是我从写好的代码里抠出来的,哈哈哈哈哈。有不足的地方希望指正一下。谢谢。
完整的代码:请点传送门