Java项目——云R记

Java入门级项目:云R记

视频地址

https://www.bilibili.com/video/BV1YU4y1p7wj?from=search&seid=1307576622141006226&spm_id_from=333.337.0.0

完整代码

https://github.com/ShadowLim/Java_Project

1、项目介绍

  • 云R记软件就是用于记录日常生活点滴。一款简单快速的个人记事备忘工具。会议记录、日程安排、生活备忘,奇思妙想、快乐趣事以及任何突发灵感都可快速记录到系统中。
  • 本系统采用B/S 架构,使用 BootStrap + Jsp + Servlet + MySQL+ Tomcat
    开发,使用 Maven构建,采用 Junit单元测试、Log4j搭建日志、使用POI导入导出报表,操作 DB 使用大名鼎鼎的 DBUtil
  • 本项目包含用户模块、类型模块、云记模块、数据报表、首页模块这几个核心模块,核心主旨是掌握功能的分析以及前后台数据交互。

2、需求分析

tb_note表

字段名称 字段类型 字段描述
noteId int 主键,自增
title varchar 标题
content text 内容
typeId int 外键,从属tb_note_type
pubTime timestamp 发布时间
lon float 经度
lat float 纬度

tb_note_type表

字段名称 字段类型 字段描述
typeId int 主键,自动增长
typeName varchar 类别名,在同一个用户下唯一
userId int 从属用户

tb_user表

字段名称 字段类型 字段描述
userId int 主键,自动增长
uname varchar 用户名
upwd varchar 密码
nick varchar 昵称
head varchar 头像
mood varchar 心情

3、 环境搭建

3.1 创建项目

  • 3.1.1 新建项目:
    选择 “File” ——> “New” ——> “Project…” ,选择 “Maven”,设置 JDK 版本,选择 Maven Web 项目的模板

Java项目——云R记_第1张图片

  • 3.1.2 配置Maven:
    Java项目——云R记_第2张图片

  • 3.1.3 设置项目信息:
    设置项目的名称、存放的工作空间,以及对应的 GroupId

Java项目——云R记_第3张图片

  • 3.1.4 项目结构

创建好 Maven Web项目之后,对应的目录结构如下:
Java项目——云R记_第4张图片

  • 3.1.5 修改配置
    在 pomx.xml 配置文件中,修改配置、添加依赖与插件



  4.0.0

  com.lezijie
  lezijie-note
  1.0-SNAPSHOT
  war

  lezijie-note Maven Webapp
  
  http://www.example.com

  
    UTF-8
    1.8
    1.8
  

  

    
    
      junit
      junit
      4.12
      test
    

    
    
      javax.servlet
      javax.servlet-api
      4.0.1
      provided
    

    
    
      org.slf4j
      slf4j-log4j12
      1.7.2
    
    
      org.slf4j
      slf4j-api
      1.7.2
    

    
    
      mysql
      mysql-connector-java
      8.0.19
    

    
    
      com.alibaba
      fastjson
      1.2.62
    

    
    
      cn.hutool
      hutool-all
      5.4.7
    

    
    
      commons-io
      commons-io
      2.4
    

    
    
      commons-fileupload
      commons-fileupload
      1.3.1
    

    
    
      javax.servlet
      jstl
      1.2
    

    
    
      org.projectlombok
      lombok
      1.18.12
      provided
    

  

  
    lezijie-note

    
      
      
        org.apache.maven.plugins
        maven-compiler-plugin
        2.3.2
        
          1.8
          1.8
          UTF-8
        
      

      
      
        org.apache.tomcat.maven
        tomcat7-maven-plugin
        2.1
        
          8080 
          /note 
          tomcat7 
        
      

    
  



3.2 分层思想

  • 在项目的 src/main 目录下,新建 java 目录(源文件夹)与 resources 目录 (资源文件夹)。

Java项目——云R记_第5张图片

  • 定义包结构

Java项目——云R记_第6张图片

  • Java 的几种对象(PO,VO,DAO,BO,POJO)解释
  • PO:
    persistant object 持久对象,可以看成是与数据库中的表相映射的 Java 对象。
    最简单的 PO 就是对应数据库中某个表中的一条记录,多个记录可以用 PO 的集合。
    PO 中应该不包含任何对数据库的操作。
  • VO:
    value object 值对象。通常用于业务层之间的数据传递,和 PO 一样也是仅仅包含数据而已
    但应是抽象出的业务对象,可以和表对应,也可以不,这根据业务的需要。
  • DAO:
    data access object 数据访问对象,此对象用于访问数据库
    通常和PO结合使用,DAO 中包含了各种数据库的操作方法
    通过它的方法,结合 PO 对数据库进行相关的操作。
  • BO:
    business object 业务对象,封装业务逻辑的 java 对象
    通过调用 DAO 方法,结合 PO,VO 进行业务操作。
  • POJO:
    plain ordinary Java object 简单无规则 java 对象。

3.3 数据库连接

  • 配置文件

在 src/main/resources 目录下,新建 db.properties 文件

# 连接MYSQL数据库的配置文件 注:等号的前后不要写空格
# 驱动名
jdbcName=com.mysql.cj.jdbc.Driver
# 数据库连接 ( db_lezijie_note是数据库的名称)
dbUrl=jdbc:mysql://localhost:3306/db_lezijie_note?
useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
# 数据库的连接账号 (账号基本上都是root)
dbName=root
# 数据库的连接密码 (每个人的数据库密码可能不一致,需要修改)
dbPwd=root1234
  • 编写 DBUtil

在 com.lezijie.note.util 目录下,新建 Java 类

package com.lezijie.note.util;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**
 * 乐字节:专注线上IT培训
 * 需要视频资料,请添加:lezijie007
 */
public class DBUtil {

    // 得到配置文件对象
    private static Properties properties = new Properties();

