news 2026/4/23 14:59:32

Forest项目中将Derby数据库替换为MySQL

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Forest项目中将Derby数据库替换为MySQL

Forest项目中将Derby数据库替换为MySQL

在开发Java Web应用时,我们常常会用到像Apache Derby这样的嵌入式数据库——启动快、配置简单,特别适合教学演示或本地调试。比如经典的Forest示例项目,默认就使用Derby作为持久化存储。但一旦进入类生产环境,问题就来了:连接不稳定、并发支持弱、缺乏运维工具……这时候就得换上真正“能打”的数据库,比如MySQL。

那怎么把一个原本跑在Derby上的Java EE项目,平滑迁移到MySQL?别担心,整个过程其实并不复杂,只要搞清楚几个关键点:数据源怎么改、SQL脚本有哪些坑、驱动包放哪儿。接下来我就带你一步步完成这次迁移,照着做就行,连初学者也能搞定。


从web.xml开始:重新定义数据源

Forest这类基于Java EE/Jakarta EE的应用,通常会在web.xml里通过JNDI注册全局数据源。原来的配置长这样:

<data-source> <name>java:global/ForestDataSource</name> <class-name>org.apache.derby.jdbc.EmbeddedDataSource</class-name> <database-name>forest</database-name> <property> <name>createDatabase</name> <value>create</value> </property> </data-source>

现在我们要把它换成MySQL的连接方式。找到你的WEB-INF/web.xml文件,把上面那段替换成如下内容:

<data-source> <name>java:global/ForestDataSource</name> <class-name>com.mysql.cj.jdbc.MysqlDataSource</class-name> <server-name>localhost</server-name> <port-number>3306</port-number> <user>root</user> <password>admin</password> <property> <name>databaseName</name> <value>forest</value> </property> <property> <name>useSSL</name> <value>false</value> </property> <property> <name>allowPublicKeyRetrieval</name> <value>true</value> </property> </data-source>

这里有几个细节要注意:

  • com.mysql.cj.jdbc.MysqlDataSource是MySQL Connector/J 8.x推荐的数据源实现类,比老式的DriverManager.getConnection()更适合容器管理。
  • 原来的<database-name>标签其实是非标准写法,在新版本中应该用<property>包裹databaseName
  • useSSL=falseallowPublicKeyRetrieval=true这两个参数在开发环境下很实用,避免因为证书验证或公钥获取失败导致连不上数据库(线上请务必开启SSL并妥善配置)。
  • 如果你MySQL不是跑在默认3306端口,记得改<port-number>

改完之后先别急着重启服务器——数据库还没建呢。


先动手:创建MySQL数据库与用户权限

在应用启动前,得确保目标数据库已经准备就绪。登录MySQL命令行或者用Navicat这类工具执行:

CREATE DATABASE IF NOT EXISTS forest CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

为什么强调utf8mb4?因为它是真正完整的UTF-8支持,能存emoji、特殊符号这些四字节字符。而所谓的utf8在MySQL里其实是阉割版,最多只支持三字节,容易出乱码。

如果你不想用root账户跑应用(强烈建议不要这么做),那就提前建个专用用户并授权:

CREATE USER 'forest_user'@'%' IDENTIFIED BY 'your_secure_password'; GRANT ALL PRIVILEGES ON forest.* TO 'forest_user'@'%'; FLUSH PRIVILEGES;

然后回头去web.xml里把<user><password>改成对应值即可。


persistence.xml需要动吗?

大多数情况下,不需要

Forest项目一般使用JPA(比如EclipseLink或Hibernate),它的持久化单元会自动根据连接的数据库类型选择合适的方言。典型的persistence.xml看起来是这样的:

<persistence-unit name="forest"> <jta-data-source>java:global/ForestDataSource</jta-data-source> <properties> <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/> </properties> </persistence-unit>

只要<jta-data-source>指向的是正确的JNDI名称,Hibernate就能从JDBC URL推断出这是MySQL,并启用MySQL8Dialect。但如果你之前手动指定过方言,比如:

<property name="hibernate.dialect" value="org.hibernate.dialect.DerbyTenSevenDialect"/>

