第 5 章 结合javabean实现CRUD

注意

这里介绍的是在jsp中使用自己写的javabean,不过这种写法也仅仅适用于小型应用,只打算学两下jsp玩玩的朋友可以到此为止了,完成了这章就不必继续下去了,此后难度会加大不少。

如果你不满足以下任一条件,请继续阅读,否则请跳过此后的部分,进入下一章:第 6 章 贴近servlet
  1. 了解如何在jsp中使用自定义的javabean。
  2. 了解一些jsp动作(action)。
  3. 使用jdbc操作数据库。

5.1. 概念和命名方式

需要提及的两个名词概念:
  1. CRUD是Create(创建)、Read(读取)、Update(更新)和Delete(删除)的缩写,一般应用有这四项也就足够了。
    我们这里的例子是对联系人信息进行CRUD操作。
  2. javabean是把一些操作集合在一起写成一个java类,想要进行什么操作直接调用这个类里的方法就行。
    咱们这里使用javabean的地方有两处,一个是链接数据库并进行CRUD操作,另一个把每条数据都写 成一个类。
对于CRUD应用,有一些大家默认的命名来表示不同的操作。
  1. list.jsp。读取所有信息并显示到页面上,这个是CRUD中Read(读取)。
  2. create.jsp。进入添加联系信息的页面,等待用户输入信息。
    save.jsp。接收用户提交的信息,添加到数据库中。
    这两步对应CRUD中的Create(创建)。
  3. edit.jsp。进入修改联系信息的页面,等待用户修改信息。
    update.jsp。接收用户提交的信息,修改数据库中对应的信息。
    这两步对应CRUD中的Update(更新)。
  4. remove.jsp。删除用户选择的信息。这步对应CRUD中的Delete(删除)。
下面我们将按照用户浏览的顺序对这些页面进行介绍。

5.2. Read(读取)

启动服务器,访问http://localhost:8080/05-01/就会看到联系信息列表。
看一下05-01目录里边的7个jsp页面,其中6个页面都已经介绍了,只剩index.jsp。
index.jsp是tomcat默认的索引页面,在用户访问http://localhost:8080/05-01/的时候会自动执行index.jsp,但我们更希望用户能直接进入list.jsp页面看到所有的联系方式,所以在index.jsp里使用forward跳转到list.jsp。
<%@ page contentType="text/html; charset=gb2312"%>
<jsp:forward page="list.jsp"/>
第一行是我们曾经讲过的设置中文编码。第二行叫做jsp action(jsp动作),它的写法和html标签很相像,有了它们我们可以节省很多java代码。比如,这个jsp动作就与下面的代码功能相同。
<%request.getRequestDispatcher("list.jsp").forward(request, response);
%>
从长度来看,jsp动作明显占有绝对优势,在单独使用forward的时候,建议大家优先考虑<jsp:forward page="list.jsp"/>的写法。
现在来看list.jsp里的内容,我们是如何获得这些联系信息,并把这些联系信息显示到页面上。
为了便于操作,我们将所有对数据库的操作都封装到anni.ContactDao中,这就是所谓的javabean了。现在我们想要获得所有联系信息时,只要创建一个ContactDao的实例,然后调用contactDao.getAll()获得装满联系信息的List列表就好了。
在创建ContactDao实例的时候,我们使用了另一个jsp动作:jsp:useBean,它就写在list.jsp的第二行。
<jsp:useBean class="anni.ContactDao" id="contactDao" scope="application"/>
看到这里,可能有朋友提问了,既然只是创建一个对象的实例,为什么不用new呢,那要比这样写的代码少许多,也更容易理解,为什么我们还要执意使用jsp:useBean?只是为了尝试新技术吗?
这里我提醒大家注意一下标签中的scope="application",application正是我们介绍过的四个作用域之一,既然有了这个属性就说明事情没有new这么简单了,实际上正因为scope的属性,这段jsp:usebean实际上等价于下面的代码。
<%anni.ContactDao contactDao = (anni.ContactDao) application.getAttribute("contactDao");if (contactDao == null) {contactDao = new anni.ContactDao();application.setAttribute("contactDao", contactDao);}
%>
它会先去scope定义的作用域application中取得contactDao对应的对象,这个contactDao正是标签中定义的id,转换的对象类型则是标签中class属性的值。
好的,我们先从application中获得contactDao对应的对象,然后判断得到的是否为null,如果为null说明此变量还没有初始化,这时就要使用new创建一个对象实例并放入application中。最后我们得到的就是这个contactDao实例。
现在我们得到了一个contactDao实例,并把它放到application作用域中,供所有用户公用,通过使用jsp:useBean,我们下面就可以直接使用它获得需要的数据。
<%List list = contactDao.getAll();for (int i = 0; i < list.size(); i++) {pageContext.setAttribute("contact", list.get(i));pageContext.setAttribute("row", i % 2 != 0 ? "odd" : "even");
%><tr class="${row}" οnmοuseοver="this.className='highlight';" οnmοuseοut="this.className='${row}';"><td>${contact.username}</td><td>${contact.sex}</td><td>${contact.email}</td><td>${contact.qq}</td><td>${contact.descn}</td><td><a href="edit.jsp?id=${contact.id}">修改</a> | <a href="remove.jsp?id=${contact.id}">删除</a></td></tr>
<%}
%>
首先我们调用contactDao.getAll()获得联系信息的List列表,然后使用for循环将这些信息都输出到页面。
下面的操作比较有趣,我们每获得一条信息就把它放入pageContext中。之所以这样做,是为了在下面使用el表达式显示信息。
el表达式的一个特点就是必须放到作用域里才能调用,如果使用Contact contact = (Contact) list.get(i);而不放到pageContext中,后面的${contact.username}就无法找到contact了。
这里还要提及el表达式的进一步用法,${contact.username}实际上得到的是contact.getUsername()返回的结果。这里有一个默认的转换规则,假设有一个getUsername()方法,我们先要去掉开头的get,然后将get后的那个字母小写,得到的username就是与el表达式中对应的部分。
有趣的是${contact.username}仅仅与getUsername()方法对应,无论contact有没有String username这个变量。如果我们想使用${contact.nameAndSex},只要写一个public String getNameAndSex()方法返回我们想要的数据即可,不需要添加String nameAndSex;变量。
另一个放到pageContext中的row就很直观了,我们根据行数的奇偶来决定当前行使用的css样式,这样就可以显示出斑马线的效果了,这项功能并非必要,只是为了娱乐。

