1、Jar 包

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>

    <!--统一版本属性管理-->
    <lombok.version>1.18.12</lombok.version>
    <junit.version>4.11</junit.version>
    <mysql.version>5.1.47</mysql.version>
    <fastjson.version>1.2.62</fastjson.version>

</properties>

<dependencies>
    <!-- servlet begin -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.2</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <!-- servlet end -->

    <!-- lombok begin -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>${lombok.version}</version>
      <scope>provided</scope>
    </dependency>
    <!-- lombok end -->

    <!-- junit begin -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <!-- junit end -->

    <!-- mysql-connector-java begin -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>${mysql.version}</version>
    </dependency>
    <!-- mysql-connector-java end -->

    <!-- fastjson begin 做数据转JSON格式 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>${fastjson.version}</version>
    </dependency>
    <!-- fastjson begin -->


    <!--  邮件发送需要的jar包 -->
    <dependency>
      <groupId>javax.mail</groupId>
      <artifactId>mail</artifactId>
      <version>1.4</version>
    </dependency>
    <dependency>
      <groupId>javax.activation</groupId>
      <artifactId>activation</artifactId>
      <version>1.1</version>
    </dependency>
    <dependency>
      <groupId>com.sun.mail</groupId>
      <artifactId>jakarta.mail</artifactId>
      <version>1.6.7</version>
    </dependency>
     <!--  邮件发送需要的jar包end -->

    <!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
    <!-- apache的commons-long 使用StringUtils.isEmpty()做非空校验   -->
    <dependency>
      <groupId>commons-lang</groupId>
      <artifactId>commons-lang</artifactId>
      <version>2.6</version>
    </dependency>
    <!-- apache的commons-long end>

  </dependencies>

jdbc.properties

driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/kh96_smbms?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2b8
user=root
password=root

2、登录 登出

2.1 系统常量

系统常量习惯放在常量类中,方便以后的代码扩展和维护

public  class   CommConstant {

    //系统默认当前页码 为1
    public static final Integer DEFAULT_INT_PAGE_NO =  1;

    //系统默认 每页容量为 5
    public static final Integer DEFAULT_INT_PAGE_SIZE =  5;

    //系统默认提示信息
    public static final String SYS_TIP_MESSAGE = "message";

    //系统默认登录session信息 :id
    public static final String SYS_USER_SESSION_ID = "userId";
    public static final String SYS_USER_SESSION_USER_CODE = "userCode";
    public static final String SYS_USER_SESSION_USER_PWD = "userPwd";
}

2.2 登录表单

 <form class="loginForm" action="${pageContext.request.contextPath }/userServlet?method=login"  name="actionForm" id="actionForm"  method="post" >
     <div class="info">${error }</div>
     <div class="inputbox">
         <label for="user">用户名:</label>
         <input type="text" class="input-text" id="userCode" name="userCode" placeholder="请输入用户名" required/>
     </div>	
     <div class="inputbox">
         <label for="mima">密码:</label>
         <input type="password" id="userPassword" name="userPassword" placeholder="请输入密码"/>
     </div>	
     <div class="subBtn">

         <input type="submit" value="登录"/>
         <input type="reset" value="重置"/>
         <input type="button" id="codeLogin" value="邮箱登录"/>
     </div>
</form>

2.3 登录方式

2.3.1 一般登录

  1. 获取用户名和密码,跳转到登录请求,查询用户是否存在;
  2. 存在将登录用户对象存放到sesssion域中(方便展示用户信息),跳转到用户展示页面
  3. 不存在转发到登录页面(携带错误提示信息回来);

2.3.2 邮箱登录

  1. 输入用户名判断用户是否开启邮箱登录(邮箱字段是否有值)
  2. 开启就可以使用邮箱登录按钮,不开启就不能使用
  3. 点击邮箱登录后,后台异步发送验证码,并跳转到邮件登录页面

授权码获取-> qq邮箱获取授权码

2.3.2.1 判断用户是否开启邮箱验证登录

开启,邮件登录按钮可以使用;

没有开启,邮件登录不可以使用;

是否开启邮箱验证登录javascript

//对验证码登录的处理
//一开始的验证码登录按钮不能使用,只有用户邮箱存在才可以使用
//这里有个问题,用户编码不唯一(用编码查询用户邮箱),最好使用唯一的字段进行登录,查邮箱的时候才好查找
//校验用户邮箱是否存在
//一开始 不可以使用邮箱登录,用户名正确才可以
$("#codeLogin").attr("disabled","true");
$("#codeLogin").css("background-color","#aaa");
$("#userCode").blur(function () {
    $.getJSON("userServlet?method=checkUserCodeExist",{"userCode":$("#userCode").val()} ,function(data){
        //判断添加返回结果
        //alert(data);
        if(data == true){
            //alert("用户邮箱存在");
            $("#codeLogin").attr("disabled",false);
            $("#codeLogin").css("background-color","#54a4d7"); //蓝色
        }else{
            //alert("用户没有开启邮箱登录!!!");
            $("#codeLogin").attr("disabled",true);
            $("#codeLogin").css("background-color","#aaa"); //灰色
        }
    });
});