那就一定要改成:

<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/>

否则你会发现一些语法报错,比如分页查询用不了LIMIT,或者自增主键生成策略有问题。


别忘了加JDBC驱动包

GlassFish、Payara这类Java EE应用服务器不会自带MySQL驱动,所以你必须手动把mysql-connector-java.jar放进类路径。

最稳妥的位置是服务器的domain库目录:

domains/domain1/lib/mysql-connector-java-8.0.33.jar

下载哪个版本?建议选8.0.x系列,兼容MySQL 5.7+和8.0+。如果是Maven项目,也可以直接打入WEB-INF/lib,但独立部署时还是推荐放在domain lib下统一管理。

✅ 放好后重启服务器,确认日志没有类找不到的错误。


SQL脚本适配:这才是最容易踩坑的地方

Forest项目通常附带三组初始化脚本:drop.sqlcreate.sqldata.sql。它们最初是为Derby写的,直接扔给MySQL可能会炸。

drop.sql:先关外键检查

Derby允许DROP TABLE IF EXISTS无脑删表,但MySQL对顺序敏感,尤其是有外键约束的时候。稳妥做法是先关闭约束检查:

SET FOREIGN_KEY_CHECKS = 0; DROP TABLE IF EXISTS ORDER_DETAIL; DROP TABLE IF EXISTS CUSTOMER_ORDER; DROP TABLE IF EXISTS PRODUCT; DROP TABLE IF EXISTS CATEGORY; DROP TABLE IF EXISTS PERSON_GROUPS; DROP TABLE IF EXISTS PERSON; DROP TABLE IF EXISTS GROUPS; DROP TABLE IF EXISTS ORDER_STATUS; SET FOREIGN_KEY_CHECKS = 1;

这样不管依赖关系如何,都能干净清除旧结构。


create.sql:核心DDL重写指南

这是最关键的一步。下面是针对MySQL优化后的完整版本:

