SMBMS
mysql -hlocalhost -uroot -p
set global time_zone = ‘+8:00’;
10、MVC三层架构
什么是MVC:Module View Controller 模型,视图,控制器
10.1、早些年
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ecklOf6u-1651655697760)(${images}/image-20210504092643736.png)]
用户直接访问控制层,控制层就可以直接操作数据库:
servlet---CRUD---->数据库
弊端:程序十分臃肿,不利于维护
servlet的代码中:处理请求,响应,视图跳转,处理JDBC,处理业务代码,处理逻辑代码
架构:没有什么是再加一层解决不了的
程序员调用JDBC
|
JDBC
|
MySQL Oracle sqlserver 。。。。
10.2、三层架构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1085VXdM-1651655697761)(${images}/image-20210504094912630.png)]
Module
- 业务处理:业务逻辑(service)
- service层叫服务层,被称为服务,肯定是相比之下比较高层次的一层结构,相当于将几种操作封装起来。
- 数据持久层:CRUD(Dao)
- dao层叫数据访问层,全称为data access object,属于一种比较底层,比较基础的操作,具体到对于某个表、某个实体的增删改查
面对接口编程
- 在java中接口是多继承的,而类是单继承的,如果你需要一个类实现多个service,你用接口可以实现,用类定义service就没那么灵活
- 要提供不同的数据库的服务时,我们只需要面对接口用不同的类实现即可,而不用重复地定义类
- 编程规范问题,接口化的编程为的就是将实现封装起来,然调用者只关心接口不关心实现,也就是“高内聚,低耦合”的思想。
View
- 展示数据
- 提供链接发起servlet请求(a,from,img…)
Controller(Servlet)
- 就收用户的请求:(request ,请求参数,session信息)
- 交给业务层处理对应的代码
- 控制视图的跳转
登录---->接受用户的登录请求---->处理用户的请求(获取用户登录的数据,username,password,)--->交给业务层处理登录业务(判断用户名密码是否正确,事务)---->Dao层查询用户名密码是否一样--->数据库
页面:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rsCot7bq-1651655697762)(${images}/image-20210505145503659.png)]
数据库:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2jfxnNKu-1651655697762)(${images}/image-20210505145847018.png)]
项目如何搭建?
考虑不考虑使用maven?依赖jar
1.项目搭建准备工作
1.搭建一个maven web项目
- 配置pom.xml
- 配置web.xml
- 添加java和resoures文件夹
2.配置Tomcat
3.测试Tomcat是否能跑起来
4.导入jar包
- jsp
- servlet
- mysql
- 。。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--jsp,servelt,mysql-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
int和Interger的区别
- int是基本数据类型,Integer是引用数据类型;
- int默认值是0,Integer默认值是null;
- int类型直接存储数值,Integer需要实例化对象,指向对象的地址。
1、都是封装类,都是new出来的,肯定不相等。因为对象的内存地址不一样。
2、都是封装类,都不是new出来的,如果值在-128~127之间,那就相等,否则不相等。
3、如果是封装类和基本类型进行比较,只要数值相等那就相等,否则就不相等。因为封装类和基本数据类型进行比较的时候会有一个自动拆箱操作。
4、都是基本数据类型,如果数值相等,那就相等;否则不相等
5.创建项目包的结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ISn3wpGA-1651655697763)(${images}/image-20210505155712949.png)]
6.编写实体类
ORM映射:表——>类映射
7.编写基础公共类
1.数据库配置3文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A8RHJNvs-1651655697764)(${images}/image-20210505160158032.png)]
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/chenlong?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
username=root
password=123
2.编写数据库公共类
package com.kuang.dao;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
//操作数据库的公共类
public class BaseDao {
private static String driver;
private static String url;
private static String username;
private static String passworld;
//静态代码块,类加载的时候就加在了
static {
//读取配置文件
//1.通过类加载器读取对应的资源
InputStream inputStream = BaseDao.class.getClassLoader().getResourceAsStream("db.properties");
//2.创建properties对象
Properties properties = new Properties();
try {
properties.load(inputStream);
} catch (IOException e) {
}
driver=properties.getProperty("driver");
url=properties.getProperty("url");
username=properties.getProperty("username");
passworld=properties.getProperty("passworld");
}
//编写数据库连接对象的公共方法
public static Connection getConnection(){
Connection conn=null;
try {
//1.加载驱动
Class.forName(driver);
//2.获取连接对象
conn = DriverManager.getConnection(url, username, passworld);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
public static ResultSet executeQuery(String sql, Object [] params ,Connection conn, PreparedStatement pstm, ResultSet rs){
try {
//预编译
pstm=conn.prepareStatement(sql);
//循环遍历参数数组,并将参数设入SQL中
for (int i = 1; i < params.length; i++) {
pstm.setObject(i,params[i-1]);//数组的index从0开始,而PreparedStatement中设置占位符的值的index从1开始
}
rs=pstm.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}
return rs;
}
//3、编写修改公共方法
/**
* 用于修改数据的公共方法,注意:使用发送SQL语句的对象为PreparedStatement
* @param sql:修改数据的sql语句模板
* @param params:模板中占位符对应的值
*
* =====下面两个对象需要调用出传入也是为了统一管理和关闭资源=====
* @param conn:调用出使用BaseDao.getConnection()获取到数据库连接对象传入
* @param pstmt:调用出只是传入null的引用。这个对象真正的实例化放在这个方法里面
*
* @return 返回受影响的行数
*/
public static int executeUpdate(String sql,Object[] params,Connection conn,PreparedStatement pstmt){
int result = 0;
try {
pstmt = conn.prepareStatement(sql);
for (int i=1;i< params.length;i++){//循环遍历参数数组,并将参数设入SQL中
pstmt.setObject(i,params[i-1]);//注意:数组的index从0开始,而PreparedStatement中设置占位符的值的index从1开始
}
result = pstmt.executeUpdate();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return result;
}
//4、编写关闭资源公共方法
/**
* 关闭资源
* @param conn:调用出使用BaseDao.getConnection()获取到数据库连接对象传入
* @param pstmt:调用出只是传入null的引用。这个对象真正的实例化放在这个方法里面
* @param rs:返回的结果集,和pstmt只是传入null的引用。这个对象真正的实例化放在这个方法里面
* @return:返回关闭资源的结果
*
* 注意:关闭资源的时候要倒着关
*/
public static boolean close(Connection conn,PreparedStatement pstmt,ResultSet rs){
boolean flag = true;
if (rs!=null){
try {
rs.close();
rs = null;//让这个变量为null,gc就会自动对其进行回收
} catch (SQLException throwables) {
throwables.printStackTrace();
flag = false;//关闭失败就将flag设置false
}
}
if (pstmt!=null){
try {
pstmt.close();
pstmt = null;
} catch (SQLException throwables) {
throwables.printStackTrace();
flag = false;
}
}
if (conn!=null){
try {
conn.close();
conn = null;
} catch (SQLException throwables) {
throwables.printStackTrace();
flag = false;
}
}
return flag;//返回关闭结果
}
}
}
3.编写字符集编码过滤器
8.导入静态资源
2.登录功能实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iOYGgmix-1651655697764)(${images}/image-20210506231905429.png)]
1.编写前端页面
编写前端页面时要放入web目录下
前端的代码可以直接使用已有的,我们主要进行网页背后的执行力代码
记得是login.jsp
2.设置欢迎页面
在web-inf中配置web.xml
<!--设置欢迎页面-->
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
![]($%7bimages%7d/image-20210507220655267.png)
3.编写Dao层用户的登录的接口
- 创建一个UserDao接口,然后我们按照“面向接口编程”的原则去实现UserDaolmpl中实现这个接口的方法
- 使用面向接口编程的的好处就是我们在接口中只需要定义方法,而不需要实现方法,整个结构和思路都很清晰
- 其次就是将设计和实现分离,保证设计专注于设计,实现专注于实习。
package com.kuang.dao.user;
import com.kuang.pojo.User;
import java.sql.Connection;
public interface UserDao {
/*
得到要进行登录的用户
input:conn数据库连接对象,
usercode:通过用户id查询用户的数据
output;返回一个user对象,表示查到的用户
*/
public User GetLoginUserInfo(Connection conn, String usercode);
}
4.编写Dao接口的实现类
package com.kuang.dao.user;
import com.kuang.dao.BaseDao;
import com.kuang.pojo.User;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class UserDaoImpl implements UserDao {
@Override
public User GetLoginUserInfo(Connection conn, String usercode) {
PreparedStatement pstm=null;
ResultSet rs=null;
User user=null;
if (conn!=null){
String sql="select * from smbms_user where userCode = ?" ;
Object [] param={usercode};
try{
rs= BaseDao.execute(sql,param,conn,pstm,rs);//调用项目搭建阶段准备的查询语句
if (rs.next()){
user= new User();
user.setId(rs.getInt("id"));
user.setUserCode(rs.getString("userCode"));
user.setUserName(rs.getString("userName"));
user.setUserPassword(rs.getString("userPassword"));
user.setGender(rs.getInt("gender"));
user.setBirthday(rs.getDate("birthday"));
user.setPhone(rs.getString("phone"));
user.setAddress(rs.getString("address"));
user.setUserRole(rs.getInt("userRole"));
user.setCraeateBy(rs.getInt("createdBy"));
user.setCreateDate(rs.getTimestamp("creationDate"));
user.setModifyBy(rs.getInt("modifyBy"));
user.setModifyDate(rs.getTimestamp("modifyDate"));user.setId(rs.getInt("id"));
}
//关闭资源
BaseDao.close(null,pstm,rs);
//因为数据库的连接可能不只是这一个操作,所以我们不应该做完一件事就把数据库连接对象销毁,所以conn处传的null
} catch (Exception e) {
e.printStackTrace();
}
}
return user;
}
}
5.编写service接口
用户登录
public interface UserService {
/**
* 用户登录身份验证
* @param userCode :用户账号
* @param passworld;密码
* 注意,密码判断在我们service层进行,在Dao层只是简单的此操作数据库,没有其他逻辑代码
* 在servlet层只是接受和转发请求以及控制视图跳转
* 而对于业务层(service)就适合用来实现业务逻辑代码的
* @return User用户对象
*/
//用户登录
public User login(String userCode, String passworld);
}
6.业务层实现类
package com.kuang.service.User;
import com.kuang.dao.BaseDao;
import com.kuang.dao.user.UserDao;
import com.kuang.dao.user.UserDaoImpl;
import com.kuang.pojo.User;
import org.junit.Test;
import java.sql.Connection;
public class UserServiceImpl implements UserService
{
//业务层需要使用Dao,所以直接将Dao作为一个成员变量来使用
private UserDao userDao;
public UserServiceImpl(){
userDao = new UserDaoImpl();//在业务层实例化的时候,就让它得到dao对象,后面就可以直接取用
}
@Override
public User login(String userCode, String passworld) {
Connection conn=null;
User user=null;
try{
conn= BaseDao.getConnection();//获取数据库连接对象
//通过业务层调dao层
user=userDao.GetLoginUserInfo(conn,userCode);//调用userdao中的获取信息的方法
}catch (Exception e){
e.printStackTrace();
}finally {
BaseDao.close(conn,null,null);
}
return user;
}
@Test
public void test(){
UserServiceImpl userService= new UserServiceImpl();
User admin = userService.login("admin", "adwada");
System.out.println(admin.getUserPassword());
}
}
7.编写servlet
package com.kuang.servlet.user;
import com.kuang.pojo.User;
import com.kuang.service.User.UserService;
import com.kuang.service.User.UserServiceImpl;
import com.kuang.util.Constant;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LoginServlet extends HttpServlet {
//Servlet控制层调用业务层代码
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入LoginServlet");
//获取用户名和密码
String userCode = req.getParameter("userCode");
String userPassword = req.getParameter("userPassword");
//和数据库中的密码进行对比,调用业务层
UserService userService=new UserServiceImpl();
User user = userService.login("userCode", "userPassword");//这里已经把登录的人查出来了
if (user!=null){//查有此人,可以登录
//将用户信息放在session中
req.getSession().setAttribute(Constant.USER_SESSION,user);
//跳转到主页
resp.sendRedirect("jsp/frame.jsp");
}else {//查无此人
//转发登录页面,顺带提示用户名密码错误
req.setAttribute("error","用户名密码不正确");
req.getRequestDispatcher("login.jsp").forward(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
8.注册servlet
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.kuang.servlet.user.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login.do</url-pattern>
</servlet-mapping>
9.测试功能,保证成功运行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6dHuN7LM-1651655697765)(${images}/image-20210509155303558.png)]
3.登录功能优化
注销功能:
思路:移出session,返回登录页面
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//移出用户的session
req.getSession().removeAttribute(Constant.USER_SESSION);
resp.sendRedirect(req.getContextPath()+"/login.isp");//返回登录页面
}
注册mxl
<servlet>
<servlet-name>LogoutServlet</servlet-name>
<servlet-class>com.kuang.servlet.user.LogoutServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LogoutServlet</servlet-name>
<url-pattern>/jsp/logout.do</url-pattern>
</servlet-mapping>
登录拦截器
编写登录拦截Filter
package com.kuang.filter;
import com.kuang.pojo.User;
import com.kuang.util.Constant;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SysFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//过滤器,从session中获取用户
User user = (User) request.getSession().getAttribute(Constant.USER_SESSION);
if(user==null){//用户已经被移除,或者未登录
response.sendRedirect("/smbms/error.jsp");
}else {
filterChain.doFilter(req,resp);
}
}
@Override
public void destroy() {
}
}
注册过滤器
<!-- 用户登录过滤器-->
<filter>
<filter-name>SysFilter</filter-name>
<filter-class>com.kuang.filter.SysFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SysFilter</filter-name>
<url-pattern>/jsp/*</url-pattern>
</filter-mapping>
测试,成功!!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wWM3IGvw-1651655697765)(${images}/image-20210509222338075.png)]
至此登录,注销,权限都已完成,还剩密码修改,ajax验证旧密码,用户底层管理,用户管理分页,smbms架构分析即学习方法,
文件传输原理,文件上传,邮件发送,网站注册发送邮件实现,后话
4.密码修改
1.导入前端素材
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-27lGM2ES-1651655697766)(${images}/image-20210509223532649.png)]
2.写项目,建议从底层向上层写
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-myBUoKAK-1651655697766)(${images}/image-20210509230447268.png)]
3.UserDao接口
///修改当前用户的密码
public int UpdatePwd(Connection con,int id , int passworld) throws SQLException;
/*
返回修改的条数,pwd是String?
*/
4.UserDaoImpl接口实现类
///修改当前用户的密码
@Override
public int UpdatePwd(Connection con, int id, int passworld) throws SQLException {
int execute=0;//提升作用域
PreparedStatement pstm=null;
Object[] params={passworld,id};
String sql="UPDATE `smbms_user` SET `userPassword`=? WHERE id=?";
//提高安全性,防止没有连接就可以修改
if(con!=null){
execute= BaseDao.execute(sql, params, con, pstm);
BaseDao.close(null,pstm,null);
}
return execute;
}
5.service层
5.1userService接口
//用户修改密码
public boolean updatePwd(int id,int pwd);
5.2userService接口实现
@Override
public boolean updatePwd(int id, int pwd) {
Connection conn=null;
conn= BaseDao.getConnection();
boolean flag=false;
try {
//业务层需要使用Dao,所以直接将Dao作为一个成员变量来使用
/*
userdao已经在前面生成了,可以直接使用
*/
if (userDao.UpdatePwd(conn,id,pwd)>0){
flag=true;
}
}catch (SQLException e){
e.printStackTrace();
}finally {
BaseDao.close(conn,null,null);
}
return flag;
}
6.编写servlet
package com.thhh.servlet.user;
import com.mysql.jdbc.StringUtils;
import com.thhh.pojo.User;
import com.thhh.service.user.UserService;
import com.thhh.service.user.UserServiceImpl;
import com.thhh.utils.Constants;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//实现servlet复用
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
boolean flag = false;
Object user = req.getSession().getAttribute(Constants.USER_SESSION);
String newpassword = req.getParameter("newpassword");
if (user!=null && !StringUtils.isNullOrEmpty(newpassword)){//获取到了这个用户对象且获取到的新密码不为空
UserService userService = new UserServiceImpl();
flag = userService.updatePwd(newpassword,((User)user).getId());//servlet调用业务层
if (flag){//修改成功
req.setAttribute("message","密码修改成功!请使用新密码重新登陆");
//移除用户的session,利用过滤器阻止用户再进行操作,直接跳转error.jsp页面
req.getSession().removeAttribute(Constants.USER_SESSION);
}else{
req.setAttribute("message","密码修改失败");
}
}else {
//用户可以进行密码修改,则user一定不是null,所以跳入这个分支的原因一定是newpassword = NULL
req.setAttribute("message","密码设置有误,请重新输入!");
}
//无论是修改成功还是失败,都重定向到密码修改页面,就是在刷新页面,否则我们设置在req中的message属性不会被前端读到
req.getRequestDispatcher("pwdmodify.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
7.注册servlet
<servlet>
<servlet-name>UserServlet</servlet-name>
<servlet-class>com.thhh.servlet.user.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/jsp/user.do</url-pattern>
</servlet-mapping>
8.实现复用
实现复用需要提取出方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9WfpDwB6-1651655697766)(${images}/image-20210511172215534.png)]
package com.kuang.servlet.user;
import com.kuang.pojo.User;
import com.kuang.service.User.UserService;
import com.kuang.service.User.UserServiceImpl;
import com.kuang.util.Constant;
import com.mysql.cj.util.StringUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Connection;
//实现servlet复用
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getParameter("method");
if(method!=null&&method.equals("savepwd")){
this.updatePwd(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
public void updatePwd(HttpServletRequest req, HttpServletResponse resp){
//获取用户的session中获取id
Object o = req.getSession().getAttribute(Constant.USER_SESSION);
String newpassword = req.getParameter("newpassword");
boolean flag=false;
if(o!=null&& (!StringUtils.isNullOrEmpty(newpassword))){
UserService userService = new UserServiceImpl();
flag = userService.updatePwd(((User)o).getId(), newpassword);
if (flag){
req.setAttribute("message","succsess");
//密码修改成功移出session
req.getSession().removeAttribute(Constant.USER_SESSION);
}else {
req.setAttribute("message","fail");
}
}else {
req.setAttribute("message","pwd is error");
try {
req.getRequestDispatcher("pwdmodify.jsp").forward(req,resp);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5.ajax验证旧密码
什么是ajax
1.什么是Ajax
- AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)
- AJAX 不是新的编程语言,而是一种使用现有标准的新方法
- AJAX 是在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的艺术
- 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新
- 这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新
- 传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面
2.怎么实现旧密码验证?
直接的做法就是去数据库中查找,但是这样的画我们又要写一条线:Dao开始–>service–>servlet–>JSP
我们可以采取一种很简单的方式进行:在用户登陆的时候我们将整个用户对象都存储到session中了,所以我们可以利用Ajax的异步请求+session中的user对象实现旧密码的验证
做法就是在后端复用的servlet中再定义一个方法,这个方法专门负责对比Ajax传递给后端的旧密码是否和session中User对象的密码一致,并通过对比的情况和结果来返回JSON数据给前端
3.编写servlet
oldpassword.on("blur",function(){
$.ajax({
type:"GET",
url:path+"/jsp/user.do",//Ajax提交的URL,可见和我们修改密码的JSP页面的表单提交的URL一样,所以我们应该复用servlet
data:{method:"pwdmodify",oldpassword:oldpassword.val()},//提交的参数
//上面两句话等价于 = path+/jsp/user.do ? method=pwdmodify & oldpassword=oldpassword.val()
dataType:"json",
success:function(data){
if(data.result == "true"){//旧密码正确
validateTip(oldpassword.next(),{"color":"green"},imgYes,true);
}else if(data.result == "false"){//旧密码输入不正确
validateTip(oldpassword.next(),{"color":"red"},imgNo + " 原密码输入不正确",false);
}else if(data.result == "sessionerror"){//当前用户session过期,请重新登录
validateTip(oldpassword.next(),{"color":"red"},imgNo + " 当前用户session过期,请重新登录",false);
}else if(data.result == "error"){//旧密码输入为空
validateTip(oldpassword.next(),{"color":"red"},imgNo + " 请输入旧密码",false);
}
},
error:function(data){
//请求出错
validateTip(oldpassword.next(),{"color":"red"},imgNo + " 请求错误",false);
}
});
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hmn0nYnv-1651655697767)(${images}/1747479-20200909202727331-1672399838.png)]
4.验证旧密码
//2.验证旧密码
//直接与session中的user对象的密码进行对比即可,不用再去查数据库
public void pwdModify(HttpServletRequest req, HttpServletResponse resp){
Object user = req.getSession().getAttribute(Constants.USER_SESSION);
String oldpassword = req.getParameter("oldpassword");
//使用Map集合存储返回给前端的数据
Map<String,String> resultMap = new HashMap<String, String>();
if (user==null){//session中的user对象失效了
resultMap.put("result","sessionerror");
}else if (StringUtils.isNullOrEmpty(oldpassword)){//输入的密码为空
resultMap.put("result","error");
}else {
String userPassword = ((User)user).getUserPassword();
if (userPassword.equals(oldpassword)){//输入的密码匹配
resultMap.put("result","true");
}else {//输入的密码不匹配
resultMap.put("result","false");
}
}
//将Map集合中的数据转为JSON格式传输给前端
try {
resp.setContentType("application/JSON");//设置返回数据是一个JSON,这样浏览器拿到这个数据之后就会按照JSON的数据格式来解析它
PrintWriter writer = resp.getWriter();//获取输出流
writer.write(JSONArray.toJSONString(resultMap));//使用阿里巴巴提供的一个JSON工具类,直接将其他数据格式的数据转为JSON数据格式,然后直接将其写出去
writer.flush();//刷新缓冲区
writer.close();//关闭资源
} catch (IOException e) {
e.printStackTrace();
}
}
5、测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N6ZSKEj6-1651655697767)(${images}/1747479-20200909202751837-335071298.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3WE25KhO-1651655697768)(${images}/1747479-20200909202812308-1507354166.png)]
6.用户管理功能实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IgIoADry-1651655697768)(${images}/1747479-20200909202936680-574336968.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n1Qqun0r-1651655697769)(${images}/1747479-20200909202943395-25047443.png)]
1.导入分页工具类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wFX68NLN-1651655697769)(${images}/1747479-20200909202959922-1612350389.png)]
查看一下这个工具类的源码[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q5cySxW5-1651655697770)(${images}/1747479-20200909203010937-1786980443.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wyZCtJmH-1651655697770)(${images}/1747479-20200909203016663-107565294.png)]
OOP的3大特性:封装、继承、多态,其中封装 = 属性私有+属性的get/set() + 在set中限制一些不安全的赋值操作(这一步可以留到service层再做,但是在封装的时候做更好,这样减少了service层的代码,且体现了封装的特性)
2.用户列表页面导入
首先是HTML文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ruDmtZVW-1651655697771)(${images}/1747479-20200909203034599-1462656386.png)]
分析:
- Dao层使用聚合函数COUNT(*)查询用户表一共多少条记录
- service层用于统计总共有多少条数据
- servlet层用于向前端返回总共的数据条数
3.获取用户数量
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2uI8dCz8-1651655697771)(${images}/1747479-20200909203104060-1507838416.png)]
1、UserDao接口
因为是获取用户的数量,所以和用户表有关,那这个接口方法就放在前面已经创建好的UserDao中
方法定义的时候需要哪些参数?
查看前端素材:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-82fay8pl-1651655697771)(${images}/1747479-20200909203120578-1278957015.png)]
可见,查询界面我们需要实现按照用户名查询、按照角色名称查询和整表查询
再去看看数据库中的数据表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B3apb1HT-1651655697772)(${images}/1747479-20200909203133549-1455463515.png)]
联表查询的基本SQL语句
SELECT COUNT(1)
FROM smbms_user u,smbms_role r
WHERE u.userRole = r.id;
这是MYSQL的方言版内连接查询,内连接两张表的地位平等,只要我们加了WHERE过滤笛卡尔积,那么输出的结果就只包含有主外键关联的字段
但是从前面的需求我们可以看出,前端素材提供了按照姓名查询、按照职位查询和整表查询,所以我们需要在上面联表查询的基础上再添加一些筛选条件,添加的手段就是使用"AND 条件"来实现
在Java中实现我们可以使用StringBuffer来实现sql语句的拼接
package com.thhh.dao.user;
import com.thhh.pojo.User;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
public interface UserDao {
/**
* 1、得到要进行登陆的用户
* @param conn:数据库连接对象
* @param userCode:通过用户的用户名userCode查询用户数据
* @return
*/
public User getLoginUserInfo(Connection conn,String userCode);
/**
* 2、修改用户密码
* @param conn:数据库连接对象
* @param id:修改密码的用户的ID
* @param newPwd:新密码
* @return:影响行数
*/
public int updatePwd(Connection conn,String newPwd,int id);
/**
* 3、用于获取数据库中用户总数
* @param conn:数据库连接对象
* @param userName:用户名,这个参数主要用于按照用户名称查询用户信息
* @param userRole:用户角色,这个参数主要用于按照用户角色查询用户信息
* @return :查询到的行数
*/
public int getUserCount(Connection conn, String userName, int userRole) throws SQLException;
}
2、UserDaoImpl接口实现
//3、根据用户名/角色名获取用户总数
@Override
public int getUserCount(Connection conn, String userName, int userRole) throws SQLException {
PreparedStatement pstmt = null;
ResultSet rs = null;
int count = 0;
if (conn!=null){
StringBuffer sql = new StringBuffer();//使用字符串缓冲区,这样就可以动态的在sql后面追加AND条件了
sql.append("SELECT COUNT(1) COUNT FROM smbms_user u,smbms_role r WHERE u.userRole = r.id");//基本的联表查询SQL语句
List<Object> list = new ArrayList<Object>();//创建一个list来存储我们要拼接的筛选条件,由于我们不能限制传入的参数的数据类型,所以泛型指定为object
if (!StringUtils.isNullOrEmpty(userName)){//判断传入的用户名是不是空,如果不是空表明前端指定了按照姓名查询的参数
sql.append("AND userName like ?");
list.add("%"+userName+"%");
}
if (userRole>0){
sql.append("AND userRole = ?");
list.add(userRole);
}
Object[] params = list.toArray();//获得BaseDao中为pstmt对象设置参数的参数列表
rs = BaseDao.executeQuery(sql.toString(), params, conn, pstmt, rs);//调用查询定义好的查询方法
if (rs.next()){//获取查询结果
count = rs.getInt("COUNT");//COUNT是在SQL语句中为查询结果取的别名
}
BaseDao.close(null,pstmt,rs);//关闭资源
}
return count;
}
注意回顾知识点:
- StringBuffer类:一个专门用来弥补String内容不可修改的类,调用这个类对象的append()可以实现在字符串的末尾加上新字符串且不会产生新的字符串对象(String对象的拼接操作其实在底层就是产生了新的String对象)
- 使用List集合来存储参数,这样做的好处就在于我们可以动态的向集合中添加参数,而不是像前面使用数组那样固定了数组长度;其实集合也是用来弥补数组长度不可修改的缺陷而出现的,使用集合存储数据即使数据很多也不会产生新的集合对象,而数组不一样,数组一经创建长度就固定了;而针对上面要实现的需求我们不知道到底前端要进行哪一种操作,所以我们不能定义一个定长的数组,即使按照最大需求量定义数组,虽然可以满足要求,但是在不能充分使用的时候就是资源的浪费
- 上面使用的动态的拼接SQL语句的做法很明智:既使用了StringBuffer对象控制SQL语句在提交之前可变,又使用了List集合来存储参数,在提交的时候才将其转为数组
3、UserService接口
package com.thhh.service.user;
import com.thhh.pojo.User;
public interface UserService {
/**
* 1、获取登陆用户对象,对用户登陆身份进行验证
* @param userCode:用户账号
* @param userPassword:用户密码,注意,密码判断我们在service层进行;
* 在Dao层只是简单的操作数据库,没有其他的逻辑代码;在servlet层中只是接收和转发请求以及控制视图跳转
* 而对于业务层(service)就是用来实现业务逻辑代码的
* @return
*/
public User login(String userCode,String userPassword);
/**
* 2、根据用户ID修改用户密码
* @param newPwd:新密码
* @param id:用户ID
* @return
*/
public boolean updatePwd(String newPwd, int id);
/**
* 3、获取用户总数
* @param userName:按照用户姓名查,查到的用户总数
* @param userRole:按照用户角色查,查到的用户总数
* @return:返沪对应查询条件查到的用户总数
*/
public int getUserCount(String userName, int userRole) ;
}
只看方法3
4、UserServiceImpl接口实现
/**
* 3、按照条件查询符合条件的用户总数
* @param userName:按照用户姓名查,查到的用户总数
* @param userRole:按照用户角色查,查到的用户总数
* @return
*/
@Override
public int getUserCount(String userName, int userRole) {
Connection conn = null;
int rs = 0;
try {
conn = BaseDao.getConnection();//获取数据库连接对象
rs = userDao.getUserCount(conn,userName,userRole);//业务层调用Dao层获取业务结果
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
BaseDao.close(conn,null,null);//关闭资源
}
return rs;//业务层将业务结果返回给servlet
}
5、测试
bug1:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GtIuByXB-1651655697773)(${images}/1747479-20200909203209066-625874060.png)]
4.获取用户列表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yZoodXGk-1651655697773)(${images}/1747479-20200909203224671-30813080.png)]
1、UserDao接口
/**
* 4、用户获取用户列表
* @param conn:数据库连接对象
* ===========后面两个参数用于条件查询用户数据
* @param userName:按照用户名查找
* @param userRole:按照角色名称查找
* ===========后面两个参数用于对按照上面条件查询出来的结果进行分页处理
* @param currentPageNo:翻到第多少页
* @param pageSize:每一页多少条数据
* @return:返回满足条件的user对象集合
*/
public List<User> getUserList(Connection conn, String userName, int userRole, int currentPageNo, int pageSize) throws SQLException;
2、UserDaoImpl接口实现
//4、获取满足条件的用户对象集合
@Override
public List<User> getUserList(Connection conn, String userName, int userRole, int currentPageNo, int pageSize) throws SQLException {
PreparedStatement pstmt = null;
ResultSet rs = null;
List<User> userList = null;
if (conn!=null){
userList = new ArrayList<User>();
StringBuffer sql = new StringBuffer();
sql.append("SELECT u.*,r.roleName as userRoleName FROM smbms_user u,smbms_role r WHERE u.userRole = r.id");
List<Object> temp = new ArrayList<Object>();
if (userName!=null){
sql.append(" AND u.userName LIKE ?");
temp.add("%"+userName+"%");
}
if (userRole>0){
sql.append(" AND u.userRole = ?");
temp.add(userRole);
}
sql.append(" ORDER BY u.creationDate DESC LIMIT ?,?");//在sql最后追加一个排序和分页
//5
//1 5
//2 10
//3 15
currentPageNo = (currentPageNo-1)*pageSize;//减一的原因就是MYSQL分页的index从0开始
temp.add(currentPageNo);//从哪一个下标开始
temp.add(pageSize);//从currentPageNo连续取几个
Object[] params = temp.toArray();
rs = BaseDao.executeQuery(sql.toString(),params,conn,pstmt,rs);
while (rs.next()){
User _user = new User();
_user.setId(rs.getInt("id"));
_user.setUserCode(rs.getString("userCode"));
_user.setUserName(rs.getString("userName"));
_user.setGender(rs.getInt("gender"));
_user.setBirthday(rs.getDate("birthday"));
_user.setPhone(rs.getString("phone"));
_user.setUserRole(rs.getInt("userRole"));
_user.setUserRoleName(rs.getString("userRoleName"));//这个属性是在POJO中新加入的,数据表中没有
userList.add(_user);//将查到的这个对象分装为对象并存入List集合中
}
BaseDao.close(null,pstmt,rs);
}
return userList;
}
3、UserService接口
/**
* 4、根据用户名/用户角色名称来查询数据,返回一个User对象集合,而currentPageNo+pageSize用于前端做分页操作
* @param userName
* @param userRole
* @param currentPageNo
* @param pageSize
* @return:满足条件+limit的User对象集合
*/
public List<User> getUserList(String userName, int userRole, int currentPageNo, int pageSize);
4、Userservice接口实现
/**
* 4、根据用户名/用户角色名称来查询数据,返回一个User对象集合,而currentPageNo+pageSize用于前端做分页操作
* @param userName
* @param userRole
* @param currentPageNo
* @param pageSize
* @return:满足条件+limit的User对象集合
*/
@Override
public List<User> getUserList(String userName, int userRole, int currentPageNo, int pageSize) {
Connection conn = null;
List<User> userList = null;
try {
conn = BaseDao.getConnection();//获取连接
userList = userDao.getUserList(conn,userName,userRole,currentPageNo,pageSize);//业务层调用Dao层获取业务结果
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
BaseDao.close(conn,null, null);//关闭资源
}
return userList;//业务层将业务结果返回给servlet
}
5、测试
@Test
public void test(){
List userList = new UserServiceImpl().getUserList(null,0,2,5);
//(3-1)*5 = 10,所以展示的是10~14条数据,但是一共只有12条,注意:MYSQL中结果index从0开始
for (Object o : userList) {
System.out.println(((User)o).getUserName());
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8XnCAW1O-1651655697774)(${images}/1747479-20200909203248681-1355249713.png)]
测试完成!
5.获取角色列表
Dao
RoleDao
package com.kuang.dao.role;
import com.kuang.pojo.Role;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
public interface RoleDao {
//获取角色列表
public List<Role> getRoleList(Connection conn) throws SQLException;
}
RoleDaoImpl
package com.kuang.dao.role;
import com.kuang.dao.BaseDao;
import com.kuang.pojo.Role;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class RoleDaoImpl implements RoleDao {
@Override
public List<Role> getRoleList(Connection conn) throws SQLException {
PreparedStatement pstm = null;
ResultSet rs = null;
ArrayList<Role> rolelist=new ArrayList<>();
if (conn != null) {
String sql = "select * from smbms_role";
Object param[] = {};
rs = BaseDao.execute(sql, param, conn, pstm, rs);
while (rs!=null){
Role _role=new Role();
_role.setRoleName(rs.getString("id"));
_role.setRoleCode(rs.getString("roleCode"));
_role.setId(rs.getInt("id"));
rolelist.add(_role);
}
BaseDao.close(conn,pstm,rs);
}
return rolelist;
}
}
Service
RoleService
package com.kuang.service.Role;
import com.kuang.pojo.Role;
import java.util.List;
public interface RoleService {
//在dao层已经查到了角色表,并返回了一个RoleList
//在service层应该干啥呢?就是调用dao层的代码,没有任何其余操作,只不过改善一下逻辑
public List<Role> getRoleList();
}
RoleService
package com.kuang.service.Role;
import com.kuang.dao.BaseDao;
import com.kuang.dao.role.RoleDao;
import com.kuang.dao.role.RoleDaoImpl;
import com.kuang.pojo.Role;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
public class RoleServiceImpl implements RoleService {
//引入dao方便调用
RoleDao roleDao;
public RoleServiceImpl( ) {
roleDao =new RoleDaoImpl();
}
@Override
public List<Role> getRoleList() {
Connection connection=null;
List<Role> RoleList=null;
try {
connection = BaseDao.getConnection();
RoleList= roleDao.getRoleList(connection);
}catch (SQLException e){
e.printStackTrace();
}finally {
BaseDao.close(connection,null,null);
}
return RoleList;
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5QVj2uYk-1651655697774)(${images}/image-20210515211510851.png)]
7.smbms用户分页管理
为了我们职责统一,可以把角色的操作单独放在一个包里,和pojo对应
8、项目总结
1.可以参照正式项目,将其拖入Tomcat总运行
扩展功能文件上传和下载
1、文件上传
1.准备工作
1.创建空java项目的方法
关于项目结构的使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fysWh1xY-1651655697774)(${images}/image-20210516093246464.png)]
- 选择空的,然后创建file
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uASmLzuS-1651655697774)(${images}/image-20210515221933987.png)]
- 导包,从官网下载,创建lib目录,添加为资源,在把包拖进去
4.修复
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3lO2YrrN-1651655697775)(${images}/image-20210516093142770.png)]
2.注意事项
【文件上传的注意事项】
1.为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WBNF目录下。
2为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名
!!1.时间戳
2.uuid
3.md5加密
4.位运算
3.要限制上传文件的最大值
4.可以限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法
【需要用到的类详解】
ServletFileUpload负责处理上传的文件数据并将表单中每个输入项封装成一个 Fileltem对象,在使用 ServletFileUpload对象解析请求时需要Dis kFileltem Factory对象。所以,我们需要在进行解析工作前构造好
DiskFileltem Facto对象,通过 ServletFileUpload对象的构造方法或 setFileltem Factory(方法设置 ServletFileUpload对象的 fileltem Factory属性Fileltem类
FileItem类
在HTML页面nput必须有 name
<input type="file” name="filename">
表单如果包含一个文件上传输入项的话,这个表单的 enctype属性就必须设置为 multipart/form-data
on connection=null;
List RoleList=null;
try {
connection = BaseDao.getConnection();
RoleList= roleDao.getRoleList(connection);
}catch (SQLException e){
e.printStackTrace();
}finally {
BaseDao.close(connection,null,null);
}
return RoleList;
}
}
[外链图片转存中...(img-5QVj2uYk-1651655697774)]
# 7.smbms用户分页管理
==为了我们职责统一,可以把角色的操作单独放在一个包里,和pojo对应==
# ==8、项目总结==
1.可以参照正式项目,将其拖入Tomcat总运行
2.
# 扩展功能文件上传和下载
## 1、文件上传
### 1.准备工作
1.创建空java项目的方法
> 关于项目结构的使用
[外链图片转存中...(img-fysWh1xY-1651655697774)]
1. 选择空的,然后创建file
2. [外链图片转存中...(img-uASmLzuS-1651655697774)]
3. 导包,从官网下载,创建lib目录,添加为资源,在把包拖进去
4.修复
[外链图片转存中...(img-3lO2YrrN-1651655697775)]
[外链图片转存中...(img-NBRoawfh-1651655697775)]
### 2.注意事项
**【文件上传的注意事项】**
1.为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于==WBNF目录下==。
2为防止文件覆盖的现象发生,要为上传文件**产生一个唯一的文件名**
**!!1.时间戳**
**2.uuid**
**3.md5加密**
**4.位运算**
**3.要限制上传文件的最大值**
4.可以限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法
**【需要用到的类详解】**
ServletFileUpload负责处理上传的文件数据并将表单中每个输入项封装成一个 Fileltem对象,在使用 ServletFileUpload对象解析请求时需要Dis kFileltem Factory对象。所以,我们需要在进行解析工作前构造好
DiskFileltem Facto对象,通过 ServletFileUpload对象的构造方法或 setFileltem Factory(方法设置 ServletFileUpload对象的 fileltem Factory属性Fileltem类
### **FileItem类**
在HTML页面nput必须有 name
```html
<input type="file” name="filename">
表单如果包含一个文件上传输入项的话,这个表单的 enctype属性就必须设置为 multipart/form-data