基于JFinal完成一个简单的学生信息管理系统

项目列表
按照图中顺序从上往下
package com.demo.blog; import com.jfinal.aop.Before; import com.jfinal.aop.Inject; import com.jfinal.core.Controller; import com.jfinal.core.Path; import com.demo.common.model.Blog; /** * 本 demo 仅表达最为粗浅的 jfinal 用法,更为有价值的实用的企业级用法 * 详见 JFinal 俱乐部: https://jfinal.com/club * * 所有 sql 与业务逻辑写在 Service 中,不要放在 Model 中,更不 * 要放在 Controller 中,养成好习惯,有利于大型项目的开发与维护 */ @Path("/blog") @Before(BlogInterceptor.class) public class BlogController extends Controller { @Inject BlogService service; public void index() { setAttr("blogPage", service.paginate(getParaToInt(0, 1), 10)); render("blog.html"); } public void add() { } /** * save 与 update 的业务逻辑在实际应用中也应该放在 serivce 之中, * 并要对数据进正确性进行验证,在此仅为了偷懒 */ @Before(BlogValidator.class) public void save() { getBean(Blog.class).save(); redirect("/blog"); } public void edit() { setAttr("blog", service.findById(getParaToInt())); } /** * save 与 update 的业务逻辑在实际应用中也应该放在 serivce 之中, * 并要对数据进正确性进行验证,在此仅为了偷懒 */ @Before(BlogValidator.class) public void update() { getBean(Blog.class).update(); redirect("/blog"); } public void delete() { service.deleteById(getParaToInt()); redirect("/blog"); } }
package com.demo.blog; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; /** * 本 demo 仅表达最为粗浅的 jfinal 用法,更为有价值的实用的企业级用法 * 详见 JFinal 俱乐部: https://jfinal.com/club * * BlogInterceptor * 此拦截器仅做为示例展示,在本 demo 中并不需要 */ public class BlogInterceptor implements Interceptor { public void intercept(Invocation inv) { System.out.println("Before invoking " + inv.getActionKey()); inv.invoke(); System.out.println("After invoking " + inv.getActionKey()); } }
package com.demo.blog; import com.demo.common.model.Blog; import com.jfinal.plugin.activerecord.Page; /** * 本 demo 仅表达最为粗浅的 jfinal 用法,更为有价值的实用的企业级用法 * 详见 JFinal 俱乐部: https://jfinal.com/club * * BlogService * 所有 sql 与业务逻辑写在 Service 中,不要放在 Model 中,更不 * 要放在 Controller 中,养成好习惯,有利于大型项目的开发与维护 */ public class BlogService { /** * 所有的 dao 对象也放在 Service 中,并且声明为 private,避免 sql 满天飞 * sql 只放在业务层,或者放在外部 sql 模板,用模板引擎管理: * https://jfinal.com/doc/5-13 */ private Blog dao = new Blog().dao(); public Page<Blog> paginate(int pageNumber, int pageSize) { return dao.paginate(pageNumber, pageSize, "select *", "from blog order by id asc"); } public Blog findById(int id) { return dao.findById(id); } public void deleteById(int id) { dao.deleteById(id); } }
package com.demo.blog; import com.demo.common.model.Blog; import com.jfinal.core.Controller; import com.jfinal.validate.Validator; /** * 本 demo 仅表达最为粗浅的 jfinal 用法,更为有价值的实用的企业级用法 * 详见 JFinal 俱乐部: https://jfinal.com/club * * BlogValidator. */ public class BlogValidator extends Validator { protected void validate(Controller controller) { validateRequiredString("blog.title", "titleMsg", "请输入Blog标题!"); validateRequiredString("blog.content", "contentMsg", "请输入Blog内容!"); } protected void handleError(Controller controller) { controller.keepModel(Blog.class); String actionKey = getActionKey(); if (actionKey.equals("/blog/save")) controller.render("add.html"); else if (actionKey.equals("/blog/update")) controller.render("edit.html"); } }
package com.demo.common.model.base; import com.jfinal.plugin.activerecord.Model; import com.jfinal.plugin.activerecord.IBean; /** * Generated by JFinal, do not modify this file. */ @SuppressWarnings("serial") public abstract class BaseBlog<M extends BaseBlog<M>> extends Model<M> implements IBean { /** * id注释 */ public void setId(java.lang.Integer id) { set("id", id); } /** * id注释 */ public java.lang.Integer getId() { return getInt("id"); } /** * title注释 */ public void setTitle(java.lang.String title) { set("title", title); } /** * title注释 */ public java.lang.String getTitle() { return getStr("title"); } /** * content注释 */ public void setContent(java.lang.String content) { set("content", content); } /** * content注释 */ public java.lang.String getContent() { return getStr("content"); } }
package com.demo.common.model; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Date; import javax.sql.DataSource; import com.demo.common.DemoConfig; import com.jfinal.plugin.activerecord.dialect.MysqlDialect; import com.jfinal.plugin.activerecord.generator.Generator; import com.jfinal.plugin.activerecord.generator.TypeMapping; import com.jfinal.plugin.druid.DruidPlugin; /** * 本 demo 仅表达最为粗浅的 jfinal 用法,更为有价值的实用的企业级用法 * 详见 JFinal 俱乐部: https://jfinal.com/club * * 在数据库表有任何变动时,运行一下 main 方法,极速响应变化进行代码重构 */ public class _JFinalDemoGenerator { public static DataSource getDataSource() { DruidPlugin druidPlugin = DemoConfig.createDruidPlugin(); druidPlugin.start(); return druidPlugin.getDataSource(); } public static void main(String[] args) { // model 所使用的包名 (MappingKit 默认使用的包名) String modelPackageName = "com.demo.common.model"; // base model 所使用的包名 String baseModelPackageName = modelPackageName + ".base"; // base model 文件保存路径 String baseModelOutputDir = System.getProperty("user.dir") + "/src/main/java/" + baseModelPackageName.replace('.', '/'); System.out.println("输出路径:"+ baseModelOutputDir); // model 文件保存路径 (MappingKit 与 DataDictionary 文件默认保存路径) String modelOutputDir = baseModelOutputDir + "/.."; // 创建生成器 Generator generator = new Generator(getDataSource(), baseModelPackageName, baseModelOutputDir, modelPackageName, modelOutputDir); // 配置是否生成备注 generator.setGenerateRemarks(true); // 设置数据库方言 generator.setDialect(new MysqlDialect()); // 设置是否生成链式 setter 方法,强烈建议配置成 false,否则 fastjson 反序列化会跳过有返回值的 setter 方法 generator.setGenerateChainSetter(false); // 添加不需要生成的表名到黑名单 generator.addBlacklist("adv"); // 设置是否在 Model 中生成 dao 对象 generator.setGenerateDaoInModel(false); // 设置是否生成字典文件 generator.setGenerateDataDictionary(false); // 设置需要被移除的表名前缀用于生成modelName。例如表名 "osc_user",移除前缀 "osc_"后生成的model名为 "User"而非 OscUser generator.setRemovedTableNamePrefixes("t_"); // 将 mysql 8 以及其它原因之下生成 jdk 8 日期类型映射为 java.util.Date,便于兼容老项目,也便于习惯使用 java.util.Date 的同学 TypeMapping tm = new TypeMapping(); tm.addMapping(LocalDateTime.class, Date.class); tm.addMapping(LocalDate.class, Date.class); // tm.addMapping(LocalTime.class, LocalTime.class); // LocalTime 暂时不变 generator.setTypeMapping(tm); // 生成 generator.generate(); } }
package com.demo.common.model; import com.jfinal.plugin.activerecord.ActiveRecordPlugin; /** * Generated by JFinal, do not modify this file. * <pre> * Example: * public void configPlugin(Plugins me) { * ActiveRecordPlugin arp = new ActiveRecordPlugin(...); * _MappingKit.mapping(arp); * me.add(arp); * } * </pre> */ public class _MappingKit { public static void mapping(ActiveRecordPlugin arp) { arp.addMapping("blog", "id", Blog.class); } }
package com.demo.common.model; import com.demo.common.model.base.BaseBlog; /** * 本 demo 仅表达最为粗浅的 jfinal 用法,更为有价值的实用的企业级用法 * 详见 JFinal 俱乐部: https://jfinal.com/club * * Blog model. * 数据库字段名建议使用驼峰命名规则,便于与 java 代码保持一致,如字段名: userId */ @SuppressWarnings("serial") public class Blog extends BaseBlog<Blog> { }
package com.demo.common; import com.demo.common.model._MappingKit; import com.jfinal.config.Constants; import com.jfinal.config.Handlers; import com.jfinal.config.Interceptors; import com.jfinal.config.JFinalConfig; import com.jfinal.config.Plugins; import com.jfinal.config.Routes; import com.jfinal.kit.Prop; import com.jfinal.kit.PropKit; import com.jfinal.plugin.activerecord.ActiveRecordPlugin; import com.jfinal.plugin.druid.DruidPlugin; import com.jfinal.server.undertow.UndertowServer; import com.jfinal.template.Engine; /** * 本 demo 仅表达最为粗浅的 jfinal 用法,更为有价值的实用的企业级用法 * 详见 JFinal 俱乐部: https://jfinal.com/club * * API 引导式配置 */ public class DemoConfig extends JFinalConfig { static Prop p; /** * 启动入口,运行此 main 方法可以启动项目,此 main 方法可以放置在任意的 Class 类定义中,不一定要放于此 */ public static void main(String[] args) { UndertowServer.start(DemoConfig.class); } /** * PropKit.useFirstFound(...) 使用参数中从左到右最先被找到的配置文件 * 从左到右依次去找配置,找到则立即加载并立即返回,后续配置将被忽略 */ static void loadConfig() { if (p == null) { p = PropKit.useFirstFound("demo-config-pro.txt", "demo-config-dev.txt"); } } /** * 配置常量 */ public void configConstant(Constants me) { loadConfig(); me.setDevMode(p.getBoolean("devMode", false)); /** * 支持 Controller、Interceptor、Validator 之中使用 @Inject 注入业务层,并且自动实现 AOP * 注入动作支持任意深度并自动处理循环注入 */ me.setInjectDependency(true); // 配置对超类中的属性进行注入 me.setInjectSuperClass(true); } /** * 配置路由 */ public void configRoute(Routes me) { // 使用 jfinal 4.9.03 新增的路由扫描功能 me.scan("com.demo."); } public void configEngine(Engine me) { me.addSharedFunction("/common/_layout.html"); me.addSharedFunction("/common/_paginate.html"); } /** * 配置插件 */ public void configPlugin(Plugins me) { // 配置 druid 数据库连接池插件 DruidPlugin druidPlugin = new DruidPlugin(p.get("jdbcUrl"), p.get("user"), p.get("password")); me.add(druidPlugin); // 配置ActiveRecord插件 ActiveRecordPlugin arp = new ActiveRecordPlugin(druidPlugin); // 所有映射在 MappingKit 中自动化搞定 _MappingKit.mapping(arp); me.add(arp); } public static DruidPlugin createDruidPlugin() { loadConfig(); return new DruidPlugin(p.get("jdbcUrl"), p.get("user"), p.get("password")); } /** * 配置全局拦截器 */ public void configInterceptor(Interceptors me) { } /** * 配置处理器 */ public void configHandler(Handlers me) { } }
package com.demo.index; import com.jfinal.core.Controller; import com.jfinal.core.Path; /** * 本 demo 仅表达最为粗浅的 jfinal 用法,更为有价值的实用的企业级用法 * 详见 JFinal 俱乐部: https://jfinal.com/club * * IndexController */ @Path(value = "/", viewPath = "/index") public class IndexController extends Controller { public void index() { render("index.html"); } }
<fieldset class="solid"> <legend>新建学生信息</legend> <input type="hidden" name="blog.id" value="#(blog.id??)" /> <div> <label>姓名</label> <input type="text" name="blog.title" value="#(blog.title??)" />#(titleMsg) </div> <div> <label>班级</label> <input type="text" name="blog.content" value="#(blog.content??)"/> #(contentMsg) </div> <div> <label> </label> <input value="提交" type="submit"> </div> </fieldset>
#@layout() #define main() <h1>学生管理 ---> 新建学生信息 </h1> <div class="form_box"> <form action="/blog/save" method="post"> #include("_form.html") </form> </div> #end
#@layout() #define main() <h1>学生管理系统 <a href="/blog/add">新建学生信息</a> </h1> <div class="table_box"> <table class="list"> <tbody> <tr> <th width="4%">id</th> <th width="25%">姓名</th> <th width="25%">班级</th> <th width="12%">操作</th> </tr> #for(x : blogPage.getList()) <tr> <td style="text-align:left;">#(x.id)</td> <td style="text-align:left;">#(x.title)</td> <td style="text-align:left;">#(x.content)</td> <td style="text-align:left;"> <a href="/blog/delete/#(x.id)">删除</a> <a href="/blog/edit/#(x.id)">修改</a> </td> </tr> #end </tbody> </table> #@paginate(blogPage.pageNumber, blogPage.totalPage, "/blog/") </div> #end
#@layout() #define main() <h1>学生管理 ---> 修改学生信息 </h1> <div class="form_box"> <form action="/blog/update" method="post"> #include("_form.html") </form> </div> #end
#define layout() <!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xml:lang="zh-CN" xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN"> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> <link href="/assets/css/manage.css" media="screen" rel="stylesheet" type="text/css" /> <script src="/assets/js/jquery-1.4.4.min.js" type="text/javascript" ></script> </head> <body> <div class="manage_container"> <div class="manage_head"> <div class="manage_logo"> <a href="https://jfinal.com" target="_blank">JFinal web framework</a> </div> <div id="nav"> <ul> <li><a href="/"><b>首页</b></a></li> <li><a href="/blog"><b>学生管理</b></a></li> </ul> </div> </div> <div class="main"> #@main() </div> </div> </body> </html> #end
#define paginate(currentPage, totalPage, link) #if(totalPage <= 0 || currentPage > totalPage) #return #end #setLocal(startPage = currentPage - 4) #if (startPage < 1) #setLocal(startPage = 1) #end #setLocal(endPage = currentPage + 4) #if (endPage > totalPage) #setLocal(endPage = totalPage) #end <div class="pagination"> #if (currentPage <= 8) #setLocal(startPage = 1) #end #if ((totalPage - currentPage) < 8) #setLocal(endPage = totalPage) #end #if (currentPage == 1) <span class="disabled prev_page">上页</span> #else <a href="#(link)#(currentPage - 1)#(append)" class="prev_page">上页</a> #end #if (currentPage > 8) <a href="#(link)#(1)#(append)">#(1)</a> <a href="#(link)#(2)#(append)">#(2)</a> <span class="gap">…</span> #end #for(i : [startPage..endPage]) #if (currentPage == i) <span class="current">#(i)</span> #else <a href="#(link)#(i)#(append)">#(i)</a> #end #end #if ((totalPage - currentPage) >= 8) <span class="gap">…</span> <a href="#(link)#(totalPage - 1)#(append)">#(totalPage - 1)</a> <a href="#(link)#(totalPage)#(append)">#(totalPage)</a> #end #if (currentPage == totalPage) <span class="disabled next_page">下页</span> #else <a href="#(link)#(currentPage + 1)#(append)" class="next_page" rel="next">下页</a> #end </div> #end
#@layout() #define main() <h1>JFinal Demo 项目首页</h1> <div class="table_box"> <p>欢迎来到 JFinal极速开发世界!</p> <br><br><br> 本Demo采用 JFinal Template 作为视图文件。 点击<a href="/blog"><b>此处</b></a>开始试用Demo。 <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> </div> #end