-- 创建并切换数据库 CREATE DATABASE IF NOT EXISTS forest CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE forest; -- 启用事务保证一致性 SET autocommit=0; START TRANSACTION; -- 类别表 CREATE TABLE CATEGORY ( ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(45) NOT NULL, TAGS VARCHAR(45) ); CREATE UNIQUE INDEX SQL_CATEGORY_ID_INDEX ON CATEGORY(ID); -- 用户表 CREATE TABLE PERSON ( ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY, FIRSTNAME VARCHAR(50) NOT NULL, LASTNAME VARCHAR(100) NOT NULL, EMAIL VARCHAR(45) NOT NULL UNIQUE, ADDRESS VARCHAR(45) NOT NULL, CITY VARCHAR(45) NOT NULL, PASSWORD VARCHAR(100), DTYPE VARCHAR(31) ); CREATE UNIQUE INDEX SQL_PERSON_EMAIL_INDEX ON PERSON(EMAIL); CREATE UNIQUE INDEX SQL_PERSON_ID_INDEX ON PERSON(ID); -- 组表 CREATE TABLE GROUPS ( ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(50) NOT NULL, DESCRIPTION VARCHAR(300) ); -- 用户-组关联表 CREATE TABLE PERSON_GROUPS ( GROUPS_ID INT NOT NULL, EMAIL VARCHAR(45) NOT NULL, CONSTRAINT PK_PERSON_GROUPS PRIMARY KEY (GROUPS_ID, EMAIL) ); ALTER TABLE PERSON_GROUPS ADD CONSTRAINT FK_PERSON_GROUPS_GROUP FOREIGN KEY (GROUPS_ID) REFERENCES GROUPS(ID); ALTER TABLE PERSON_GROUPS ADD CONSTRAINT FK_PERSON_GROUPS_PERSON FOREIGN KEY (EMAIL) REFERENCES PERSON(EMAIL); CREATE INDEX SQL_PERSONGROUPS_EMAIL_INDEX ON PERSON_GROUPS(EMAIL); CREATE INDEX SQL_PERSONGROUPS_ID_INDEX ON PERSON_GROUPS(GROUPS_ID); -- 订单状态表 CREATE TABLE ORDER_STATUS ( ID INT NOT NULL PRIMARY KEY, STATUS VARCHAR(45) NOT NULL, DESCRIPTION VARCHAR(200) ); CREATE UNIQUE INDEX SQL_ORDERSTATUS_ID_INDEX ON ORDER_STATUS(ID); -- 客户订单表 CREATE TABLE CUSTOMER_ORDER ( ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY, AMOUNT FLOAT(52) NOT NULL, DATE_CREATED TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, CUSTOMER_ID INT NOT NULL, STATUS_ID INT NOT NULL ); ALTER TABLE CUSTOMER_ORDER ADD CONSTRAINT FK_CUSTOMER_ORDER_ORDER_STATUS FOREIGN KEY (STATUS_ID) REFERENCES ORDER_STATUS(ID); ALTER TABLE CUSTOMER_ORDER ADD CONSTRAINT FK_CUSTOMER_ORDER_CUSTOMER FOREIGN KEY (CUSTOMER_ID) REFERENCES PERSON(ID); CREATE INDEX SQL_ORDER_STATUS_ID_INDEX ON CUSTOMER_ORDER(STATUS_ID); CREATE INDEX SQL_ORDER_CUSTOMER_ID_INDEX ON CUSTOMER_ORDER(CUSTOMER_ID); CREATE UNIQUE INDEX SQL_ORDER_ID_INDEX ON CUSTOMER_ORDER(ID); -- 商品表 CREATE TABLE PRODUCT ( ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(45) NOT NULL, PRICE DECIMAL(10,2) NOT NULL, DESCRIPTION VARCHAR(145) NOT NULL, IMG VARCHAR(45), CATEGORY_ID INT NOT NULL, IMG_SRC LONGBLOB ); ALTER TABLE PRODUCT ADD CONSTRAINT FK_PRODUCT_CATEGORY FOREIGN KEY (CATEGORY_ID) REFERENCES CATEGORY(ID); CREATE UNIQUE INDEX SQL_PRODUCT_ID_INDEX ON PRODUCT(ID); -- 订单明细表 CREATE TABLE ORDER_DETAIL ( ORDER_ID INT NOT NULL, PRODUCT_ID INT NOT NULL, QTY INT NOT NULL, CONSTRAINT SQL_ORDER_PRODUCT_PK PRIMARY KEY (ORDER_ID, PRODUCT_ID) ); ALTER TABLE ORDER_DETAIL ADD CONSTRAINT FK_ORDER_DETAIL_PRODUCT FOREIGN KEY (PRODUCT_ID) REFERENCES PRODUCT(ID); ALTER TABLE ORDER_DETAIL ADD CONSTRAINT FK_ORDER_DETAIL_ORDER FOREIGN KEY (ORDER_ID) REFERENCES CUSTOMER_ORDER(ID); CREATE UNIQUE INDEX SQL_ORDER_DETAIL_INDEX ON ORDER_DETAIL(ORDER_ID, PRODUCT_ID); CREATE INDEX SQL_ORDER_PRODUCT_ID_INDEX ON ORDER_DETAIL(PRODUCT_ID); CREATE INDEX SQL_ORDER_DETAIL_ID_INDEX ON ORDER_DETAIL(ORDER_ID); COMMIT;

重点差异总结一下:

功能Derby 写法MySQL 调整
自增主键GENERATED ALWAYS AS IDENTITYAUTO_INCREMENT
大对象字段BLOB(1073741823)改用LONGBLOB更合理
字符集默认Latin1显式声明utf8mb4
时间戳默认值TIMESTAMP DEFAULT CURRENT_TIMESTAMP支持,无需改动

特别提醒:IMG_SRC用来存图片二进制流,小图可能没问题,但如果上传大文件,记得检查MySQL的max_allowed_packet设置,否则会报“packet too large”错误。


data.sql:数据插入基本兼容

初始化数据这块改动不大,主要是注意字符串引号和日期格式。例如:

