JFinal 详细增删改查、拦截器、校验、分页 Demo讲解

最近在学习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">&times;</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 (爱我)

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值