是否开启邮箱验证servlet

//根据用户编码查看用户邮箱是否存在
public void checkUserCodeExist(HttpServletRequest req, HttpServletResponse resp) throws Exception {
    System.out.println("=============根据用户编码查看用户邮箱是否存在=====================");
    //获取用户编码
    String userCode = req.getParameter("userCode");

    //调用业务层查看用户邮箱是否存在
    User emailLoginUser = userService.getUserByUserCode(userCode);
    if(emailLoginUser != null && emailLoginUser.getUserEmail() != null){
        //如果用户存在,且有邮箱
        System.out.println(userCode+"用户邮箱为===》userEmail="+emailLoginUser.getUserEmail());
        //将用户存入session域中,当邮箱登录成功之后,用emailLoginUser 替换  loginUser
        req.getSession().setAttribute("emailLoginUser",emailLoginUser);

        resp.getWriter().print(true);
    }else{
        System.out.println(userCode+"用户没有开启邮箱登录!!!");
        resp.getWriter().print(false);
    }
}
2.3.2.1 异步请求发送验证码

异步请求发送验证码javascript

//发送验证码 跳转到验证码登录页面
$("#codeLogin").click(function () {
    $.getJSON("userServlet?method=sendEmail",null ,function(data){
        //判断添加返回结果
        //alert(data)
        if(data == true){
            alert("请注意接收验证码!!!");
            location.href = "emailLogin.jsp";
        }else{
            alert("验证码发送失败!!!");
        }
    });
});

异步发送验证码servlet

//发送验证码
public void sendEmail(HttpServletRequest req, HttpServletResponse resp) throws Exception {
    System.out.println("=============发送验证码=====================");
    //获取 session中的 emailLoginUser
    User emailLoginUser = (User)req.getSession().getAttribute("emailLoginUser");
    //发送验证码
    //随机生成6位 验证码
    String emailCode = "143233";
    System.out.println("验证码==》"+emailCode);
    //发送
    //new SendEmilCode().sendCode(emailLoginUser.getUserEmail(),emailCode);
    //多线程  异步发送
    new Sendmail(emailLoginUser.getUserEmail(),emailCode).start();

    //并将验证码存到session中,方便验证
    req.getSession().setAttribute("emailCode",emailCode);

    resp.getWriter().print(true);

}

发送验证码工具类

public class Sendmail extends Thread {

    //发邮件的人
    private String from = "发件人邮箱";
    //邮箱的用户名
    private String username = "邮箱的用户名";
    //邮箱的密码
    private String password = "邮箱授权码";
    //发送邮件的服务器地址
    private String host = "smtp.qq.com";


    //收邮件的人
    private String loginUserEmail;
    //发送打验证码
    private  String sendEmailCode;


    public Sendmail(String loginUserEmail,String sendEmailCode){
        this.loginUserEmail = loginUserEmail;
        this.sendEmailCode = sendEmailCode;
    }


