多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

J2EE期末项目实战:Servlet+DAO+SQLite完整链路解析

J2EE期末项目实战:Servlet+DAO+SQLite完整链路解析 简介这是一份面向高校计算机相关专业本科生的J2EE课程设计与毕业设计实战项目资源聚焦宠物主题Web应用开发适用于期末大作业、课程设计、工程实训及初学者全栈练手。资源包含完整可运行的‘爱狗之家’系统工程涵盖MVC分层结构的Java源码19个.java、编译类文件19个.class、JSP页面18个、Hibernate配置与DAO实现如DB.class、UsDaoimp.class、DogAction.class等、数据库交互逻辑及前端样式资源CSS/JS/图片共197个文件压缩包大小22.22MB。已有37人下载学习项目经实测功能完备支持本地快速部署复现配套工程文件结构清晰便于理解SSH框架整合流程设计报告撰写、功能模块拆解、常见报错定位及二次扩展开发如增删宠物管理、用户权限细化均可直接参考。1. “爱狗之家”J2EE期末大作业一个典型但极易翻车的Web层DAO层SQLite落地项目你花三天搭好Tomcat、配好web.xml、写完UsAction和UsDaoImpl最后页面一刷——404、空指针、数据库表不存在、中文乱码全齐了。这不是你代码写得差而是“爱狗之家”这类J2EE期末作业本质是用最简技术栈暴露最真实工程断层它不考Spring Boot自动装配只考你能不能让一个UsAction真正在浏览器里调通UsDaoImpl查出dog_info.db里的3条流浪犬记录它不考高并发但会卡在DB Browser for SQLite里改完表结构后Java程序还连着旧schema它甚至不强制用MySQL——很多同学交作业时才发现老师给的.zip里就一个lydb.db文件而你的Class.forName(com.mysql.jdbc.Driver)早把JDBC驱动写死了。这个项目适合两类人刚学完Servlet生命周期但没碰过真实数据库连接的学生以及想快速验证J2EE分层思想Action→Service→DAO→DB是否真能跑通的自学者。它不炫技但每一步都踩在初学者最容易忽略的细节上路径映射、资源加载顺序、SQLite JDBC驱动版本兼容性、DAO方法命名与SQL语句的隐式耦合。2. 从.zip解压到Tomcat可部署目录结构、依赖注入与web.xml的硬核对齐2.1 解压后必须立刻检查的5个物理路径拿到J2ee 期末大作业 爱狗之家.zip别急着导入IDE。先解压用命令行或资源管理器逐层确认以下结构是否完整缺任何一项后续90%概率启动失败love-dog/ # 项目根目录名字可能为love_dog、aidog等但必须含WEB-INF ├── WEB-INF/ │ ├── web.xml # 必须存在且url-pattern要和你写的Action类名匹配 │ ├── lib/ │ │ ├── sqlite-jdbc-3.42.0.0.jar # 关键SQLite JDBC驱动版本必须≥3.36.0.0 │ │ └── servlet-api.jar # Tomcat 8建议用javax.servlet-api-4.0.1.jar │ └── classes/ │ ├── action/ # UsAction.class必须在此目录下不是包路径是编译后class位置 │ ├── dao/ # UsDaoImpl.class、UsDao.class │ └── model/ # Dog.class、User.class等实体类 ├── index.jsp # 入口页面form actionUsAction必须指向正确URL ├── db/ │ └── lydb.db # SQLite数据库文件注意路径在代码里是否写成db/lydb.db └── js/ css/ images/ # 静态资源不影响核心逻辑但影响验收观感提示如果classes/下是com/example/action/UsAction.class这种包路径说明你用IDE直接编译过但Tomcat默认不扫描子包——要么手动把class文件按包名建目录classes/com/example/action/要么在web.xml里用servlet-classcom.example.action.UsAction/servlet-class显式声明全限定名。2.2 web.xml中3处决定生死的配置细节WEB-INF/web.xml不是模板是运行时契约。以下三处配置必须与代码严格一致否则UsAction根本不会被容器实例化配置项正确写法以UsAction为例常见错误后果servlet-nameUsActionusaction、UsActionServletweb.xml解析失败Tomcat启动报错url-pattern/UsAction或/action/UsAction/usaction大小写敏感、/UsAction.do但代码没配.do拦截浏览器访问http://localhost:8080/love-dog/UsAction返回404load-on-startup1非必需但强烈建议缺失或为负数Action类延迟加载首次请求可能因DAO未初始化而NPE关键验证点启动Tomcat后查看catalina.out日志必须出现类似Initializing Servlet UsAction字样。没有说明servlet和servlet-mapping的servlet-name值不一致——这是血泪经验里最高频的翻车点。2.3 UsAction如何真正拿到UsDaoImpl手写工厂模式的最小实现J2EE期末作业通常不用Spring所以UsAction里不能写Autowired。必须手动new或通过简单工厂获取DAO实例。常见可靠做法是// action/UsAction.java public class UsAction extends HttpServlet { private UsDao userDao; // 接口类型非实现类 Override public void init() throws ServletException { // 在init()中初始化DAO避免每次doGet/doPost都new try { userDao new UsDaoImpl(); // 直接实例化实现类 } catch (Exception e) { throw new ServletException(DAO初始化失败, e); } } Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ListUser users userDao.findAll(); // 调用DAO方法 req.setAttribute(userList, users); req.getRequestDispatcher(/list.jsp).forward(req, resp); } }参数说明init()方法由Servlet容器在加载时调用一次比在doGet里new UsDaoImpl()更高效userDao声明为接口类型UsDao而非实现类UsDaoImpl符合面向接口编程原则也方便后续替换实现比如换成MySQL版DAO。3. SQLite数据库接入实战从lydb.db文件到UsDaoImpl的完整链路3.1 DB Browser for SQLite不只是看数据更是调试入口别把DB Browser for SQLite当只读工具。它直接关系到DAO能否连上数据库打开db/lydb.db确认表是否存在如dog_info、user_info字段名是否与DAO里SQL语句完全一致dog_name≠dogName执行PRAGMA table_info(dog_info)查看字段类型、是否允许NULL、主键是否为id INTEGER PRIMARY KEYSQLite自增主键必须这样写手动插入测试数据在Browse Data页点Add Record填入name旺财, age3, type金毛保存后立即在Java里调findAll()验证——这是最快确认DAO层是否通的方法。注意SQLite数据库文件路径必须是相对路径且可写。UsDaoImpl里常见写法String dbPath this.getClass().getClassLoader().getResource(db/lydb.db).getPath(); // 错误getResource()返回file:/xxx/WEB-INF/classes/db/lydb.db但Tomcat运行时classes目录下没有db子目录 // 正确做法将db/lydb.db放在WEB-INF/classes/同级目录即项目根目录用ServletContext.getRealPath()3.2 UsDaoImpl的JDBC连接池陷阱与单例改造SQLite虽轻量但多线程下仍需连接复用。直接DriverManager.getConnection()每次新建连接会导致性能骤降甚至文件锁冲突。必须封装连接获取逻辑// dao/UsDaoImpl.java public class UsDaoImpl implements UsDao { private static final String DB_PATH /db/lydb.db; // 注意路径以/开头表示相对于webapp根目录 private Connection getConnection() throws SQLException { ServletContext context getServletContext(); // 需在Action里传入或改用静态工具类 String realPath context.getRealPath(DB_PATH); if (realPath null) { throw new SQLException(数据库文件路径未找到: DB_PATH); } return DriverManager.getConnection(jdbc:sqlite: realPath); } Override public ListUser findAll() throws SQLException { String sql SELECT id, name, phone FROM user_info; try (Connection conn getConnection(); Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(sql)) { ListUser list new ArrayList(); while (rs.next()) { User u new User(); u.setId(rs.getInt(id)); u.setName(rs.getString(name)); u.setPhone(rs.getString(phone)); list.add(u); } return list; } } }关键参数说明getRealPath(/db/lydb.db)返回绝对路径如/opt/tomcat/webapps/love-dog/db/lydb.db确保JDBC能定位文件try-with-resources自动关闭Connection/Statement/ResultSet避免连接泄漏SQL语句中字段名必须与数据库表定义完全一致区分大小写否则rs.getString(name)返回null。3.3 LyAction与UsAction的命名冲突排查为什么你的Action总404标题里同时出现UsAction和LyAction说明项目可能混用了两套Action——这是老师故意埋的坑。常见情况web.xml里注册了servlet-nameLyAction/servlet-name但index.jsp里写form actionUsActionLyAction.java和UsAction.java同时存在但只有UsAction实现了业务逻辑LyAction是空壳LyAction可能是登录ActionUsAction是用户管理Action两者共用同一个DAO但URL路径不同。验证方法查web.xml中所有servlet标签记录每个servlet-name查所有.java文件确认哪些类继承HttpServlet对比servlet-mapping中的url-pattern与JSP里form action的值——必须完全匹配包括大小写、斜杠、后缀。4. 避坑J2EE期末作业里最常踩的5个深坑及现场急救方案4.1 现象Tomcat启动成功但访问/UsAction返回404原因web.xml中servlet-mapping的url-pattern与浏览器请求URL不一致或servlet-class路径错误。解决检查Tomcat日志catalina.out搜索Servlet关键字确认UsAction是否被注册在浏览器地址栏输入http://localhost:8080/love-dog/UsAction项目名必须与war包名一致若项目名是love-dog.war则URL必须带love-dog若部署为ROOT则URL为http://localhost:8080/UsAction。4.2 现象DAO查询返回空List但DB Browser里明明有数据原因SQLite JDBC驱动版本过低3.36.0.0不支持ROWID以外的自增主键写法或数据库路径错误导致连了空库。解决删除WEB-INF/lib/下旧版sqlite-jdbc-*.jar下载 官方最新版 如sqlite-jdbc-3.42.0.0.jar在UsDaoImpl.getConnection()里打印realPath确认路径指向的是你用DB Browser编辑的那个lydb.db执行SELECT * FROM sqlite_master WHERE typetable;确认表名拼写无误SQLite表名区分大小写。4.3 现象中文存入数据库后显示为??或乱码原因SQLite默认编码为UTF-8但JDBC URL未指定编码或Tomcat服务器默认编码非UTF-8。解决修改JDBC URLjdbc:sqlite: realPath ?charsetutf8在web.xml中添加过滤器强制UTF-8filter filter-nameCharacterEncodingFilter/filter-name filter-classorg.apache.catalina.filters.SetCharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param /filter filter-mapping filter-nameCharacterEncodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping4.4 现象UsDaoImpl里new UsDaoImpl()报NoClassDefFoundError原因UsDaoImpl.class未编译进WEB-INF/classes/或UsDao.class接口缺失。解决进入WEB-INF/classes/目录用ls -R确认dao/UsDaoImpl.class和dao/UsDao.class同时存在若用IDE编译检查输出路径是否为WEB-INF/classes而非target/classes手动编译javac -d WEB-INF/classes -cp WEB-INF/lib/*:. src/dao/UsDaoImpl.java。4.5 现象点击按钮跳转到LyAction却执行了UsAction逻辑原因web.xml中两个Action的url-pattern冲突如都设为/Action或JSP里form action写错。解决逐行检查web.xml确保每个servlet-mapping的url-pattern唯一在LyAction.java和UsAction.java的doGet()开头加System.out.println(LyAction executed);重启Tomcat看日志输出用浏览器开发者工具Network面板确认实际请求的URL路径。5. 进阶验证用JUnit 4跑通DAO层单元测试绕过Tomcat直击数据逻辑期末作业验收时老师可能只看功能是否可用但你想真正吃透UsDaoImpl就得脱离Servlet容器单独测试DAO。JUnit 4是最轻量的选择——不需要Maven只要把junit-4.13.2.jar和hamcrest-core-1.3.jar丢进WEB-INF/lib/即可。5.1 写一个能独立运行的DAO测试类在src/test/下新建UsDaoImplTest.java注意不要放WEB-INF/classes/里避免部署冲突// src/test/UsDaoImplTest.java import org.junit.Test; import java.sql.SQLException; import static org.junit.Assert.*; public class UsDaoImplTest { Test public void testFindAllReturnsNonEmptyList() throws SQLException { // 绕过ServletContext直接用绝对路径连接数据库 String dbPath /path/to/your/love-dog/db/lydb.db; // 替换为你本地的绝对路径 UsDaoImpl dao new UsDaoImpl(dbPath); // 改造UsDaoImpl构造函数接受dbPath参数 assertNotNull(dao); // 执行查询 java.util.ListUser users dao.findAll(); assertNotNull(users); assertTrue(用户列表应非空, users.size() 0); assertTrue(第一条记录姓名不应为空, users.get(0).getName() ! null !users.get(0).getName().trim().isEmpty()); } }改造UsDaoImpl构造函数public class UsDaoImpl implements UsDao { private final String dbPath; public UsDaoImpl(String dbPath) { this.dbPath dbPath; } // 新增构造函数 private Connection getConnection() throws SQLException { return DriverManager.getConnection(jdbc:sqlite: dbPath); } // 其余方法不变 }5.2 用DB Browser for SQLite生成测试数据的标准化流程别靠手动填表测DAO。用DB Browser的Execute SQL页批量插入-- 清空并重建user_info表谨慎操作 DROP TABLE IF EXISTS user_info; CREATE TABLE user_info ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, phone TEXT UNIQUE ); INSERT INTO user_info (name, phone) VALUES (张三, 13800138000), (李四, 13900139000), (王五, 15000150000);为什么这步关键UNIQUE约束能验证DAO插入重复手机号时是否抛出SQLExceptionAUTOINCREMENT确保id字段自增避免DAO里手动设setId()出错批量插入3条数据刚好覆盖findAll()的边界场景空、单条、多条。5.3 验证DAO方法命名与SQL的隐式契约UsDaoImpl里的3个潜规则UsDaoImpl不是随便写的它的方法名直接决定了SQL语句的结构。这是J2EE分层里最易被忽略的契约DAO方法名隐含SQL模式必须满足的条件不满足的后果findById(int id)SELECT * FROM user_info WHERE id ?参数名必须叫id且表中主键字段名也是idPreparedStatement绑定参数时报Invalid column indexfindByPhone(String phone)SELECT * FROM user_info WHERE phone ?方法参数名phone必须与SQL中WHERE后的字段名一致查询永远返回空因为?绑定到了错误字段insert(User user)INSERT INTO user_info (name, phone) VALUES (?, ?)User对象的getter方法名getName()、getPhone()必须与SQL字段顺序严格对应插入数据错位如电话存到姓名字段我当年debug一整天就因为把User类的getTel()改成getPhone()但SQL里还是VALUES (?, ?)——JDBC按getter顺序绑定结果电话号存进了姓名字段。这种玄学问题只有写单元测试DB Browser对照才能揪出来。希望帮到你。本文还有配套的精品资源点击获取
返回列表