本课程是基于 实验楼 线上环境制作的,因此文档叙述和截图均与其有关。使用其他实验环境也没有太大影响,实验步骤是类似的。
本实验环境采用带桌面的 Ubuntu Linux 环境,实验中可能会用到桌面上的程序:
很多同学可能先后学习了 Spring、Struts 以及 Hibernate 课程(学习本项目课之前,您也应该先学习这三门课程):
今天我们就带给大家 3 种框架结合应用的实例,也就是常说的 SSH(Spring + Struts + Hibernate)集成框架,它是应用十分广泛的一种 Java Web 应用程序开发框架。通过这个例子,你会感受到 SSH 框架的强大之处,也会对之前学习的各种框架的应用和理解更加清晰、透彻。
SSH 框架也体现了典型的分层结构,分层有很多好处,例如项目整体的构架更加清晰,代码也更易维护和扩展。
通常来说,SSH 整体上大致可以用下图描述:
在后面的编码过程中,最好结合这个框架分析图来理解,会更加清晰。
新建 Dynamic Web Project,命名为 SSH(遇到窗口显示不完整的问题,请将窗口放到最大化即可)
先下载依赖包(由于依赖包比较大,这里分成了 3 个小包分别下载,但都要导入到项目中):
$ wget http://labfile.oss.aliyuncs.com/courses/339/lib-part1.zip
$ wget http://labfile.oss.aliyuncs.com/courses/339/lib-part2.zip
$ wget http://labfile.oss.aliyuncs.com/courses/339/lib-part3.zip
分别解压 3 个小包,解压之后, 将 3 个部分所有的 jar 包 ,全部都复制到工程目录下的 WebContent/WEB-INF/lib
目录下面(截图只包含部分 jar 包):
修改 web.xml(WebContent/WEB-INF/web.xml)文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>SSH</display-name>
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
<!-- 配置 spring -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置 spring 资源文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans.xml</param-value>
</context-param>
<!-- 配置 struts -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
在 Java Resources/src
目录下,新建各层 package,方便后面的编码工作:
输入密码:shiyanlou
在 model 下新建一个实体类 User.java
,以及它的映射文件 User.hbm.xml
:
User.java
这个对象包含一个 id 主键,一个 name 属性以及 company 属性,很简单。
package shiyanlou.ssh.model;
public class User {
private int id;
private String name;
private String company;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
}
User.hbm.xml
根据上面的对象,它的配置文件为:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="shiyanlou.ssh.model">
<!-- class 和 table 对应 -->
<class name="User" table="ssh_test">
<!-- 一一对应 -->
<id name="id" column="id"></id>
<property name="name" column="name"></property>
<property name="company" column="company"></property>
</class>
</hibernate-mapping>
在 dao 下新建一个 UserDao.java
接口,以及它的实现类 UserDaoImpl.java
:
UserDao.java
package shiyanlou.ssh.dao;
import java.util.List;
import shiyanlou.ssh.model.User;
public interface UserDao {
// 用以登录时检查数据库中是否存在该用户
public boolean checkUser(User user);
// 添加用户
public boolean addUser(User user);
// 删除用户
public boolean deleteUser(int id);
// 修改用户信息
public boolean updateUser(User user);
// 查询用户
public List<User> queryUser(User user);
public User queryById(int id);
// 查询所有用户
public List<User> queryAll();
}
UserDaoImpl.java
package shiyanlou.ssh.dao;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import shiyanlou.ssh.model.User;
public class UserDaoImpl implements UserDao {
private SessionFactory sessionFactory;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
// 登录时,检查用户是否存在于数据库中
@Override
public boolean checkUser(User user) {
// TODO Auto-generated method stub
// 开启 session
Session session = sessionFactory.openSession();
// 开启事务
session.beginTransaction();
// 查询语句
Query query = session.createQuery(" from User u where u.id=:id and u.name=:name");
// 设定查询语句中变量的值
query.setParameter("id", user.getId());
query.setParameter("name", user.getName());
// 查询结果
User u = (User)query.uniqueResult();
// 事务提交并关闭 session
session.getTransaction().commit();
session.close();
// 查询结果不为 null,说明存在则返回 true
if (null != u ) {
return true;
}
return false;
}
// 增加用户
@Override
public boolean addUser(User user) {
// TODO Auto-generated method stub
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(user);
session.getTransaction().commit();
session.close();
return true;
}
// 删除用户
@Override
public boolean deleteUser(int id) {
// TODO Auto-generated method stub
Session session = sessionFactory.openSession();
session.beginTransaction();
// 通过 id 找到该 user
User u = (User)session.get(User.class, id);
session.delete(u);
session.getTransaction().commit();
session.close();
return true;
}
// 更新用户信息
@Override
public boolean updateUser(User user) {
// TODO Auto-generated method stub
Session session = sessionFactory.openSession();
session.beginTransaction();
session.update(user);
session.getTransaction().commit();
session.close();
return true;
}
// 用户查询,用于在网页上根据不同条件的查询
//(这里可以根据 name 或 company 查询,在后面的 JSP 页面中会体现)
@Override
public List<User> queryUser(User user) {
// TODO Auto-generated method stub
Session session = sessionFactory.openSession();
session.beginTransaction();
// 使用 StringBuffer 便于后面根据不同条件,连接查询语句
StringBuffer hq = new StringBuffer(" from User u where 1=1");
// 参数的值的集合
ArrayList<String> params = new ArrayList<String>();
// name 不为空,则在查询语句中连接 name 查询条件
// 并添加 name 的值,便于后面查询
if (user.getName() != null && !"".equals(user.getName())) {
hq.append(" and u.name=?");
params.add(user.getName());
}
// 同上
if (user.getCompany() != null && !"".equals(user.getCompany())) {
hq.append(" and u.company=?");
params.add(user.getCompany());
}
// 根据参数的数量,设定查询条件的个数
Query query = session.createQuery(hq.toString());
for (int i = 0; i < params.size(); i++) {
query.setString(i, params.get(i));
}
// 查询结果
List<User> users = query.list();
session.getTransaction().commit();
session.close();
return users;
}
// 通过 id 查找用户
@Override
public User queryById(int id) {
// TODO Auto-generated method stub
Session session = sessionFactory.openSession();
session.beginTransaction();
User u = (User)session.get(User.class, id);
session.getTransaction().commit();
session.close();
return u;
}
// 查找所有用户
@Override
public List<User> queryAll() {
// TODO Auto-generated method stub
Session session = sessionFactory.openSession();
session.beginTransaction();
Query query = session.createQuery(" from User u");
List<User> users = query.list();
session.getTransaction().commit();
session.close();
return users;
}
}
hibernate.cfg.xml
在 Java Resources/src
目录下,新建 Hibernate 配置文件 hibernate.cfg.xml
:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 数据库连接设置 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/ssh</property>
<property name="connection.username">root</property>
<property name="connection.password"></property>
<!-- 数据库方言设置:MySQL -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 开启 Hibernate 自动 session 管理 -->
<property name="current_session_context_class">thread</property>
<!-- 设置 SQL 语句输出到 Eclipse 控制台 -->
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<!-- 第一次加载 hibernate 时根据 model 类会自动建立起表的结构(前提是先建立好数据库),以后加载 hibernate 时根据 model 类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。-->
<property name="hbm2ddl.auto">update</property>
<!-- 映射文件 -->
<mapping resource="shiyanlou/ssh/model/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
在 service 下新建一个 UserService.java
接口,以及它的实现类 UserServiceImpl.java
,service 为 dao 的上层,它是直接调用 dao 层的相关操作的,相对来说要简单很多,因此这里也不详细解释了。
UserService.java
package shiyanlou.ssh.service;
import java.util.List;
import shiyanlou.ssh.model.User;
public interface UserService {
public boolean checkUser(User user);
public boolean addUser(User user);
public boolean deleteUser(int id);
public boolean updateUser(User user);
public List<User> queryUser(User user);
public User queryById(int id);
public List<User> queryAll();
}
UserServiceImpl.java
package shiyanlou.ssh.service;
import java.util.List;
import shiyanlou.ssh.dao.UserDao;
import shiyanlou.ssh.model.User;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public boolean checkUser(User user) {
// TODO Auto-generated method stub
if ( userDao.checkUser(user) ) {
return true;
}
return false;
}
@Override
public boolean addUser(User user) {
// TODO Auto-generated method stub
return userDao.addUser(user);
}
@Override
public boolean deleteUser(int id) {
// TODO Auto-generated method stub
return userDao.deleteUser(id);
}
@Override
public boolean updateUser(User user) {
// TODO Auto-generated method stub
return userDao.updateUser(user);
}
@Override
public List<User> queryUser(User user) {
// TODO Auto-generated method stub
return userDao.queryUser(user);
}
@Override
public User queryById(int id) {
// TODO Auto-generated method stub
return userDao.queryById(id);
}
@Override
public List<User> queryAll() {
// TODO Auto-generated method stub
return userDao.queryAll();
}
}
在 action 包下,新建类 UserAction.java
,action 为 service 的上层,所以它会调用 service:
UserAction.java
package shiyanlou.ssh.action;
import java.util.Map;
import shiyanlou.ssh.model.User;
import shiyanlou.ssh.service.UserService;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class UserAction extends ActionSupport{
/**
*
*/
private static final long serialVersionUID = 1L;
private User user;
private UserService userService;
private Map<String, Object> request;
private Map<String, Object> session;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
// 将查询的所有结果放到上下文中
// key 为 users,后面的 jsp 页面会用到
public String list() {
request = (Map)ActionContext.getContext().get("request");
request.put("users", userService.queryAll());
return "success";
}
// 登录检查
public String checkUser() {
boolean flag = userService.checkUser(user);
// 检查有效
if (flag) {
// 将当前 user 对象放到 session 中
session = ActionContext.getContext().getSession();
session.put("user1", user);
// 返回所有结果显示在页面
return list();
}
// 检查无效
return "error";
}
// 删除用户,之后再返回所有查询结果动态显示在页面上
public String deleteUser() {
userService.deleteUser(user.getId());
return list();
}
// 同上
public String addUser() {
userService.addUser(user);
return list();
}
// 修改用户信息,通过 id 找到要修改的用户
// 并返回 "update" 字符串,用以跳转到修改页面
public String updateUser() {
user = userService.queryById(user.getId());
return "update";
}
// 在修改页面完成修改,调用 service 的 update
// 之后再返回所有查询结果动态显示在页面上
public String updateUserImpl() {
userService.updateUser(user);
return list();
}
// 根据条件查询用户,查询结果放到上下文中
// tag 用以标记,后面在 jsp 页面有体现
public String queryUser() {
request = (Map)ActionContext.getContext().get("request");
request.put("users2", userService.queryUser(user));
request.put("tag", 2);
return list();
}
}
beans.xml
在 Java Resources/src
目录下新建 Spring 配置文件 beans.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"
default-autowire="byName"
>
<!-- sessionFactory 的配置 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
</bean>
<!-- 事物管理器 -->
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 配置哪些类哪些方法使用事务 -->
<aop:config>
<!-- 只对 service 层实施事务 -->
<aop:pointcut expression="execution(public * shiyanlou.ssh.service..*.*(..))" id="businessService"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="businessService"/>
</aop:config>
<!-- 事务的传播特性 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="check*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 各层的映射对应关系 -->
<bean id="userDao" class="shiyanlou.ssh.dao.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<bean id="userService" class="shiyanlou.ssh.service.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<bean id="userAction" class="shiyanlou.ssh.action.UserAction">
<property name="userService" ref="userService"></property>
</bean>
</beans>
structs.xml
同样在 Java Resources/src
目录下,新建 Struts 配置文件 struts.xml
:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<!-- 启用开发者模式 -->
<constant name="struts.devMode" value="true"></constant>
<!-- 启用动态方法调用 -->
<constant name="struts.enable.DynamicMethodInvocation" value="true"/>
<!-- 根据不同的返回结果,给出不同的响应界面 -->
<package name="user" extends="struts-default" namespace="/">
<action name="user" class="shiyanlou.ssh.action.UserAction">
<result name="error">/error.jsp</result>
<result name="success">/success.jsp</result>
<result name="update">/updateuser.jsp</result>
</action>
</package>
</struts>
现在编码工作进入最后的阶段了,我们需要做的,就是根据前面的 Struts 的配置以及各种业务处理逻辑,建立各种 JSP 页面。
实验中,所有的页面,都在 WebContent/
下新建。
login.jsp
登录页面,也是程序启动的默认首页,登录时验证 id 和 name 是否存在于数据库中。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- 获取相关路径 -->
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Login Page</title>
</head>
<body>
<center>
<h1>Please Login First</h1><br/>
<!-- 登录时 form 表单提交给 checkUser Action 处理 -->
<form action="<%=basePath%>user!checkUser.action" method="post">
User Id:<input type="text" name="user.id" style="width: 100px;"/><br/>
User Name<input type="text" name="user.name" style="width: 100px;"/>
<br/>
<input type="submit" value="Login"/>
</form>
</center>
</body>
</html>
success.jsp
登录验证成功,进入管理页面。
<%@ page language="java" import="java.util.*" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!-- c 标签,后面会使用到 -->
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Welcome</title>
<style type="text/css">
td{
text-align: center;
width: 100px;
}
</style>
</head>
<body>
<!-- checkUser Action 返回 user1,这里就可以得到用户名-->
<div align="right">Welcome, ${user1.name } | <a href="logout.jsp">Exit</a>
</div>
<center>
<table border="1">
<tr>
<td>user id</td><td>user name</td><td>company</td><td colspan="2" style="center">Options</td>
</tr>
<!-- UserAction 中的 list() 返回 users,遍历它就得到所有的查询结果 -->
<c:forEach items="${users}" var="u">
<!-- 以表格形式展现所有结果,最后一栏是操作(包含删除和修改) -->
<!-- 删除、修改都提交给各自的 Action 处理,通过 id 来判断是哪个用户 -->
<tr>
<td>${u.id}</td><td>${u.name}</td><td>${u.company}</td><td><a href="user!deleteUser.action?user.id=${u.id}">Delete</a></td><td><a href="user!updateUser.action?user.id=${u.id}">Modify</a></td>
</tr>
</c:forEach>
</table>
<br/>
<!-- 添加用户按钮 -->
<input type="submit" value="Add User" onclick="window.location.href='adduser.jsp'"/>
</center>
<br>
<br>
<br>
<br>
<!-- 查询用户输入框,包含 name、company 两种查询条件 -->
<center>
<form action="user!queryUser.action" method="post">
user name<input type="text" name="user.name"/>
company<input type="text" name="user.company"/>
<input type="submit" value="Search"/>
</form>
</center>
<br>
<br>
<br><br>
<!-- tag>0 表示查询结果正常-->
<c:if test="${tag>0}">
<center>
<table border="1">
<!-- 查询结果也以表格形式展现 -->
<tr>
<td>user id</td><td>user name</td><td>company</td>
</tr>
<!-- users2 就是 UserAction 中的 queryUser() 返回的结果,同样是遍历 -->
<c:forEach items="${users2}" var="u">
<tr>
<td>${u.id}</td><td>${u.name}</td><td>${u.company }</td>
</tr>
</c:forEach>
</table>
</center>
</c:if>
</body>
</html>
error.jsp
登录验证失败,给出一个错误页面提示即可,非常简单。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Sorry!</title>
</head>
<body>
Login failed!
</body>
</html>
adduser.jsp
增加用户的页面,注意 id 为主键,因此不能为空,也不能和其他的重复。把表单提交给 addUser Action 就可以了。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Add New User</title>
</head>
<body>
<center> <h1>Add User</h1><br/>
user id cannot be null or same as others
<form action="user!addUser.action" method="post">
user id<input type="text" name="user.id"/><br/>
user name<input type="text" name="user.name"/><br/>
company<input type="text" name="user.company"/><br/>
<input type="submit" value="Add"/>
</form>
</center>
</body>
</html>
updateuser.jsp
修改用户信息,同上面类似(不提供 id 修改)。注意这里的 Action 为 updateUserImpl
。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Modify User Info</title>
</head>
<body>
<center> <h1>Modify User Info</h1><br/>
<form action="user!updateUserImpl.action" method="post">
<input type="hidden" name="user.id" value="${user.id}" readonly="readonly"/><br/>
user name<input type="text" name="user.name" value="${user.name}"/><br/>
company<input type="text" name="user.company" value="${user.company}"/><br/>
<input type="submit" value="OK"/>
</form>
</center>
</body>
</html>
logout.jsp
退出网站,把 session 清空,再设置页面跳转到首页登录页面即可。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
session.invalidate();
response.sendRedirect("login.jsp");
%>
右击项目,选择 Run As
—> Run On Server
,稍等片刻,即可看到登录页面:
输入下面的命令,可以看到该数据库中,已经有 ssh_test
这张 table 了,但是目前还是张空表:
因为登录时我们要检查输入的数据是否存在与数据库中,因此我们先插入一条数据,例如:
再输入刚刚插入的数据,即可登录并进入管理页面(可以看到刚刚插入的数据,显示在页面上了):
点击 Add User
按钮,输入数据来新增用户,例如:
点击 Add
即可添加成功,页面展示:
对比验证后台数据库中表的数据,也是一致的:
我们把上面新增的 id 为 124 的用户删除掉,点击 Options 中的 Delete 即可:
对比验证后台数据库中表的数据(保持一致),可以看到之前是两条数据,现在也只有一条了:
点击 Options 中的 Modify 修改用户信息,例如把 id 为 123 的用户信息修改为:
可以看到修改成功了:
后台数据库也是一致的:
为了演示查找的特性,我们先多新建几个用户,例如:
我们先查找 name="hr"
(company 为空)的用户,结果为:
再查找 company="shiyanlou"
(name 为空)的用户,结果为:
最后,再查找 name="hr"
并且 company="shiyanlou"
的用户:
可以看到,条件查找是很灵活、多样的。
本次项目课的内容比较多,建议多动手操作几遍,并且仔细回顾和思考,才能真正理解。
退出操作存在不完善的地方,退出后按返回键仍然可以进行操作,考虑如何完善它。