5.3. Create(创建)

选择list.jsp中的“添加联系信息”,即进入create.jsp添加信息页面。
create.jsp中没有包含java,它提供给用户一个输入信息的表单,用户填写过信息后就能点击提交按钮,将数据提交给save.jsp处理。
save.jsp中与create.jsp相反,里边只有处理数据的java代码,没有显示的内容。
<jsp:useBean class="anni.ContactDao" id="contactDao" scope="application"/>
<jsp:useBean class="anni.Contact" id="contact"/>
<jsp:setProperty name="contact" property="*"/>
<%contactDao.save(contact);response.sendRedirect("list.jsp");
%>
jsp:useBean的用处我们已经了解了,先从application中取出contactDao,再创建一个contact。在创建contact的时候没有指定scope,默认情况下只会使用new创建这个局部变量,不会对任何作用域产生影响。
jsp:setProperty是新事物了,它的作用就是为某个javabean设置数据。之前我们已经使用jsp:useBean创建了一个contact实例,现在我们通过name="contact"设置这个实例的数据,property可以指定一个属性,比如property="username",也可以使用星号(*)批量设置所有可以找到的属性,这个jsp动作实际上与下面的代码等价。
contact.setUsername(request.getParameter("username"));
contact.setSex(request.getParameter("sex"));
contact.setEmail(request.getParameter("email"));
contact.setQq(request.getParameter("qq"));
contact.setDescn(request.getParameter("descn"));
这些数据都是由create.jsp提交过来的,只要它们的名称与contact中的方法对应(这次是set开头的方法了),jsp:setProperty就可以自动为它们进行赋值,转换的规则与get的方法名是类似的。
通过这一系列的jsp动作,我们得到的contact中已经设置好了用户刚刚填写的数据,现在只要进行保存就好了。
contactDao.save(contact);
response.sendRedirect("list.jsp");
contactDao.save()也已经封装好了,直接调用便完成添加功能。操作完成后记得要调用sendRedirect,将页面重定向到list.jsp,查看添加后的结果。

5.4. Update(更新)

