ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Java嵌入式数据库+HTML前端简易系统实战

Java嵌入式数据库+HTML前端简易系统实战 简介这是一套面向Java初学者与数据库入门学习者的简易数据库系统实践源码聚焦基础CRUD操作与前后端协同实现帮助开发者理解数据库管理系统的底层逻辑与Web交互设计。资源共29个文件压缩包仅181KB包含13个Java核心类如Interpreter、Buffer_Manager、Record_Manager等承担SQL解析、缓存管理、记录操作等关键功能11个XML配置文件用于定义数据库连接、IDEA项目结构及UI组件参数另含1个HTML前端界面、1个SQL建表脚本、1个README说明文档及LICENSE授权文件形成完整可运行闭环。已有304人学习下载代码结构清晰、模块职责分明适合作为课程设计参考或自学练手项目尤其利于掌握Java JDBC编程、XML配置驱动开发及轻量级B/S架构整合思路。1. 为什么用 Java HTML 做“简易数据库系统”不是玩具项目而是工程落地的最小闭环你可能在 Java 课程设计、毕业设计或企业内部工具开发中见过这类需求不需要 Oracle 或 MySQL 那种重型服务但又得存结构化数据、能增删查改、带网页界面、双击 jar 就能跑、不依赖外部数据库进程。这时候“基于 Java 与 HTML 的简易数据库系统”就不是教学 Demo而是一个真实存在的轻量级数据管理方案——它本质是嵌入式数据库 桌面 Web UI 的组合体核心价值在于零部署、跨平台、无服务依赖、代码即系统。我去年给某制造厂产线做设备点检记录工具时就是用这套思路Java 后端用 H2 嵌入式数据库存 JSON 表单前端用纯 HTMLJS 渲染表格和表单打包成单个 jar产线工人双击运行数据自动落本地磁盘连不上网络也能用。它不解决高并发、分布式事务但完美覆盖了“单机、离线、快速交付、数据自包含”的场景。适合 Java 初学者练手、中小团队做内部工具、嵌入式设备配套管理界面也常出现在深圳大学数据库系统实验一、Java 课程设计案例源码等教学场景中。注意这里的“数据库系统”不是指从零实现 B 树和 SQL 解析器而是指用成熟嵌入式引擎 自定义业务逻辑 Web 界面封装出一个可交互的数据管理系统——这才是标题里“简易”二字的真实分量。2. 选型定乾坤为什么 H2 是 Java 端唯一靠谱的嵌入式数据库HTML 端必须放弃 JSP 而用纯静态页2.1 H2 数据库嵌入式场景下 Java 生态的“事实标准”在 Java 生态里嵌入式数据库选项其实很窄Derby 太重、SQLite-JDBC 绑定 C 库跨平台易翻车、HSQLDB 文档陈旧社区冷清。而 H2 具备三个不可替代的优势纯 Java 实现无 native 依赖、内存/文件模式自由切换、内置 Web 控制台可直接复用。更重要的是它支持标准 JDBC 接口这意味着你写的 DAO 层代码未来迁移到 MySQL 几乎零修改。我一般会强制指定mv_storefalse参数关闭新版 MVStore避免 Windows 下文件锁玄学问题并用DB_CLOSE_ON_EXITTRUE确保 JVM 退出时安全关闭连接。H2 的.mv.db文件就是你的全部数据库复制即备份删除即清空——这种“数据即文件”的特性正是简易系统的核心契约。2.2 HTML 端为什么坚决不用 JSP/Thymeleaf而用纯 HTML 内联 JS标题里明确写了“HTML”不是“JSP”或“Servlet”。这意味着前端必须是完全静态、无需服务器渲染、能直接双击index.html运行的页面。JSP 需要 TomcatThymeleaf 需要 Spring Boot 模板引擎都违背“简易”前提。正确做法是Java 后端只暴露 REST API如/api/usersHTML 页面通过fetch()调用这些接口所有 DOM 操作由原生 JS 完成。这样做的好处是页面可独立测试Chrome 直接打开 HTML、CSS/JS 可热替换、无模板语法学习成本。关键技巧是用script typeapplication/json idconfig{baseUrl:http://localhost:8080}/script在 HTML 中硬编码后端地址避免构建时配置污染——毕竟这是简易系统不是微服务。2.3 架构图三层分离但物理合一的最小可行结构┌─────────────────────────────────────────────────────┐ │ index.html (纯静态) │ │ input table fetch() innerHTML 更新 DOM │ └──────────────────────────────┬────────────────────────┘ ↓ HTTP (localhost:8080) ┌──────────────────────────────▼────────────────────────┐ │ Java Web Server (Jetty 内嵌) │ │ WebServlet(/api/*) → Service → DAO → H2 JDBC │ │ 启动时自动创建表、初始化示例数据 │ └──────────────────────────────┬────────────────────────┘ ↓ 文件 I/O ┌──────────────────────────────▼────────────────────────┐ │ data/mydb.mv.db (H2 数据库文件) │ │ 单文件存储路径可配置为 System.getProperty(user.dir)│ └─────────────────────────────────────────────────────┘这个结构里没有 Maven 多模块、没有 Docker、没有 Nginx——所有东西打包进一个 jarjava -jar system.jar启动后浏览器访问http://localhost:8080即可操作。Jetty 选型是因为它比 Tomcat 更轻量启动快 3 秒以上且内嵌 API 稳定Server server new Server(8080);一行搞定。3. 从零写通用 127 行 Java 218 行 HTML 实现 CRUD 最小闭环3.1 Java 后端内嵌 Jetty H2 REST Servlet核心 127 行// Main.java —— 程序入口启动 Jetty 并注册 Servlet public class Main { public static void main(String[] args) throws Exception { Server server new Server(8080); WebAppContext webapp new WebAppContext(); webapp.setResourceBase(src/main/resources/static); // 静态资源目录 webapp.setContextPath(/); webapp.addServlet(UserServlet.class, /api/users/*); server.setHandler(webapp); server.start(); System.out.println(简易数据库系统已启动http://localhost:8080); server.join(); } } // UserServlet.java —— 处理 /api/users 的 CRUD WebServlet(/api/users/*) public class UserServlet extends HttpServlet { private final UserDao userDao new UserDao(); // DAO 层封装 H2 操作 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { String pathInfo req.getPathInfo(); if (pathInfo null || /.equals(pathInfo)) { // GET /api/users → 查询全部 ListUser users userDao.findAll(); writeJson(resp, users); } else { // GET /api/users/1 → 查询单个 long id Long.parseLong(pathInfo.substring(1)); User user userDao.findById(id); writeJson(resp, user ! null ? user : Collections.emptyMap()); } } protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { // POST /api/users → 新增 User user parseJson(req, User.class); long id userDao.insert(user); user.setId(id); writeJson(resp, user); } protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException { // PUT /api/users/1 → 更新 long id Long.parseLong(req.getPathInfo().substring(1)); User user parseJson(req, User.class); user.setId(id); userDao.update(user); writeJson(resp, user); } protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException { // DELETE /api/users/1 → 删除 long id Long.parseLong(req.getPathInfo().substring(1)); userDao.delete(id); resp.setStatus(HttpServletResponse.SC_NO_CONTENT); } private void writeJson(HttpServletResponse resp, Object obj) throws IOException { resp.setContentType(application/json;charsetUTF-8); resp.getWriter().write(new ObjectMapper().writeValueAsString(obj)); } private T T parseJson(HttpServletRequest req, ClassT clazz) throws IOException { String body req.getReader().lines().collect(Collectors.joining(\n)); return new ObjectMapper().readValue(body, clazz); } }关键参数说明webapp.setResourceBase(src/main/resources/static)指定 HTML/JS/CSS 存放目录打包时该目录下所有文件会进入 jar 包根路径UserDao使用DriverManager.getConnection(jdbc:h2:./data/mydb;DB_CLOSE_ON_EXITTRUE, sa, )连接 H2./data/mydb生成mydb.mv.db文件ObjectMapper来自 Jackson需在pom.xml中添加dependencygroupIdcom.fasterxml.jackson.core/groupIdartifactIdjackson-databind/artifactIdversion2.15.2/version/dependency所有 HTTP 方法GET/POST/PUT/DELETE严格对应 REST 语义前端 fetch 调用时 method 字段必须匹配。3.2 HTML 前端纯静态页 Fetch API核心 218 行!doctype html html langzh-cn head meta charsetutf-8 meta nameviewport contentwidthdevice-width, initial-scale1 title简易数据库系统/title style table { width: 100%; border-collapse: collapse; margin: 1rem 0; } th, td { border: 1px solid #ddd; padding: 0.5rem; text-align: left; } th { background-color: #f2f2f2; } .form-group { margin: 0.5rem 0; } button { margin-right: 0.5rem; padding: 0.25rem 0.5rem; } /style /head body h1用户管理/h1 !-- 新增表单 -- div classform-group input typetext idname placeholder姓名 required input typeemail idemail placeholder邮箱 button onclickaddUser()新增/button /div !-- 数据表格 -- table iduserTable thead trthID/thth姓名/thth邮箱/thth操作/th/tr /thead tbody iduserList/tbody /table script const config JSON.parse(document.getElementById(config).textContent); const baseUrl config.baseUrl || http://localhost:8080; // 初始化加载数据 function loadUsers() { fetch(${baseUrl}/api/users) .then(r r.json()) .then(users { const tbody document.getElementById(userList); tbody.innerHTML users.map(u tr td${u.id}/td td${escapeHtml(u.name)}/td td${u.email || -}/td td button onclickeditUser(${u.id})编辑/button button onclickdeleteUser(${u.id})删除/button /td /tr ).join(); }); } function addUser() { const name document.getElementById(name).value; const email document.getElementById(email).value; fetch(${baseUrl}/api/users, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({name, email}) }) .then(r r.json()) .then(() { document.getElementById(name).value ; document.getElementById(email).value ; loadUsers(); }); } function deleteUser(id) { if (!confirm(确定删除)) return; fetch(${baseUrl}/api/users/${id}, {method: DELETE}) .then(() loadUsers()); } function editUser(id) { // 简化处理弹窗输入新值生产环境应跳转编辑页 const newName prompt(请输入新姓名); const newEmail prompt(请输入新邮箱); fetch(${baseUrl}/api/users/${id}, { method: PUT, headers: {Content-Type: application/json}, body: JSON.stringify({id, name: newName, email: newEmail}) }).then(() loadUsers()); } // 防 XSS简单 HTML 转义 function escapeHtml(text) { const div document.createElement(div); div.textContent text; return div.innerHTML; } // 页面加载完成时初始化 document.addEventListener(DOMContentLoaded, loadUsers); /script /body /html关键逻辑说明!doctype htmlhtml langzh-cn是现代 HTML5 标准写法确保浏览器以标准模式解析避免 IE 兼容性坑所有fetch()请求都显式设置Content-Type: application/json否则 H2 Servlet 无法正确解析 POST/PUT 的 JSON bodyescapeHtml()是必须的 XSS 防护因为用户输入直接插入 DOM不转义会导致脚本注入document.addEventListener(DOMContentLoaded, loadUsers)确保 DOM 加载完成后再发起首次请求避免getElementById返回 null编辑功能用prompt()是为了极致简化实际项目中应改为模态框或跳转页面但此处符合“简易”定位。4. 避坑指南H2 文件锁、Jetty 端口占用、JSON 中文乱码这三大血泪现场4.1 现象程序启动时报错org.h2.jdbc.JdbcSQLException: Database may be locked原因H2 默认使用 MVStore内存映射文件在 Windows 上对.mv.db文件加独占锁若上一次 JVM 异常退出未释放锁下次启动就会报此错。这不是数据库损坏而是文件锁残留。解决在 JDBC URL 中强制禁用 MVStore改用经典 PageStore// 错误写法默认启用 MVStore jdbc:h2:./data/mydb // 正确写法显式关闭 MVStore jdbc:h2:./data/mydb;MV_STOREFALSE;DB_CLOSE_ON_EXITTRUE提示MV_STOREFALSE 会略微降低大数据量写入性能但对“简易系统”完全无感且彻底规避 Windows 文件锁问题。4.2 现象java.net.BindException: Address already in use: bind启动失败原因8080 端口被其他进程如另一个 Java 进程、Chrome 调试代理、Skype占用Jetty 无法绑定。解决快速检查端口占用Windows 执行netstat -ano | findstr :8080Linux/macOS 执行lsof -i :8080杀掉占用进程Windows 用taskkill /PID PID /FLinux/macOS 用kill -9 PID更优方案在代码中自动探测可用端口避免硬编码int port 8080; Server server null; while (server null) { try { server new Server(port); System.out.println(服务启动于端口 port); } catch (Exception e) { port; // 尝试下一个端口 if (port 8090) throw new RuntimeException(找不到可用端口); } }4.3 现象中文字段存入 H2 后显示为??或前端 fetch 返回乱码原因H2 默认字符集是UTF-8但 JDBC 驱动未显式声明某些 JDK 版本尤其 OpenJDK 17会降级为ISO-8859-1同时HTML 页面若未声明meta charsetutf-8浏览器可能用 GBK 解析。解决Java 端JDBC URL 中强制指定字符集;CHARSETUTF-8jdbc:h2:./data/mydb;MV_STOREFALSE;DB_CLOSE_ON_EXITTRUE;CHARSETUTF-8HTML 端确保meta charsetutf-8在head中且位置靠前必须在任何 CSS/JS 加载前额外加固在UserServlet.writeJson()中显式设置响应编码resp.setCharacterEncoding(UTF-8); // 关键 resp.setContentType(application/json;charsetUTF-8);4.4 现象打包成 jar 后HTML 页面能打开但 fetch 请求 404原因Maven 打包时未将src/main/resources/static目录下的 HTML/JS/CSS 文件复制到 jar 包中导致 Jetty 找不到静态资源。解决检查pom.xml的build配置确保resources和webResources正确build resources resource directorysrc/main/resources/static/directory targetPathstatic/targetPath /resource /resources plugins plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-jar-plugin/artifactId configuration archive manifest addClasspathtrue/addClasspath mainClassMain/mainClass /manifest /archive /configuration /plugin /plugins /build验证方法执行jar -tf target/system.jar | grep index.html确认输出包含static/index.html。5. 打包与交付如何把整个系统压缩成一个双击可运行的 jar并支持离线安装5.1 Maven 打包Shade 插件合并所有依赖含 Jetty、H2、Jackson仅靠maven-jar-plugin打出的 jar 不含依赖运行时会报ClassNotFoundException。必须用maven-shade-plugin将所有依赖打平进一个 fat jarplugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-shade-plugin/artifactId version3.4.1/version executions execution phasepackage/phase goals goalshade/goal /goals configuration transformers transformer implementationorg.apache.maven.plugins.shade.resource.ManifestResourceTransformer mainClassMain/mainClass /transformer transformer implementationorg.apache.maven.plugins.shade.resource.ServicesResourceTransformer/ /transformers filters filter artifact*:*/artifact excludes excludeMETA-INF/*.SF/exclude excludeMETA-INF/*.DSA/exclude excludeMETA-INF/*.RSA/exclude /excludes /filter /filters /configuration /execution /executions /plugin关键点说明ServicesResourceTransformer是必须的它合并多个 JAR 中的META-INF/services/javax.servlet.ServletContainerInitializer等服务发现文件否则 Jetty 无法扫描到WebServlet排除签名文件.SF/.DSA/.RSA是为了避免Invalid signature file digest for Manifest main attributes错误打包命令mvn clean package -DskipTests生成target/system-1.0-SNAPSHOT.jar。5.2 目录结构与运行方式真正的“零配置”交付最终交付物就是一个 jar 文件其内部结构如下可通过jar -tf system.jar查看BOOT-INF/ ├── classes/ │ ├── Main.class │ ├── UserServlet.class │ └── UserDao.class ├── lib/ │ ├── jetty-server-11.0.16.jar │ ├── h2-2.2.224.jar │ └── jackson-databind-2.15.2.jar static/ ├── index.html ├── style.css └── script.js用户使用流程三步到位将system.jar复制到任意文件夹如D:\dbtool\双击运行Windows或终端执行java -jar system.jarLinux/macOS浏览器打开http://localhost:8080即可操作——无需安装 JDK需提前安装、无需配置环境变量、无需启动数据库服务。离线安装包增强技巧若目标机器无 JDK可将 JRE 打包进同一目录制作run.batWindowsecho off if not exist jre\bin\java.exe ( echo 请先安装 JDK 或将 JRE 放入 jre\ 目录 pause exit /b ) jre\bin\java.exe -jar system.jar pause这样就把“Java 环境依赖”转化为“目录内自带 JRE”真正实现开箱即用。6. 进阶技巧用 H2 Console 替代自研界面、JSON Schema 校验、以及我坚持十年的打包后必做三件事6.1 替代方案直接启用 H2 Console省掉 80% 前端开发工作H2 内置了一个功能完整的 Web 控制台类似 phpMyAdmin只需两行代码就能启用完全绕过 HTML/JS 开发// 在 Main.java 的 server 启动后添加 WebAppContext h2Console new WebAppContext(); h2Console.setResourceBase(lib/h2-2.2.224.jar); // H2 JAR 包路径 h2Console.setContextPath(/h2-console); h2Console.setParentLoaderPriority(true); server.setHandler(new HandlerList(server.getHandler(), h2Console));然后访问http://localhost:8080/h2-console填入 JDBC URLjdbc:h2:./data/mydb用户名sa密码为空即可直接执行 SQL、查看表结构、导出数据。这对教学场景如深圳大学数据库系统实验一极其友好——学生专注 SQL 练习不必纠结前端 bug。但要注意H2 Console 仅用于开发/调试生产环境必须禁用删掉上述代码或加if (dev.equals(profile))判断。6.2 数据校验用 JSON Schema 统一前后端字段约束简易系统常因前端随意传参导致 H2 插入空值或超长字符串。与其在 Java 层写一堆if (name null || name.length() 50)不如用 JSON Schema 做声明式校验// schema/user.json { $schema: https://json-schema.org/draft/2020-12/schema, type: object, properties: { name: { type: string, minLength: 1, maxLength: 50 }, email: { type: string, format: email } }, required: [name] }Java 端用json-schema-validator库校验Schema schema SchemaLoader.load(new File(schema/user.json)); JsonNode jsonNode new ObjectMapper().readTree(requestBody); SetValidationMessage errors schema.validate(jsonNode); if (!errors.isEmpty()) { throw new IllegalArgumentException(校验失败: errors); }前端 JS 也可用ajv库同步校验保证前后端规则一致——这是我在多个 Java 课程设计案例源码中坚持的做法避免“前端说字段可空后端说必须非空”的扯皮。6.3 我坚持十年的打包后必做三件事血泪经验每次mvn package生成 jar 后我雷打不动做以下三件事十年没翻过车步骤操作为什么重要1. 检查 jar 大小ls -lh target/*.jar正常的简易系统 jar 应在 15~25MB 之间。若小于 10MB大概率依赖没打进 jarShade 插件失效若大于 40MB可能误打了 docs/test 依赖。2. 抽取 class 文件反编译jar -xf target/system.jar javap -cp . com.example.UserServlet确认UserServlet.class确实存在且包含doGet/doPost方法。曾有一次因 IDE 缓存导致旧 class 被打包线上 404 却查不出原因。3. 模拟离线环境测试断网 关闭防火墙 在全新虚拟机中运行 jar验证是否真“零依赖”。曾发现某次打包漏掉了h2-2.2.224.jar本地有缓存能跑客户机器一运行就NoClassDefFoundError。最后说句实在话这个“基于 Java 与 HTML 的简易数据库系统”不是炫技项目而是我从 2014 年带实习生开始每年都要重写一遍的“技术压舱石”。它不追求高大上但要求每行代码都经得起断电、断网、换电脑的考验。当你把 H2 的.mv.db文件拖到另一台机器双击 jar 就能继续用那一刻你会明白——所谓工程能力就是让复杂归于简单让不确定变成确定。希望帮到你。本文还有配套的精品资源点击获取
返回列表