JFinal 读取 H2 数据库长文本乱码问题的终极解决方案
问题背景
在使用 JFinal + H2 开发个人博客Oily Blog系统时,遇到了一个棘手的问题:文章内容在保存后,前端显示不完整,出现乱码或内容被截断的现象。
问题复现
现象
- 数据库中的
CONTENT字段完整存储(长度正确) - 控制台输出长度正确,但内容显示不完整
- 前端页面显示的内容被截断
问题代码
// 直接使用 JFinal 的 Model 读取
Article article = Article.dao.findFirst("SELECT * FROM ARTICLE WHERE SLUG = ?", slug);
String content = article.getStr("CONTENT"); // 这里读取长文本的内容末尾不能正常显示
解决方案
方案一:重写 Model 的 getStr 方法
在 AppConfig 中保存 DruidPlugin 实例:
public class AppConfig extends JFinalConfig {
private static DruidPlugin dp;
@Override
public void configPlugin(Plugins me) {
dp = new DruidPlugin(jdbcUrl, user, password);
// ... 其他配置
}
public static DruidPlugin getDruidPlugin() {
return dp;
}
}
在 Article 模型中重写 getStr 方法:
@Override
public String getStr(String attr) {
if ("CONTENT".equals(attr)) {
try (Connection conn = AppConfig.getDruidPlugin().getDataSource().getConnection();
PreparedStatement pst = conn.prepareStatement("SELECT CONTENT FROM ARTICLE WHERE ID = ?")) {
pst.setInt(1, this.getInt("ID"));
ResultSet rs = pst.executeQuery();
if (rs.next()) {
return rs.getString("CONTENT");
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
return super.getStr(attr);
}
方案二:使用 Db.execute() 读取(推荐)
这是最简洁优雅的解决方案:
public void index() {
String slug = getPara(0);
// 查询基本信息(不包含 CONTENT)
Article article = Article.dao.findFirst(
"SELECT ID, TITLE, SLUG FROM ARTICLE WHERE SLUG = ? AND STATUS = 1", slug
);
if (article == null) {
redirect("/");
return;
}
// 单独读取 CONTENT 字段
String sql = "SELECT CONTENT FROM ARTICLE WHERE ID = " + article.getInt("ID");
String content = (String)Db.execute(conn -> {
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
return rs.next() ? rs.getString("CONTENT") : null;
});
// 处理文章内容
if (content != null && !content.trim().isEmpty()) {
String html = MarkdownUtil.toHtml(content);
article.set("CONTENT_HTML", html);
}
setAttr("article", article);
render("article.html");
}
方案三:封装工具方法
public class ArticleUtil {
public static String getContentById(int id) {
String sql = "SELECT CONTENT FROM ARTICLE WHERE ID = " + id;
return (String)Db.execute(conn -> {
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
return rs.next() ? rs.getString("CONTENT") : null;
});
}
}
方案对比
| 方案 | 优点 | 缺点 | 推荐度 |
|---|---|---|---|
| 重写 getStr | 一次修改,全局生效 | 需要修改 Model | ⭐⭐⭐⭐ |
| Db.execute() | 简洁、灵活、无侵入 | 比getStr复杂 | ⭐⭐⭐⭐⭐ |
| 封装工具类 | 复用性好 | 需要额外维护 | ⭐⭐⭐⭐ |
验证方法
// 验证读取是否完整
String content = (String)Db.execute(conn -> {
PreparedStatement ps = conn.prepareStatement("SELECT CONTENT FROM ARTICLE WHERE ID = 1");
ResultSet rs = ps.executeQuery();
return rs.next() ? rs.getString("CONTENT") : null;
});
System.out.println("长度:" + content.length());
System.out.println("结尾:" + content.substring(Math.max(0, content.length() - 100)));
注意
- 使用JFinal 框架,其他数据库(MySQL、PostgreSQL、SQLite)是否存在此类问题?期待验证。