    static {
        try {
            // 加载配置文件(输入流)
            InputStream in = DBUtil.class.getClassLoader().getResourceAsStream("db.properties");
            // 通过load()方法将输入流的内容加载到配置文件对象中
            properties.load(in);
            // 通过配置文件对象的getProperty()方法获取驱动名,并加载驱动
            Class.forName(properties.getProperty("jdbcName"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取数据库连接
     * @return
     */
    public static Connection getConnetion() {
        Connection connection = null;
        try {
            // 得到数据库连接的相关信息
            String dbUrl = properties.getProperty("dbUrl");
            String dbName = properties.getProperty("dbName");
            String dbPwd = properties.getProperty("dbPwd");
            // 得到数据库连接
            connection = DriverManager.getConnection(dbUrl, dbName, dbPwd);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }

        return connection;
    }


    /**
     * 关闭资源
     * @param resultSet
     * @param preparedStatement
     * @param connection
     */
    public static void close(ResultSet resultSet,
                             PreparedStatement preparedStatement,
                             Connection connection) {

        try {
            // 判断资源对象如果不为空,则关闭
            if (resultSet != null) {
                resultSet.close();
            }
            if (preparedStatement != null) {
                preparedStatement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

3.4 单元测试

  • 3.4.1 测试目录

在 src/main 目录下,新建 test 目录(测试目录),新建 java 目录(测试源文件夹)

Java项目——云R记_第7张图片

  • 3.4.2 测试数据库

在 test/java 目录的 com.lezijie.note 包下,新建测试类

/**
* 单元测试类
*/
public class TestDB {
@Test
public void testConnection() {
System.out.println(DBUtil.getConnection());
}
}

Java项目——云R记_第8张图片

3.5 使用日志

  • 配置文件

在 src/main/resources 目录下,新建 log4j.properties 文件

### 设置###stdout,
log4j.rootLogger = all,D,E,stdout

### 输出信息到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 输出DEBUG 级别以上的日志到=D://logs/log.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = D://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG 
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR 
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]

观察D和E盘会发现有「D://logs/log.log」和「E://logs/error.log」两个文件生成。

  • 使用方式

下面两个代码均贴在上方的TestDB.java中

// 使用日志工厂类,记入日志
private static Logger logger = LoggerFactory.getLogger(DBUtil.class);
// 记录日志
logger.info("在{}时,获取数据库连接", new SimpleDateFormat("yyyy-MM-dd
HH:mm:ss").format(new Date()));

4、 用户模块

用户模块
通过用户行为来区分 actionName
用户登录 login
进入个人中心 userCenter
加载头像 userHead
验证昵称的唯一性 checkNick
修改用户信息 updateUser
用户退出 logout

在 src/main/wabapp 目录下, 新建 statics 文件夹,将静态资源拷贝进来(js、css、images及相关插
件)

5、 类型模块

  • 通过用户行为来区分 actionName
  • 查询类型列表 list
  • 删除类型 delete
  • 添加/修改类型 addOrUpdate

6、 云记模块

  • 通过用户行为来区分 actionName
  • 进入发布云记页面 view
  • 添加或修改云记 addOrUpdate
  • 查询云记详情 detail
  • 删除云记 delete
  • 分页查询云记列表 list

7、云记主页模块

  • 标题查询云记列表
  • 日期分组查询云记列表
  • 类型分组查询云记列表

8、 报表统计模块

  • 用户发布云记位置的散点图
  • 云记发布月份的柱状图

9、代码实现

9.1 note目录

9.1.1 dao层

  • BaseDao
package com.lezijie.note.dao;

import com.lezijie.note.util.DBUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

/**
 * 基础的JDBC操作类
 *      更新操作 (添加、修改、删除)
 *      查询操作
 *          1. 查询一个字段 (只会返回一条记录且只有一个字段;常用场景:查询总数量)
 *          2. 查询集合
 *          3. 查询某个对象
 */
public class BaseDao {

    /**
     * 更新操作
     *  添加、修改、删除
     *  1. 得到数据库连接
     *  2. 定义sql语句 (添加语句、修改语句、删除语句)
     *  3. 预编译
     *  4. 如果有参数,则设置参数,下标从1开始 (数组或集合、循环设置参数)
     *  5. 执行更新,返回受影响的行数
     *  6. 关闭资源
     *
     *  注:需要两个参数:sql语句、所需参数的集合
     *
     * @param sql
     * @param params
     * @return
     */
    public static int executeUpdate(String sql, List params) {
        int row = 0; // 受影响的行数
        Connection connection = null;
        PreparedStatement preparedStatement = null;

        try {
            // 得到数据库连接
            connection = DBUtil.getConnetion();
            // 预编译
            preparedStatement = connection.prepareStatement(sql);
            // 如果有参数,则设置参数,下标从1开始
            if (params != null && params.size() > 0) {
                // 循环设置参数,设置参数类型为Object
                for (int i = 0; i < params.size(); i++){
                    preparedStatement.setObject(i+1, params.get(i));
                }
            }
            // 执行更新,返回受影响的行数
            row = preparedStatement.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            DBUtil.close(null, preparedStatement, connection);
        }

        return row;
    }

    /**
     * 查询一个字段 (只会返回一条记录且只有一个字段;常用场景:查询总数量)
     *  1. 得到数据库连接
     *  2. 定义sql语句
     *  3. 预编译
     *  4. 如果有参数,则设置参数,下标从1开始 (数组或集合、循环设置参数)
     *  5. 执行查询,返回结果集
     *  6. 判断并分析结果集
     *  7. 关闭资源
     *
     *  注:需要两个参数:sql语句、所需参数的集合
     *
     * @param sql
     * @param params
     * @return
     */
    public static Object findSingleValue(String sql, List params) {
        Object object = null;
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            // 获取数据库连接
            connection = DBUtil.getConnetion();
            // 预编译
            preparedStatement = connection.prepareStatement(sql);
            // 如果有参数,则设置参数,下标从1开始
            if (params != null && params.size() > 0) {
                // 循环设置参数,设置参数类型为Object
                for (int i = 0; i < params.size(); i++){
                    preparedStatement.setObject(i+1, params.get(i));
                }
            }
            // 执行查询,返回结果集
            resultSet = preparedStatement.executeQuery();
            // 判断并分析结果集
            if (resultSet.next()) {
                object = resultSet.getObject(1);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            DBUtil.close(resultSet, preparedStatement, connection);
        }

        return object;
    }

    /**
     * 查询集合 (JavaBean中的字段与数据库中表的字段对应)
     *  1. 获取数据库连接
     *  2. 定义SQL语句
     *  3. 预编译
     *  4. 如果有参数,则设置参数,下标从1开始 (数组或集合、循环设置参数)
     *  5. 执行查询,得到结果集
     *  6. 得到结果集的元数据对象(查询到的字段数量以及查询了哪些字段)
     *  7. 判断并分析结果集
     *      8. 实例化对象
     *      9. 遍历查询的字段数量,得到数据库中查询到的每一个列名
     *      10. 通过反射,使用列名得到对应的field对象
     *      11. 拼接set方法,得到字符串
     *      12. 通过反射,将set方法的字符串反射成类中的指定set方法
     *      13. 通过invoke调用set方法
     *      14. 将对应的JavaBean设置到集合中
     *  15. 关闭资源
     * @param sql
     * @param params
     * @param cls
     * @return
     */
    public static List queryRows(String sql, List params, Class cls) {
        List list = new ArrayList();
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try{
            // 得到数据库连接
            connection = DBUtil.getConnetion();
            // 预编译
            preparedStatement = connection.prepareStatement(sql);
            // 如果有参数,则设置参数,下标从1开始
            if (params != null && params.size() > 0) {
                // 循环设置参数,设置参数类型为Object
                for (int i = 0; i < params.size(); i++){
                    preparedStatement.setObject(i+1, params.get(i));
                }
            }
            // 执行查询,返回结果集
            resultSet = preparedStatement.executeQuery();

            // 得到结果集的元数据对象(查询到的字段数量以及查询了哪些字段)
            ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
            // 得到查询的字段数量
            int fieldNum  = resultSetMetaData.getColumnCount();

            // 判断并分析结果集
            while (resultSet.next()) {
                // 实例化对象
                Object object = cls.newInstance();
                // 遍历查询的字段数量,得到数据库中查询的每一个列名
                for (int i = 1; i <= fieldNum; i++) {
                    // 得到查询的每一个列名
                    // getColumnLabel():获取列名或别名
                    // getColumnName():获取列名
                    String columnName = resultSetMetaData.getColumnLabel(i); // 如果是tb_user,userId字段
                    // 通过反射,使用列名得到对应的field对象
                    Field field = cls.getDeclaredField(columnName);
                    // 拼接set方法,得到字符串
                    String setMethod = "set" + columnName.substring(0,1).toUpperCase() + columnName.substring(1);
                    // 通过反射,将set方法字符串反射成类中对应的set方法
                    Method method = cls.getDeclaredMethod(setMethod, field.getType());
                    // 得到查询的每一个字段对应的值
                    Object value = resultSet.getObject(columnName);
                    // 通过invoke方法调用set方法
                    method.invoke(object, value);
                }
                // 将Javabean设置到集合中
                list.add(object);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(resultSet, preparedStatement, connection);
        }
        return list;
    }

    /**
     * 查询对象
     * @param sql
     * @param params
     * @param cls
     * @return
     */
    public static Object queryRow(String sql, List params, Class cls) {
        List list = queryRows(sql, params, cls);
        Object object = null;
        // 如果集合不为空,则获取查询的第一条数据
        if (list != null && list.size() > 0) {
            object = list.get(0);
        }

        return object;
    }

}


  • NoteDao
package com.lezijie.note.dao;

import cn.hutool.core.util.StrUtil;
import com.lezijie.note.po.Note;
import com.lezijie.note.vo.NoteVo;

import java.util.ArrayList;
import java.util.List;

public class NoteDao {

    /**
     * 添加或修改云记,返回受影响的行数
     * @param note
     * @return
     */
    public int addOrUpdate(Note note) {
        // 定义SQL语句
        String sql = "";

        // 设置参数
        List params = new ArrayList<>();
        params.add(note.getTypeId());
        params.add(note.getTitle());
        params.add(note.getContent());

        // 判断noteId是否为空;如果为空,则为添加操作;如果不为空,则为修改操作
        if (note.getNoteId() == null) { // 添加操作
            sql = "insert into tb_note (typeId, title, content, pubTime, lon, lat) values (?,?,?,now(),?,?)";
            params.add(note.getLon());
            params.add(note.getLat());
        } else { // 修改操作
            sql = "update tb_note set typeId = ?, title = ? , content = ? where noteId = ?";
            params.add(note.getNoteId());
        }

        // 调用BaseDao的更新方法
        int row = BaseDao.executeUpdate(sql, params);
        return row;
    }

    /**
     * 查询当前登录用户的云记数量,返回总记录数
     * @param userId
     * @return
     */
    public long findNoteCount(Integer userId, String title, String date, String typeId) {
        // 定义SQL语句
        String sql = "SELECT count(1) FROM tb_note n INNER JOIN " +
                " tb_note_type t on n.typeId = t.typeId " +
                " WHERE userId = ? ";

        // 设置参数
        List params = new ArrayList<>();
        params.add(userId);

        // 判断条件查询的参数是否为空 (如果查询的参数不为空,则拼接sql语句,并设置所需要的参数)
        if (!StrUtil.isBlank(title)) { // 标题查询
            // 拼接条件查询的sql语句
            sql += " and title like concat('%',?,'%') ";
            // 设置sql语句所需要的参数
            params.add(title);

        } else if (!StrUtil.isBlank(date)) { // 日期查询
            // 拼接条件查询的sql语句
            sql += " and date_format(pubTime,'%Y年%m月') = ? ";
            // 设置sql语句所需要的参数
            params.add(date);

        } else if (!StrUtil.isBlank(typeId)) { // 类型查询
            // 拼接条件查询的sql语句
            sql += " and n.typeId = ? ";
            // 设置sql语句所需要的参数
            params.add(typeId);
        }

        // 调用BaseDao的查询方法
        long count = (long) BaseDao.findSingleValue(sql, params);

        return count;
    }

    /**
     * 分页查询当前登录用户下当前页的数据列表,返回note集合
     * @param userId
     * @param index
     * @param pageSize
     * @return
     */
    public List findNoteListByPage(Integer userId, Integer index, Integer pageSize,
                                         String title, String date, String typeId) {
        // 定义SQL语句
        String sql = "SELECT noteId,title,pubTime FROM tb_note n INNER JOIN " +
                "tb_note_type t on n.typeId = t.typeId WHERE userId = ? ";

        // 设置参数
        List params = new ArrayList<>();
        params.add(userId);

        // 判断条件查询的参数是否为空 (如果查询的参数不为空,则拼接sql语句,并设置所需要的参数)
        if (!StrUtil.isBlank(title)) {
            // 拼接条件查询的sql语句
            sql += " and title like concat('%',?,'%') ";
            // 设置sql语句所需要的参数
            params.add(title);
        }  else if (!StrUtil.isBlank(date)) { // 日期查询
            // 拼接条件查询的sql语句
            sql += " and date_format(pubTime,'%Y年%m月') = ? ";
            // 设置sql语句所需要的参数
            params.add(date);
        } else if (!StrUtil.isBlank(typeId)) { // 类型查询
            // 拼接条件查询的sql语句
            sql += " and n.typeId = ? ";
            // 设置sql语句所需要的参数
            params.add(typeId);
        }

        // 拼接分页的sql语句 (limit语句需要写在sql语句最后)
        sql += " order by pubTime desc limit ?,?";
        params.add(index);
        params.add(pageSize);

        // 调用BaseDao的查询方法
        List noteList = BaseDao.queryRows(sql, params, Note.class);

        return noteList;
    }



    /**
     * 通过日期分组查询当前登录用户下的云记数量
     * @param userId
     * @return
     */
    public List findNoteCountByDate(Integer userId) {
        // 定义SQL语句
        String sql = "SELECT count(1) noteCount,DATE_FORMAT(pubTime,'%Y年%m月') groupName FROM tb_note n " +
                " INNER JOIN tb_note_type t ON n.typeId = t.typeId WHERE userId = ? " +
                " GROUP BY DATE_FORMAT(pubTime,'%Y年%m月')" +
                " ORDER BY DATE_FORMAT(pubTime,'%Y年%m月') DESC ";

        // 设置参数
        List params = new ArrayList<>();
        params.add(userId);

        // 调用BaseDao的查询方法
        List list = BaseDao.queryRows(sql, params, NoteVo.class);

        return list;
    }

    /**
     * 通过类型分组查询当前登录用户下的云记数量
     * @param userId
     * @return
     */
    public List findNoteCountByType(Integer userId) {
        // 定义SQL语句
        String sql = "SELECT count(noteId) noteCount, t.typeId, typeName groupName FROM tb_note n " +
                " RIGHT JOIN tb_note_type t ON n.typeId = t.typeId WHERE userId = ? " +
                " GROUP BY t.typeId ORDER BY COUNT(noteId) DESC ";

        // 设置参数
        List params = new ArrayList<>();
        params.add(userId);

        // 调用BaseDao的查询方法
        List list = BaseDao.queryRows(sql, params, NoteVo.class);

        return list;
    }


    /**
     * 通过id查询云记对象
     * @param noteId
     * @return
     */
    public Note findNoteById(String noteId) {
        // 定义SQL
        String sql = "select noteId,title,content,pubTime,typeName,n.typeId from tb_note n " +
                " inner join tb_note_type t on n.typeId=t.typeId where noteId = ?";

        // 设置参数
        List params = new ArrayList<>();
        params.add(noteId);

        // 调用BaseDao的查询方法
        Note note = (Note) BaseDao.queryRow(sql, params, Note.class);

        return note;
    }

    /**
     * 通过noteId删除云记记录,返回受影响的行数
     * @param noteId
     * @return
     */
    public int deleteNoteById(String noteId) {
        String sql = "delete from tb_note where noteId= ?";

        // 设置参数
        List params = new ArrayList<>();
        params.add(noteId);

        // 调用BaseDao
        int row = BaseDao.executeUpdate(sql, params);

        return row;
    }

    /**
     * 通过用户ID查询云记列表
     * @param userId
     * @return
     */
    public List queryNoteList(Integer userId) {
        // 定义SQL语句
        String sql = "select lon, lat from  tb_note n inner join tb_note_type t on n.typeId = t.typeId where userId = ?";

        // 设置参数
        List params = new ArrayList<>();
        params.add(userId);

        // 调用BaseDao
        List list = BaseDao.queryRows(sql, params, Note.class);

        return list;
    }
}

  • NoteTypeDao
package com.lezijie.note.dao;

import com.lezijie.note.po.NoteType;
import com.lezijie.note.util.DBUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class NoteTypeDao {


    /**
     * 通过用户ID查询类型集合
         1. 定义SQL语句
            String sql = "select typeId,typeName,userId from tb_note_type where userId = ? ";
         2. 设置参数列表
         3. 调用BaseDao的查询方法,返回集合
         4. 返回集合
     * @param userId
     * @return
     */
    public List findTypeListByUserId(Integer userId) {
        // 1. 定义SQL语句
        String sql = "select typeId,typeName,userId from tb_note_type where userId = ? ";
        // 2. 设置参数列表
        List params = new ArrayList<>();
        params.add(userId);
        // 3. 调用BaseDao的查询方法,返回集合
        List list = BaseDao.queryRows(sql, params, NoteType.class);
        // 4. 返回集合
        return list;
    }

    /**
     * 通过类型ID查询云记记录的数量,返回云记数量
     *
     * @param typeId
     * @return
     */
    public long findNoteCountByTypeId(String typeId) {
        // 定义SQL语句
        String sql = "select count(1) from tb_note where typeId = ?";
        // 设置参数集合
        List params = new ArrayList<>();
        params.add(typeId);
        // 调用BaseDao
        long count = (long) BaseDao.findSingleValue(sql, params);
        return count;
    }


    /**
     * 通过类型ID删除指定的类型记录,返回受影响的行数
     * @param typeId
     * @return
     */
    public int deleteTypeById(String typeId) {
        // 定义SQL语句
        String sql = "delete from tb_note_type where typeId = ?";
        // 设置参数集合
        List params = new ArrayList<>();
        params.add(typeId);
        // 调用BaseDao
        int row = BaseDao.executeUpdate(sql, params);
        return row;
    }

    /**
     * 查询当前登录用户下,类型名称是否唯一
     *     返回1,表示成功
     *     返回0,表示失败
     * @param typeName
     * @param userId
     * @param typeId
     * @return
     */
    public Integer checkTypeName(String typeName, Integer userId, String typeId) {
        // 定义SQL语句
        String sql = "select * from tb_note_type where userId = ? and typeName = ?";
        // 设置参数
        List params = new ArrayList<>();
        params.add(userId);
        params.add(typeName);
        // 执行查询操作
        NoteType noteType = (NoteType) BaseDao.queryRow(sql, params, NoteType.class);
        // 如果对象为空,表示可用
        if (noteType == null) {
            return 1;
        } else {
            // 如果是修改操作,则需要判断是否是当前记录本身
            if (typeId.equals(noteType.getTypeId().toString())) {
                return 1;
            }
        }
        return 0;
    }

    /**
     * 添加方法,返回主键
     * @param typeName
     * @param userId
     * @return
     */
    public Integer addType(String typeName, Integer userId) {
        Integer key = null;
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            // 得到数据库连接
            connection = DBUtil.getConnetion();
            // 定义SQL语句
            String sql = "insert into tb_note_type (typeName,userId) values (?,?)";
            // 预编译
            preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            // 设置参数
            preparedStatement.setString(1,typeName);
            preparedStatement.setInt(2, userId);
            // 执行更新,返回受影响的行数
            int row = preparedStatement.executeUpdate();
            // 判断受影响的行数
            if (row > 0) {
                // 获取返回主键的结果集
                resultSet = preparedStatement.getGeneratedKeys();
                // 得到主键的值
                if (resultSet.next()) {
                    key = resultSet.getInt(1);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            DBUtil.close(resultSet,preparedStatement,connection);
        }

        return key;
    }

    /**
     * 修改方法,返回受影响的行数
     * @param typeName
     * @param typeId
     * @return
     */
    public Integer updateType(String typeName, String typeId) {
        // 定义SQL语句
        String sql = "update tb_note_type set typeName = ? where typeId = ?";
        // 设置参数
        List params = new ArrayList<>();
        params.add(typeName);
        params.add(typeId);
        // 调用BaseDao的更新方法
        int row = BaseDao.executeUpdate(sql, params);
        return row;
    }
}

  • UserDao
package com.lezijie.note.dao;

import com.lezijie.note.po.User;
import com.lezijie.note.util.DBUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class UserDao {

    /**
     * 通过用户名查询用户对象
     *  1. 定义sql语句
     *  2. 设置参数集合
     *  3. 调用BaseDao的查询方法
     * @param userName
     * @return
     */
    public User queryUserByName(String userName) {
        // 1. 定义sql语句
        String sql = "select * from tb_user where uname = ?";

        // 2. 设置参数集合
        List params = new ArrayList<>();
        params.add(userName);

        // 3. 调用BaseDao的查询方法
        User user = (User) BaseDao.queryRow(sql, params, User.class);

        return user;
    }


    /**
        通过用户名查询用户对象, 返回用户对象
         1. 获取数据库连接
         2. 定义sql语句
         3. 预编译
         4. 设置参数
         5. 执行查询,返回结果集
         6. 判断并分析结果集
         7. 关闭资源
     * @param userName
     * @return
     */
    public User queryUserByName02(String userName) {
        User user = null;
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            // 1. 获取数据库连接
            connection = DBUtil.getConnetion();
            // 2. 定义sql语句
            String sql = "select * from tb_user where uname = ?";
            // 3. 预编译
            preparedStatement = connection.prepareStatement(sql);
            // 4. 设置参数
            preparedStatement.setString(1, userName);
            // 5. 执行查询,返回结果集
            resultSet = preparedStatement.executeQuery();
            // 6. 判断并分析结果集
            if (resultSet.next()) {
                user = new User();
                user.setUserId(resultSet.getInt("userId"));
                user.setUname(userName);
                user.setHead(resultSet.getString("head"));
                user.setMood(resultSet.getString("mood"));
                user.setNick(resultSet.getString("nick"));
                user.setUpwd(resultSet.getString("upwd"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 7. 关闭资源
            DBUtil.close(resultSet,preparedStatement,connection);
        }

       return user;
    }

    /**
     * 通过昵称与用户ID查询用户对象
     *  1. 定义SQL语句
     *     通过用户ID查询除了当前登录用户之外是否有其他用户使用了该昵称
     *          指定昵称  nick (前台传递的参数)
     *          当前用户  userId (session作用域中的user对象)
     *          String sql = "select * from tb_user where nick = ? and userId != ?";
     *  2. 设置参数集合
     *  3. 调用BaseDao的查询方法
     * @param nick
     * @param userId
     * @return
     */
    public User queryUserByNickAndUserId(String nick, Integer userId) {
        // 1. 定义SQL语句
        String sql = "select * from tb_user where nick = ? and userId != ?";
        // 2. 设置参数集合
        List params = new ArrayList<>();
        params.add(nick);
        params.add(userId);
        // 3. 调用BaseDao的查询方法
        User user = (User) BaseDao.queryRow(sql, params, User.class);
        return user;
    }

    /**
     * 通过用户ID修改用户信息
         1. 定义SQL语句
            String sql = "update tb_user set nick = ?, mood = ?, head = ? where userId = ? ";
         2. 设置参数集合
         3. 调用BaseDao的更新方法,返回受影响的行数
         4. 返回受影响的行数
     * @param user
     * @return
     */
    public int updateUser(User user) {
        // 1. 定义SQL语句
        String sql = "update tb_user set nick = ?, mood = ?, head = ? where userId = ? ";
        // 2. 设置参数集合
        List params = new ArrayList<>();
        params.add(user.getNick());
        params.add(user.getMood());
        params.add(user.getHead());
        params.add(user.getUserId());
        // 3. 调用BaseDao的更新方法,返回受影响的行数
        int row = BaseDao.executeUpdate(sql, params);
        return row;
    }
}


9.1.2 filter层

  • EncodingFilter
package com.lezijie.note.filter;

import cn.hutool.core.util.StrUtil;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 *
 * 字符乱码处理过滤器
 *
 * 请求乱码解决
    乱码原因:
        服务器默认的解析编码为ISO-8859-1,不支持中文。
    乱码情况:
        POST请求
            Tomcat7及以下版本    乱码
            Tomcat8及以上版本    乱码

        GET请求
             Tomcat7及以下版本    乱码
             Tomcat8及以上版本    不乱码

    解决方案:
        POST请求:
            无论是什么版本的服务器,都会出现乱码,需要通过request.setCharacterEncoding("UTF-8")设置编码格式。(只针对POST请求有效)
        GET请求
            Tomcat8及以上版本,不会乱码,不需要处理。
            Tomcat7及以下版本,需要单独处理。
                new String(request.getParamater("xxx").getBytes("ISO-8859-1"),"UTF-8");

 */
@WebFilter("/*") // 过滤所有资源
public class EncodingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 基于HTTP
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        // 处理POST请求 (只针对POST请求有效,GET请求不受影响)
        request.setCharacterEncoding("UTF-8");

        // 得到请求类型 (GET|POST)
        String method = request.getMethod();

        // 如果是GET请求,则判断服务器版本
        if ("GET".equalsIgnoreCase(method)) { // 忽略大小写比较
            // 得到服务器版本
            String serverInfo = request.getServletContext().getServerInfo(); // Apache Tomcat/7.0.79
            // 通过截取字符串,得到具体的版本号
            String version = serverInfo.substring(serverInfo.lastIndexOf("/")+1,serverInfo.indexOf("."));
            // 判断服务器版本是否是Toncat7级以下
            if (version != null && Integer.parseInt(version) < 8) {
                // Tomcat7及以下版本的服务器的GET请求
                MyWapper myRequest = new MyWapper(request);
                // 放行资源
                filterChain.doFilter(myRequest, response);
                return;
            }
        }

        filterChain.doFilter(request,response);
    }


    /**
     * 1. 定义内部类 (类的本质是request对象)
     * 2. 继承 HttpServletRequestWrapper包装类
     * 3. 重写getarameterP()方法
     */
    class MyWapper extends HttpServletRequestWrapper {

        // 定义成员变量 HttpServletRequest对象 (提升构造器中request对象的作用域)
        private HttpServletRequest request;

        /**
         * 带参构造
         *  可以得到需要处理的request对象
         * @param request
         */
        public MyWapper(HttpServletRequest request) {
            super(request);
            this.request = request;
        }

        /**
         * 重写getParameter方法,处理乱码问题
         * @param name
         * @return
         */
        @Override
        public String getParameter(String name) {
            // 获取参数 (乱码的参数值)
            String value = request.getParameter(name);
            // 判断参数值是否为空
            if (StrUtil.isBlank(value)) {
                return value;
            }
            // 通过new String()处理乱码
            try {
                value = new String(value.getBytes("ISO-8859-1"), "UTF-8");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return value;
        }
    }
}
  • LoginAccessFilter
package com.lezijie.note.filter;

import com.lezijie.note.po.User;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


/**
 * 非法访问拦截
 * 拦截的资源:
 * 所有的资源   /*
 * 

* 需要被放行的资源 * 1. 指定页面,放行 (用户无需登录的即可访问的页面;例如:登录页面login.jsp、注册页面register.jsp等) * 2. 静态资源,放行 (存放在statics目录下的资源;例如:js、css、images等) * 3. 指定行为,放行 (用户无需登录即可执行的操作;例如:登录操作actionName=login等) * 4. 登录状态,放行 (判断session作用域中是否存在user对象;存在则放行,不存在,则拦截跳转到登录页面) *

* 免登录(自动登录) * 通过Cookie和Session对象实现 *

* 什么时候使用免登录: * 当用户处于未登录状态,且去请求需要登录才能访问的资源时,调用自动登录功能 *

* 目的: * 让用户处于登录状态(自动调用登录方法) *

* 实现: * 从Cookie对象中获取用户的姓名与密码,自动执行登录操作 * 1. 获取Cookie数组 request.getCookies() * 2. 判断Cookie数组 * 3. 遍历Cookie数组,获取指定的Cookie对象 (name为user的cookie对象) * 4. 得到对应的cookie对象的value (姓名与密码:userName-userPwd) * 5. 通过split()方法将value字符串分割成数组 * 6. 从数组中分别得到对应的姓名与密码值 * 7. 请求转发到登录操作 user?actionName=login&userName=姓名&userPwd=密码 * 8. return *

* 如果以上判断都不满足,则拦截跳转到登录页面 */ @WebFilter("/*") public class LoginAccessFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // 基于HTTP HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; // 得到访问的路径 String path = request.getRequestURI(); // 格式:项目路径/资源路径 // 1. 指定页面,放行 (用户无需登录的即可访问的页面;例如:登录页面login.jsp、注册页面register.jsp等) if (path.contains("/login.jsp")) { filterChain.doFilter(request, response); return; } // 2. 静态资源,放行 (存放在statics目录下的资源;例如:js、css、images等) if (path.contains("/statics")) { filterChain.doFilter(request, response); return; } // 3. 指定行为,放行 (用户无需登录即可执行的操作;例如:登录操作actionName=login等) if (path.contains("/user")) { // 得到用户行为 String actionName = request.getParameter("actionName"); // 判断是否是登录操作 if ("login".equals(actionName)) { filterChain.doFilter(request, response); return; } } // 4. 登录状态,放行 (判断session作用域中是否存在user对象;存在则放行,不存在,则拦截跳转到登录页面) // 获取Session作用域中的user对象 User user = (User) request.getSession().getAttribute("user"); // 判断user对象是否为空 if (user != null) { filterChain.doFilter(request, response); return; } /** * 免登录 (自动登录) * 从Cookie对象中获取用户的姓名与密码,自动执行登录操作 */ // 1. 获取Cookie数组 request.getCookies() Cookie[] cookies = request.getCookies(); // 2. 判断Cookie数组 if (cookies != null && cookies.length > 0) { // 3. 遍历Cookie数组,获取指定的Cookie对象 (name为user的cookie对象) for (Cookie cookie : cookies) { if ("user".equals(cookie.getName())) { // 4. 得到对应的cookie对象的value (姓名与密码:userName-userPwd) String value = cookie.getValue(); // admin-123456 // 5. 通过split()方法将value字符串分割成数组 String[] val = value.split("-"); // 6. 从数组中分别得到对应的姓名与密码值 String userName = val[0]; String userPwd = val[1]; // 7. 请求转发到登录操作 user?actionName=login&userName=姓名&userPwd=密码 String url = "user?actionName=login&rem=1&userName=" + userName + "&userPwd=" + userPwd; request.getRequestDispatcher(url).forward(request, response); // 8. return return; } } } // 拦截请求,重定向跳转到登录页面 response.sendRedirect("login.jsp"); } @Override public void destroy() { } }

9.1.3 po层

  • Note
package com.lezijie.note.po;

import lombok.Getter;
import lombok.Setter;

import java.util.Date;

@Getter
@Setter
public class Note {

    private Integer noteId; // 云记ID
    private String title; // 云记标题
    private String content; // 云记内容
    private Integer typeId; // 云记类型ID
    private Date pubTime; // 发布时间
    private Float lon; // 经度
    private Float lat; // 纬度
    
    private String typeName;
}
  • NoteType
package com.lezijie.note.po;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class NoteType {
    private Integer typeId; // 类型ID
    private String typeName; // 类型名称
    private Integer userId; // 用户ID
}
  • User
package com.lezijie.note.po;


import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class User {

    private Integer userId; // 用户ID
    private String uname; // 用户名称
    private String upwd; // 用户密码
    private String nick; // 用户昵称
    private String head; // 用户头像
    private String mood; // 用户签名

    public void setUserId(Integer userId) {
        this.userId = userId;
    }
}

9.1.4 service层

  • NoteService
package com.lezijie.note.service;

import cn.hutool.core.util.StrUtil;
import com.lezijie.note.dao.NoteDao;
import com.lezijie.note.po.Note;
import com.lezijie.note.util.Page;
import com.lezijie.note.vo.NoteVo;
import com.lezijie.note.vo.ResultInfo;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class NoteService {
    private NoteDao noteDao = new NoteDao();

    /**
     * 添加或修改云记
         1. 参数的非空判断
            如果为空,code=0,msg=xxx,result=note对象,返回resultInfo对象
         2. 设置回显对象 Note对象
         3. 调用Dao层,添加云记记录,返回受影响的行数
         4. 判断受影响的行数
             如果大于0,code=1
             如果不大于0,code=0,msg=xxx,result=note对象
         5. 返回resultInfo对象
     * @param typeId
     * @param title
     * @param content
     * @return
     */
    public ResultInfo addOrUpdate(String typeId, String title,
                                        String content, String noteId, String lon, String lat) {
        ResultInfo resultInfo = new ResultInfo<>();

        // 1. 参数的非空判断
        if (StrUtil.isBlank(typeId)) {
            resultInfo.setCode(0);
            resultInfo.setMsg("请选择云记类型!");
            return  resultInfo;
        }
        if (StrUtil.isBlank(title)) {
            resultInfo.setCode(0);
            resultInfo.setMsg("云记标题不能为空!");
            return  resultInfo;
        }
        if (StrUtil.isBlank(content)) {
            resultInfo.setCode(0);
            resultInfo.setMsg("云记内容不能为空!");
            return  resultInfo;
        }

        // 设置经纬度的默认,默认设置为北京  116.404, 39.915
        if (lon == null || lat == null) {
            lon = "116.404";
            lat = "39.915";
        }

        // 2. 设置回显对象 Note对象
        Note note = new Note();
        note.setTitle(title);
        note.setContent(content);
        note.setTypeId(Integer.parseInt(typeId));
        note.setLon(Float.parseFloat(lon));
        note.setLat(Float.parseFloat(lat));

        // 判断云记ID是否为空
        if (!StrUtil.isBlank(noteId)) {
            note.setNoteId(Integer.parseInt(noteId));
        }

        resultInfo.setResult(note);

        // 3. 调用Dao层,添加云记记录,返回受影响的行数
        int row = noteDao.addOrUpdate(note);

        // 4. 判断受影响的行数
        if (row > 0) {
            resultInfo.setCode(1);
        } else {
            resultInfo.setCode(0);
            resultInfo.setResult(note);
            resultInfo.setMsg("更新失败!");
        }
        return resultInfo;
    }


    /**
     * 分页查询云记列表
         1. 参数的非空校验
            如果分页参数为空,则设置默认值
         2. 查询当前登录用户的云记数量,返回总记录数 (long类型)
         3. 判断总记录数是否大于0
         4. 如果总记录数大于0,调用Page类的带参构造,得到其他分页参数的值,返回Page对象
         5. 查询当前登录用户下当前页的数据列表,返回note集合
         6. 将note集合设置到page对象中
         7. 返回Page对象
     * @param pageNumStr
     * @param pageSizeStr
     * @param userId
     * @param title  条件查询的参数:标题
     * @return
     */
    public Page findNoteListByPage(String pageNumStr, String pageSizeStr,
                                         Integer userId, String title, String date, String typeId) {
        // 设置分页参数的默认值
        Integer pageNum = 1; // 默认当前页是第一页
        Integer pageSize = 5; // 默认每页显示5条数据
        // 1. 参数的非空校验 (如果参数不为空,则设置参数)
        if (!StrUtil.isBlank(pageNumStr)) {
            // 设置当前页
            pageNum = Integer.parseInt(pageNumStr);
        }
        if (!StrUtil.isBlank(pageSizeStr)) {
            // 设置每页显示的数量
            pageSize = Integer.parseInt(pageSizeStr);
        }

        // 2. 查询当前登录用户的云记数量,返回总记录数 (long类型)
        long count = noteDao.findNoteCount(userId, title, date, typeId);

        // 3. 判断总记录数是否大于0
        if (count < 1) {
            return null;
        }

        // 4. 如果总记录数大于0,调用Page类的带参构造,得到其他分页参数的值,返回Page对象
        Page page = new Page<>(pageNum, pageSize, count);

        // 得到数据库中分页查询的开始下标
        Integer index = (pageNum -1) * pageSize;

        // 5. 查询当前登录用户下当前页的数据列表,返回note集合
        List noteList = noteDao.findNoteListByPage(userId, index, pageSize, title, date, typeId);

        // 6. 将note集合设置到page对象中
        page.setDataList(noteList);

        // 7. 返回Page对象
        return page;
    }



    /**
     * 通过日期分组查询当前登录用户下的云记数量
     * @param userId
     * @return
     */
    public List findNoteCountByDate(Integer userId) {
        return noteDao.findNoteCountByDate(userId);
    }

    /**
     * 通过类型分组查询当前登录用户下的云记数量
     * @param userId
     * @return
     */
    public List findNoteCountByType(Integer userId) {
        return noteDao.findNoteCountByType(userId);
    }


    /**
     * 查询云记详情
         1. 参数的非空判断
         2. 调用Dao层的查询,通过noteId查询note对象
         3. 返回note对象
     * @param noteId
     * @return
     */
    public Note findNoteById(String noteId) {
        // 1. 参数的非空判断
        if (StrUtil.isBlank(noteId)) {
            return  null;
        }
        // 2. 调用Dao层的查询,通过noteId查询note对象
        Note note = noteDao.findNoteById(noteId);
        // 3. 返回note对象
        return note;
    }

    /**
     * 删除云记
         1. 判断参数
         2. 调用Dao层的更新方法,返回受影响的行数
         3. 判断受影响的行数是否大于0
            如果大于0,返回1;否则返回0
     * @param noteId
     * @return
     */
    public Integer deleteNote(String noteId) {
        // 1. 判断参数
        if (StrUtil.isBlank(noteId)) {
            return 0;
        }
        // 2. 调用Dao层的更新方法,返回受影响的行数
        int row = noteDao.deleteNoteById(noteId);
        // 3. 判断受影响的行数是否大于0
        if (row > 0) {
            return 1;
        }
        return 0;
    }

    /**
     * 通过月份查询对应的云记数量
     * @param userId
     * @return
     */
    public ResultInfo> queryNoteCountByMonth(Integer userId) {
        ResultInfo> resultInfo = new ResultInfo<>();

        // 通过月份分类查询云记数量
        List noteVos = noteDao.findNoteCountByDate(userId);

        // 判断集合是否存在
        if (noteVos != null && noteVos.size() > 0) {
            // 得到月份
            List monthList = new ArrayList<>();
            // 得到云记集合
            List noteCountList = new ArrayList<>();

            // 遍历月份分组集合
            for (NoteVo noteVo: noteVos) {
                monthList.add(noteVo.getGroupName());
                noteCountList.add((int)noteVo.getNoteCount());
            }

            // 准备Map对象,封装对应的月份与云记数量
            Map map = new HashMap<>();
            map.put("monthArray", monthList);
            map.put("dataArray", noteCountList);

            // 将map对象设置到ResultInfo对象中
            resultInfo.setCode(1);
            resultInfo.setResult(map);
        }
        return resultInfo;
    }

    /**
     * 查询用户发布云记时的坐标
     * @param userId
     * @return
     */
    public ResultInfo> queryNoteLonAndLat(Integer userId) {
        ResultInfo> resultInfo = new ResultInfo<>();

        // 通过用户ID查询云记列表
        List noteList = noteDao.queryNoteList(userId);

        // 判断是否为空
        if (noteList != null && noteList.size() > 0) {
            resultInfo.setCode(1);
            resultInfo.setResult(noteList);
        }
        return resultInfo;
    }
}
  • NoteTypeService
package com.lezijie.note.service;

import cn.hutool.core.util.StrUtil;
import com.lezijie.note.dao.NoteTypeDao;
import com.lezijie.note.po.NoteType;
import com.lezijie.note.vo.ResultInfo;

import java.util.List;

public class NoteTypeService {

    private NoteTypeDao typeDao = new NoteTypeDao();

    /**
     * 查询类型列表
         1. 调用Dao层的查询方法,通过用户ID查询类型集合
         2. 返回类型集合
     * @param userId
     * @return
     */
    public List findTypeList(Integer userId) {
        List typeList = typeDao.findTypeListByUserId(userId);
        return typeList;
    }

    /**
     * 删除类型
         1. 判断参数是否为空
         2. 调用Dao层,通过类型ID查询云记记录的数量
         3. 如果云记数量大于0,说明存在子记录,不可删除
            code=0,msg="该类型存在子记录,不可删除",返回resultInfo对象
         4. 如果不存在子记录,调用Dao层的更新方法,通过类型ID删除指定的类型记录,返回受影响的行数
         5. 判断受影响的行数是否大于0
            大于0,code=1;否则,code=0,msg="删除失败"
        6. 返回ResultInfo对象
     * @param typeId
     * @return
     */
    public ResultInfo deleteType(String typeId) {
        ResultInfo resultInfo = new ResultInfo<>();
        // 1. 判断参数是否为空
        if (StrUtil.isBlank(typeId)) {
            resultInfo.setCode(0);
            resultInfo.setMsg("系统异常,请重试!");
            return resultInfo;
        }

        // 2. 调用Dao层,通过类型ID查询云记记录的数量
        long noteCount = typeDao.findNoteCountByTypeId(typeId);

        // 3. 如果云记数量大于0,说明存在子记录,不可删除
        if (noteCount > 0) {
            resultInfo.setCode(0);
            resultInfo.setMsg("该类型存在子记录,不可删除!!");
            return resultInfo;
        }

        // 4. 如果不存在子记录,调用Dao层的更新方法,通过类型ID删除指定的类型记录,返回受影响的行数
        int row = typeDao.deleteTypeById(typeId);

        // 5. 判断受影响的行数是否大于0
        if (row > 0) {
            resultInfo.setCode(1);
        } else {
            resultInfo.setCode(0);
            resultInfo.setMsg("删除失败!");
        }
        return resultInfo;
    }


    /**
     * 添加或修改类型
         1. 判断参数是否为空 (类型名称)
            如果为空,code=0,msg=xxx,返回ResultInfo对象
         2. 调用Dao层,查询当前登录用户下,类型名称是否唯一,返回0或1
            如果不可用,code=0,msg=xxx,返回ResultInfo对象
         3. 判断类型ID是否为空
             如果为空,调用Dao层的添加方法,返回主键 (前台页面需要显示添加成功之后的类型ID)
             如果不为空,调用Dao层的修改方法,返回受影响的行数
         4. 判断 主键/受影响的行数 是否大于0
            如果大于0,则更新成功
                code=1,result=主键
            如果不大于0,则更新失败
                code=0,msg=xxx
     * @param typeName
     * @param userId
     * @param typeId
     * @return
     */
    public ResultInfo addOrUpdate(String typeName, Integer userId, String typeId) {
        ResultInfo resultInfo = new ResultInfo<>();
        // 1. 判断参数是否为空 (类型名称)
        if (StrUtil.isBlank(typeName)) {
            resultInfo.setCode(0);
            resultInfo.setMsg("类型名称不能为空!");
            return resultInfo;
        }

        //  2. 调用Dao层,查询当前登录用户下,类型名称是否唯一,返回0或1 (1=可用;0=不可用)
        Integer code = typeDao.checkTypeName(typeName, userId, typeId);
        // 如果不可用,code=0,msg=xxx,返回ResultInfo对象
        if (code == 0) {
            resultInfo.setCode(0);
            resultInfo.setMsg("类型名称已存在,请重新输入!");
            return resultInfo;
        }

        // 3. 判断类型ID是否为空
        // 返回的结果
        Integer key = null; // 主键或受影响的行数
        if (StrUtil.isBlank(typeId)) {
            // 如果为空,调用Dao层的添加方法,返回主键 (前台页面需要显示添加成功之后的类型ID)
            key = typeDao.addType(typeName, userId);
        } else {
            // 如果不为空,调用Dao层的修改方法,返回受影响的行数
            key = typeDao.updateType(typeName, typeId);
        }

        //  4. 判断 主键/受影响的行数 是否大于0
        if (key > 0) {
            resultInfo.setCode(1);
            resultInfo.setResult(key);
        } else {
            resultInfo.setCode(0);
            resultInfo.setMsg("更新失败!");
        }
        return resultInfo;
    }
}
  • UserService
package com.lezijie.note.service;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.lezijie.note.dao.UserDao;
import com.lezijie.note.po.User;
import com.lezijie.note.vo.ResultInfo;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;

public class UserService {

    private UserDao userDao = new UserDao();

    /**
     * 用户登录
         1. 判断参数是否为空
             如果为空
                 设置ResultInfo对象的状态码和提示信息
                 返回resultInfo对象
         2. 如果不为空,通过用户名查询用户对象
         3. 判断用户对象是否为空
             如果为空
                 设置ResultInfo对象的状态码和提示信息
                 返回resultInfo对象
         4. 如果用户对象不为空,将数据库中查询到的用户对象的密码与前台传递的密码作比较 (将密码加密后再比较)
             如果密码不正确
                 设置ResultInfo对象的状态码和提示信息
                 返回resultInfo对象
         5. 如果密码正确
            设置ResultInfo对象的状态码和提示信息
         6. 返回resultInfo对象
     * @param userName
     * @param userPwd
     * @return
     */
    public ResultInfo userLogin(String userName, String userPwd) {
        ResultInfo resultInfo = new ResultInfo<>();

        // 数据回显:当登录实现时,将登录信息返回给页面显示
        User u = new User();
        u.setUname(userName);
        u.setUpwd(userPwd);
        // 设置到resultInfo对象中
        resultInfo.setResult(u);

        //  1. 判断参数是否为空
        if (StrUtil.isBlank(userName) || StrUtil.isBlank(userPwd)) {
            // 如果为空 设置ResultInfo对象的状态码和提示信息
            resultInfo.setCode(0);
            resultInfo.setMsg("用户姓名或密码不能为空!");
            // 返回resultInfo对象
            return resultInfo;
        }

        // 2. 如果不为空,通过用户名查询用户对象
        User user = userDao.queryUserByName(userName);

        // 3. 判断用户对象是否为空
        if (user == null) {
            // 如果为空,设置ResultInfo对象的状态码和提示信息
            resultInfo.setCode(0);
            resultInfo.setMsg("该用户不存在!");
            // 返回resultInfo对象
            return resultInfo;
        }

        //  4. 如果用户对象不为空,将数据库中查询到的用户对象的密码与前台传递的密码作比较 (将密码加密后再比较)
        // 将前台传递的密码按照MD5算法的方式加密
        userPwd = DigestUtil.md5Hex(userPwd);
        // 判断加密后的密码是否与数据库中的一致
        if (!userPwd.equals(user.getUpwd())) {
            // 如果密码不正确
            resultInfo.setCode(0);
            resultInfo.setMsg("用户密码不正确!");
            return resultInfo;
        }

        resultInfo.setCode(1);
        resultInfo.setResult(user);
        return resultInfo;
    }

    /**
     * 验证昵称的唯一性
     * 1. 判断昵称是否为空
     *    如果为空,返回"0"
     * 2. 调用Dao层,通过用户ID和昵称查询用户对象
     * 3. 判断用户对象存在
     *    存在,返回"0"
     *    不存在,返回"1"
     * @param nick
     * @param userId
     * @return
     */
    public Integer checkNick(String nick, Integer userId) {
        // 1. 判断昵称是否为空
        if (StrUtil.isBlank(nick)) {
            return 0;
        }
        // 2. 调用Dao层,通过用户ID和昵称查询用户对象
        User user = userDao.queryUserByNickAndUserId(nick, userId);

        // 3. 判断用户对象存在
        if (user != null) {
            return 0;
        }
        return 1;
    }

    /**
     * 修改用户信息
         1. 获取参数(昵称、心情)
         2. 参数的非空校验(判断必填参数非空)
            如果昵称为空,将状态码和错误信息设置resultInfo对象中,返回resultInfo对象
         3. 从session作用域中获取用户对象(获取用户对象中默认的头像)
         4. 实现上上传文件
             1. 获取Part对象 request.getPart("name"); name代表的是file文件域的那么属性值
             2. 通过Part对象获取上传文件的文件名
             3. 判断文件名是否为空
             4. 获取文件存放的路径  WEB-INF/upload/目录中
             5. 上传文件到指定目录
         5. 更新用户头像 (将原本用户对象中的默认头像设置为上传的文件名)
         6. 调用Dao层的更新方法,返回受影响的行数
         7. 判断受影响的行数
            如果大于0,则修改成功;否则修改失败
         8. 返回resultInfo对象

     * @param request
     * @return
     */
    public ResultInfo updateUser(HttpServletRequest request) {
        ResultInfo resultInfo = new ResultInfo<>();
        // 1. 获取参数(昵称、心情)
        String nick = request.getParameter("nick");
        String mood = request.getParameter("mood");

        // 2. 参数的非空校验(判断必填参数非空)
        if (StrUtil.isBlank(nick)) {
            // 如果昵称为空,将状态码和错误信息设置resultInfo对象中,返回resultInfo对象
            resultInfo.setCode(0);
            resultInfo.setMsg("用户昵称不能卫空!");
            return resultInfo;
        }

        // 3. 从session作用域中获取用户对象(获取用户对象中默认的头像)
        User user = (User) request.getSession().getAttribute("user");
        // 设置修改的昵称和头像
        user.setNick(nick);
        user.setMood(mood);

        // 4. 实现上上传文件
        try {
            // 1. 获取Part对象 request.getPart("name"); name代表的是file文件域的name属性值
            Part part = request.getPart("img");
            // 2. 通过Part对象获取上传文件的文件名 (从头部信息中获取上传的文件名)
            String header = part.getHeader("Content-Disposition");
            // 获取具体的请求头对应的值
            String str = header.substring(header.lastIndexOf("=") + 2);
            // 获取上传的文件名
            String fileName = str.substring(0, str.length() - 1);
            // 3. 判断文件名是否为空
            if (!StrUtil.isBlank(fileName)) {
                // 如果用户上传了头像,则更新用户对象中的头像
                user.setHead(fileName);
                // 4. 获取文件存放的路径  WEB-INF/upload/目录中
                String filePath = request.getServletContext().getRealPath("/WEb-INF/upload/");
                // 5. 上传文件到指定目录
                part.write(filePath + "/" + fileName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 6. 调用Dao层的更新方法,返回受影响的行数
        int row = userDao.updateUser(user);
        // 7. 判断受影响的行数
        if (row > 0) {
            resultInfo.setCode(1);
            // 更新session中用户对象
            request.getSession().setAttribute("user", user);
        } else {
            resultInfo.setCode(0);
            resultInfo.setMsg("更新失败!");
        }
        return resultInfo;
    }
}

9.1.5 util层

  • DBUtil
package com.lezijie.note.util;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**
 * 乐字节:专注线上IT培训
 * 需要视频资料,请添加:lezijie007
 */
public class DBUtil {

    // 得到配置文件对象
    private static Properties properties = new Properties();

    static {
        try {
            // 加载配置文件(输入流)
            InputStream in = DBUtil.class.getClassLoader().getResourceAsStream("db.properties");
            // 通过load()方法将输入流的内容加载到配置文件对象中
            properties.load(in);
            // 通过配置文件对象的getProperty()方法获取驱动名,并加载驱动
            Class.forName(properties.getProperty("jdbcName"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取数据库连接
     * @return
     */
    public static Connection getConnetion() {
        Connection connection = null;
        try {
            // 得到数据库连接的相关信息
            String dbUrl = properties.getProperty("dbUrl");
            String dbName = properties.getProperty("dbName");
            String dbPwd = properties.getProperty("dbPwd");
            // 得到数据库连接
            connection = DriverManager.getConnection(dbUrl, dbName, dbPwd);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }

        return connection;
    }


    /**
     * 关闭资源
     * @param resultSet
     * @param preparedStatement
     * @param connection
     */
    public static void close(ResultSet resultSet,
                             PreparedStatement preparedStatement,
                             Connection connection) {

        try {
            // 判断资源对象如果不为空,则关闭
            if (resultSet != null) {
                resultSet.close();
            }
            if (preparedStatement != null) {
                preparedStatement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
  • JsonUtil
package com.lezijie.note.util;

import com.alibaba.fastjson.JSON;

import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

/**
 * 将对象转换成JOSN格式的字符串,响应给ajax的回调函数
 */
public class JsonUtil {

    public static void toJson(HttpServletResponse response, Object result) {
        try {
            // 设置响应类型及编码格式 (json类型)
            response.setContentType("application/json;charset=UTF-8");
            // 得到字符输出流
            PrintWriter out = response.getWriter();
            // 通过fastjson的方法,将ResultInfo对象转换成JSON格式的字符串
            String json = JSON.toJSONString(result);
            // 通过输出流输出JSON格式的字符串
            out.write(json);
            // 关闭资源
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
  • Page
package com.lezijie.note.util;

import lombok.Getter;
import lombok.Setter;

import java.util.List;

/**
 * 分页工具类
 */
@Getter
@Setter
public class Page {

    private Integer pageNum; // 当前页  (前台传递的参数;如果前台未传递,则默认第一页)
    private Integer pageSize; // 每页显示的数量 (前台传递或后台设定)
    private long totalCount; // 总记录数 (后台数据库中查询得到;count()函数)

    private Integer totalPages; // 总页数 (总记录数/每页显示的数量;将参数转换为浮点型,执行除法操作,向上取整)
    private Integer prePage; // 上一页  (当前页-1;如果当前页-1小于1,则上一页为1)
    private Integer nextPage; // 下一页  (当前页+1;如果当前页+1大于总页数,则下一页为总页数的值)

    private Integer startNavPage; // 导航开始页 (当前页-5;如果当前页-5小于1,则导航开始页为1,此时导航结束页为导航开始数+9;如果导航开始数+9大于总页数,则导航结束页为总页数)
    private Integer endNavPage; // 导航结束页 (当前页+4;如果当前页+4大于总页数,则导航结束页为总页数,此时导航开始页为导航结束页-9;如果导航结束页-9小于1,则导航开始页为1)

    private List dataList; // 当前页的数据集合 (查询数据库中指定页的数据列表)


    /**
     * 带参构造
     *      通过指定参数,得到其他分页参数的值
     * @param pageNum
     * @param pageSize
     * @param totalCount
     */
    public Page(Integer pageNum, Integer pageSize, long totalCount) {
        this.pageNum = pageNum;
        this.pageSize = pageSize;
        this.totalCount = totalCount;

        // 总页数 (总记录数/每页显示的数量;将参数转换为浮点型,执行除法操作,向上取整)
        this.totalPages = (int)Math.ceil(totalCount/(pageSize * 1.0));

        // 上一页  (当前页-1;如果当前页-1小于1,则上一页为1)
        this.prePage = pageNum - 1 < 1 ? 1 : pageNum - 1;

        // 下一页  (当前页+1;如果当前页+1大于总页数,则下一页为总页数的值)
        this.nextPage = pageNum + 1 > totalPages ? totalPages : pageNum + 1;

        this.startNavPage = pageNum - 5; // 导航开始页  (当前页-5)
        this.endNavPage = pageNum + 4; // 导航结束页 (当前页+4)

        // 导航开始页 (当前页-5;如果当前页-5小于1,则导航开始页为1,此时导航结束页为导航开始数+9;如果导航开始数+9大于总页数,则导航结束页为总页数)
        if (this.startNavPage < 1) {
            // 如果当前页-5小于1,则导航开始页为1
            this.startNavPage = 1;
            // 此时导航结束页为导航开始数+9;如果导航开始数+9大于总页数,则导航结束页为总页数
            this.endNavPage = this.startNavPage + 9 > totalPages ? totalPages : this.startNavPage + 9;
        }
        // 导航结束页 (当前页+4;如果当前页+4大于总页数,则导航结束页为总页数,此时导航开始页为导航结束页-9;如果导航结束页-9小于1,则导航开始页为1)
        if (this.endNavPage > totalPages) {
            // 如果当前页+4大于总页数,则导航结束页为总页数
            this.endNavPage = totalPages;
            // 此时导航开始页为导航结束页-9;如果导航结束页-9小于1,则导航开始页为1
            this.startNavPage = this.endNavPage - 9 < 1 ? 1 : this.endNavPage - 9;
        }
    }
}

9.1.6 vo层

  • NoteVo
package com.lezijie.note.vo;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class NoteVo {

    private String groupName; // 分组名称
    private long noteCount; // 云记数量

    private Integer typeId; // 类型ID

}
  • ResultInfo
package com.lezijie.note.vo;

import lombok.Getter;
import lombok.Setter;

/**
 * 封装返回结果的类
 *      状态码
 *          成功=1,失败=0
 *      提示信息
 *      返回的对象(字符串、JavaBean、集合、Map等)
 */
@Getter
@Setter
public class ResultInfo {

    private Integer code; // 状态码 成功=1,失败=0
    private String msg; // 提示信息
    private T result; // 返回的对象(字符串、JavaBean、集合、Map等)

}

9.1.7 web层

  • IndexServlet
package com.lezijie.note.web;

import com.lezijie.note.po.Note;
import com.lezijie.note.po.User;
import com.lezijie.note.service.NoteService;
import com.lezijie.note.util.Page;
import com.lezijie.note.vo.NoteVo;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@WebServlet("/index")
public class IndexServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 设置首页导航高亮
        request.setAttribute("menu_page", "index");

        // 得到用户行为 (判断是什么条件查询:标题查询、日期查询、类型查询)
        String actionName = request.getParameter("actionName");
        // 将用户行为设置到request作用域中 (分页导航中需要获取)
        request.setAttribute("action", actionName);

        // 判断用户行为
        if ("searchTitle".equals(actionName)) { // 标题查询

            // 得到查询条件:标题
            String title = request.getParameter("title");
            // 将查询条件设置到request请求域中(查询条件的回显)
            request.setAttribute("title", title);

            // 标题搜索
            noteList(request, response, title, null, null);

        } else if ("searchDate".equals(actionName)) { // 日期查询

            // 得到查询条件:日期
            String date = request.getParameter("date");
            // 将查询条件设置到request请求域中(查询条件的回显)
            request.setAttribute("date", date);

            // 日期搜索
            noteList(request, response, null, date, null);

        } else if ("searchType".equals(actionName)) { // 类型查询

            // 得到查询条件:类型ID
            String typeId = request.getParameter("typeId");
            // 将查询条件设置到request请求域中(查询条件的回显)
            request.setAttribute("typeId", typeId);

            // 日期搜索
            noteList(request, response, null, null, typeId);

        } else {
            // 分页查询云记列表
            noteList(request, response, null, null, null);
        }


        // 设置首页动态包含的页面
        request.setAttribute("changePage","note/list.jsp");
        // 请求转发到index.jsp
        request.getRequestDispatcher("index.jsp").forward(request, response);
    }


    /**
     * 分页查询云记列表
         1. 接收参数 (当前页、每页显示的数量)
         2. 获取Session作用域中的user对象
         3. 调用Service层查询方法,返回Page对象
         4. 将page对象设置到request作用域中
     * @param request
     * @param response
     * @param title 标题
     */
    private void noteList(HttpServletRequest request, HttpServletResponse response,
                          String title, String date, String typeId) {
        // 1. 接收参数 (当前页、每页显示的数量)
        String pageNum = request.getParameter("pageNum");
        String pageSize = request.getParameter("pageSize");

        // 2. 获取Session作用域中的user对象
        User user = (User) request.getSession().getAttribute("user");

        // 3. 调用Service层查询方法,返回Page对象
        Page page = new NoteService().findNoteListByPage(pageNum, pageSize, user.getUserId(), title, date, typeId);

        // 4. 将page对象设置到request作用域中
        request.setAttribute("page", page);

        // 通过日期分组查询当前登录用户下的云记数量
        List dateInfo = new NoteService().findNoteCountByDate(user.getUserId());
        // 设置集合存放在request作用域中
        request.getSession().setAttribute("dateInfo", dateInfo);

        // 通过类型分组查询当前登录用户下的云记数量
        List typeInfo = new NoteService().findNoteCountByType(user.getUserId());
        // 设置集合存放在request作用域中
        request.getSession().setAttribute("typeInfo", typeInfo);
    }
}
  • NoteServlet
package com.lezijie.note.web;

import cn.hutool.core.util.StrUtil;
import com.lezijie.note.po.Note;
import com.lezijie.note.po.NoteType;
import com.lezijie.note.po.User;
import com.lezijie.note.service.NoteService;
import com.lezijie.note.service.NoteTypeService;
import com.lezijie.note.vo.ResultInfo;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@WebServlet("/note")
public class NoteServlet extends HttpServlet {

    private NoteService noteService = new NoteService();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 设置首页导航栏的高亮值
        request.setAttribute("menu_page", "note");

        // 得到用户行为
        String actionName = request.getParameter("actionName");

        // 判断用户行为
        if ("view".equals(actionName)) {

            // 进入发布云记页面
            noteView(request, response);

        } else if ("addOrUpdate".equals(actionName)) {

            // 添加或修改云记
            addOrUpdate(request, response);

        } else if ("detail".equals(actionName)) {

            // 查询云记详情
            noteDetail(request, response);

        } else if ("delete".equals(actionName)) {

            // 删除云记
            noteDelete(request, response);

        }
    }

    /**
     * 删除云记
         1. 接收参数 (noteId)
         2. 调用Service层删除方法,返回状态码 (1=成功,0=失败)
         3. 通过流将结果响应给ajax的回调函数 (输出字符串)
     * @param request
     * @param response
     */
    private void noteDelete(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 1. 接收参数 (noteId)
        String noteId = request.getParameter("noteId");
        // 2. 调用Service层删除方法,返回状态码 (1=成功,0=失败)
        Integer code = noteService.deleteNote(noteId);
        // 3. 通过流将结果响应给ajax的回调函数 (输出字符串)
        response.getWriter().write(code + "");
        response.getWriter().close();
    }

    /**
     * 查询云记详情
         1. 接收参数 (noteId)
         2. 调用Service层的查询方法,返回Note对象
         3. 将Note对象设置到request请求域中
         4. 设置首页动态包含的页面值
         5. 请求转发跳转到index.jsp
     * @param request
     * @param response
     */
    private void noteDetail(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 接收参数 (noteId)
        String noteId = request.getParameter("noteId");
        // 2. 调用Service层的查询方法,返回Note对象
        Note note = noteService.findNoteById(noteId);
        // 3. 将Note对象设置到request请求域中
        request.setAttribute("note", note);
        // 4. 设置首页动态包含的页面值
        request.setAttribute("changePage","note/detail.jsp");
        // 5. 请求转发跳转到index.jsp
        request.getRequestDispatcher("index.jsp").forward(request, response);
    }


    /**
     * 添加或修改操作
         1. 接收参数 (类型ID、标题、内容)
         2. 调用Service层方法,返回resultInfo对象
         3. 判断resultInfo的code值
            如果code=1,表示成功
                重定向跳转到首页 index
            如果code=0,表示失败
                将resultInfo对象设置到request作用域
                请求转发跳转到note?actionName=view
     * @param request
     * @param response
     */
    private void addOrUpdate(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        // 1. 接收参数 (类型ID、标题、内容)
        String typeId = request.getParameter("typeId");
        String title = request.getParameter("title");
        String content = request.getParameter("content");

        // 获取经纬度
        String lon = request.getParameter("lon");
        String lat = request.getParameter("lat");

        // 如果是修改操作,需要接收noteId
        String noteId = request.getParameter("noteId");

        // 2. 调用Service层方法,返回resultInfo对
        ResultInfo resultInfo = noteService.addOrUpdate(typeId, title, content, noteId, lon, lat);

        // 3. 判断resultInfo的code值
        if (resultInfo.getCode() == 1) {
            // 重定向跳转到首页 index
            response.sendRedirect("index");
        } else {
            // 将resultInfo对象设置到request作用域
            request.setAttribute("resultInfo", resultInfo);
            // 请求转发跳转到note?actionName=view
            String url = "note?actionName=view";
            // 如果是修改操作,需要传递noteId
            if (!StrUtil.isBlank(noteId)) {
                url += "¬eId="+noteId;
            }
            request.getRequestDispatcher(url).forward(request, response);
        }

    }

    /**
     * 进入发布云记页面
         1. 从Session对象中获取用户对象
         2. 通过用户ID查询对应的类型列表
         3. 将类型列表设置到request请求域中
         4. 设置首页动态包含的页面值
         5. 请求转发跳转到index.jsp
     * @param request
     * @param response
     */
    private void noteView(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        /* 修改操作 */
        // 得到要修改的云记ID
        String noteId = request.getParameter("noteId");
        // 通过noteId查询云记对象
        Note note = noteService.findNoteById(noteId);
        // 将note对象设置到请求域中
        request.setAttribute("noteInfo", note);
        /* 修改操作 */


        // 1. 从Session对象中获取用户对象
        User user = (User) request.getSession().getAttribute("user");
        // 2. 通过用户ID查询对应的类型列表
        List typeList = new NoteTypeService().findTypeList(user.getUserId());
        // 3. 将类型列表设置到request请求域中
        request.setAttribute("typeList", typeList);

        // 4. 设置首页动态包含的页面值
        request.setAttribute("changePage","note/view.jsp");
        // 5. 请求转发跳转到index.jsp
        request.getRequestDispatcher("index.jsp").forward(request, response);
    }
}
  • NoteTypeServlet
package com.lezijie.note.web;

import com.lezijie.note.po.NoteType;
import com.lezijie.note.po.User;
import com.lezijie.note.service.NoteTypeService;
import com.lezijie.note.util.JsonUtil;
import com.lezijie.note.vo.ResultInfo;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@WebServlet("/type")
public class NoteTypeServlet extends HttpServlet {

    private NoteTypeService typeService = new NoteTypeService();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 设置首页导航的高亮值
        request.setAttribute("menu_page", "type");

        // 得到用户行为
        String actionName = request.getParameter("actionName");

        // 判断用户行为
        if ("list".equals(actionName)) {

            // 查询类型列表
            typeList(request, response);

        } else if ("delete".equals(actionName)) {

            // 删除类型
            deleteType(request, response);

        } else if ("addOrUpdate".equals(actionName)) {

            // 添加或修改类型
            addOrUpdate(request, response);

        }
    }

    /**
     * 添加或修改类型
         1. 接收参数 (类型名称、类型ID)
         2. 获取Session作用域中的user对象,得到用户ID
         3. 调用Service层的更新方法,返回ResultInfo对象
         4. 将ResultInfo转换成JSON格式的字符串,响应给ajax的回调函数
     * @param request
     * @param response
     */
    private void addOrUpdate(HttpServletRequest request, HttpServletResponse response) {
        // 1. 接收参数 (类型名称、类型ID)
        String typeName = request.getParameter("typeName");
        String typeId = request.getParameter("typeId");
        // 2. 获取Session作用域中的user对象,得到用户ID
        User user = (User) request.getSession().getAttribute("user");
        // 3. 调用Service层的更新方法,返回ResultInfo对象
        ResultInfo resultInfo = typeService.addOrUpdate(typeName, user.getUserId(), typeId);
        // 4. 将ResultInfo转换成JSON格式的字符串,响应给ajax的回调函数
        JsonUtil.toJson(response, resultInfo);
    }

    /**
     * 删除类型
         1. 接收参数(类型ID)
         2. 调用Service的更新操作,返回ResultInfo对象
         3. 将ResultInfo对象转换成JSON格式的字符串,响应给ajax的回调函数
     * @param request
     * @param response
     */
    private void deleteType(HttpServletRequest request, HttpServletResponse response) {
        // 1. 接收参数(类型ID)
        String typeId = request.getParameter("typeId");
        // 2. 调用Service的更新操作,返回ResultInfo对象
        ResultInfo resultInfo = typeService.deleteType(typeId);
        // 3. 将ResultInfo对象转换成JSON格式的字符串,响应给ajax的回调函数
        JsonUtil.toJson(response, resultInfo);
    }


    /**
     * 查询类型列表
         1. 获取Session作用域设置的user对象
         2. 调用Service层的查询方法,查询当前登录用户的类型集合,返回集合
         3. 将类型列表设置到request请求域中
         4. 设置首页动态包含的页面值
         5. 请求转发跳转到index.jsp页面
     * @param request
     * @param response
     */
    private void typeList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 获取Session作用域设置的user对象
        User user = (User) request.getSession().getAttribute("user");
        // 2. 调用Service层的查询方法,查询当前登录用户的类型集合,返回集合
        List typeList = typeService.findTypeList(user.getUserId());
        // 3. 将类型列表设置到request请求域中
        request.setAttribute("typeList", typeList);
        // 4. 设置首页动态包含的页面值
        request.setAttribute("changePage","type/list.jsp");
        // 5. 请求转发跳转到index.jsp页面
        request.getRequestDispatcher("index.jsp").forward(request, response);
    }
}
  • ReportServlet

package com.lezijie.note.web;

import com.lezijie.note.po.Note;
import com.lezijie.note.po.User;
import com.lezijie.note.service.NoteService;
import com.lezijie.note.util.JsonUtil;
import com.lezijie.note.vo.ResultInfo;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;

@WebServlet("/report")
public class ReportServlet extends HttpServlet {

    private NoteService noteService = new NoteService();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 设置首页导航栏的高亮值
        request.setAttribute("menu_page", "report");

        // 得到用户行为
        String actionName = request.getParameter("actionName");

        // 判断用户行为
        if ("info".equals(actionName)) {

            // 进入报表页面
            reportInfo(request, response);

        } else if ("month".equals(actionName)) {

            // 通过月份查询对应的云记数量
            queryNoteCountByMonth(request, response);

        } else if ("location".equals(actionName)) {

            // 查询用户发布云记时的坐标
            queryNoteLonAndLat(request, response);

        }

    }

    /**
     * 查询用户发布云记时的坐标
     * @param request
     * @param response
     */
    private void queryNoteLonAndLat(HttpServletRequest request, HttpServletResponse response) {
        // 从Session作用域中获取用户对象
        User user = (User) request.getSession().getAttribute("user");
        // 调用Service层的查询方法,返回ResultInfo对象
        ResultInfo> resultInfo = noteService.queryNoteLonAndLat(user.getUserId());
        // 将ResultInfo对象转换成JSON格式的字符串,响应给AJAX的回调函数
        JsonUtil.toJson(response, resultInfo);
    }

    /**
     * 通过月份查询对应的云记数量
     * @param request
     * @param response
     */
    private void queryNoteCountByMonth(HttpServletRequest request, HttpServletResponse response) {
        // 从Session作用域中获取用户对象
        User user = (User) request.getSession().getAttribute("user");
        // 调用Service层的查询方法,返回ResultInfo对象
        ResultInfo> resultInfo = noteService.queryNoteCountByMonth(user.getUserId());
        // 将ResultInfo对象转换成JSON格式的字符串,响应给ajax的回调函数
        JsonUtil.toJson(response, resultInfo);
    }

    /**
     * 进入报表页面
     * @param request
     * @param response
     */
    private void reportInfo(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 设置首页动态包含的页面值
        request.setAttribute("changePage","report/info.jsp");
        // 请求转发跳转到index.jsp
        request.getRequestDispatcher("index.jsp").forward(request, response);

    }
}
  • UserServlet
package com.lezijie.note.web;

import com.lezijie.note.po.User;
import com.lezijie.note.service.UserService;
import com.lezijie.note.vo.ResultInfo;
import org.apache.commons.io.FileUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;

@WebServlet("/user")
@MultipartConfig
public class UserServlet extends HttpServlet {

    private UserService userService = new UserService();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 设置首页导航高亮
        request.setAttribute("menu_page", "user");

        // 接收用户行为
        String actionName = request.getParameter("actionName");
        // 判断用户行为,调用对应的方法
        if ("login".equals(actionName)) {

            // 用户登录
            userLogin(request, response);

        } else if ("logout".equals(actionName)) {

            // 用户退出
            userLogOut(request, response);

        } else if ("userCenter".equals(actionName)) {

            // 进入个人中心
            userCenter(request, response);

        } else if ("userHead".equals(actionName)) {

            // 加载头像
            userHead(request, response);

        } else if ("checkNick".equals(actionName)) {

            // 验证昵称的唯一性
            checkNick(request, response);

        } else if ("updateUser".equals(actionName)) {

            // 修改用户信息
            updateUser(request, response);

        }
    }

    /**
     * 修改用户信息
         注:文件上传必须在Servlet类上提那家注解!!! @MultipartConfig
         1. 调用Service层的方法,传递request对象作为参数,返回resultInfo对象
         2. 将resultInfo对象存到request作用域中
         3. 请求转发跳转到个人中心页面 (user?actionName=userCenter)
     * @param request
     * @param response
     */
    private void updateUser(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 调用Service层的方法,传递request对象作为参数,返回resultInfo对象
        ResultInfo resultInfo = userService.updateUser(request);
        // 2. 将resultInfo对象存到request作用域中
        request.setAttribute("resultInfo", resultInfo);
        // 3. 请求转发跳转到个人中心页面 (user?actionName=userCenter)
        request.getRequestDispatcher("user?actionName=userCenter").forward(request, response);
    }

    /**
     * 验证昵称的唯一性
     *  1. 获取参数(昵称)
     *  2. 从session作用域获取用户对象,得到用户ID
     *  3. 调用Service层的方法,得到返回的结果
     *  4. 通过字符输出流将结果响应给前台的ajax的回调函数
     *  5. 关闭资源
     * @param request
     * @param response
     */
    private void checkNick(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 1. 获取参数(昵称)
        String nick  = request.getParameter("nick");
        // 2. 从session作用域获取用户对象,得到用户ID
        User user = (User) request.getSession().getAttribute("user");
        // 3. 调用Service层的方法,得到返回的结果
        Integer code = userService.checkNick(nick, user.getUserId());
        // 4. 通过字符输出流将结果响应给前台的ajax的回调函数
        response.getWriter().write(code + "");
        // 5. 关闭资源
        response.getWriter().close();
    }

    /**
     * 加载头像
     *  1. 获取参数 (图片名称)
     *  2. 得到图片的存放路径 (request.getServletContext().getealPathR("/"))
     *  3. 通过图片的完整路径,得到file对象
     *  4. 通过截取,得到图片的后缀
     *  5. 通过不同的图片后缀,设置不同的响应的类型
     *  6. 利用FileUtils的copyFile()方法,将图片拷贝给浏览器
     * @param request
     * @param response
     */
    private void userHead(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 1. 获取参数 (图片名称)
        String head = request.getParameter("imageName");
        // 2. 得到图片的存放路径 (得到项目的真实路径:request.getServletContext().getealPathR("/"))
        String realPath = request.getServletContext().getRealPath("/WEB-INF/upload/");
        // 3. 通过图片的完整路径,得到file对象
        File file = new File(realPath + "/" + head);
        // 4. 通过截取,得到图片的后缀
        String pic = head.substring(head.lastIndexOf(".")+1);
        // 5. 通过不同的图片后缀,设置不同的响应的类型
        if ("PNG".equalsIgnoreCase(pic)) {
            response.setContentType("image/png");
        } else if ("JPG".equalsIgnoreCase(pic) || "JPEG".equalsIgnoreCase(pic)) {
            response.setContentType("image/jpeg");
        } else if ("GIF".equalsIgnoreCase(pic)) {
            response.setContentType("image/gif");
        }
        // 6. 利用FileUtils的copyFile()方法,将图片拷贝给浏览器
        FileUtils.copyFile(file, response.getOutputStream());
    }

    /**
     * 进入个人中心
     *  1. 设置首页动态包含的页面值
     *  2. 请求转发跳转到index.jsp
     * @param request
     * @param response
     */
    private void userCenter(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 1. 设置首页动态包含的页面值
        request.setAttribute("changePage", "user/info.jsp");
        // 2. 请求转发跳转到index
        request.getRequestDispatcher("index.jsp").forward(request, response);

    }

    /**
     * 用户退出
     *  1. 销毁Session对象
     *  2. 删除Cookie对象
     *  3. 重定向跳转到登录页面
     * @param request
     * @param response
     */
    private void userLogOut(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 1. 销毁Session对象
        request.getSession().invalidate();
        // 2. 删除Cookie对象
        Cookie cookie = new Cookie("user", null);
        cookie.setMaxAge(0); // 设置0,表示删除cookie
        response.addCookie(cookie);
        // 3. 重定向跳转到登录页面
        response.sendRedirect("login.jsp");
    }


    /**
     * 用户登录
         1. 获取参数 (姓名、密码)
         2. 调用Service层的方法,返回ResultInfo对象
         3. 判断是否登录成功
             如果失败
                 将resultInfo对象设置到request作用域中
                 请求转发跳转到登录页面
             如果成功
                将用户信息设置到session作用域中
                判断用户是否选择记住密码(rem的值是1)
                    如果是,将用户姓名与密码存到cookie中,设置失效时间,并响应给客户端
                    如果否,清空原有的cookie对象
                重定向跳转到index页面
     * @param request
     * @param response
     */
    private void userLogin(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 1. 获取参数 (姓名、密码)
        String userName = request.getParameter("userName");
        String userPwd = request.getParameter("userPwd");

        // 2. 调用Service层的方法,返回ResultInfo对象
        ResultInfo resultInfo = userService.userLogin(userName, userPwd);

        // 3. 判断是否登录成功
        if (resultInfo.getCode() == 1) { // 如果成功
            //  将用户信息设置到session作用域中
            request.getSession().setAttribute("user", resultInfo.getResult());
            //  判断用户是否选择记住密码(rem的值是1)
            String rem = request.getParameter("rem");
            // 如果是,将用户姓名与密码存到cookie中,设置失效时间,并响应给客户端
            if ("1".equals(rem)) {
                 // 得到Cookie对象
                Cookie cookie = new Cookie("user",userName +"-"+userPwd);
                // 设置失效时间
                cookie.setMaxAge(3*24*60*60);
                // 响应给客户端
                response.addCookie(cookie);
            } else {
                // 如果否,清空原有的cookie对象
                Cookie cookie = new Cookie("user", null);
                // 删除cookie,设置maxage为0
                cookie.setMaxAge(0);
                // 响应给客户端
                response.addCookie(cookie);
            }
            // 重定向跳转到index页面
            response.sendRedirect("index");

        } else { // 失败
            // 将resultInfo对象设置到request作用域中
            request.setAttribute("resultInfo", resultInfo);
            // 请求转发跳转到登录页面
            request.getRequestDispatcher("login.jsp").forward(request, response);
        }

    }
}

9.2 webapp目录

……

10、报错记录

10.1 问题描述:「 在代码不出错的情况下,(判断是否为空)点击“登录”按钮 并未出现提示信息 」

10.2 问题描述:「 tomcat端口冲突解决 Address already in use: JVM_Bind :8080 」

最可能的解决方案:

① cmd 命令输入 netstat –ano | findstr 8080

② 查看到 8080端口对应的进程号xxxx

③ 在任务管理器中寻找改PID
④ 右键结束该任务 或者 仍然在命令行下输入 taskkill –pid xxxx

如果上面方法没有解决 选择重启电脑

转到index
request.getRequestDispatcher(“index.jsp”).forward(request, response);

}

/**
 * 用户退出
 *  1. 销毁Session对象
 *  2. 删除Cookie对象
 *  3. 重定向跳转到登录页面
 * @param request
 * @param response
 */
private void userLogOut(HttpServletRequest request, HttpServletResponse response) throws IOException {
    // 1. 销毁Session对象
    request.getSession().invalidate();
    // 2. 删除Cookie对象
    Cookie cookie = new Cookie("user", null);
    cookie.setMaxAge(0); // 设置0,表示删除cookie
    response.addCookie(cookie);
    // 3. 重定向跳转到登录页面
    response.sendRedirect("login.jsp");
}


/**
 * 用户登录
     1. 获取参数 (姓名、密码)
     2. 调用Service层的方法,返回ResultInfo对象
     3. 判断是否登录成功
         如果失败
             将resultInfo对象设置到request作用域中
             请求转发跳转到登录页面
         如果成功
            将用户信息设置到session作用域中
            判断用户是否选择记住密码(rem的值是1)
                如果是,将用户姓名与密码存到cookie中,设置失效时间,并响应给客户端
                如果否,清空原有的cookie对象
            重定向跳转到index页面
 * @param request
 * @param response
 */
private void userLogin(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    // 1. 获取参数 (姓名、密码)
    String userName = request.getParameter("userName");
    String userPwd = request.getParameter("userPwd");

    // 2. 调用Service层的方法,返回ResultInfo对象
    ResultInfo resultInfo = userService.userLogin(userName, userPwd);

    // 3. 判断是否登录成功
    if (resultInfo.getCode() == 1) { // 如果成功
        //  将用户信息设置到session作用域中
        request.getSession().setAttribute("user", resultInfo.getResult());
        //  判断用户是否选择记住密码(rem的值是1)
        String rem = request.getParameter("rem");
        // 如果是,将用户姓名与密码存到cookie中,设置失效时间,并响应给客户端
        if ("1".equals(rem)) {
             // 得到Cookie对象
            Cookie cookie = new Cookie("user",userName +"-"+userPwd);
            // 设置失效时间
            cookie.setMaxAge(3*24*60*60);
            // 响应给客户端
            response.addCookie(cookie);
        } else {
            // 如果否,清空原有的cookie对象
            Cookie cookie = new Cookie("user", null);
            // 删除cookie,设置maxage为0
            cookie.setMaxAge(0);
            // 响应给客户端
            response.addCookie(cookie);
        }
        // 重定向跳转到index页面
        response.sendRedirect("index");

    } else { // 失败
        // 将resultInfo对象设置到request作用域中
        request.setAttribute("resultInfo", resultInfo);
        // 请求转发跳转到登录页面
        request.getRequestDispatcher("login.jsp").forward(request, response);
    }

}

}



### 9.2 webapp目录  
> ……

## 10、报错记录
### 10.1 问题描述:「  在代码不出错的情况下,(判断是否为空)点击“登录”按钮 并未出现提示信息 」



### 10.2 问题描述:「 tomcat端口冲突解决 Address already in use: JVM_Bind :8080 」


「 **最可能的解决方案:** 」

 ① cmd 命令输入 netstat –ano | findstr 8080

 ② 查看到 8080端口对应的进程号xxxx

③ 在任务管理器中寻找改PID
④ 右键结束该任务 或者 仍然在命令行下输入 taskkill –pid xxxx

> **如果上面方法没有解决 选择重启电脑**

  

  

你可能感兴趣的:(Java项目,java,web)