点击列表右侧的“修改”,对这一行显示的联系信息进行修改。
虽然页面的布局与create.jsp基本相同,但为了预先显示需要修改的数据,我们需要根据请求中的id值,去数据库中查找对应的联系信息,再显示到jsp中。
点击“修改”的时候,id便附加到url后了。
<a href="edit.jsp?id=${contact.id}">修改</a>
edit.jsp中获得id的值,根据id从contactDao获得对应的联系信息contact,再将contact放到pageContext供后面的el表达式使用。
<jsp:useBean class="anni.ContactDao" id="contactDao" scope="application"/>
<%String id = request.getParameter("id");Contact contact = contactDao.get(Long.parseLong(id));pageContext.setAttribute("contact", contact);
%>
在html中显示信息的时候,直接使用el表达式,如果属性值不存在,el也会自动输出空字符串"",这对我们来说都是非常便捷的。
<input type="text" name="username" value="${contact.username}" />
edit.jsp中还有一点儿隐藏的玄机,表单中定义了一个名叫id的隐藏属性,这样服务器才能知道我们需要修改那一条数据。
<input type="hidden" name="id" value="${contact.id}" />
既然是隐藏的,我们便不能在页面上看到它,但在提交的时候它还会与其他数据一起发送到服务器。
修改信息之后,点击提交发送请求。
update.jsp中与save.jsp相似,唯一不同的是这次我们调用的是contactDao.update()而不是contactDao.save()。
contactDao.update(contact);
update()会根据id的值修改数据库中对应的数据,而不是添加一条新数据,这从跳转后的list.jsp可以看出来。

5.5. Delete(删除)

点击列表右侧的“删除”,就会删除这条数据。
remove.jsp与修改数据时一样,都需要传递一个id来指定要操作哪一条记录。
<a href="remove.jsp?id=${contact.id}">删除</a>
remove.jsp依然没有任何显示,仅仅使用java操作。
<jsp:useBean class="anni.ContactDao" id="contactDao" scope="application"/>
<%String id = request.getParameter("id");contactDao.remove(Long.parseLong(id));response.sendRedirect("list.jsp");
%>
因为只需要id和contactDao,remove.jsp的代码十分单纯,首先从request中获得id,然后删除id对应的记录,最后页面重定向到list.jsp。删除便成功了。

5.6. 用jdbc操作数据库

虽然有人说JDBC是Java Database Bridge Connection(java数据库桥接)的缩写,但sun公司一直没有承认这种解释。不过jdbc确实是一种桥接方式,所有服务器厂商都为jdbc提供对应自己数据库的驱动,我们只要学会使用jdbc中的类和方法,就可以通过它操作任何一款数据库了。
这次我们使用的是一个名叫hsqldb的嵌入型数据库,它是使用java编写的,把hsqldb-1.8.0.7.jar放到WEB-INF/lib/目录下就可以使用了。
现在看我们是如何连接数据库的,hsqldb-1.8.0.7.jar中已经为我们提供了jdbc驱动,为了方便调用我们将jdbc的配置封装在anni.DbUtils中。
  1. Class.forName("org.hsqldb.jdbcDriver");加载jdbc驱动。
    Class.forName()是惯用写法,可以强制加载指定的类,org.hsqldb.jdbcDriver是hsqldb驱动的名称,只需要记忆即可。
  2. 与数据库建立连接。
    DriverManager.getConnection("jdbc:hsqldb:res:/hsqldb/contact", "sa", "");
    三个参数分别是连接数据库使用的url,登录用户名和密码。
    url以jdbc:hsqldb:开头,表明它将使用hsqldb的驱动,后面的res:/hsqldb/contact是hsqldb的一种连接方式,它将去classpath下的hsqldb目录中读取名为contact的数据库文件作为初始配置,在这里classpath就是指的WEB-INF/classes/,你可以在WEB-INF/classes/hsqldb/下看到两个数据库文件,contact.properties和contact.script。
  3. 数据库连接十分消耗系统资源,一定要记得在使用完成后关闭,一旦忘记关闭,资源很快就会耗尽,你会得到一连串无法连接数据库的错误。
    anni.DbUtils中我们提供了一个close()方法来关闭连接。
完整的anni.DbUtils代码在WEB-INF/src/DbUtils.java。
数据库contact的表结构写在WEB-INF/sql/import.sql中。
create table contact (id bigint,username varchar(100),sex varchar(100),email varchar(100),qq varchar(100),descn varchar(200)
);
与之对应的anni.Contact的结构大致如下。
package anni;
public class Contact {private Long id;private String username;private String sex;private String email;private String qq;private String descn;// getter and setter
}
javabean中的属性与数据库中的字段一一对应,习惯上将属性定义为private,并配上对应的getter与setter方法,这样就构成了一个典型的javabean。anni.Contact的源代码在WEB-INF/src/Contact.java。
anni.ContactDao是完成CRUD操作的主体,在这里集合了anni.Contact和anni.DbUtils为jsp提供调用的方法。
  1. Read(读取)。
    /*** 获得所有联系簿.** @return contact列表*/
    public List<Contact> getAll() throws Exception {Connection conn = null;Statement state = null;ResultSet rs = null;List<Contact> list = new ArrayList<Contact>();try {conn = DbUtils.getConn();state = conn.createStatement();rs = state.executeQuery("select * from contact");while (rs.next()) {Contact contact = new Contact();contact.setId(rs.getLong("id"));contact.setUsername(rs.getString("username"));contact.setSex(rs.getString("sex"));contact.setEmail(rs.getString("email"));contact.setQq(rs.getString("qq"));contact.setDescn(rs.getString("descn"));list.add(contact);}} finally {DbUtils.close(rs, state, conn);}return list;
    }
    第一步,使用DbUtils.getConn()建立与数据库的连接Connection。
    第二步,从Connection创建一个Statement。
    第三步,使用Statement执行sql查询语句,返回查询结果集ResultSet。
    第四步,将ResultSet中的数据转换成Contact队列。
    第五步,关闭数据库的连接,并返回Contact队列作为结果。
    Connection -> Statement -> ResultSet -> close()是一个查询功能的基本结构。
    这里使用的sql语句会获得数据库中所有的联系信息,所以我们循环读取ResultSet最后得到一个Contact队列。另一个方法public Contact get(Long id)中会根据指定的主键获得一条对应记录,虽然依然返回ResultSet,但这次ResultSet中只包含一条数据,所以最终只会获得一个Contact对象。
  2. Create(创建)
    /*** 向数据库插入一条数据.** @param contact 联系信息*/
    public void save(Contact contact) throws Exception  {Connection conn = null;PreparedStatement state = null;try {conn = DbUtils.getConn();state = conn.prepareStatement("insert into contact(username,sex,email,qq,descn) values(?,?,?,?,?)");state.setString(1, contact.getUsername());state.setString(2, contact.getSex());state.setString(3, contact.getEmail());state.setString(4, contact.getQq());state.setString(5, contact.getDescn());state.executeUpdate();} finally {DbUtils.close(null, state, conn);}
    }
    第一步,使用DbUtils.getConn()建立与数据库的连接Connection。
    第二步,从Connection创建一个PreparedStatement。
    第三步,向PreparedStatement中设置参数。
    第四步,使用PreparedStatement执行更新。
    第五步,关闭数据库的连接,并返回Contact队列作为结果。
    Connection -> PreparedStatement -> 设置数据,执行更新 -> close()是一个更新数据库的基本结构,更新不需要返回数据,所以没有ResultSet。
    PreparedStatement可以帮助我们免受拼接字符串之苦,如果依旧使用Statement,会产生下面这种代码。
    state = conn.createStatement("insert into contact(username,sex,email,qq,descn) values('" +contact.getUsername() + "','" +contact.getSex() + "','" +contact.getEmail() + "','" +contact.getQq() + "','" +contact.getDescn() + "')");
    代码又臭又长,容易出错外加难以维护,你少写一个单引号不会引起编译错误,但是运行的时候会给你一大堆错误,最后连问题在哪里都找不到,推荐大家在需要设置参数的时候尽量使用PreparedStatement。
    另外PreparedStatement也能防止系统被“注入***”。
  3. Update(更新)
    update contact set username=?,sex=?,email=?,qq=?,descn=? where id=?
  4. Delete(删除)
    delete from contact where id=?
anni.ContactDao的代码在WEB-INF/src/ContactDao.java中。有关jdbc的操作既枯燥又无聊,实际上只需要记住几条sql语句就足够我们进行CRUD这种常用操作了,对于普通开发者来讲,我们也不需要再去了解更多深入的功能。
我们提供了批处理文件编译这些java代码,双击WEB-INF/src/compile.bat即可对这三个java文件进行编译,编译后的class文件自动输出到WEB-INF/classes/对应的目录下。

注意

hsqldb的数据库文件我们已经准备妥当,但我们也提供为数据库重新导入数据的脚本,执行WEB-INF/sql/run.bat会执行import.sql中的sql脚本,sqltool.rc中是连接数据库的配置。
.java, .sql, .bat, .rc文件都可以用记事本打开查看,修改内容后直接保存即可。


转载于:https://blog.51cto.com/77857/167120

