百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

一个易学易用高效便捷的MVC和ORM框架

ccwgpt 2024-09-13 16:16 97 浏览 0 评论

开发目的

@copyright 杨同峰 保留所有权利

本文可以转载,但请保留版权信息。

SSH框架配置复杂、难用。个人认为这不是一个框架应该有的样子。框架应该使用简单、配置简单、代码简洁。于是参照Django的一些特性,编写了这个MVC+ORM框架。

特性

  1. 大量的默认约定,避免了大量的配置
  2. 配置方便、使用便捷、易于上手
  3. 支持延迟加载技术的List
  4. 和JSTL无缝兼容

配置

  1. 新建一个Web Project(MyEclipse为例)
  2. 将以下jar放到WebRoot/Web-INF下面
  3. yangmvc-1.6-all-in-one.jar
  4. 下载地址
  5. http://git.oschina.net/yangtf/YangMVC/attach_files
  6. 在web.xml中(web-app标签内)加入
 <filter>
 <filter-name>yangmvc</filter-name>
 <filter-class>org.docshare.mvc.MVCFilter</filter-class>
 <init-param>
 <param-name>controller</param-name>
 <param-value>org.demo</param-value>
 </init-param>
 <init-param>
 <param-name>template</param-name>
 <param-value>/view</param-value>
 </init-param>
 </filter>
 
 <filter-mapping>
 <filter-name>yangmvc</filter-name>
 <url-pattern>/*</url-pattern>
 </filter-mapping>
 <context-param>
 <param-name>dbhost</param-name>
 <param-value>localhost</param-value>
 </context-param>
 <context-param>
 <param-name>dbusr</param-name>
 <param-value>root</param-value>
 </context-param>
 <context-param>
 <param-name>dbpwd</param-name>
 <param-value>123456</param-value>
 </context-param>
 <context-param>
 <param-name>dbname</param-name>
 <param-value>mvc_demo</param-value>
 </context-param>
 <context-param>
 <param-name>dbport</param-name>
 <param-value>3306</param-value>
 </context-param> 

所有需要配置的都在这里了。这里做个简要说明

MVCFilter是我们MVC框架的入口。(不管是啥MVC框架都免不了这个)

它有controller和template两个参数。

controller 是你控制器存放位置的包名。 比如这里是org.demo 你建立的控制器都必须写在这个包中

template是你存放模板(视图)的地方。这个路径是相对于WebRoot即网站根目录的。

比如这里的配置(/view)是WebRoot下的view目录。

dbhost dbname dbusr dbpwd 是数据库的 地址、数据库名、用户名和密码。目前这个MVC框架只支持MySQL,后续会添加其他数据库的支持。

注意,模板目录(template参数所配置的值)以/开头,如/view。

YangMVC的第零个例子-HelloWorld程序

public class IndexController extends Controller {
 public void index(){
 output("Hello YangMVC");
 }
}

他的作用就是显示一句话。如图

第零个例子的显示

IndexController来处理应用的根目录下的请求。 index方法来处理这个目录下的默认请求。

YangMVC第一个Demo

在org.demo包下建立此类:

public class BookController extends Controller {
 public void index(){
 DBTool tool = Model.tool("book");
 LasyList list = tool.all().limit(0, 30);
 put("books", list);
 render();
 }
}

在WebRoot/view/book/下建立一个index.jsp

其中核心的代码为

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
(此处省略一堆无关的HTML代码)
<table class="table table-bordered">
 <c:forEach var="b" items="${books }">
 <tr>
 <td>${b.id }</td>
 <td>${b.name }</td>
 <td>${b.author }</td>
 <td>${b.chaodai }</td>
 <td>${b.tm_year }</td>
 <td>
 <a href='book/edit?id=${b.id}'>编辑</a>
 <a href='book/del?id=${b.id}'>删除</a>
 
 </td>
 </tr>
 </c:forEach>
</table>

一个显示列表的网页就此搞定。访问应用目录下的book/目录即可显示出结果

这里写图片描述

你作出的结果可能没那么好看,这完全取决于css。

在YangMVCDemo / WebRoot / view / book / mvc.css 中有一个漂亮的表格定义。

你可以通过类似下面的语句来加入到网页中

<link href="view/book/mvc.css" rel="stylesheet">

注意路径要对。

说明:

这个BookController是一个控制器,它的每一个公共方法都对应一个网页(如果不想对应,你需要将其设为私有的)

Model和DBTool是整个ORM框架的核心。Model表示模型,它用来与数据库表相对应。在创建一个Model时,会指定对应的表名。

这里和Hibernate不同,Hibernate需要预先生成所有数据库表的对应类, 而这个Model可以与任何表格关联,而不需要预先生成任何一个类。 这正是YangMVC中的ORM的优势所在。

DBTool tool = Model.tool("book");

程序中使用Model的静态方法tool获取一个DBTool对象,tool传入的参数book是数据库的表名。

这样DBTool就和book表建立了关联。

LasyList list = tool.all().limit(0, 30);

伙计们快看,这是个LasyList,一个支持懒惰加载机制的列表。它是List类的子类,这也就是它为什么能在JSTL中使用foreach变量的原因。

首先我们调用了tool的all()方法,天哪,难道要加载book表的所有数据,兄弟不用害怕,在这个时候,它并没有进行任何数据的读写,指示记录了现在要访问book表的所有数据这一信息。 all()方法会返回一个LasyList对象。这么设计的原因是我们后面可以跟一连串的过滤方法。方便我们编程。我们可以写出这样的东西:

list = tool.all().gt("id", 12).lt("id", 33).eq("name","haha").like("author","王");

这个例子相当于执行了如下SQL语句:

 select * from book where id>12 and id<33 and name='haha' and author like '%王%'

在上面的例子中, all()返回的LasyList又调用了它的limit方法,这一步仍然没有真正访问数据库。

那么访问数据库从哪里开始呢? 从你获取这个列表的一项时。

一个List,可以使用枚举的方法来访问

for(Model m : list){
 
}

也可以使用get方法来访问。如

Model m = list.get(12)

在你访问具体它的一个元素(Model)时,数据库查询才会启动。而且也不是将所有数据放到内存中。比如你通过上面for的方法枚举时,其实它是通过ResultSet的next游标在移动,所以它很高效!也避免了无用的数据库操作。

put("book",list)

该方法将查询得到的book塞入request中,在jsp网页中就可以使用JSTL来使用它。因为它是一个List,所以用forEach去访问他。

Model 的一个对象对应于数据库表的一行(一条记录),Model是一个Map的子类!!!,所以在JSTL中,你可以使用

${ b.name } 的方式来访问名为b的Model 的name项。 它相当于

 Model m = ....
 m.get("name")

是不是很方便??? 真的是非常方便的。。

第二个Demo

添加书籍页面

 public void add(){
 DBTool tool = Model.tool("book");
 //处理提交数据
 if(isPost()){ //isPost
 Model m = tool.create(); //创建新的
 Log.d(m);
 paramToModel(m);
 tool.save(m);
 put("msg","添加成功");
 }
 //显示数据
 renderForm(tool.create());
 }

对应的/view/book/add.jsp (这是默认对应的模板地址)的核心内容

 <div style="margin-left:100px">
 <h1>添加书籍 ${msg }</h1>
 ${book_form }
 </div>

这里写图片描述

上面的例子控制器其实是对应两个页面。 在收到Get请求的时候显示表单,在用户提交数据时,做插入操作,并显示表单。(我们当然可以把这两个页面写到两个不同的方法中)

我们还是使用Model.tool获取一个DBTool。

先来看显示表单,就一句话

 renderForm(tool.create());

tool的create方法会返回一个Model对象,这个对象和book表相关联(因为tool和book表关联)。

并将这个Model传递给renderForm方法。这个方法会根据book表格的元数据自动创建一个表格。

哇偶!

那么这个Form插入到网页的什么位置呢? 将 ${book_form } 放入网页中 即可。

如果来的是POST请求(使用isPost()方法来判断)

使用tool的create方法创建一个新的Model, 尽快还有其他创建Model对象的方式,但如果你希望插入,请尽量使用这种方式。

paramToModel(m) ,这个方法会自动查找表单中,名字与数据库字段名匹配的项,并自动赋值给Model的相应项。是不是很方便。。。

想起了Struts那悲催的功能定义。 泪奔。。。。

随后直接调用tool的save方法将其保存到数据库中!OK了!万事大吉!

细心的小朋友会问: 数据库中的字段名都是英文的如name,为什么在网页上显示的是中文???

看看我的数据库表格定义

CREATE TABLE `book` (
 `id` int(11) NOT NULL auto_increment COMMENT '编号',
 `file_name` varchar(50) default NULL,
 `name` varchar(50) default NULL COMMENT '名称',
 `author` varchar(50) default NULL COMMENT '作者',
 `chaodai` varchar(50) default NULL COMMENT '朝代',
 `tm_year` varchar(50) default NULL COMMENT '年代',
 `about` longtext COMMENT '简介',
 `type` varchar(50) default NULL COMMENT '类型',
 `catalog_id` int(11) default NULL COMMENT '分类',
 PRIMARY KEY (`id`),
 KEY `catalog` USING BTREE (`catalog_id`)
) ENGINE=InnoDB AUTO_INCREMENT=912 DEFAULT CHARSET=utf8;

真相大白与天下,我是通过给字段加注释实现的这一点。只要你将数据库表格加上注释,它就会自动获取注释并显示,对于没有注释的字段,则会显示字段名。如那个扎眼的file_name

好了,这几行代码就搞定了输入表单和表单的处理。

第三个demo-编辑(自动创建的修改表单)

细心的朋友发现,我们是按照CRUD的逻辑来将的。下面是编辑网页。

 public void edit() throws NullParamException{
 DBTool tool = Model.tool("book");
 //处理提交数据
 if(isPost()){ //isPost
 Model m = tool.get(paramInt("id"));
 Log.d(m);
 paramToModel(m);
 tool.save(m);
 put("msg","修改成功");
 }
 //显示数据
 Integer id = paramInt("id");
 checkNull("id", id);
 renderForm(tool.get(id));
 }

HTML页面放在/view/book/edit.jsp中,核心代码只是将add.jsp中的添加二字改为了"编辑“二字。

 <div style="margin-left:100px">
 <h1>编辑书籍 ${msg }</h1>
 ${book_form }
 </div>

这个代码长了一点, 有17行。对于用YangMVC的,已经算够长的了。它仍然是两个网页!!!

你可以吧显示表单的代码和处理表单的分到两个方法中写。

先看显示数据。 首先使用paramInt方法获取URL参数id,我们就是要编辑id指定的书籍。

调用checkNull来检查一下。 在我的开发生涯中,遇到各种参数检查,所以这个功能是必须有的,如果checkNull不过,就会抛出一个异常。 这样做的目的是不要让这种参数检查干扰我们正常的逻辑。这不就是异常之所以存在的意义么?

如果缺少这个参数,页面会提示说缺少这个参数。

下面使用tool.get(id)方法来获取一个Model(一条记录)。这个方法是根据表格的主键进行查询,返回的不是列表而是一个具体的Model对象。在这里我建议主键应当是整数、且是数据库自增的。

renderForm传入一个model,这个model中有数据,就会被显示出来。

就这样。编辑功能写好了。

有的朋友问,如果不想用默认的表单怎么办? 那你自己写一个表单在你的模板里就是了。只不过,你可以先用这个方法吧表单生成出来,然后按你的意图修改就成了。这也节省大量时间啊。做过Form的请举手。

第四个DEMO-删除

 public void del(){
 Integer id = paramInt("id");
 Model.tool("book").del(id);
 jump("index");
 
 
 }

瞧瞧就这点代码了, 获取参数id,并调用tool的del方法删除。最后一句我们第一次见,就是跳转。跳转到同目录下的index这个默认页(显示的是书籍列表)

控制器创建

控制器是一个Java类,类有若干方法。在YangMVC的设计中,控制器的每一个公共的方法都映射对应一个网页。这样一个Java类可以写很多的网页。 方便管理。(当然,你也可以在一个控制器中只写一个方法来支持网页,这没问题(⊙﹏⊙)b)

所有的控制器都要继承 org.docshare.mvc.Controller 这个类。充当控制器方法的方法应当是没有参数没有返回值的。如上面demo所示。

public class IndexController extends Controller {
 public void index(){
 output("Hello YangMVC");
 }
}

这些控制器都要写在配置所制定的package中,或者子package中。如在上面的配置中

 <init-param>
 <param-name>controller</param-name>
 <param-value>org.demo</param-value>
 </init-param>

这个包为org.demo所有的控制器都要卸载这个包内。(你可以写到外面,但它不会管用O(∩_∩)O~)

路径映射

所谓路径映射就是要将 一个控制器(一个Java类)和一个网址建立关联。 用户访问某网址时,框架自动调用控制器的某个函数。

因为本框架设计思想希望配置尽可能少,所以这里的路径映射是通过命名关系的。

假设应用的根目录为

http://localhost:8080/YangMVC/

如在org.demo下(这个目录可以在web.xml中配置,可见上一节)有一个BookController。

那么这个类的路径是 http://localhost:8080/YangMVC/book/

用户访问这个路径时,框架会调用BookController 的index方法。如果没有这个方法则会报错。

index方法用以处理某个路径下的默认网页(网站以斜杠结尾的都会调用某个类的index方法来处理)。

book这个地址,将第一个字母大写,后面追加Controller。于是

book (路径名)-> Book -> BookController(类名)

这就是路径和类名的默认关联。

在这个网站后加入方法名可以访问BookController的 任何一个公共方法。

如 http://localhost:8080/YangMVC/book/edit 与BookController的edit方法关联。

需要注意的是,如果你写的是 http://localhost:8080/YangMVC/book/edit/ (比上一个网站多了一个斜杠), 则它对应的是 book.EditController下的index方法 而不是BookController下的edit方法。

控制器方法

获取request中的参数

String s = param("name");
Integer id = paramInt("id");

输出方法

output方法

 output("Hello YangMVC");

这个方法输出一个文本到网页上(输出流中),并关闭输出流。因为它会关闭流,所以你不要调用它两次。你如果需要输出多次,以将内容放到StringBuffer中,然后统一输出。

render方法

 public void paramDemo(){
 put("a", "sss");
 render("/testrd.jsp");
 
 }

这里的testrd.jsp是模板目录(/view)目录下的。 /view/testrd.jsp

这里的参数应该是相对于模板目录的相对路径。

render方法使用参数制定的网页(一个包含JSTL的jsp文件),将其输出。可以通过put来制定参数。下面会详细讲。

render()方法

这个render方法是没有参数的,它会使用默认模板,如果这个模板不存在,就会提示错误。
 public void renderDemo(){
 request.setAttribute("a", "sss");
 render();
 
 }

在配置 controller 为org.demo , template为/view 这种情况下。

org.demo.IndexController的renderDemo方法会对应/view/renderDemo.jsp

之所以模板存在于模板根目录下,是因为这个IndexController是处理应用根目录的。他们有对应关系。

如果是org.demo.BookController,它对应 app根目录下的 /book/ 目录。

它的add方法对应路径 /book/add

如果应用名为hello,那么完成路径应该是 /hello/book/add

outputJSON 方法

该方法将参数转化为JSON,并向网页输出。

 public void jsonDemo(){
 Map<String, Object> map = new HashMap<String, Object>();
 map.put("id", 12);
 map.put("name", "Yang MVC");
 map.put("addtm",new Date());
 
 outputJSON(map);
 }
这个代码稍长,其实上面的所有都是生成一个Map,最后一句输出。outputJSON可以输出List,Map和任何Java对象。内部转换是使用fastjson实现的。

自动生成并输出一个表单

public void renderForm(Model m,String template,String postTo)

该函数会根据模型对应的表结构,自动生成一个表单,并将其内容放入 表格名_form 中,如book表会输出到 book_form 中。

在网页中,直接写 ${book_form}就可以将表单放下去。

template制定对应的模板文件,可以省略,省略后按照默认规则查找模板文件。

postTo设定 表单提交的网页,可以省略,默认是"",即当前网页(Controller)。

获取参数的方法

  1. param(String p) 获取参数p的值,以String类型返回
  2. paramInt(String p) 获取参数p的值,以Int类型返回,如果不是整数,则会出现异常
  3. public Model paramToModel(Model m)
  4. 根据名称匹配的原则,将与模型中参数名相同的参数的值放入模型中。并返回该模型。
  5. 是收集表单数据到模型中的神器,手机后就可以直接进行数据库操作了。
  6. paramWithDefault 获取参数,但同时带上默认值,如果没这个参数则返回默认值。

检查方法

public void checkNull(String name,Object obj)

检查obj是否为null,如果是抛出NullParamException异常。

ORM框架

Model与DBTool

Model 对象对应数据库的表格,它会与一个表格进行绑定。DBTool相当于是它的DAO类。

YangMVC的ORM组件可以单独使用。使用前需要先配置数据库:

 Config.dbhost = "localhost";
 Config.dbname = "dc2";
 Config.dbpwd = "123456";
 Config.dbusr ="root";
 Config.dbport="3306";

也可以和MVC框架一起使用。配置时在web.xml中配置

创建一个DBTool对象

 DBTool tool = Model.tool("book");

其中book是数据库表的名字。

创建一个空的Model

DBTool tool = Model.tool("book");

Model m = tool.create(); //创建新的

根据主键读取一个Model

 Model m = tool.get(12);

查询表中所有的行

 LasyList list = tool.all();
all返回一个LasyList对象。这个对象在此事并没有真正进行数据库查询,只有在页面真正读取时才会读取数据库。这是它叫做Lasy的原因。此处借鉴了Django的实现机制。

查询的limit语句

 LasyList list = tool.all().limit(30);
 list = tool.all().limit(10,30);
 

查询的等式约束

 tool.all().eq("name","本草纲目")

查询的不等式约束

 tool.all().gt("id",12) //id < 12
 tool.all().lt("id",33) //id <33
 tool.all().gte("id",12) //id>=12
 tool.all().lte("id",33) //id<=33
 tool.all().ne("id",33) //不相等
 

模糊查询

 tool.all().like("name","本草")
查找所有名字中包含本草的书。返回一个LasyList

排序

 tool.all().orderby("id",true);
按照id的增序排列。 如果是false,则是降序。

级联查询

因为这些上面的过滤器函数全部都会返回一个LasyList对象, 所以可以采用级联的方式进行复杂查询。如:

list = tool.all().gt("id", 12).lt("id", 33).eq("name","haha").like("author","王");

这个例子相当于执行了如下SQL语句:

 select * from book where id>12 and id<33 and name='haha' and author like '%王%'

根据原始sql获取(version >=1.5.4)

 LasyList list = LasyList.fromRawSql("select name from book");

使用原始的sql获取的List中的模型将和数据库表没有关联。

Model的相关功能

model 是一个继承自Map<String,Object> 的类,所以对于

Model m;

你可以在网页中使用${m.name}的方式来访问它的name键对应的值。相当于m.get("name")

这种写法在JSTL中非常有用。让Model继承Map的初衷就在于此:方便在JSTL中使用。

大家也许注意到了LasyList是一个继承自List<Model> 的类.

这就使得不管是LasyList还是Model在JSTL中访问都极为的便利。

访问所有的键值(即DAO对象的所有属性)

 model.keySet();

访问某一个属性的值

 model.get(key)

设置某一个属性的值

 model.put(key,value)

相关推荐

如何让老师看完文章后还啧啧称奇?满分作文有框架,这3点是关键

历年来语文考试中,作文的分数都占着相当大的一个比例,同时作文也是最容易拉开差距的一个模块。别人拿满分,而你却只有20分左右,分数的差距就是这样拉开的。作文想拿很高的分数却是不容易,但不是完全不可能的事...

小学作文写作技巧和方法,万能公式框架法。家长收藏

小学语文老师用心整理,将写作框架编成万能公式。作文的写作是语文学习中的重要一环,从小学到初中再到高中,作文一直是语文考试中占分比重最高的部分。小学阶段的语文写作相对来说比较简单,主要是打基础,但是很多...

如何用爆款改写技巧提升文章吸引力结构重塑:打破原文框架悬念前

如何用爆款改写技巧提升文章吸引力?结构重塑:打破原文框架悬念前置法-在开头设置悬念或提出反常识的结论,吸引读者注意力。例如,将“接纳不完美是治愈的开始”改写为“天天逼自己当完美超人?别杠了!生活本就...

守护袁昆:是否有必要按框架去写文章,拍摄剪辑视频?

(文/守护袁昆)如今不管是写文章还是剪辑视频,越来越多的朋友喜欢用框架、用脚本,作为互联网创作者,我们是否有必要按框架去写文章,拍摄剪辑短视频呢?其实在内容创作过程中,是否使用框架始终是一个充满争议的...

揭秘!爆款文章的秘密:让读者无法抗拒的文章框架

说说我自己一开始写文章都会犯一个毛病,文章,通常是想到哪里,写到哪里,“管不住字儿”。这样往往会出现以下问题绊住我们继续写下去1、很容易,写着就跑偏了,最终出来的成品和最初的设想偏离很大2、会写得很慢...

想要写出逻辑清晰的文章,你需要掌握哪些写作结构

想要写出好文章,就必须要了解文章的结构和框架。一篇文章结构清晰,读者就很容易跟上作者的思路,看出文章的重点内容。如果你对新媒体的文章有进行过研究,你就会发现很多公众号的文章结构都是类似的。所以你需要掌...

写作结构拆解:从选题到框架,如何让文章说服力翻倍?

你有没有想过,为什么有些文章能轻松获得很高的阅读量,而你的文章却始终无人问津?其实,写出爆款文章并没有想象中那么难。关键在于选择一个吸引人的主题,并用一个清晰的写作框架,通过2-3个有力的子观点支撑...

申论怎么写?结构怎么弄?这篇文章是最基本的好文章框架

写作总被吐槽逻辑混乱?三步搭建框架法,新手也能写出漂亮文章

一、结构决定论:信息传递的桥梁写作者和读者之间始终存在一道隐形的鸿沟。作者脑海中的想法如同一棵枝繁叶茂的大树,但直接倾倒给读者时,往往只剩下零散的枝叶,信息在传达过程中的丢失,作者输出的和读者读到的不...

如何搭建文章框架:新手写作者很有必要看

#头条深一度-深度阅读计划#见面好呀,我是潼臻~37岁,边上班边带娃的二胎妈妈藏起生活里的琐碎,期望你我都能遇到更好的自己~~~~~~~~~~~~~~~~~~果然多读书是可以真切学习到有用的东西最近把...

模型上下文协议(MCP)的可视化向导

最近,模型上下文协议(MCP)引起了广泛关注。你一定听说过它。今天,让我们来了解一下它是什么。直观地说,MCP就像是AI应用的USB-C接口。正如USB-C提供了一种标准化的方式,用于将...

97个人放一页PPT!用对Smartart架构图直接开挂!

从讯飞出差回来的路上,在高铁上看到一条微博,关于红楼梦人物的思维导图:下面很多家长说,帮助很大,能帮助上学的孩子梳理清人物关系,我看了下,清楚是清楚,但真的不太好看!作为一名PPT博主,我就顺带在高...

技术架构规范与实践(二)架构设计示例

1.逻辑架构1.1领域概念1.2宏观应用架构1.3宏观流程1.4微服务拆分与分层2.技术架构3.开发架构3.1后端技术栈分类名称版本描述后端框架/组件JavaJdk8(openjdk:8u342)后...

倾斜柱模板安装加固

1、适用范围:呈梯形逐层向内侧倾斜的框架柱。2、工艺流程:定位放线-配模-校正梁位置-安装加固。3、工艺方法:(1)定位放线首先现场技术管理人员对每颗不同标高的梁底、梁中边线及200mm控制线进行平面...

地基与基础工程、主体工程节点构造

#去班味吧#桩头凿除环切法工艺说明:1、根据桩头预留长度(深入承台10cm)放样桩顶标高,施工人员根据测量结果在基桩上用红油漆标注环切线;2、在切割线以上部分桩底,人工用钢钎打入桩底约15cm,打入时...

取消回复欢迎 发表评论: