最近在学习JFinal,正好总结下学到的知识,专门写了总结的Demo(Maven),Demo和sql链接都挂在文章末尾。
本文需要一定的jfianl常用api的基础知识
项目目录
普通的maven项目目录
项目需要的依赖:(本身只是想将关于代码的业务实现部分,因为如果要写全部的话有点多,不过的我写的本意是想让初学的人也能看懂,所以在这里加上详细的步骤,一步步打也能实现的步骤)
<dependency>
<groupId>com.jfinal</groupId>
<artifactId>jfinal-undertow</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>com.jfinal</groupId>
<artifactId>jfinal</artifactId>
<version>4.9.01</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.66</version>
</dependency>
<!-- mysql 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.44</version>
</dependency>
接下来创建entity,controller、sql模版、业务层
entity
public class XyFankui {
private static final fankui fan = new fankui().dao();
}
controller
使用注解需要在controller’配置
@Override
public void configConstant(Constants me){
me.setViewType(ViewType.JFINAL_TEMPLATE);
me.setInjectDependency(true);//开启
me.setInjectSuperClass(true);//父类注入
}
public class Controller_OneDay<T> extends Controller{
@Inject
private ServiceRegister sest;
}//这里controller继承jfianl的controller,并使用注解注入业务层
sql模版(用主sql文件引入其他sql文件)
添加sql模板需要在jfianl配置里添加
@Override
public void configPlugin(Plugins me) {
DruidPlugin dp = new DruidPlugin("jdbc:mysql://127.0.0.1/smartcampus", "root", "123");//配置数据源
me.add(dp);
ActiveRecordPlugin arp = new ActiveRecordPlugin(dp);
me.add(arp);
arp.setShowSql(true);//开发模式下控制台显示sql语句
arp.setDevMode(true);//热加载
arp.addSqlTemplate("main.sql");//添加
arp.addSqlTemplate("operation.sql");//添加
}
//主sql文件
#namespace("operation")
#include("引入sql文件.sql");
#end
//实现sql文件,这里演示的是登录sql
#sql("register")
select id from xy_fankui where createuserid = #para(createuserid) and fkneirong = #para(fkneirong)
#end
业务层
//没啥好说的
public class ServiceRegister {
}
路由配置
public void configRoute(Routes me) {
// TODO Auto-generated method stub
me.setBaseViewPath("/ViewPath");
me.add("/abc", Controller_OneDay.class, “FirstAttempt”);
}
这里配置的寻找页面的根路径为viewpath的FirstAttempt文件夹,访问路径为/add
这里启动类的端口为12
登录
1.编写接收sql语句的业务逻辑
对应sql
#sql("register")
select id from xy_fankui where createuserid = #para(createuserid) and fkneirong = #para(fkneirong)
#end
public String Register(String username, String password) {
//对属性进行赋值
Kv kv = Kv.by("createuserid", username).set("fkneirong", password);
//用querystr返回字符串并返回,temolate调用sql时需要用主模版.sqlname的语法来获取
String str = Db.template("operation.register", kv).queryStr();
return str;
}
2.controller调用
@Inject
private ServiceRegister sest;
public void register_index() {
//进入登录页面
render("Register.html");
}
public void register_from() throws IOException {
//返回状态
String bol = null;
//获取返回结果
String str = sest.Register(get("username"),get("password"));
//进行返回值判断
if(str == null || str == "") {
bol = "<h1 style=\"color:red\">登录失败</h1>";
//转发
forwardAction("/abc/register_index");
}else {
bol = "<h1 style=\"color:red\">登录成功</h1>";
//添加此人session信息
setSessionAttr("str_id", str);
render("success.html");
}
//返回前端ajax数据
getResponse().getOutputStream().write(bol.getBytes("utf-8"));
}
3.接收前台传过来的数据进行登录
<script type="text/javascript">
//对buttan进行click时间
$("#Buttan").click(function(){
$.post({
url:"/abc/register_from",
success:function(data){
}
});
})
</script>
登录失败页面
登录成功页面
登录页面进入之后是关于数据的分页显示功能
编写业务层sql
public Page<Record> pageFind(int pagenumber) {
Page<Record> page = Db.paginate(pagenumber, 4, "select id,createuserid,fkneirong,createtime", "from xy_fankui");
return page;
}
//每页显示4条,根据传入的参数来进行第几页分页
public void page() {
/**
* 这里返回sql的分页数据,再返回给前端ajax的json数组,get获取前端传入的参数
**/
//int pagenumber = Integer.parseInt(get("pagesize"));
Page<Record> page = sest.pageFind(convent_Help.pase_int(get("pagesize")))
//当前页
System.out.println(page.getPageNumber());
//每页大小
System.out.println(page.getPageSize());
//总页数
System.out.println(page.getTotalPage());
//总条数
System.out.println(page.getTotalRow());
renderJson("page", page);
}
前端html页面如下:
恭喜你
<table class="table text-dot" id="table_page" ></table>
js如下
$(document).ready(function(){
page(1);//调用分页实现函数
});
分页函数
function page(pagesize){
$.ajax({
url:"/abc/page",
type:"get",
data:{
"pagesize":pagesize
},
success:function(data){
//对返回的数组进行解析和添加数据
var list = data.page.list;//返回的json中有一个名为list的数组存放这分页数据
//进行遍历index数组下标,obj获取参数的值
$.each(list ,function (index, obj){
//如果th不在追加分页数据的地方添加,直接在body添加会使遍历追加的时候重复添加
var first = "";
if(index == 0){
first = "<tr><th>用户姓名</th><th>注册时间</th><th>用户密码</th><th>操作</th></tr>";
}
$("#table_page").append(
first+ //添加数据,如果是index是第一次就添加头部,如果不是则添加为""
"<tr class='"+index+"'>"+//这个用于接下来实现下一页和删除等功能他的值为0.1.2.3
"<td class='' id='username'>"+obj['createuserid']+"</td>"+
"<td class='text-sub' id='time'>"+obj['createtime']+"</td>"+
"<td class='text-success' id='password'>"+obj['fkneirong']+"</td>"+
"<td class='ip'><a href='/abc/remove/"+obj['id']+" '>删除</a><button id='updata'class='up' data-toggle='modal' data-target='#myModal'>修改</button></td>"+
"</tr>"
);
//这里的样式使用的国产一个叫拼图的框架样式,官网文档里面有详细解释
});
},
error:function(){
alert("失败");
}
});
}
效果展示图
这里姓名用的是一个int字段代替(因为是其他项目的表,所以只好改改业务方面的,后面添加的时候表里有一个必填字段所以就加上了,此字段无意义)
接下来实现下一页/上一页的功能
由于前台使用ajax请求,控制器那边传参到页面有点小问题,所以先暂时实现了上一页到固定页,下一页没有做总页数限制,大家可以自行添加
上一页/下一页body代码
//调用page函数,下一页传入2,
<span style="color:red" onclick="page(2)">下一页</span>
span style="color:red" onclick="page(3)">上一页</span>
js代码
//这边定义了一个全局变量
var pagenumber = 1;
if(pagesize == 2){
//如果参数为2则把number++并赋值给size实现下一页的功能
pagenumber ++;
pagesize = pagenumber;
}
//记得在success下的第一行添加
$("#table_page").empty();
//每次分页前清空内容,不然会造成数据冗余在页面
下面的删除和更新才是最好玩的O(∩_∩)O哈哈~
由于我们是追加的,所以删除和更改不能用onclick事件,因为这是相当于未来的
但是又得实现,根据点击的不同行获取当前行的id并进行删除和修改(忘了做打开修改页面的数据回显了哈哈哈)
实现思路
1.先把后台每次传入的4行数据id按照顺序放在数组里
2.通过点击不同行获取当前点击的是第几个td控件,(记得是3.17什么的,这个他们是固定的不会变化的,当然你可以不使用动态的直接使用写死的值)
他们是按照从小到大的顺序,所以也把他们放入数组里
3.点击的时候根据获取点击的控件值去td控件数组里面进行字符串对比
获取值的下标位置再根据这个下标获取id数组的值
4.然获取id数组里面相应的数据下标值
从而获取id
修改(此处打开的是模态框)
body页面(引入的bootstrap,验证引入的拼图)
<!-- 模态框(Modal) -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content" >
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="myModalLabel">更改</h4>
</div>
<!-- 表单放置 -->
<form action="/abc/update" method="post" id="clicks">
<div class="modal-body">
<div class="input-group">
<div class="input-block"><!-- 必须是整数 -->
<input class="input input-auto" style="width:500px" name="createuserid" placeholder="必填姓名,必须是整数" type="text" required pattern="^[-\+]?\d+$" />
</div>
<br/>
<div class="input-block"><!-- 必须是中文字符 -->
<input class="input input-auto" style="width:500px" name="fkneirong" placeholder="必填内容,必须是中文字符" type="text" required />
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="submit" class="btn btn-primary" id="upda">提交更改</button>
</div>
</form>
</div><!-- /.modal-content -->
</div><!-- /.modal -->
</div>
//在each循环上面第一行放入数组定义
//获取所有的4个id值放入数组
var id = new Array();
//获取元素位置的值(可以不需要获取因为元素的值比较固定)
var index = new Array();
//这个时候each循环的下面放入这行代码
var i = $(".0 .ip").index(".table td");
var i2 = $(".1 .ip").index(".table td");
var i3 = $(".2 .ip").index(".table td");
var i4 = $(".3 .ip").index(".table td");
index.push(i,i2,i3,i4);
//之前循环追加的index作用来了,根据index生成的序号来获取他们元素的位置下标如何放入数组
var first = "";
if(index == 0){
first = "<tr><th>用户姓名</th><th>注册时间</th><th>用户密码</th><th>操作</th></tr>";
}
//在这里添加把每次遍历的id放入id数组的操作
id.push(obj['id']);
这个时候数据id和元素下标已经都获取了
打开模态框这里使用的button(不太好办因为不能使用onclick)
之前追加表格代码的最后一行
//添加了一个ipclass,等会就通过他来获取点击事件进行操作
"<td class='ip'><a href='/abc/remove/"+obj['id']+" '>删除</a><button id='updata'class='up' data-toggle='modal' data-target='#myModal'>修改</button></td>"+
//在index.push(i,i2,i3,i4);后面第一行添加on事件(因为不能使用onclick,但是可以使用on来添加click事件,和其他2个但是其他2个已经不推荐使用了)
$(".ip").on("click",function(){
//当用户点击这一行时
//点击的元素的下标值
var element;
//获得当前用户点击的td下标
var number = $("tr td").index(this);
$.each(index,function(ins, id){
//获取点击的元素位置对应数组里的位值
if(id == number){
//获取下标
element = ins;
}
});
//真正要传递的id值
//real不要设置为局部变量
real = id[element];
alert(real);
});
这里需要给page函数添加一个返回值用于打开模态框操作提交传值
function page(){
return real;//返回id值
}
提交
//模态框的提交按钮,用id来添加点击事件
<button type="submit" class="btn btn-primary" id="upda">提交更改</button>
$(document).ready(function(){
page(1);
$("#upda").click(function(){
var id = page(1);//获取id
//这里用的enjoy的参数传递方式,改变其form的action属性、
//博主原来用的&id=id的方式传递,但是会提示找不到页面
$("#clicks").attr("action",'/abc/update/'+id +' ');
});
});
让我们倒着来顺一下更新的逻辑
//controller代码
@Before(Validator_updata.class) //这里使用校验拦截器来实现业务逻辑,后面说
public void update() {
//如果返回成功就直接重新进入分页页面进行自动刷新
render("success.html");
}
sql模板
#sql("update")
UPDATE xy_fankui SET createuserid = #para(createuserid),fkneirong = #para(fkneirong) WHERE id = #para(id)
#end
业务层
public int update(String fkneirong, int createuserid, int id) {
//kv是jfianl封装的map
Kv kv = Kv.by("fkneirong", fkneirong).set("createuserid",createuserid).set("id", id);
int success = Db.template("operation.update", kv).update();
return success;//返回查询成功失败状态
}
更新校验器
public class Validator_updata extends Validator{
//注入业务
@Inject
private ServiceRegister sest;
@Override
protected void validate(Controller c) {
// TODO Auto-generated method stub
//短路校验
setShortCircuit(true);
//使输入的id不超过范围
validateInteger("createuserid", 1, 1000000000, "error", "输入的数字超过最大限制");
//通过c.获取输入的内容
String fkneirong = c.get("fkneirong");
//这里使用的是下文的一个简单的把string转换为int的封装类
int success = sest.update(fkneirong,
convent_Help.pase_int(c.get("createuserid")),
convent_Help.pase_int(c.get(0)));
if(success == 0)addError("error", "增加出现错误····");
//如果状态为0则添加失败,增加error信息
}
//失败路线
@Override
protected void handleError(Controller c) {
// TODO Auto-generated method stub
//获取上文的error信息,不能使用get获取,因为controller上面调用的是setattr
c.set("msg", c.getAttr("error"));
c.render("success.html");
//如果失败返回主页面并打印错误信息
}
}
封装转换类
public static int pase_int(String str) {
return Integer.parseInt(str);
}
//前台用来展示错误信息
<h3 style="color:red">#(msg??)</h3>
当姓名输入超过范围时
出现未知错误同理
删除
controller
public void remove() {
sest.remove(convent_Help.pase_int(get(0)));
//成功就返回主页
render("success.html");
}
业务
public void remove(int id) {
Db.template("operation.remove", Kv.by("id", id) ).delete();
}
sql
#sql("remove")
DELETE FROM xy_fankui WHERE id=#para(id)
#end
接下来是增加的功能
//前台跳转页面
<a href="/abc/insert" style="color:blue; font-size:50px">添加---</a>
controller
public void insert() {
//跳转页面
render("insert.html");
}
//body页面+错误信息展示
<div>
<form action="/abc/upload" method="POST">
<div class="input-group" style="left:420px">
<div class="input-block"><!-- 必须是整数 -->
<label>姓名id:</label>
<input class="input input-auto" style="width:500px;" value="#(createuserid??)" name="createuserid" placeholder="必填姓名,必须是整数" type="text" required pattern="^[-\+]?\d+$" />
</div>
<br/>
<div class="input-block"><!-- 必须是中文字符 -->
<label>内容:</label>
<input class="input input-auto" style="width:500px" value="#(fkneirong??)" name="fkneirong" placeholder="必填内容,必须是中文字符" type="text" required />
</div>
<br/>
<div class="input-block"><!-- 必须是整数 -->
<label>标识:</label>
<input class="input input-auto" style="width:500px" value="#(biaoshi??)" name="biaoshi" placeholder="必填内容,必须是1或者2" type="text" required pattern="^[-\+]?\d+$"/>
</div>
</div>
<br/>
<div>
<input type="submit" class="btn btn-danger" value="登录" style="position: relative; left: 500px;" id="Buttan" />
<input type="reset" class="btn btn-danger" value="重置" style="position: relative; left: 690px;" id="repLacement"/>
</div>
</form>
</div>
<br/>
<div style="position:absolute;left:500px">
<h4 style="font-size:20px;color:red;">#(msg??)</h4>
</div>
业务
public boolean insert(int createuserid,String fkneirong, int biaoshi) {
Record re = new Record().set("createuserid", createuserid)
.set("fkneirong", fkneirong).set("biaoshi", biaoshi);
boolean bool = Db.save("xy_fankui", re);
return bool;
}
sql
#sql("update")
UPDATE xy_fankui SET createuserid = #para(createuserid),fkneirong = #para(fkneirong) WHERE id = #para(id)
#end
添加页面提交controller
//校验拦截器
@Before(Validator_insert.class)
public void upload() {
render("success.html");
}
拦截器insert
@Inject
private ServiceRegister sest;
@Override
protected void validate(Controller c) {
// TODO Auto-generated method stub
//短路校验
setShortCircuit(true);
validateInteger("createuserid", 1, 1000000000, "error", "输入的数字超过最大限制");
String fkneirong = c.get("fkneirong");
int success = sest.update(fkneirong,
convent_Help.pase_int(c.get("createuserid")),
convent_Help.pase_int(c.get(0)));
if(success == 0)addError("error", "增加出现错误····");
}
@Override
protected void handleError(Controller c) {
// TODO Auto-generated method stub
c.set("msg", c.getAttr("error"));
c.render("success.html");
}
至此功能已完成,但是有个问题,用户可以随意通过路径来访问不同的页面跳过登录,所以需要使用拦截器进行主页面和添加页面的拦截
controller 添加拦截action
@Before(ControllerInter.class)
public void insert() {
}
@Before(ControllerInter.class)
public void page() {
//int pagenumber = Integer.parseInt(get("pagesize"));
Page<Record> page = sest.pageFind(convent_Help.pase_int(get("pagesize")));
//当前页
System.out.println(page.getPageNumber());
//每页大小
System.out.println(page.getPageSize());
//总页数
System.out.println(page.getTotalPage());
//总条数
System.out.println(page.getTotalRow());
renderJson("page", page);
}
@Before(ControllerInter.class)
public void register_from() throws IOException {
String bol = null;
String str = sest.Register(get("username"),get("password"));
if(str == null || str == "") {
bol = "<h1 style=\"color:red\">登录失败</h1>";
forwardAction("/abc/register_index");
}else {
bol = "<h1 style=\"color:red\">登录成功</h1>";
//添加此人session信息,用户拦截器进行判断是否登录过
setSessionAttr("str_id", str);
render("success.html");
}
getResponse().getOutputStream().write(bol.getBytes("utf-8"))
}
创建一个类 implements Interceptor
@Inject
private ServiceRegister sest;
public void intercept(Invocation inv) {
// TODO Auto-generated method stub
//获取调用拦截件的action名称
String methodname = inv.getMethodName();
//获取登录成功的session如果为空则返回到登录页面
String name = inv.getController().getSessionAttr("str_id");
System.err.println(name);
if(name == null || name == "") {
inv.getController().render("Register.html");
System.err.println("已成功拦截");
}
//由于这里不加这个的话,insert页面里面是空没有render返回,会出错
if(name != null && name != "") {
if("insert".equals(methodname)) {
inv.getController().render("insert.html");
}
}
//该方法表示继续执行后面的代码
inv.invoke();
System.err.println("拦截成功:"+methodname);
}
Demo和sql脚本的下载地址:
链接:https://pan.baidu.com/s/1aMvHfL8HrPmWhrLeR-6__Q
提取码:aiwo (爱我)