当前位置: 首页 > news >正文

通讯录系统数据库设计与实现

1. 数据库设计与配置

1.1 数据库建表 SQL

-- 创建数据库,使用UTF-8编码 CREATE DATABASE IF NOT EXISTS `contact_system` DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; USE `contact_system`; -- 用户表 CREATE TABLE `users` ( `id` INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID', `user_name` VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名', `password` VARCHAR(50) NOT NULL COMMENT '密码(明文存储,便于调试)', `create_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; -- 联系人表 CREATE TABLE `tb_contact` ( `id` INT PRIMARY KEY AUTO_INCREMENT COMMENT '联系人ID', `name` VARCHAR(50) NOT NULL COMMENT '姓名', `phone` VARCHAR(20) NOT NULL COMMENT '电话', `email` VARCHAR(100) COMMENT '邮箱', `group_name` ENUM('家人','同事','朋友','其他') DEFAULT '其他' COMMENT '分组', `remark` VARCHAR(500) COMMENT '备注', `create_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间', `user_id` INT NOT NULL COMMENT '所属用户ID', FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='联系人表'; -- 插入测试数据 INSERT INTO `users` (`user_name`, `password`) VALUES ('admin', '123456'), ('user1', '123456'); INSERT INTO `tb_contact` (`name`, `phone`, `email`, `group_name`, `remark`, `user_id`) VALUES ('张三', '13800138000', 'zhangsan@example.com', '同事', '技术部同事', 1), ('李四', '13900139000', 'lisi@example.com', '朋友', '大学同学', 1), ('王五', '13600136000', 'wangwu@example.com', '家人', '父亲', 2);

1.2 Navicat 连接步骤

  1. 打开Navicat,点击"连接" → "MySQL"
  2. 配置连接参数:
    • 连接名:任意(如ContactSystem)
    • 主机:localhost(本地)或远程IP
    • 端口:3306(默认)
    • 用户名:root(或自定义用户)
    • 密码:数据库密码
  3. 点击"测试连接",成功后点击"确定"
  4. 双击新创建的连接,右键选择"运行SQL文件",执行上述建表SQL

2. 项目结构搭建

2.1 标准项目目录结构

ContactSystem/ ├── src/ │ ├── entity/ │ │ ├── Users.java │ │ └── Contact.java │ ├── servlet/ │ │ ├── LoginServlet.java │ │ ├── LogoutServlet.java │ │ ├── AddContactServlet.java │ │ ├── UpdateContactServlet.java │ │ ├── DeleteContactServlet.java │ │ └── SelectContactServlet.java │ ├── service/ │ │ ├── UserBo.java │ │ └── ContactBo.java │ ├── dao/ │ │ ├── DBUtil.java │ │ ├── UserDao.java │ │ └── ContactDao.java │ └── common/ │ └── Validate.java ├── WebContent/ │ ├── WEB-INF/ │ │ ├── lib/ │ │ │ └── mysql-connector-java-8.0.xx.jar │ │ └── web.xml │ ├── login.jsp │ ├── index.jsp │ ├── addContact.jsp │ ├── editContact.jsp │ └── contactList.jsp └── 项目配置文件

2.2 IDEA项目创建步骤

  1. 打开IDEA → File → New → Project
  2. 选择Java Enterprise → Web Application
  3. 项目名:ContactSystem,不使用Maven
  4. Application Server选择Tomcat 9.x
  5. 创建完成后,手动创建上述目录结构
  6. 下载MySQL驱动jar包放入WEB-INF/lib目录

3. 核心代码实现

3.1 实体类(entity层)

Users.java

package entity; import java.util.Date; public class Users { private int id; private String userName; private String password; private Date createTime; // 构造方法 public Users() {} public Users(String userName, String password) { this.userName = userName; this.password = password; } // Getter和Setter方法 public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } }

Contact.java

package entity; import java.util.Date; public class Contact { private int id; private String name; private String phone; private String email; private String groupName; private String remark; private Date createTime; private int userId; // 构造方法 public Contact() {} public Contact(String name, String phone, String email, String groupName, String remark, int userId) { this.name = name; this.phone = phone; this.email = email; this.groupName = groupName; this.remark = remark; this.userId = userId; } // Getter和Setter方法(略,参考Users.java格式) }

3.2 数据库工具类(dao层)

DBUtil.java

package dao; import java.sql.*; public class DBUtil { private static final String DRIVER = "com.mysql.cj.jdbc.Driver"; private static final String URL = "jdbc:mysql://localhost:3306/contact_system?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"; private static final String USERNAME = "root"; private static final String PASSWORD = "你的数据库密码"; // 加载数据库驱动 static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } // 获取数据库连接 public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USERNAME, PASSWORD); } // 关闭资源 public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } // 开启事务 public static void beginTransaction(Connection conn) throws SQLException { if (conn != null) { conn.setAutoCommit(false); } } // 提交事务 public static void commitTransaction(Connection conn) throws SQLException { if (conn != null) { conn.commit(); } } // 回滚事务 public static void rollbackTransaction(Connection conn) { try { if (conn != null) { conn.rollback(); } } catch (SQLException e) { e.printStackTrace(); } } }

3.3 数据访问层(dao层)

UserDao.java

package dao; import entity.Users; import java.sql.*; public class UserDao { // 用户登录验证 public Users login(String userName, String password) { Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; Users user = null; try { conn = DBUtil.getConnection(); // 使用PreparedStatement防止SQL注入 String sql = "SELECT id, user_name, password FROM users WHERE user_name = ? AND password = ?"; pstmt = conn.prepareStatement(sql); pstmt.setString(1, userName); pstmt.setString(2, password); rs = pstmt.executeQuery(); if (rs.next()) { user = new Users(); user.setId(rs.getInt("id")); user.setUserName(rs.getString("user_name")); user.setPassword(rs.getString("password")); } } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(conn, pstmt, rs); } return user; } // 用户注册(扩展功能) public boolean register(Users user) { Connection conn = null; PreparedStatement pstmt = null; boolean success = false; try { conn = DBUtil.getConnection(); String sql = "INSERT INTO users (user_name, password) VALUES (?, ?)"; pstmt = conn.prepareStatement(sql); pstmt.setString(1, user.getUserName()); pstmt.setString(2, user.getPassword()); int rows = pstmt.executeUpdate(); success = rows > 0; } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(conn, pstmt, null); } return success; } }

ContactDao.java

package dao; import entity.Contact; import java.sql.*; import java.util.ArrayList; import java.util.List; public class ContactDao { // 添加联系人 public boolean addContact(Contact contact) { Connection conn = null; PreparedStatement pstmt = null; boolean success = false; try { conn = DBUtil.getConnection(); String sql = "INSERT INTO tb_contact (name, phone, email, group_name, remark, user_id) VALUES (?, ?, ?, ?, ?, ?)"; pstmt = conn.prepareStatement(sql); pstmt.setString(1, contact.getName()); pstmt.setString(2, contact.getPhone()); pstmt.setString(3, contact.getEmail()); pstmt.setString(4, contact.getGroupName()); pstmt.setString(5, contact.getRemark()); pstmt.setInt(6, contact.getUserId()); int rows = pstmt.executeUpdate(); success = rows > 0; } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(conn, pstmt, null); } return success; } // 分页查询联系人 public List<Contact> getContactsByPage(int userId, int page, int pageSize, String keyword) { List<Contact> contacts = new ArrayList<>(); Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try { conn = DBUtil.getConnection(); StringBuilder sql = new StringBuilder( "SELECT * FROM tb_contact WHERE user_id = ?" ); // 添加搜索条件 if (keyword != null && !keyword.trim().isEmpty()) { sql.append(" AND (name LIKE ? OR phone LIKE ? OR email LIKE ?)"); } sql.append(" ORDER BY create_time DESC LIMIT ?, ?"); pstmt = conn.prepareStatement(sql.toString()); int paramIndex = 1; pstmt.setInt(paramIndex++, userId); if (keyword != null && !keyword.trim().isEmpty()) { String likeKeyword = "%" + keyword + "%"; pstmt.setString(paramIndex++, likeKeyword); pstmt.setString(paramIndex++, likeKeyword); pstmt.setString(paramIndex++, likeKeyword); } pstmt.setInt(paramIndex++, (page - 1) * pageSize); pstmt.setInt(paramIndex, pageSize); rs = pstmt.executeQuery(); while (rs.next()) { Contact contact = new Contact(); contact.setId(rs.getInt("id")); contact.setName(rs.getString("name")); contact.setPhone(rs.getString("phone")); contact.setEmail(rs.getString("email")); contact.setGroupName(rs.getString("group_name")); contact.setRemark(rs.getString("remark")); contact.setCreateTime(rs.getTimestamp("create_time")); contact.setUserId(rs.getInt("user_id")); contacts.add(contact); } } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(conn, pstmt, rs); } return contacts; } // 获取联系人总数(用于分页) public int getContactCount(int userId, String keyword) { Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; int count = 0; try { conn = DBUtil.getConnection(); StringBuilder sql = new StringBuilder( "SELECT COUNT(*) FROM tb_contact WHERE user_id = ?" ); if (keyword != null && !keyword.trim().isEmpty()) { sql.append(" AND (name LIKE ? OR phone LIKE ? OR email LIKE ?)"); } pstmt = conn.prepareStatement(sql.toString()); int paramIndex = 1; pstmt.setInt(paramIndex++, userId); if (keyword != null && !keyword.trim().isEmpty()) { String likeKeyword = "%" + keyword + "%"; pstmt.setString(paramIndex++, likeKeyword); pstmt.setString(paramIndex++, likeKeyword); pstmt.setString(paramIndex++, likeKeyword); } rs = pstmt.executeQuery(); if (rs.next()) { count = rs.getInt(1); } } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(conn, pstmt, rs); } return count; } // 其他CRUD方法:updateContact、deleteContact、getContactById(略,参考上述方法实现) }

3.4 业务逻辑层(service层)

UserBo.java

package service; import dao.UserDao; import entity.Users; public class UserBo { private UserDao userDao = new UserDao(); // 用户登录业务逻辑 public Users login(String userName, String password) { // 参数校验 if (userName == null || userName.trim().isEmpty() || password == null || password.trim().isEmpty()) { return null; } // 调用DAO层进行验证 return userDao.login(userName.trim(), password.trim()); } // 用户注册业务逻辑 public boolean register(Users user) { // 业务规则校验 if (user.getUserName() == null || user.getUserName().trim().isEmpty() || user.getPassword() == null || user.getPassword().trim().isEmpty()) { return false; } // 用户名长度限制 if (user.getUserName().length() < 3 || user.getUserName().length() > 20) { return false; } return userDao.register(user); } }

ContactBo.java

package service; import dao.ContactDao; import entity.Contact; import java.util.List; public class ContactBo { private ContactDao contactDao = new ContactDao(); // 添加联系人业务逻辑 public boolean addContact(Contact contact) { // 参数校验 if (contact == null || contact.getName() == null || contact.getName().trim().isEmpty() || contact.getPhone() == null || contact.getPhone().trim().isEmpty()) { return false; } // 手机号格式校验 if (!contact.getPhone().matches("^1[3-9]\\d{9}$")) { return false; } // 邮箱格式校验(可选) if (contact.getEmail() != null && !contact.getEmail().trim().isEmpty()) { if (!contact.getEmail().matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$")) { return false; } } return contactDao.addContact(contact); } // 分页查询联系人 public List<Contact> getContactsByPage(int userId, int page, int pageSize, String keyword) { // 参数校验 if (userId <= 0 || page <= 0 || pageSize <= 0) { return null; } // 限制最大页数 if (pageSize > 100) { pageSize = 100; } return contactDao.getContactsByPage(userId, page, pageSize, keyword); } // 获取总页数 public int getTotalPages(int userId, int pageSize, String keyword) { int total = contactDao.getContactCount(userId, keyword); return (int) Math.ceil((double) total / pageSize); } }

3.5 工具类(common层)

Validate.java

package common; import javax.servlet.http.HttpServletRequest; public class Validate { // 检查字符串是否为空或null public static boolean isEmpty(String str) { return str == null || str.trim().isEmpty(); } // 检查手机号格式 public static boolean isPhoneValid(String phone) { if (isEmpty(phone)) return false; return phone.matches("^1[3-9]\\d{9}$"); } // 检查邮箱格式 public static boolean isEmailValid(String email) { if (isEmpty(email)) return false; return email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$"); } // 防止XSS攻击,过滤特殊字符 public static String filterXSS(String input) { if (isEmpty(input)) return input; return input.replaceAll("<", "&lt;") .replaceAll(">", "&gt;") .replaceAll("\"", "&quot;") .replaceAll("'", "&#x27;") .replaceAll("/", "&#x2F;"); } // 从请求参数中安全获取值 public static String getParameter(HttpServletRequest request, String paramName) { String value = request.getParameter(paramName); if (value != null) { value = filterXSS(value.trim()); } return value; } }

3.6 Servlet控制器(servlet层)

LoginServlet.java

package servlet; import entity.Users; import service.UserBo; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/LoginServlet") public class LoginServlet extends HttpServlet { private UserBo userBo = new UserBo(); @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置编码,兼容GB2312和UTF-8 request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); // 获取参数 String userName = request.getParameter("userName"); String password = request.getParameter("password"); // 参数校验 if (userName == null || userName.trim().isEmpty() || password == null || password.trim().isEmpty()) { request.setAttribute("error", "用户名和密码不能为空"); request.getRequestDispatcher("login.jsp").forward(request, response); return; } // 业务处理 Users user = userBo.login(userName, password); if (user != null) { // 登录成功,创建Session HttpSession session = request.getSession(); session.setAttribute("user", user); session.setMaxInactiveInterval(30 * 60); // 30分钟超时 // 重定向到首页 response.sendRedirect("index.jsp"); } else { // 登录失败,安全提示 request.setAttribute("error", "用户名或密码错误"); request.getRequestDispatcher("login.jsp").forward(request, response); } } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 防止直接访问 response.sendRedirect("login.jsp"); } }

SelectContactServlet.java

package servlet; import entity.Contact; import entity.Users; import service.ContactBo; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; import java.util.List; @WebServlet("/SelectContactServlet") public class SelectContactServlet extends HttpServlet { private ContactBo contactBo = new ContactBo(); @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 登录拦截检查 HttpSession session = request.getSession(false); if (session == null || session.getAttribute("user") == null) { response.sendRedirect("login.jsp"); return; } // 设置编码 request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); // 获取当前用户 Users user = (Users) session.getAttribute("user"); int userId = user.getId(); // 获取分页参数 String pageStr = request.getParameter("page"); String keyword = request.getParameter("keyword"); int page = 1; int pageSize = 10; if (pageStr != null && !pageStr.trim().isEmpty()) { try { page = Integer.parseInt(pageStr); if (page < 1) page = 1; } catch (NumberFormatException e) { page = 1; } } // 获取搜索关键词 if (keyword != null) { keyword = keyword.trim(); } // 查询数据 List<Contact> contacts = contactBo.getContactsByPage(userId, page, pageSize, keyword); int totalPages = contactBo.getTotalPages(userId, pageSize, keyword); // 设置请求属性 request.setAttribute("contacts", contacts); request.setAttribute("currentPage", page); request.setAttribute("totalPages", totalPages); request.setAttribute("keyword", keyword); // 转发到列表页面 request.getRequestDispatcher("contactList.jsp").forward(request, response); } }

3.7 JSP页面实现

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>通讯录管理系统 - 登录</title> <style> body { font-family: Arial, sans-serif; background: #f5f5f5; } .login-box { width: 300px; margin: 100px auto; padding: 20px; background: white; border-radius: 5px; box-shadow: 0 0 10px rgba(0,0,0,0.1); } .form-group { margin-bottom: 15px; } label { display: block; margin-bottom: 5px; } input { width: 100%; padding: 8px; box-sizing: border-box; } .error { color: red; font-size: 12px; } .btn { width: 100%; padding: 10px; background: #007bff; color: white; border: none; border-radius: 3px; cursor: pointer; } </style> </head> <body> <div class="login-box"> <h2>用户登录</h2> <form action="LoginServlet" method="post"> <div class="form-group"> <label>用户名:</label> <input type="text" name="userName" required> </div> <div class="form-group"> <label>密码:</label> <input type="password" name="password" required> </div> <div class="form-group"> <button type="submit" class="btn">登录</button> </div> <% if (request.getAttribute("error") != null) { %> <div class="error"><%= request.getAttribute("error") %></div> <% } %> </form> <p>测试账号:admin/123456 或 user1/123456</p> </div> </body> </html>

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="entity.Users" %> <% // 登录检查 Users user = (Users) session.getAttribute("user"); if (user == null) { response.sendRedirect("login.jsp"); return; } %> <html> <head> <title>通讯录管理系统 - 首页</title> <style> body { font-family: Arial, sans-serif; margin: 0; padding: 20px; } .header { background: #007bff; color: white; padding: 10px; margin-bottom: 20px; } .menu a { margin-right: 15px; color: #007bff; text-decoration: none; } .welcome { float: right; } </style> </head> <body> <div class="header"> <h1>通讯录管理系统</h1> <div class="welcome"> 欢迎,<%= user.getUserName() %> | <a href="LogoutServlet" style="color: white;">退出登录</a> </div> </div> <div class="menu"> <a href="SelectContactServlet">联系人列表</a> <a href="addContact.jsp">添加联系人</a> </div> <div class="content"> <h2>系统功能</h2> <ul> <li>联系人管理(增删改查)</li> <li>联系人分组(家人/同事/朋友/其他)</li> <li>分页显示和搜索功能</li> <li>用户会话管理</li> </ul> </div> </body> </html>

contactList.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="entity.Contact, java.util.List" %> <% // 登录检查 if (session.getAttribute("user") == null) { response.sendRedirect("login.jsp"); return; } List<Contact> contacts = (List<Contact>) request.getAttribute("contacts"); Integer currentPage = (Integer) request.getAttribute("currentPage"); Integer totalPages = (Integer) request.getAttribute("totalPages"); String keyword = (String) request.getAttribute("keyword"); if (keyword == null) keyword = ""; %> <html> <head> <title>联系人列表</title> <style> table { width: 100%; border-collapse: collapse; margin-top: 20px; } th, td { border: 1px solid #ddd; padding: 8px; text-align: left; } th { background-color: #f2f2f2; } .pagination { margin-top: 20px; } .search-box { margin-bottom: 20px; } </style> </head> <body> <h2>联系人列表</h2> <div class="search-box"> <form action="SelectContactServlet" method="get"> <input type="text" name="keyword" value="<%= keyword %>" placeholder="搜索姓名/电话/邮箱"> <button type="submit">搜索</button> <a href="addContact.jsp">添加联系人</a> </form> </div> <table> <tr> <th>姓名</th> <th>电话</th> <th>邮箱</th> <th>分组</th> <th>备注</th> <th>操作</th> </tr> <% if (contacts != null && !contacts.isEmpty()) { for (Contact contact : contacts) { %> <tr> <td><%= contact.getName() %></td> <td><%= contact.getPhone() %></td> <td><%= contact.getEmail() != null ? contact.getEmail() : "" %></td> <td><%= contact.getGroupName() %></td> <td><%= contact.getRemark() != null ? contact.getRemark() : "" %></td> <td> <a href="editContact.jsp?id=<%= contact.getId() %>">编辑</a> <a href="DeleteContactServlet?id=<%= contact.getId() %>" onclick="return confirm('确定删除吗?')">删除</a> </td> </tr> <% } } else { %> <tr> <td colspan="6" style="text-align: center;">暂无联系人数据</td> </tr> <% } %> </table> <% if (totalPages != null && totalPages > 0) { %> <div class="pagination"> <% for (int i = 1; i <= totalPages; i++) { if (i == currentPage) { %> <span><%= i %></span> <% } else { %> <a href="SelectContactServlet?page=<%= i %>&keyword=<%= keyword %>"><%= i %></a> <% } } %> </div> <% } %> <p><a href="index.jsp">返回首页</a></p> </body> </html>

3.8 web.xml配置

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 字符编码过滤器 --> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 登录拦截过滤器 --> <filter> <filter-name>LoginFilter</filter-name> <filter-class>filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>LoginFilter</filter-name> <url-pattern>/contactList.jsp</url-pattern> <url-pattern>/addContact.jsp</url-pattern> <url-pattern>/editContact.jsp</url-pattern> <url-pattern>/SelectContactServlet</url-pattern> <url-pattern>/AddContactServlet</url-pattern> <url-pattern>/UpdateContactServlet</url-pattern> <url-pattern>/DeleteContactServlet</url-pattern> </filter-mapping> <!-- Servlet映射 --> <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/LoginServlet</url-pattern> </servlet-mapping> <servlet> <servlet-name>SelectContactServlet</servlet-name> <servlet-class>servlet.SelectContactServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>SelectContactServlet</servlet-name> <url-pattern>/SelectContactServlet</url-pattern> </servlet-mapping> <!-- 欢迎页面 --> <welcome-file-list> <welcome-file>login.jsp</welcome-file> </welcome-file-list> <!-- 会话超时配置(30分钟) --> <session-config> <session-timeout>30</session-timeout> </session-config> </web-app>

4. Tomcat部署与配置

4.1 Tomcat 9.x部署步骤

  1. 下载Tomcat 9.x:从Apache官网下载Tomcat 9.x压缩包
  2. 解压到指定目录:如D:\apache-tomcat-9.0.x
  3. 环境变量配置
    • 新建CATALINA_HOME,值为Tomcat安装目录
    • Path中添加%CATALINA_HOME%\bin
  4. 启动Tomcat
    # Windows %CATALINA_HOME%\bin\startup.bat # Linux/Mac $CATALINA_HOME/bin/startup.sh
  5. 验证启动:访问http://localhost:8080看到Tomcat首页即成功

4.2 IDEA项目部署

  1. 配置Tomcat

    • Run → Edit Configurations → 点击"+" → Tomcat Server → Local
    • 配置Tomcat Home目录
    • Deployment标签页添加Artifact
  2. 项目部署配置

    • File → Project Structure → Artifacts
    • 点击"+" → Web Application: Exploded → From Modules
    • 选择项目模块,设置Output directory
  3. 运行项目

    • 点击Run按钮启动Tomcat
    • 访问http://localhost:8080/ContactSystem/login.jsp

5. 常见问题排查方案

5.1 404错误

问题原因解决方案
URL路径错误检查web.xml中的servlet映射配置
项目未正确部署检查Tomcat的webapps目录是否有项目文件夹
文件路径错误确保JSP文件在WebContent目录下
上下文路径错误检查IDEA中Deployment的Application Context设置

5.2 登录失败问题

问题现象排查步骤
提示"用户名或密码错误"1. 检查数据库用户数据
2. 查看控制台SQL日志
3. 验证UserDao.login方法
登录后Session丢失1. 检查session.setMaxInactiveInterval设置
2. 验证web.xml中session-timeout配置
密码验证失败1. 检查数据库密码字段长度
2. 验证密码是否包含特殊字符

5.3 乱码问题

乱码场景解决方案
表单提交中文乱码Servlet中添加request.setCharacterEncoding("UTF-8")
数据库读取乱码连接URL添加characterEncoding=UTF-8参数
JSP页面显示乱码页面顶部添加<%@ page contentType="text/html;charset=UTF-8" %>
响应乱码Servlet中添加response.setContentType("text/html;charset=UTF-8")

5.4 SQL连接问题

// 常见连接错误排查 public static void testConnection() { try { Connection conn = DBUtil.getConnection(); if (conn != null && !conn.isClosed()) { System.out.println("数据库连接成功"); conn.close(); } } catch (SQLException e) { System.err.println("连接失败:" + e.getMessage()); // 检查:1. MySQL服务是否启动 2. 用户名密码是否正确 3. 数据库名是否正确 } }

5.5 Tomcat启动失败

  1. 端口占用:检查8080端口是否被占用

参考来源

  • Java Web实现用户登录功能
  • 【JAVA项目实战】【图书管理系统】用户查询功能【Servlet】+【Jsp】+【Mysql】
  • java web简单的注册登录界面实现(servlet+mysql+jsp+idea)
  • jsp+dao+bean+servlet(MVC模式)实现简单用户登录和注册页面(连接数据库,登录页面包含验证码,两周内免登陆等功能)
  • IDEA+Java+JSP+Mysql+Tomcat实现Web学生信息管理系统
  • JSP+servlet+JDBC实现简单的注册登录功能
http://www.jsqmd.com/news/812761/

相关文章:

  • 2026年民宿烤漆门权威厂家排行 核心能力实测对比 - 优质品牌商家
  • 别再纠结了!Mkdocs、Sphinx、Teadocs、docsify,哪个文档框架更适合你的项目?(附快速上手对比)
  • JESD204B接口技术:高速数据传输与确定性延迟设计
  • 数据科学智能代理规则库:从经验到自动化决策的工程实践
  • 2026年当下,如何挑选一款高效安全的暖风机?从产业格局到品牌推荐 - 2026年企业推荐榜
  • 告别迷茫:用RADE在CATIA V5中创建你的第一个CAA模块(Framework/Module/Workshop详解)
  • 开源数据安全代理规则库:构建高效访问控制与动态脱敏实战指南
  • 阶跃星辰推情感化语音模型
  • 从玩具到工具:Dobot Magician桌面机械臂开箱与Blockly图形化编程初体验
  • Token风暴来袭:科技巨头火拼升级,软件行业重塑,个体革命降临!
  • 2026届最火的十大AI辅助写作方案实测分析
  • Taotoken 用量看板与成本管理功能实际使用感受
  • RedBox容器编排工具:在Docker与K8s间的轻量级生产实践
  • 从BYOD到自建设备:工程师如何掌握硬件定义权与系统设计
  • 淘宝淘金币自动化脚本终极指南:每天节省30分钟,解放你的双手
  • 2026年Q2控糖大米品牌排行:无糖控糖大米、有机五常大米、有机大米价格、有机大米批发、有机大米标准、稻花香有机大米选择指南 - 优质品牌商家
  • StreamCap快速上手:3分钟掌握跨平台直播自动化录制工具
  • Qt For Android实战:从零搭建Qt5.14.2安卓开发环境与避坑指南
  • 基于MCP协议构建AI图像生成服务器:让Claude等助手直接画图
  • AceForge:基于约定优于配置的现代化项目脚手架工具深度解析
  • STM32F407+LAN8720网口不通?别慌,手把手教你用CubeMX和LWIP搞定RMII以太网(附完整代码)
  • AI代理如何通过MCP协议实现DeFi自动化操作与策略执行
  • 成都仿真植物景观厂家排行及实地地址一览2026:仿真草坪推荐、写字楼仿真植物、屋顶仿真草坪、幼儿园仿真草坪、庭院仿真草坪选择指南 - 优质品牌商家
  • 开源硬件集中管理面板:从聚合原理到实践搭建
  • PlotNeuralNet深度定制:教你魔改源码,画出带自定义尺寸和标注的卷积/池化层
  • ARM AArch32地址转换机制与ATS1CUR指令详解
  • 2026年热门的铝合金液冷板/6063液冷板多家厂家对比分析 - 行业平台推荐
  • 2026年避雷塔检测服务应用白皮书电力能源行业篇 - 优质品牌商家
  • Illustrator脚本合集:让你的设计工作流实现10倍效率提升
  • FPGA加速脉冲神经网络:架构设计与优化实践