    //重写run方法的实现,在run方法中发送邮件给指定的用户
    @Override
    public void run() {
        try{
            Properties prop = new Properties();
            prop.setProperty("mail.host", host);
            prop.setProperty("mail.transport.protocol", "smtp");
            prop.setProperty("mail.smtp.auth", "true");

            // 关于QQ邮箱,还要设置SSL加密,加上以下代码即可
            MailSSLSocketFactory sf = new MailSSLSocketFactory();
            sf.setTrustAllHosts(true);
            prop.put("mail.smtp.ssl.enable", "true");
            prop.put("mail.smtp.ssl.socketFactory", sf);

            //1、创建定义整个应用程序所需的环境信息的 Session 对象
            Session session = Session.getDefaultInstance(prop, new Authenticator() {
                public PasswordAuthentication getPasswordAuthentication() {
                    //发件人邮件用户名、授权码
                    return new PasswordAuthentication("2663092414@qq.com","gvxxjbnfmouoeacd");
                }
            });

            //开启Session的debug模式,这样就可以查看到程序发送Email的运行状态
            session.setDebug(true);

            //2、通过session得到transport对象
            Transport ts = session.getTransport();

            //3、使用邮箱的用户名和授权码连上邮件服务器
            ts.connect(host, username, password);

            //4、创建邮件
            MimeMessage message = new MimeMessage(session);
            message.setFrom(new InternetAddress(from)); //发件人
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(loginUserEmail)); //收件人
            message.setSubject("《超市订单管理系统》登录验证码"); //邮件的标题

            String info = "<h1 style='color: pink'>尊敬的用户您的动态登录验证码为:"+sendEmailCode+",请不要将验证码转发给他人!!!</h1>";

            message.setContent(info, "text/html;charset=UTF-8");
            message.saveChanges();

            //发送邮件
            ts.sendMessage(message, message.getAllRecipients());
            ts.close();
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
2.3.3 判断验证码是否正确

判断验证码javascript

//验证验证码是否正确
$("#emailLogin").click(function () {
    $.getJSON("userServlet?method=emailLogin",{"checkCode":$("#checkCode").val()} ,function(data){
        //判断添加返回结果
        //alert(data);
        if(data == true){
            alert("登录成功");
            location.href = "${pageContext.request.contextPath}/jsp/frame.jsp";
        }else{
            alert("验证码错误!!!");
        }
    });

    return false;
});

判断验证码servlet

//判断用户验证码是否正确
public void emailLogin(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println("=============判断用户验证码是否正确 emailLogin =====================");
    //获取用户 输入的验证码
    String checkCode = req.getParameter("checkCode");

    //获取session中  发送给用户的验证码
    String emailCode = (String) req.getSession().getAttribute("emailCode");

    //判断验证码是否正确
    //获取 session中的 emailLoginUser
    User emailLoginUser = (User)req.getSession().getAttribute("emailLoginUser");
    if(checkCode.equals(emailCode)){
        //登录成功
        //用 emailLoginUser 替换session 中的  loginUser
        req.getSession().setAttribute("loginUser",emailLoginUser);
        System.out.println(emailLoginUser.getUserCode()+"用户邮箱验证登录成功!!!");
        resp.getWriter().print(true);
    }else {
        System.out.println(emailLoginUser.getUserCode()+"用户邮箱验证登录失败!!!");
        resp.getWriter().print(false);
    }

}

2.4 登出

  1. 跳转到用户退出请求
  2. 移除session中用户对象
  3. 重定向用户登录页面
<a href="${pageContext.request.contextPath }/userServlet?method=userLogOut">退出</a>

3、分页条件查询

条件分页查询的重点在于,要拿到查询条件分页条件

为了方便获取参数,和在分页跳转的时候,方便 多页面 使用一个公用的跳转部分

将分页参数隐藏在form表单中,提交表单的时候将分页参数一起提交;

3.1 html

userlist.jsp

<%@include file="/jsp/common/head.jsp"%>
        <div class="right">
            <div class="location">
                <strong>你现在所在的位置是:</strong>
                <span>用户管理页面</span>
            </div>
            <div class="search">
           		<form id="searchForm" method="post" action="${pageContext.request.contextPath }/userServlet?method=userList">
					 <span>用户名:</span>
					 <input name="queryName" class="input-text"	type="text" id="queryName" value="${(pageSupport.callBackInfo)[0]}">
					 <span>用户角色:</span>
					 <select name="queryUserRole">
						   <option value="0" <c:if test="${(pageSupport.callBackInfo)[1] == 0 }" > selected </c:if>>--请选择--</option>
							 <c:forEach items="${roleList}" var="role">
								 <option value="${role.id}" <c:if test="${(pageSupport.callBackInfo)[1] == role.id }" > selected </c:if>>--${role.roleName}--</option>
							 </c:forEach>
	        		  </select>
						<!-- 当前页面参数 和 页面容量-->
						<input type="hidden" name="pageNo" id="pageNo" value="${pageSupport.currPageNo == null ? 1 : pageSupport.currPageNo}"/>
						<input type="hidden" name="pageSize" id="pageSize" value="${pageSupport.pageSize == null ? 5 : pageSupport.pageSize}"/>
						<!-- 当前排序字段 和 排序方式-->
						<input type="hidden" name="orderBy" id="orderBy" value="${pageSupport.orderBy == null ? 'id' : pageSupport.orderBy}"/>
						<input type="hidden" name="ascOrDesc" id="ascOrDesc" value="${pageSupport.ascOrDesc == null ? 'asc' : pageSupport.ascOrDesc}"/>
					 <input	value="查 询" type="submit" id="searchbutton">
					 <a href="${pageContext.request.contextPath}/jsp/useradd.jsp" >添加用户</a>
				</form>
            </div>
            <!--用户-->
            <table class="providerTable" cellpadding="0" cellspacing="0">
                <tr class="firstTr">
                    <th width="10%">用户编码</th>
                    <th width="20%">用户名称</th>
                    <th width="10%">性别</th>
                    <th width="10%">年龄</th>
                    <th width="10%">电话</th>
                    <th width="10%">用户角色</th>
                    <th width="30%">操作</th>
                </tr>
				<c:forEach items="${pageSupport.data}" var="user">
					<tr>
						<td>
						<span>${user.userCode}</span>
						</td>
						<td>
						<span>${user.userName}</span>
						</td>
						<td>
							<span>${user.gender == 1 ? "女" : "男"}</span>
						</td>
						<td>
						<span>${user.birthday}</span>
						</td>
						<td>
						<span>${user.phone}</span>
						</td>
						<td>
							<!-- 遍历用户角色列表 -->
							<c:forEach items="${roleList}" var ="role" varStatus="status">
								<!-- 根据用户的userRole 与  角色的 id 匹配对应的 角色 -->
								<c:if test="${ role.id eq user.userRole}" var="flag">
									<span>${role.roleName}</span>
								</c:if>
							</c:forEach>
						</td>
						<td>
						<span><a class="viewUser"   href="${pageContext.request.contextPath }/userServlet?method=userInfo&id=${user.id}" ><img src="${pageContext.request.contextPath }/images/read.png" alt="查看" title="查看"/></a></span>
						<span><a class="modifyUser" href="${pageContext.request.contextPath }/userServlet?method=toUserMod&id=${user.id}"><img src="${pageContext.request.contextPath }/images/xiugai.png" alt="修改" title="修改"/></a></span>
						<span><a class="deleteUser" href="${pageContext.request.contextPath }/userServlet?method=userDel&id=${user.id}"  onClick="return confirm('是否确认删除${user.userName}用户')" ><img src="${pageContext.request.contextPath }/images/schu.png" alt="删除" title="删除"/></a></span>
						</td>
					</tr>
				</c:forEach>
			</table>
			<input type="hidden" id="totalPageCount" value="1"/>
		  	<c:import url="rollpage.jsp">
	          	<c:param name="totalCount" value="1"/>
	          	<c:param name="currentPageNo" value="1"/>
	          	<c:param name="totalPageCount" value="1"/>
          	</c:import>
        </div>
    </section>

<%@include file="/jsp/common/foot.jsp" %>

3.2 pageSupport

主要有三类数据:

1、分页参数

2、回显参数 (条件查询的条件)

3、条件分页查询的数据

public class PageSupport<T> {
	//当前页,显示页码
	private int currPageNo = 1;
	
	//页面容量
	private int pageSize = 5;

	//总条数(带条件查询的总条数)
	private int totalCount;
	
	//总页数(根据总条数和页面容量)
	private int totalPage;
    
	//分页条件查询的数据
	private T data;

	//回显 查询数据
	private List<String> callBackInfo;

	//排序字段
	private  String orderBy = "id";

	//升序 还是 降序
	private String ascOrDesc = "asc";

	//设置总条数的时候  计算总页数
    public void setTotalCount(int totalCount) {
		//当存在总条数,确定总页数
		this.totalCount = totalCount;
		//计算总页数
		this.totalPage = this.totalCount % this.pageSize == 0 ?
				this.totalCount / this.pageSize :
					this.totalCount / this.pageSize + 1;
	}

	public int getCurrPageNo() {
		return currPageNo;
	}

	public void setCurrPageNo(int currPageNo) {
		//页码特殊处理
		if(currPageNo < 1 || this.totalCount == 0 ){
			currPageNo = 1;
		}else if(currPageNo > this.totalPage){
			currPageNo = this.getTotalPage();
		}
		this.currPageNo = currPageNo;
	}
	.......
}

3.3 userList 方法

分页 条件查询用户列表

  1. 获取 条件查询参数
  2. 获取 分页参数pageNopageSize
  3. 获取 排序参数
  4. 条件查询 总条数
  5. 创建 分页对象(指定data的类型,一般 List )
  6. 设置 pageSize (必须先放pageSize)
  7. 设置 totalCount (再放totalCount),pageSupport计算总页数
  8. 创建 回显数据集合,放入需要回显的数据
  9. 查询分页条件查询的 数据集合List
  10. 将数据集合放入pageSupport的data中
  11. pageSupport放入request中,方便转发后 遍历数据,和 回显数据
  12. 查询角色列表(展示 用户信息 的时候 和 条件查询的 时候需要使用)
  13. 角色列表集合放入session域中 (这一类需要经常使用的参数,可以放到session中,不过修改后要重置)
  14. 转发 到用户展示页面 userlist.jsp
// 分页 条件查询用户列表
public void userList(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println("=============条件 分页 查询用户列表=====================");
    //获取 条件查询 参数
    String queryName = req.getParameter("queryName");
    String queryUserRole = req.getParameter("queryUserRole")  == null ? "0" : req.getParameter("queryUserRole");

    //获取 分页 参数
    //获取显示的当前页码
    Integer pageNo = Integer.parseInt( req.getParameter("pageNo") == null ? "1" : req.getParameter("pageNo") );
    Integer pageSize = Integer.parseInt( req.getParameter("pageSize") == null ? "5" : req.getParameter("pageSize") );

    //排序条件
    String orderBy = req.getParameter("orderBy") == null ? "id" : req.getParameter("orderBy");
    String ascOrDesc = req.getParameter("ascOrDesc") == null ? "asc" : req.getParameter("ascOrDesc");

    //根据条件查询获取用户 总数
    int totalCount = userService.getUserListTotalByQueryNameAndQueryUserRole(queryName,Integer.parseInt(queryUserRole));

    //创建分页对象
    PageSupport<List<User>> pageSupport = new PageSupport<List<User>>();
    //先放pageSize
    pageSupport.setPageSize(pageSize);
    //再放totalCount
    pageSupport.setTotalCount(totalCount);

    //创建回显数据
    List callBackInfo = new ArrayList();
    //pageSupport 中放入回显数据
    //查询条件
    callBackInfo.add(queryName);
    callBackInfo.add(queryUserRole);
    pageSupport.setCallBackInfo(callBackInfo);
    //当前页码
    pageSupport.setCurrPageNo(pageNo);
    //页面容量
    pageSupport.setPageSize(pageSize);
    //排序条件
    pageSupport.setOrderBy(orderBy);
    //排序  方式 asc  desc
    pageSupport.setAscOrDesc(ascOrDesc);


    //查询所有的用户详情列表
    List<User> userList = userService.getUserListByQueryNameAndQueryUserRole(queryName,Integer.parseInt(queryUserRole),pageSupport);
    System.out.println("=============条件 分页 查询用户列表 参数=============");
    System.out.println("查询参数==》queryName = " + queryName);
    System.out.println("查询参数==》queryUserRole = " + queryUserRole);
    System.out.println("排序参数==》orderBy = " + orderBy);
    System.out.println("排序参数==》ascOrDesc = " + ascOrDesc);
    System.out.println("分页参数==》pageNo = " + pageNo);
    System.out.println("分页参数==》pageSize = " + pageSize);
    System.out.println(JSON.toJSONStringWithDateFormat(userList,"yyyy-MM-dd"));
    //req.setAttribute("userList",userList);

    //将数据放入分页对象中
    pageSupport.setData(userList);

    //将分页对象放进request域中
    req.setAttribute("pageSupport",pageSupport);


    //查询所有的  用户角色
    List<Role> roleList = roleService.findRoleList();
    System.out.println("=============查询所有的用户角色===============");
    System.out.println(JSON.toJSONStringWithDateFormat(roleList,"yyyy-MM-dd"));
    //将roleList 放进session域中,方便其他的地方直接调用,不用再次查询
    req.getSession().setAttribute("roleList",roleList);

    //内部转发到用户列表页面
    req.getRequestDispatcher( "/jsp/userlist.jsp").forward(req,resp);
    //req.getContextPath()+"/jsp/userlist.jsp"  带项目名的时候不能这样会 拼接两次 项目名
    //req.getRequestDispatcher( req.getContextPath()+"/jsp/userlist.jsp").forward(req,resp);

}

3.4 Dao层实现方法

3.4.1 selectUserListTotalByQueryNameAndQueryUserRole

条件查询用户列表 总数

//条件查询用户列表  总数
@Override
public int selectUserListTotalByQueryNameAndQueryUserRole(String queryName, Integer QueryUserRole) {

    int total = 0;

    //SQl
    String executeSql = "select count(1) as 'total' \n"
        				+ "from smbms_user where 1=1 ";


    //params
    List<Object> params = new ArrayList<Object>();

    //拼接参数 和 sql
    if(null != queryName  && !"".equals(queryName)){
        executeSql += " and userName  like concat('%',?,'%') ";
        params.add(queryName);
    }

    if(null != QueryUserRole  &&  0 != QueryUserRole){
        executeSql += " and userRole = ? ";
        params.add(QueryUserRole);
    }

    try {
        //执行
        executeSelect(executeSql, params.toArray());

        //处理数据
        while (rs.next()) {
            total = rs.getInt("total");
        }
    }catch(Exception e){
        e.printStackTrace();
    }finally {
        releaseResource(conn,pstmt,rs);
    }

    return total;
}

3.4.2 SelectUserListByQueryNameAndQueryUserRole

条件 分页 查询用户列表

1、定义返回的参数

2、定义sql

3、动态拼接查询条件

3、动态拼接查询参数(List paramList)

4、动态拼接分页条件

5、动态拼接分页参数(List paramList)

6、执行sql

7、处理数据

其中 timestampdiff(year,birthday, now()) 计算年龄的函数,最后说明;

//条件  分页 查询用户列表
@Override
public List<User> SelectUserListByQueryNameAndQueryUserRole(String queryName, Integer QueryUserRole, PageSupport pageSupport) {

    List<User> userList = new ArrayList<User>();

    //SQl
    String executeSql = "select id,userCode,userName,userPassword,gender,birthday,timestampdiff(year,birthday, now()) as userAge ,phone,address,userRole,createdBy,creationDate,modifyBy,modifyDate\n"
        + "from smbms_user where 1=1 ";


    //params
    List<Object> paramList = new ArrayList<Object>();

    //拼接参数 和 sql
    if(null != queryName  && !"".equals(queryName)){
        executeSql += " and userName  like concat('%',?,'%') ";
        paramList.add(queryName);
    }

    if(null != QueryUserRole  &&  0 != QueryUserRole){
        executeSql += " and userRole = ? ";
        paramList.add(QueryUserRole);
    }

    //增加分页SQL语句
    executeSql += " order by "+pageSupport.getOrderBy()+ " " +pageSupport.getAscOrDesc()+" limit ?,?";
    paramList.add((pageSupport.getCurrPageNo() - 1) * pageSupport.getPageSize());
    paramList.add(pageSupport.getPageSize());

    try {
        //执行
        executeSelect(executeSql, paramList.toArray());

        //处理数据
        while (rs.next()) {
            User 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.setUserAge(rs.getInt("userAge"));
            user.setPhone(rs.getString("phone"));
            user.setAddress(rs.getString("address"));
            user.setUserRole(rs.getInt("userRole"));
            user.setCreatedBy(rs.getInt("createdBy"));
            user.setCreationDate(rs.getDate("creationDate"));
            user.setModifyBy(rs.getInt("modifyBy"));
            user.setModifyDate(rs.getDate("modifyDate"));

            userList.add(user);
        }
    }catch(Exception e){
        e.printStackTrace();
    }finally {
        releaseResource(conn,pstmt,rs);
    }

    return userList;
}

角色列表查询省略;

不过如果角色列表有更改需要重置session域中的角色列表集合数据

3.5 分页跳转

  1. 点击分页跳转按钮,或者 切换分页条件排序条件
  2. 将切换以后的分页参数,放到 表单的隐藏input中
  3. $(“#searchForm”).submit(); 提交表单即可;(每个页面的表单各自提交,分页部分Javascript就可以复用)

3.5.1 分页html

<div class="page-bar">
    <ul class="page-num-ul clearfix">
        <li>共&nbsp;${pageSupport.totalCount}&nbsp;条记录
            &nbsp;<span id="currPageNo">${pageSupport.currPageNo}</span>&nbsp;
            /
            &nbsp;<span id="totalPage">${pageSupport.totalPage}</span>&nbsp;页</li>&nbsp;&nbsp;
        <a href="javaScript:void(0);" >首页</a>
        <a href="javaScript:void(0);" >上一页</a>
        <a href="javaScript:void(0);" >下一页</a>
        <a href="javaScript:void(0);" >最后一页</a>
        &nbsp;&nbsp;	每页<select name="currentPageSize" id="currentPageSize">
        <option value="3" <c:if test="${pageSupport.pageSize == 3 }" > selected </c:if>>3</option>
    <option value="5" <c:if test="${pageSupport.pageSize == 5 }" > selected </c:if>>5</option>
<option value="10" <c:if test="${pageSupport.pageSize == 10 }" > selected </c:if>>10</option>
</select>条
根据<select name="currentOrderBy" id="currentOrderBy">
    <option value="id" <c:if test="${pageSupport.orderBy == 'id' }" > selected </c:if>> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id </option>
<option value="creationDate" <c:if test="${pageSupport.orderBy == 'creationDate' }" > selected </c:if>>创建时间</option>
</select>
<select name="currentAscOrDesc" id="currentAscOrDesc">
    <option value="asc" <c:if test="${pageSupport.ascOrDesc == 'asc' }" > selected </c:if>>升序</option>
<option value="desc" <c:if test="${pageSupport.ascOrDesc == 'desc' }" > selected </c:if>>降序</option>
</select>查询
</ul>
<span class="page-go-form"><label>跳转至</label>
    <input type="text" name="inputPage" id="inputPage" class="page-key" />页
    <button type="button" class="page-btn" >GO</button>
</span>
</div> 

3.5.2 页面跳转处理

首页,上一页,下一页,尾页,及上一页和下一页隐藏

//分页跳转
//首页
$(".page-num-ul  a:eq(0)").click(function(){
    //获取id为pageNo的input标签,并给value属性赋值
    // $("#pageNo").val(1);
    //获取表单,并给表单name为pageNo的input标签赋值
    document.forms[0].pageNo.value = 1;
    $("#searchForm").submit();
});

// 上一页
$(".page-num-ul  a:eq(1)").click(function(){
    // $("#pageNo").val(parseInt($("#currPageNo").text()) - 1);
    document.forms[0].pageNo.value = parseInt($("#currPageNo").text()) - 1;
    $("#searchForm").submit();
});


// 下一页
$(".page-num-ul  a:eq(2)").click(function(){
    // $("#pageNo").val(parseInt($("#currPageNo").text()) + 1);
    document.forms[0].pageNo.value = parseInt($("#currPageNo").text()) + 1;
    $("#searchForm").submit();
});

// 尾页
$(".page-num-ul  a:eq(3)").click(function(){
    // $("#pageNo").val(parseInt($("#totalPage").text()));
    document.forms[0].pageNo.value = parseInt($("#totalPage").text());
    $("#searchForm").submit();
});

//上一页,下一页隐藏处理
//上一页
if($("#currPageNo").text() == "1"){
    //alert($("#currPageNo").text());
    $(".page-num-ul  a:eq(1)").hide();
}else {
    $(".page-num-ul  a:eq(2)").show();
}

//下一页
if($("#currPageNo").text() == $("#totalPage").text()){
    //alert($("#currPageNo").text());
    $(".page-num-ul  a:eq(2)").hide();
    						//.css("display","none");
}else {
    //alert($("#currPageNo").text());
    $(".page-num-ul  a:eq(2)").show();
    						//.css("display","inline");
}

//跳到指定页数
$(".page-btn").click(function () {
    var $inputPage = $("#inputPage");
    if($inputPage.val() < 0  || $inputPage.val() > parseInt($("#totalPage").text())){
        alert("请输入正确的页数!!!");
    }else{
        //修改表单中的当前页
        // $("#pageNo").val($inputPage.val());
        document.forms[0].pageNo.value = $inputPage.val();
        $("#searchForm").submit();
    }
});

3.5.3 修改分页条件,或排序条件

select的$(“#selected”).change(function(){});

切换select选项触发change事件

修改每页页面大小

//修改每页页面大小 重新查询
$("#currentPageSize").change(function(){
    //获取修改后的 currentPageSize
    var currentPageSize = $(this).children('option:selected').val();
    //alert(currentPageSize);
    //修改提交表单的pageSize
    // $("#pageSize").val(currentPageSize);
    document.forms[0].pageSize.value = currentPageSize;
    //修改页面大小后,再主动查询一次动漫数据
    //将当前页数,重置为1
    // $("#pageNo").val(1);
    document.forms[0].pageNo = 1;
    $("#searchForm").submit();
});

修改查询条件

//修改查询条件
$("#currentOrderBy").change(function(){
    //获取修改后的 currentOrderBy
    var currentOrderBy = $(this).children('option:selected').val();
    //修改提交表单的orderBy
    // $("#orderBy").val(currentOrderBy);
    document.forms[0].orderBy.value =  currentOrderBy;
    //修改页面大小后,再主动查询一次动漫数据
    //将当前页数,重置为1
    // $("#pageNo").val(1);
    document.forms[0].pageNo.value = 1;
    $("#searchForm").submit();
});

修改升序或降序

//升序或降序
$("#currentAscOrDesc").change(function(){
    //获取修改后的 currentAscOrDesc
    var currentAscOrDesc = $(this).children('option:selected').val();
    //修改提交表单的orderBy
    // $("#ascOrDesc").val(currentAscOrDesc);
    document.forms[0].ascOrDesc.value = currentAscOrDesc;
    //修改页面大小后,再主动查询一次动漫数据
    //将当前页数,重置为1
    // $("#pageNo").val(1);
    document.forms[0].pageNo.value = 1;
    $("#searchForm").submit();
});

4、查看详情

  1. 携带用户id跳转到用户详情请求
  2. 根据用户id查询到用户信息
  3. 存放在request域中
  4. 转发到用户详情页面
  5. EL表达式取出用户数据

查看详情a标签

<a class="viewUser"   href="${pageContext.request.contextPath }/userServlet?method=userInfo&id=${user.id}" > 查看用户详情

5、修改用户

  1. 携带用户id用户修改请求
  2. 根据id查询出用户信息
  3. 存放到request域中
  4. 转发到用户修改页面
  5. EL表达式取出用户数据(注意隐藏用户id)

用户修改a标签

<a class="modifyUser" href="${pageContext.request.contextPath }/userServlet?method=toUserMod&id=${user.id}">修改用户
  1. 修改完数据,表单提交到用户修改请求
  2. 用户信息修改成功后跳转到用户条件分页查询请求,重新查询数据

用户修改页面的form表单请求

 <form id="userForm" name="userForm" method="post" action="${pageContext.request.contextPath}/userServlet?method=userMod">

6、删除用户

  1. 携带用户id用户删除请求
  2. 删除成功后,跳转到用户条件分页查询请求,重新查询数据
<a class="deleteUser" href="${pageContext.request.contextPath }/userServlet?method=userDel&id=${user.id}"  onClick="return confirm('是否确认删除${user.userName}用户')" >

7、增加用户

  1. 跳转到用户添加页面
  2. 填写用户信息
  3. 表单提交到用户添加请求
  4. 添加完用户后,跳转到用户条件分页查询请求,重新查询数据

添加用户a标签

 <a href="${pageContext.request.contextPath}/jsp/useradd.jsp" >添加用户</a>

用户添加页面的用户添加请求表单

 <form id="userForm" name="userForm" method="post" action="${pageContext.request.contextPath }/userServlet?method=userAdd">

8、修改用户密码

  1. 校验两次用户输入的新密码是否一样
  2. 判断用户的旧密码是否正确(用户密码要从数据库中查询)
  3. 根据登录的id修改用户密码
  4. 修改成功,移除session中的登录用户对象
  5. 重定向到跳转到用户登录页面

9、注意点

9.1 tomcat项目启动不起来

注意看路径映射是否有问题;(有没有少写 “/”)

9.2 css获取到,但是渲染有问题

报错信息:Resource interpreted as Stylesheet but transferred with MIME type text/html

字符编码拦截器单独对css文件的解析类型进行处理

 @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    //设置post请求字符集  encoding为设置映射路径时,设置的初始化字符编码
    servletRequest.setCharacterEncoding(encoding);

    HttpServletRequest request = (HttpServletRequest) servletRequest;

    //静态资源放行
    //获取资源名
    String url=request.getRequestURI();
    //如果资源名包含css样式表、js样式表就特殊处理然后放行
    if(url.contains(".css")) {
        servletResponse.setContentType("text/css; charset=UTF-8");
        servletResponse.setCharacterEncoding(this.encoding);
        System.out.println("css放行");
        //放行请求
        filterChain.doFilter(servletRequest,servletResponse);
    }else {
        //设置响应字符集
        servletResponse.setContentType("text/html; charset=UTF-8");
        servletResponse.setCharacterEncoding(this.encoding);

        //放行请求
        filterChain.doFilter(servletRequest,servletResponse);
    }

}

9.3 自动导包问题

有时候自动导包导致不是自己想要的包直接用全类名即可

主要会再使用@WebServlet 和 @WebFilter 注解后会出现这个问题

javax.servlet.http.HttpServlet

@WebServlet(name = "userServlet",urlPatterns = "/userServlet")
public class UserServlet extends javax.servlet.http.HttpServlet{......}

javax.servlet.Filter

@WebFilter(filterName = "characterEncodingFilter",urlPatterns = "/*",initParams = {
        @WebInitParam(name = "encoding",value = "utf-8")
})
//
public class CharacterEncodingFilter implements javax.servlet.Filter {......}

9.4 form表单提交 拼接路径问题

form表单的action拼接路径,注意再get提交方式不能拼接参数,在post提交方式可以

form表单提交方式 action是否可以拼接参数
get 不可以(实在要使用get就在input中隐藏,这样get和post都可以)
post 可以

get提交

get提交由于不能拼接参数,所以将method参数隐藏在表单中;

action=”${pageContext.request.contextPath }/billServlet”

<form id="searchForm" method="get" action="${pageContext.request.contextPath }/billServlet">
  <input name="method" type="hidden" value="billList">
   ......
</form>

post提交

post提交可以直接拼接参数,也可以将method参数隐藏在表单中;

action=”${pageContext.request.contextPath }/billServlet?method=billList”

<form id="searchForm" method="post" action="${pageContext.request.contextPath }/billServlet?method=billList">
   ......
</form>

9.5转发和重定向到底要不要 + req.getContextPath()

9.5.1 req.getContextPath()

首先要知道 req.getContextPath() 获取到的是什么内容?

如果发布的项目路径为:/kh96_smbms

System.out.println(" req.getContextPath()==》"+req.getContextPath());
// req.getContextPath()==》 /kh96_smbms         注意它前面有 /,这个是重点

如果发布的项目名为:/

System.out.println(" req.getContextPath()==》"+req.getContextPath());
// req.getContextPath()==》         什么都没有,包括我们之前写发布名时的 / 也没有

知道 req.getContextPath() 获取的是什么我们就 恍然大悟了;

9.5.2 再看转发和重定向的路径区别

转发和重定向的路径区别

  • 重定向路径中如果有 “/”,只会获取到 站点,然后拼接上我们写的路径;
  • 转发路径中如果有 “/”,会先获取到 站点 + 项目名,再拼接上我们写的路径;

所以重定向:(前提自己写的路径前有 /)

  • 如果有项目名,我们一定要 加上 req.getContextPath(),获取到项目名,再拼接跳转的路径;

  • 如果没有项目名,我们加上或不加上,都可以

转发:(前提自己写的路径前有 /)

  • 如果有项目名,我们不能 加上 req.getContextPath(),如果加上会多拼接上一个项目名,而找不到路径;
  • 如果没有项目名,我们加上或不加上,都可以

总结:(前提自己写的路径前有 /)

  • 没有项目名,加不加 req.getContextPath() 都可以,不过建议加上
  • 有项目名:
    • 重定向一定要加req.getContextPath() ;
    • 转发一定不能加req.getContextPath() ;

10、sql的时间差函数

参考博客

用来计算时间的差距,年龄,距上一次登录多少天等等;

10.1 datediff()函数

只比较日期

DATEDIFF() 函数返回两个日期之间的天数。
date1 和 date2 参数是合法的日期或日期/时间表达式。 只有值的日期部分参与计算。

测试一下MySQL:

 SELECT DATEDIFF('2018-05-09 08:00:00','2018-05-09') AS DiffDate;
 //结果 0 ; 表示 2018-05-09 与 2018-05-09之间没有日期差。这里是不比较时分秒的。下面验证带上时分秒有没有差别。
 SELECT DATEDIFF('2018-05-09 00:00:00','2018-05-09 23:59:59') AS DiffDate;
 //结果 0 ;
 SELECT DATEDIFF('2018-05-08 23:59:59','2018-05-09 00:00:00') AS DiffDate;
 //结果 -1;
 SELECT DATEDIFF('2018-05-09 00:00:00','2018-05-08 23:59:59') AS DiffDate;
//结果 1;

10.2timestampdiff()

可以计算,年,月,日的差距;

select timestampdiff(YEAR,"2018-01-01 15:15:16","2019-08-23 15:15:16") as timestamodiff;
//结果1。相差一年。
select timestampdiff(YEAR,"2019-08-22 15:15:19","2018-12-23 15:15:16") as timestamodiff;
//结果 -1,也是相差一年。
select timestampdiff(YEAR,"2019-08-22 15:15:19","2018-08-22 15:15:16") as timestamodiff; 
//结果0,相差不到一年

select timestampdiff(MONTH,"2018-08-22 15:15:16","2018-07-23 15:15:16") as timestamodiff;
// 结果 0 相差不到一个月
select timestampdiff(MONTH,"2018-08-22 15:15:16","2018-07-01 15:15:16") as timestamodiff;
// 结果 -1 相差一个月
select timestampdiff(MONTH,"2018-08-22 15:15:19","2018-09-23 15:15:16") as timestamodiff; 
// 结果 1 想差一个月

select timestampdiff(DAY,"2018-08-22 15:15:16","2018-08-23 15:15:16") as timestamodiff;
// 结果 1 想差一天
select timestampdiff(DAY,"2018-08-22 15:15:19","2018-08-23 15:15:16") as timestamodiff;
// 结果 0 想差不到一天
select timestampdiff(DAY,"2018-08-23 15:15:19","2018-08-22 15:15:16") as timestamodiff;
// 结果 -1 想差一天