一、项目导入

  1. maven项目通过识别pom.xml来加载
  2. 导入方法
    • 方法一:点击右侧maven项目+号,选择pom文件,点击ok开始导入
    • 方法二:通过file中的Project Structure导入模块

二、启动项目

  1. 启动方法

    • 方法一:点击右侧maven项目中的tomcat:run启动
    • 方法二:添加配置,点击绿色三角启动项目

  2. 报错

    • 错误1:‘build.plugins.plugin.version’ for org.apache.maven.plugins:maven- compiler-plugin is missing. @ line 122, column 21

    • 解决:为插件添加版本信息

    • 错误2:Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!

    • 解决:添加编码属性utf-8

三、技术选型

  1. Web层

    1. Servlet:前端控制器
    2. html:视图
    3. Filter:过滤器
    4. BeanUtils:数据封装
    5. Jackson:json序列化工具
  2. Service层

    1. Javamail:java发送邮件工具
    2. Redis:nosql内存数据库
    3. Jedis:java的redis客户端
  3. Dao层

    1. Mysql:数据库
    2. Druid:数据库连接池
    3. JdbcTemplate:jdbc的工具

web层

  • html:这里要求客户访问速度快,且前后端分离,所以用html而不用jsp;如果以后搞后台管理系统,如财务管理,给内部人员,看可以用jsp
  • BeanUtils:前台要将数据传到servlet,如表单提交,则需要beanutils进行数据封装成对象
  • Jackson:html作为表嵌层,只能用异步的方式来提交到服务器去和从服务器返回数据。一般用json进行异步交互,所以在servlet中需要将服务器响应给客户端的数据用序列化为json返回

Dao层

  • JdbcTemplate:用于连接数据库

四、创建数据库

在SQLyog中执行代码:

-- 创建数据库
CREATE DATABASE travel;
-- 使用数据库
USE travel;
--创建表
这里粘贴提供好的sql文件中的代码

五、注册功能