INSERT INTO CATEGORY (NAME, TAGS) VALUES ('Plants', 'Seeds, trees, flowers ...'); INSERT INTO PERSON (FIRSTNAME, LASTNAME, EMAIL, ADDRESS, CITY, PASSWORD, DTYPE) VALUES ('Robert', 'Exampler', 'robert@example.com', 'Example street', 'San Francisco', '81dc9bdb52d04dc20036dbd8313ed055', 'Customer');

这类语句在两种数据库中都通用。唯一需要注意的是枚举型字段如DTYPE,要确保值合法且长度不超过定义。


最后一步:验证一切是否正常

做完所有修改后,重新构建并部署项目。打开浏览器访问首页,看看能不能加载商品列表、用户信息等数据。

同时可以进MySQL验证:

USE forest; SHOW TABLES; SELECT COUNT(*) FROM PERSON; SELECT * FROM PRODUCT LIMIT 5;

如果表都建好了,数据也进去了,说明迁移成功!


小结

把Forest项目从Derby换成MySQL,本质上是一次典型的企业级数据库迁移实践。虽然步骤不多,但每一步都有潜在陷阱:

  • 数据源配置要符合JNDI规范;
  • SQL脚本不能照搬,特别是DDL语句;
  • 驱动包必须正确放置;
  • 字符集、外键、事务这些细节决定成败。

完成这次迁移后,你会发现应用更稳定了,也更容易集成监控、备份等运维流程。更重要的是,你对Java EE应用的数据层工作机制有了更深理解——这比单纯跑通一个demo要有价值得多。

至于文中提到的Z-Image-ComfyUI?那是另一个故事了,专用于AI图像生成推理,跟这套传统Web应用架构没啥关系。如果你感兴趣,可以另开一篇聊聊它的部署方案。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 14:07:48

发现并分析一个PHP木马后门

发现并分析一个PHP木马后门 在一次常规的AI模型部署测试中&#xff0c;我们拉取了社区广泛推荐的 GLM-4.6V-Flash-WEB 开源视觉模型镜像。整个流程堪称“丝滑”&#xff1a;一键部署、脚本运行、网页推理&#xff0c;三步完成多模态能力上线。然而就在系统上线前的安全扫描环节…

作者头像 李华
网站建设 2026/4/23 12:47:03

差分隐私优化端到端语音识别技术

更好的端到端语音识别差分隐私技术 教师模型集成私有聚合技术相对于标准差分隐私方法&#xff0c;可将词错误率相对降低超过26%。 现代人工智能模型&#xff0c;如图像和语音识别模型&#xff0c;高度依赖数据。虽然有一些公共数据集可用于训练此类模型&#xff0c;但从实时运行…

作者头像 李华
网站建设 2026/4/22 9:40:42

AGV系统基础知识与应用详解

AGV系统基础知识与应用详解 在现代智能工厂的车间里&#xff0c;你可能会看到这样一幕&#xff1a;没有司机驾驶&#xff0c;一辆辆小巧灵活的运输车沿着既定路线穿梭于产线之间&#xff0c;精准地将物料送到工位旁&#xff0c;完成对接后自动返回。它们彼此避让、有序通行&…

作者头像 李华
网站建设 2026/4/23 9:17:30

数信云X3高性能版服务器

工欲善其事 必先利其器 生信人算力buff已加载&#xff0c;数信云提供多种场景共享服务器。新手友好X1&#xff1a;1197元/年 48线程512G内存&#xff0c;生信入门/小样本分析轻松拿捏&#xff0c;学生党闭眼冲~进阶选手X2&#xff1a;1597元/年 112线程4060GPU&…

作者头像 李华
网站建设 2026/4/23 9:16:59

鸿蒙学习实战之路-Core Vision Kit人脸检测实现指南

鸿蒙学习实战之路-Core Vision Kit人脸检测实现指南 Core Vision Kit&#xff08;基础视觉服务&#xff09;提供了机器视觉相关的基础能力&#xff0c;什么意思呢&#xff1f;通俗点说&#xff0c;就是让你的鸿蒙应用"长一双眼睛"——能看懂图片里的内容是人脸还是文…

作者头像 李华