第 5 章 结合javabean实现CRUD相关推荐

  1. AVUE crud upload组件示例

    在项目中使用avue crud组件,其中用到了upload组件. 官网中关于upload的解释非常简单,需要仔细阅读参数配置含义. 给出示例: 表banner(轮播横幅图表)使用avue进行CRUD操 ...

  2. Java基础篇:JDBC核心技术

    文章目录 第1章:JDBC概述 1.1 数据的持久化 1.2 JDBC介绍 1.3 JDBC程序编写步骤 第2章:获取数据库连接 2.1 要素一:Driver接口实现类 2.1.1 Driver接口介 ...

  3. 李兴华html css,2014MLDN(李兴华老师视频教程)

    资源内容: 2014MLDN(李兴华老师视频教程)|____开发工具          |____ideaIU-15.0.3.exe          |____ideaIU-15.0.2.exe   ...

  4. 深入浅出JDBC核心技术

    文章目录 JDBC核心技术--课程笔记 第1章:JDBC概述 1.1 数据的持久化 1.2 Java中的数据存储技术 1.3 JDBC介绍 1.4 JDBC体系结构 1.5 JDBC程序编写步骤 第2 ...

  5. 【Java基础】JDBC

    JDBC核心技术 第1章:JDBC概述 1.1 数据的持久化 持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用.大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数 ...

  6. JavaWeb--JDBC核心技术

    JavaWeb--JDBC核心技术 JDBC核心技术 第1章:JDBC概述 1.1 数据的持久化 1.2 Java中的数据存储技术 1.3 JDBC介绍 1.4 JDBC体系结构 1.5 JDBC程序 ...

  7. Java Web开发实战经典 李兴华版 读书笔记(一)

    有的时候总感觉读书没有效率,或是记不住,或是不能专注.所以,把读的书都做一个笔记.贴上来.方便日后回顾. 本人java后端开发,大概算个中级程序员.所以笔记中的难易程度都是根据我自身水平的判断. 看完 ...

  8. Java全栈(三)数据库技术:3.数据库之JDBC上

    第一章 JDBC概述 之前我们学习了JavaSE,编写了Java程序,数据保存在变量.数组.集合等中,无法持久化,后来学习了IO流可以将数据写入文件,但不方便管理数据以及维护数据的关系: 后来我们学习 ...

  9. JDBC核心技术学习

    JDBC核心技术 作者:kuikui 文章目录 JDBC核心技术 第1章:JDBC概述 1.1 数据的持久化 1.2 Java中的数据存储技术 1.3 JDBC介绍 1.4 JDBC体系结构 1.5 ...

最新文章

  1. 多传感器融合 | R-LINS概述
  2. R语言应用uniroot函数求解方程的根(一元解):仿真数据(方程式可视化、并添加y=0的水平横线)、uniroot函数求解方程的根(并添加方程根对应的垂直竖线)
  3. python pandas DataFrame 查找NaN所在的位置
  4. 假设写一段代码引导PC开机这段代码是 ? Here is a tiny quot;OSquot; :-D
  5. Redis进阶-Jedis以及Spring Boot操作 Redis 5.x Cluster
  6. 科大星云诗社动态20210904
  7. Hook技术--Activity的启动过程的拦截
  8. java 索引排序_Java培训MySQL之排序分组优化索引的选择
  9. 洛谷 - P2761 软件补丁问题(spfa+状压)
  10. Java网络编程1---基础
  11. AutoLayout的那些事儿
  12. “” '' ``区别 初学者自用
  13. java视频压缩 lz4_一种视频序列帧的压缩方法、解压方法及装置与流程
  14. 【NOI1998】免费馅饼,膜一膜XYX大爷
  15. 物理学与计算机相关参考文献,物理学专业论文参考文献
  16. 微软新开源!不用写 1 行代码的自动化测试工具
  17. c++数据格式化输出/字符串复制/字符串比较
  18. Qt-十字消除小游戏
  19. Tetris(俄罗斯方块)
  20. OSError: Could not find kaggle.json. Make sure it‘s located in /Users/peco/.kaggle

热门文章

  1. opencart配置United States Postal Service快递
  2. 《面向模式的软件体系结构3-资源管理模式》读书笔记(3)--- Eager Acquisition模式...
  3. 浮躁的世界里 我们要的是生活
  4. Mysql查看编码方式专题
  5. 彼尔盖茨的十句话,绝对让你改变一生
  6. 可以设置选项背景颜色的DropDownList
  7. Cesium 加载天地图
  8. 大型云原生项目在数字化企业落地过程解密 1
  9. [转] C#2010 在TreeView控件下显示路径下所有文件和文件夹
  10. MinGW GCC 7.1.0 2017年6月份出炉啦