1、 表单校验

  1. 实现(register.html)

    • form标签内加入action属性
    <!--注册表单-->
    <form id="registerForm" action="user">
    • 导入jquery,js实现表单的校验
    <!--导入jquery-->
    <script src="js/jquery-3.3.1.js"></script>
       
    <script>
    	/*
    	表单校验:
    	    1.用户名:单词字符,长度8到20位
    	    2.密码:单词字符,长度8到20位
    	    3.email:邮件格式
    	    4.姓名:非空
    	    5.手机号:数字,长度10位
    	    6.出生日期:非空
    	    7.验证码:非空
    	 */
       
    	//1.校验用户名
    	function checkUsername() {
    		//1.获取用户名值
    		var username = $("#username").val();
    		//2.定义正则
    		var reg_username = /^\w{8,20}$/;
    		//3.判断,给出提示信息
    		var flag = reg_username.test(username);
    		if(flag){
    			//用户名合法
                      $("#username").css("border","");
    		}else {
    			//用户名非法,加一个红色边框
                      $("#username").css("border","1px solid red");
    		}
    		return flag;
    	}
    	//2.校验密码
    	function checkPassword() {
                  //1.获取用户名值
                  var password = $("#password").val();
                  //2.定义正则
                  var reg_password = /^\w{8,20}$/;
                  //3.判断,给出提示信息
                  var flag = reg_password.test(password);
                  if(flag){
                      //用户名合法
                      $("#password").css("border","");
                  }else {
                      //用户名非法,加一个红色边框
                      $("#password").css("border","1px solid red");
                  }
                  return flag;
    	}
    	//3.校验邮箱
              function checkemail(){
    	    //1.获取邮箱
                  var email = $("#email").val();
                  //2.定义正则
                  var reg_email = /^\w+@\w+\.\w+$/;
                  //3.判断
                  var flag = reg_email.test(email);
                  if (flag){
                      $("#email").css("border","")
                  }else {
                      $("#email").css("border","1px solid red");
                  }
                  return flag;
       
              }
              //4.校验姓名:非空
              function checkName(){
    	    //1.获取姓名
                  var name = $("#name").val();
                  //2.判断
                  if(name == ""){
                      $("#name").css("border","1px solid red");
                      return false;
                  }else {
                      $("#name").css("border","");
                      return true;
                  }
              }
              //5.校验手机号:数字,长度10位
              function checkTelephone(){
    	    //1.获取手机号
                  var telephone = $("#telephone").val();
                  //2.定义正则
                  var reg_telephone = /^[0-9]{10}$/;
                  //3.判断
                  var flag = reg_telephone.test(telephone);
                  if (flag){
                      $("#telephone").css("border","");
                  }else {
                      $("#telephone").css("border","1px solid red");
                  }
                  return flag;
              }
              //6.校验出生日期:非空
              function checkSex(){
                  //1.获取姓名
                  var sex = $("#sex").val();
                  //2.判断
                  if(sex == ""){
                      $("#sex").css("border","1px solid red");
                      return false;
                  }else {
                      $("#sex").css("border","");
                      return true;
                  }
              }
              //7.校验验证码:非空
    	function checkCheck(){
    		//1.获取姓名
    		var check = $("#check").val();
    		//2.判断
    		if(check == ""){
    			$("#check").css("border","1px solid red");
    			return false;
    		}else {
    			$("#check").css("border","");
    			return true;
    		}
    	}
    	/*
    	$(function () {
    		//当表单提交时,调用所有的校验方法
    		//校验通过要转到新的页面
    		$("#registerForm").submit(function () {
    			return checkUsername() && checkPassword() && checkemail();
    			//如果这个方法没有返回值,或者返回为true,则表单提交,如果返回为false,则表单不提交
    		})
    		});
    	*/
    	$(function () {
    		//当表单提交时,调用所有的校验方法
    		//校验通过要转到新的页面
    		$("#registerForm").submit(function () {
    			//1.发送数据到服务器
       
    			if(checkUsername() && checkPassword() && checkemail()){
    				//校验通过发送ajax请求,提交表单的数据 username=张三&password=123
    				$.post("registUserServlet",$(this).serialize(),function (data) {
       
    					//处理服务器响应的数据 data {flag:true,errorMsg:"注册失败"}
    					if (data.flag){
    						//注册成功,跳转成功页面
    						location.href="register_ok.html";
    					}else {
    						//注册失败,给errorMsg添加提示信息
    						$("#errorMsg").html(data.errorMsg);
    					}
       
    				})
    			}
    			//2.跳转页面
    			return false;
    			//如果这个方法没有返回值,或者返回为true,则表单提交,如果返回为false,则表单不提交
    		});
    		//当某一个组件失去焦点时,调用对应的校验方法
    		$("#username").blur(checkUsername);  //注意:只有方法名,不含括号
                  $("#password").blur(checkPassword);
                  $("#email").blur(checkemail);
                  $("#name").blur(checkName);
                  $("#telephone").blur(checkTelephone);
                  $("#sex").blur(checkSex);
                  $("#check").blur(checkCheck);
    	})
    </script>
  1. 知识充能中……OWO

    1. JavaScript中的$函数

      • 通过ID获取Element,跟直接调用getElementById()效果是一样的
      • 例如:$(“#username”)得到的是页面id为”username”的元素Javascript RegExp对象(JavaScript正则对象)
    2. Javascript RegExp对象(JavaScript正则对象)

      • test()方法用于检测一个字符串是否匹配某个模式,如果字符串中有匹配的值返回 true ,否则返回 false

        • 语法:RegExpObject.test(string)
        • 例如
        //1.获取用户名值
        var username = $("username").val();
        //2.定义正则
        var reg_username = /^\w{8,20}$/;
        //3.判断,给出提示信息
        var flag = reg_username.test(username);

2、异步ajax提交表单

  1. 异步ajax提交表单的原因

    为了获取服务器响应的数据。因为我们前台使用的是html作为视图层,不能够直接从servlet相关的域对象获取值,只能通过ajax获取响应数据

  2. 实现(register.html)

    $(function () {
    				//当表单提交时,调用所有的校验方法
    				//校验通过要转到新的页面
    				$("#registerForm").submit(function () {
    					//1.发送数据到服务器
    
    					if(checkUsername() && checkPassword() && checkemail()){
    						//校验通过发送ajax请求,提交表单的数据 username=张三&password=123
    						$.post("registUserServlet",$(this).serialize(),function (data) {
    
    							//处理服务器响应的数据 data {flag:true,errorMsg:"注册失败"}
    							if (data.flag){
    								//注册成功,跳转成功页面
    								location.href="register_ok.html";
    							}else {
    								//注册失败,给errorMsg添加提示信息
    								$("#errorMsg").html(data.errorMsg);
    							}
    
    						});
    					}
    					//2.跳转页面
    					return false;
    					//如果这个方法没有返回值,或者返回为true,则表单提交,如果返回为false,
    					则表单不提交
    				});

3、servlet、service、dao

  1. 其中实现的功能括有:验证码判断、前台数据处理、发送邮件、点击邮件内容激活功能

    • 用户点击邮箱激活其实就是修改用户表中的status为“Y”,分别编写servlet、service、Dao的代码

  2. 实现

    • RegistUserServlet
    @WebServlet("/registUserServlet")
    public class RegistUserServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) 
        										throws ServletException, IOException {
    
    
            //验证校验
            String check = request.getParameter("check");
            //从sesion中获取验证码
            HttpSession session = request.getSession();
            String checkcode_server = (String) session.getAttribute("CHECKCODE_SERVER");
            session.removeAttribute("CHECKCODE_SERVER");//为了保证验证码只能使用一次
            //比较
            if(checkcode_server == null || !checkcode_server.equalsIgnoreCase(check)){
                //验证码错误
                ResultInfo info = new ResultInfo();
                //注册失败
                info.setFlag(false);
                info.setErrorMsg("验证码错误");
                //将info对象序列化为json
                ObjectMapper mapper = new ObjectMapper();
                String json = mapper.writeValueAsString(info);
                response.setContentType("application/json;charset=utf-8");
                response.getWriter().write(json);
                return;
            }
    
            //1.获取数据
            Map<String, String[]> map = request.getParameterMap();
    
            //2.封装对象
            User user = new User();
            try {
                BeanUtils.populate(user,map);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
    
            //3.调用service完成注册
            UserService service = new UserServiceImpl();
            boolean flag = service.regist(user);
            ResultInfo info = new ResultInfo();
            //4.响应结果
            if(flag){
                //注册成功
                info.setFlag(true);
            }else{
                //注册失败
                info.setFlag(false);
                info.setErrorMsg("注册失败!");
            }
    
            //将info对象序列化为json
            ObjectMapper mapper = new ObjectMapper();
            String json = mapper.writeValueAsString(info);
    
            //将json数据写回客户端
            //设置content-type
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(json);
    
    
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) 
        									throws ServletException, IOException {
            this.doPost(request, response);
        }
    }
    • UserService
    public interface UserService {
        boolean regist(User user);
    
        boolean active(String code);
    
        User login(User user);
    }
    • UserServiceImpl
    public class UserServiceImpl implements UserService {
    
        private UserDao userDao = new UserDaoImpl();
        /**
         * 注册用户
         * @param user
         * @return
         */
        @Override
        public boolean regist(User user) {
            //1.根据用户名查询用户对象
            User u = userDao.findByUsername(user.getUsername());
            //判断u是否为null
            if (u != null){
                //用户名存在,注册失败
                return false;
            }
    
            //2.保存用户信息
            //2.1设置激活码,唯一字符串
            user.setCode(UuidUtil.getUuid());
            //2.2设置激活状态
            user.setStatus("N");
            userDao.save(user);
    
            //3.激活邮件发送,邮件正文?
            //String content = "<a href='http://localhost/travel/activeUserServlet?code='"
            							//+user.getCode()+">点击激活【旅游网】</a>";
            String content="<a href='http://localhost/travel/activeUserServlet?code="
            							+user.getCode()+"'>点击激活【旅游网】</a>";
    
            MailUtils.sendMail(user.getEmail(),content,"激活邮件");
            return true;
        }
    
        /**
         * 激活用户
         * @param code
         * @return
         */
        @Override
        public boolean active(String code) {
            //1.根据激活码查询用户对象
            User user = userDao.findByCode(code);
            if (user != null){
                //2.调用dao的修改激活状态的方法
                userDao.updateStatus(user);
                return true;
            }else {
                return false;
            }
    
        }
    
        /**
         * 登陆方法
         * @param user
         * @returnnull
         */
        @Override
        public User login(User user) {
            return userDao.findByUsernameAndPassword(user.getUsername(),user.getPassword());
        }
    }
    • UserDao
    public interface UserDao {
        /**
         * 根据用户名查询用户信息
         * @param username
         * @return
         */
        public User findByUsername(String username);
    
        /**
         * 用户保存
         * @param user
         */
        public void save (User user);
    
        User findByCode(String code);
    
        void updateStatus(User user);
    
        User findByUsernameAndPassword(String username, String password);
    }
    • UserDaoImpl
    public class UserDaoImpl implements UserDao {
    
        private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
    
        @Override
        public User findByUsername(String username) {
            //1.定义sql语句
            User user = null;
            try {  //防止第一次注册时,会直接报错误
                String sql = "select * from tab_user where username = ?";
                //2.执行sql
                user = template.queryForObject(sql, 
                					new BeanPropertyRowMapper<User>(User.class), username);
            } catch (Exception e) {
    
            }
    
    
            return user;
        }
    
        @Override
        public void save(User user) {
            //1.定义sql
            //注意:这里添加了status和code才会在数据库保存了激活状态和激活码
            String sql = "insert into tab_user(username,password,name,birthday,sex,
            					telephone,email,status,code) values(?,?,?,?,?,?,?,?,?)";
            //2.执行sql
            //注意:这里添加了status和code才会在数据库保存了激活状态和激活码
            template.update(sql, user.getUsername(),
                    user.getPassword(),
                    user.getName(),
                    user.getBirthday(),
                    user.getSex(),
                    user.getTelephone(),
                    user.getEmail(),
                    user.getStatus(),
                    user.getCode()
            );
        }
    
        /**
         * 根据激活码查询用户对象
         * @param code
         * @return
         */
        @Override
        public User findByCode(String code) {
            User user = null;
            try {
                String sql = "select * from tab_user where code = ?";
                user = template.queryForObject(sql, 
                					new BeanPropertyRowMapper<User>(User.class), code);
            } catch (DataAccessException e) {
                e.printStackTrace();
            }
            //注意:未修改导致结果出现 “激活失败,请联系管理员”
            //return null;
            return user;
        }
    
        /**
         * 修改指定用户激活状态
         * @param user
         */
        @Override
        public void updateStatus(User user) {
            //注意:这里是tab_user和uid而不是user和id
            String sql = "update tab_user set status = 'Y' where uid = ?";
            template.update(sql,user.getUid());
        }
    
        /**
         * 根据用户名和密码查询的方法
         * @param username
         * @param password
         * @return
         */
        @Override
        public User findByUsernameAndPassword(String username, String password) {
            //1.定义sql语句
            User user = null;
            try {  //防止第一次注册时,会直接报错误
                String sql = "select * from tab_user where username = ? and password = ?";
                //2.执行sql
                user = template.queryForObject(sql, 
                			new BeanPropertyRowMapper<User>(User.class), username, password);
            } catch (Exception e) {
    
            }
    
    
            return user;
        }
    }
  3. 报错

    • 错误1:java.lang.IllegalArgumentException: Property ‘dataSource’ is required
    • 解决

    • 错误2:java:程序包XXX不存在

      Idea版本为2020.1,maven版本为3.6.3,jar包的确已经下载到自定义本地库

    • 解决

      此处改bug历时整整一天还要多些!QWQ…具体事况发展如下…

      • 无数次点击右上角Maven工程里的的Reimport All Maven Projects。失败!
      • close、import工程 。失败!
      • 删除掉本地库(自定义的本地库)的所有包,重新import工程。失败!
      • 将conf/setting.xml文件里的自定义本地库删除,使用默认;修改IDEA中的设置,设置的是自定义的本地库,重新import工程。失败!
      • 在IDEA的Terminal-local界面中执行mvn cleanmvn install,然后点击右上角Maven工程里的的Reimport All Maven Projects。成功!

      在这里执行mvn install 时,download了很多依赖;执行结束后是下图的样子 — 打包后的文件被放在了默认的本地库里,于是赶忙去默认的本地库位置查看了一下,果然刚刚download的依赖也在这里。但是,明明已经在IDEA中修改了设置 — 本地库使用自定义,结果却下载、打包都放在了默认仓库中。

      很是疑惑(・∀・(・∀・(・∀・*)还有待搞清楚具体原因QWQ

      • 难道Terminal窗口的命令和cmd窗口是一样的?(也就是说配置根据conf/setting.xml,而不是IDEA中的设置)
      • 为什么明明已经设置了自定义本地库,但是只有依赖在默认仓库里时,IDEA的项目启动后才会被识别?

      附参考链接:点击跳转

    • 错误3:javax.mail.AuthenticationFailedException: 535 Login Fail. Please enter your authorization code to login

    • 解决:修改prop.put中的参数为163.com

      附参考链接:点击跳转

    • 错误4:点击激活邮件中的激活链接后,跳转的网址中code无值

    • 解决:跳转链接的href属性值写错了

    • 错误5:错误1已经解决的情况下,code有了值,但是页面显示“激活失败,请联系管理员”

    • 解决:code值成功传到地址栏,成功跳转到对应的servlet,但是findByCode方法return null,所以返回user为空,出现了错误。这里应该将user返回

六、登录功能

1、登录页

  1. 实现

    • 按钮设置id
    <button type="button" id="btn_sub">登录</button>
    • 提交事件
    <script>
    		$(function () {
    			//1.给登录按钮绑定单击事件
    			$("#btn_sub").click(function () {
    				//2.发送ajax请求,提交表单数据
    				$.post("loginServlet",$("#loginForm").serialize(),function (data) {
    					//data : {flag:false,errorMsg:''}
    					if (data.flag){
    						//登陆成功
    						location.href="index.html";
    					}else {
    						//登陆失败
    						$("#errorMsg").html(data.errorMsg);
    
    					}
    				});
    			});
    
    		})
    	</script>

2、登陆成功姓名提示

  1. 异步交互:html页面不能通过el表达式,通过session获得用户信息,需要通过异步交互获取,ajax请求

  2. 实现

    • 页面显示部分(header.html)
    <script>
        $(function () {
            $.get("findUserServlet",{},function (data) {
                //{uid:1,name:'李四'}
                var msg = "欢迎回来,"+data.name;
                $("#span_username").html(msg);
            });
        });
    </script>
    • 取出数据(FindUserServlet)
    @WebServlet("/findUserServlet")
    public class FindUserServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) 
        					throws ServletException, IOException {
            //从session中获取登录用户
            Object user = request.getSession().getAttribute("user");
            //将user写回客户端
            ObjectMapper mapper = new ObjectMapper();
            response.setContentType("application/json;charset=utf-8");
            mapper.writeValue(response.getOutputStream(),user);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) 
        					throws ServletException, IOException {
            this.doPost(request,response);
        }
    }

3、servlet、service、dao

  1. 完成登录功能

  2. 实现

    • LoginServlet
    @WebServlet("/loginServlet")
    public class LoginServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) 
        							throws ServletException, IOException {
            //1.获取用户名和密码数据
            Map<String, String[]> map = request.getParameterMap();
            //2.封装User对象
            User user = new User();
    
            try {
                BeanUtils.populate(user,map);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            //3.调用service查询
            UserService service = new UserServiceImpl();
            User u = service.login(user);
    
    
    
            ResultInfo info = new ResultInfo();
    
            //4.判断用户对象是否为null
            if (u == null){
                //用户名或密码错误
                info.setFlag(false);
                info.setErrorMsg("用户名或密码错误");
            }
            //5.判断用户是否激活
            if (u != null && !"Y".equals(u.getStatus())){
                //用户尚未激活
                info.setFlag(false);
                info.setErrorMsg("您尚未激活,待激活");
            }
    
            //6.判断登陆成功
            if (u != null && "Y".equals(u.getStatus())){
                //登陆成功
                info.setFlag(true);
            }
    
            //存入session
            request.getSession().setAttribute("user",u);
    
            //响应数据
            ObjectMapper mapper = new ObjectMapper();
    
            response.setContentType("application/json;charset=utf-8");
            mapper.writeValue(response.getOutputStream(),info);
    
    
    
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) 
        								throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    • UserService

    注册功能代码段中的下面这行代码

    User login(User user);
    • UserServiceImpl

    注册功能代码段中的下面这行代码

    /**
     * 登陆方法
     * @param user
     * @returnnull
     */
    @Override
    public User login(User user) {
       
        return userDao.findByUsernameAndPassword(user.getUsername(),user.getPassword());
    }
    • UserDao

    注册功能代码段中的下面这行代码

    User findByUsernameAndPassword(String username, String password);
    • UserDaoImpl

    注册功能代码段中的下面这行代码

    /**
         * 根据用户名和密码查询的方法
         * @param username
         * @param password
         * @return
         */
        @Override
        public User findByUsernameAndPassword(String username, String password) {
            //1.定义sql语句
            User user = null;
            try {  //防止第一次注册时,会直接报错误
                String sql = "select * from tab_user where username = ? and password = ?";
                //2.执行sql
                user = template.queryForObject(sql, 
                			new BeanPropertyRowMapper<User>(User.class), username, password);
            } catch (Exception e) {
    
            }
    
    
            return user;
        }

七、退出登录

  1. session中有user对象即为登录

    • 实现步骤
      1. 访问servlet,将session销毁
      2. 跳转到登录页面
  2. 实现

    • 点击退出访问exitServlet(header.html)
    <a href="javascript:location.href='exitServlet';">退出</a>
    • 创建ExitServlet
    @WebServlet("/exitServlet")
    public class ExitServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response)
       									 throws ServletException, IOException {
            //1.销毁session
            request.getSession().invalidate();
    
            //2.跳转登陆页面(重定向
            response.sendRedirect(request.getContextPath()+"/login.html");
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) 
      									  throws ServletException, IOException {
            doPost(request,response);
        }
    }

八、优化Servlet

减少servlet的数量,现在是一个功能一个servlet,将其优化成为一个模块一个servlet,相当于在数据库中一张表对应一个servlet,在servlet中提供不同的方法,完成用户的请求

BaseServlet的抽取:

九、分类菜单

分类菜单的分类从数据库取出:

要启动redis服务器才能成功显示分类菜单

十、分页展示

点击分类菜单,对应显示的页面从数据库取出:

视频内容是31p-39p

其中,31p缺少route_list.html的编写,缺少的是如下代码(获取cid):

<script>
	$(function () {
		var search = location.search;
		//alert(search);  //?id = 5
		//切割字符串,拿到第二个值
		var cid = search.split("=")[1];
	})
</script>

32p缺少domain目录下PageBean的编写

33p还没有结束RouteServlet的编写,缺少的是如下代码:

//3. 调用service查询PageBean对象
PageBean<Route> pb = routeService.pageQuery(cid, currentPage, pageSize);

//4. 将pageBean对象序列化为json,返回
writeValue(pb,response);

还缺少了service、serviceImpl的创建

json数据:

十一、搜索栏

报错:PreparedStatementCallback; SQL [select count(*) from tab_route where 1=1 ]; Parameter index out of range (1 > number of parameters, which is 0).;

解决:

十二、查看详情

后台分析:

前台分析:

Route_detail.html中加载后

  1. 获取rid
  2. 发送ajax请求,获取route对象
  3. 解析对象的数据

十三、线路收藏