_Generator.java: /** * 本项目采用《JFinal 俱乐部授权协议》,保护知识产权,就是在保护我们自己身处的行业。 * * Copyright (c) 2011-2021, jfinal.com */ package com.jfinal.admin.common; import com.jfinal.admin.common.pgsql.JsonbMetaBuilder; import com.jfinal.admin.common.pgsql.JsonbPostgreSqlDialect; import com.jfinal.plugin.activerecord.dialect.MysqlDialect; import com.jfinal.plugin.activerecord.dialect.PostgreSqlDialect; import com.jfinal.plugin.activerecord.generator.ColumnMeta; import com.jfinal.plugin.activerecord.generator.Generator; import com.jfinal.plugin.activerecord.generator.MetaBuilder; import com.jfinal.plugin.druid.DruidPlugin; import javax.sql.DataSource; import java.sql.ResultSet; import java.sql.SQLException; /** * Model、BaseModel、_MappingKit 生成器 * * 添加新表,或者修改表结构时重新运行该生成器即可 */ public class _Generator { /** * 在此统一添加不参与生成的 table。不参与生成的 table 主要有: * 1:用于建立表之间关系的关联表,如 account_article * 2:部分功能使用 Db + Record 模式实现,所涉及到的 table 也不参与生成 */ private static String[] excludedTable = { }; /** * 重用 JFinalClubConfig 中的数据源配置,避免冗余配置 */ public static DataSource getDataSource() { DruidPlugin druidPlugin = AppConfig.getDruidPlugin(); druidPlugin.start(); return druidPlugin.getDataSource(); } public static void main(String[] args) { // model 所使用的包名 (MappingKit 默认使用的包名) String modelPackageName = "com.jfinal.admin.common.model"; // base model 所使用的包名 String baseModelPackageName = modelPackageName + ".base"; // base model 文件保存路径 String baseModelOutputDir = System.getProperty("user.dir") + "/src/main/java/" + baseModelPackageName.replace('.', '/'); // model 文件保存路径 (MappingKit 与 DataDictionary 文件默认保存路径) String modelOutputDir = baseModelOutputDir + "/.."; System.out.println("输出路径:"+ baseModelOutputDir); // 创建自定义方言实例 JsonbPostgreSqlDialect dialect = new JsonbPostgreSqlDialect(); // 创建自定义 MetaBuilder JsonbMetaBuilder metaBuilder = new JsonbMetaBuilder(getDataSource()); metaBuilder.setDialect(dialect); // 设置方言 metaBuilder.setGenerateRemarks(true); // 创建生成器 Generator gen = new Generator(getDataSource(), baseModelPackageName, baseModelOutputDir, modelPackageName, modelOutputDir); // 设置自定义 MetaBuilder gen.setMetaBuilder(metaBuilder); // 设置方言 gen.setDialect(dialect); // 设置自定义的 MetaBuilder(关键修改) // 设置数据库方言 //gen.setDialect(new JsonbPostgreSqlDialect()); //gen.setDialect(new PostgreSqlDialect()); // 设置是否生成字段备注 gen.setGenerateRemarks(true); // 添加不需要生成的表名 for (String table : excludedTable) { gen.addExcludedTable(table.trim()); } // 设置是否在 Model 中生成 dao 对象 gen.setGenerateDaoInModel(false); // 设置是否生成字典文件 gen.setGenerateDataDictionary(false); // 设置需要被移除的表名前缀用于生成modelName。例如表名 "osc_user",移除前缀 "osc_"后生成的model名为 "User"而非 OscUser // gen.setRemovedTableNamePrefixes("t_"); // 生成 gen.generate(); } }
JsonbMetaBuilder.java:
package com.jfinal.admin.common.pgsql; import com.jfinal.plugin.activerecord.dialect.Dialect; import com.jfinal.plugin.activerecord.generator.MetaBuilder; import com.jfinal.plugin.activerecord.generator.ColumnMeta; import com.jfinal.plugin.activerecord.generator.TableMeta; import javax.sql.DataSource; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.util.List; public class JsonbMetaBuilder extends MetaBuilder { private Dialect dialect; public JsonbMetaBuilder(DataSource dataSource) { super(dataSource); } // 添加设置方言的方法 public void setDialect(Dialect dialect) { this.dialect = dialect; super.setDialect(dialect); // 同时设置父类的方言 } @Override protected void buildColumnMetas(TableMeta tableMeta) throws SQLException { // 先调用父类方法构建基础列元数据 super.buildColumnMetas(tableMeta); // 特殊处理 JSONB 列 processJsonbColumns(tableMeta); } private void processJsonbColumns(TableMeta tableMeta) throws SQLException { String sql = dialect.forTableBuilderDoBuild(tableMeta.name); try (Statement stm = conn.createStatement(); ResultSet rs = stm.executeQuery(sql)) { ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); for (int i = 1; i <= columnCount; i++) { String columnName = rsmd.getColumnName(i); String typeName = getColumnTypeNameSafe(rsmd, i); // 检测是否为 JSONB 列 if (isJsonbColumn(typeName)) { // 找到对应的列元数据并更新 for (ColumnMeta cm : tableMeta.columnMetas) { if (columnName.equals(cm.name)) { updateColumnMetaForJsonb(cm, rsmd, i); break; } } } } } } private String getColumnTypeNameSafe(ResultSetMetaData rsmd, int columnIndex) { try { return rsmd.getColumnTypeName(columnIndex); } catch (SQLException e) { return "UNKNOWN"; } } private boolean isJsonbColumn(String typeName) { if (typeName == null) return false; String lowerType = typeName.toLowerCase(); return "jsonb".equals(lowerType) || "json".equals(lowerType); } private void updateColumnMetaForJsonb(ColumnMeta cm, ResultSetMetaData rsmd, int columnIndex) throws SQLException { // 设置 Java 类型为 Map cm.javaType = "java.util.Map<String, Object>"; // 添加类型备注 if (generateRemarks) { String typeName = rsmd.getColumnTypeName(columnIndex); if (cm.remarks == null || cm.remarks.isEmpty()) { cm.remarks = "JSONB 数据 (自动转换为 Map)"; } else { cm.remarks += " [JSONB]"; } } } }
JsonbPostgreSqlDialect.java :
package com.jfinal.admin.common.pgsql; import com.jfinal.kit.JsonKit; import com.jfinal.plugin.activerecord.Record; import com.jfinal.plugin.activerecord.dialect.PostgreSqlDialect; import com.jfinal.plugin.activerecord.*; import org.postgresql.util.PGobject; import com.alibaba.fastjson2.JSON; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.List; import java.util.Map; import java.util.Set; public class JsonbPostgreSqlDialect extends PostgreSqlDialect { @Override public void fillStatement(PreparedStatement pst, List<Object> paras) throws SQLException { processJsonbParameters(paras); super.fillStatement(pst, paras); } @Override public void fillStatement(PreparedStatement pst, Object... paras) throws SQLException { processJsonbParameters(paras); super.fillStatement(pst, paras); } private void processJsonbParameters(List<Object> paras) { for (int i = 0; i < paras.size(); i++) { Object value = paras.get(i); if (value instanceof Map) { paras.set(i, createJsonbObject((Map<?, ?>) value)); } } } private void processJsonbParameters(Object[] paras) { for (int i = 0; i < paras.length; i++) { Object value = paras[i]; if (value instanceof Map) { paras[i] = createJsonbObject((Map<?, ?>) value); } } } private PGobject createJsonbObject(Map<?, ?> map) { try { PGobject jsonObject = new PGobject(); jsonObject.setType("jsonb"); jsonObject.setValue(JsonKit.toJson(map)); return jsonObject; } catch (SQLException e) { throw new RuntimeException(e); } } @Override public void forDbSave(String tableName, String[] pKeys, Record record, StringBuilder sql, List<Object> paras) { // 遍历字段,将 Map 转为 PGobject record.getColumns().forEach((key, value) -> { if (value instanceof Map) { PGobject pgObj = new PGobject(); pgObj.setType("jsonb"); try { pgObj.setValue(JsonKit.toJson(value)); } catch (SQLException e) { throw new RuntimeException(e); } record.set(key, pgObj); } }); super.forDbSave(tableName, pKeys, record, sql, paras); } public JsonbPostgreSqlDialect() { this.recordBuilder = new JsonbRecordBuilder(); // 注入自定义RecordBuilder } @Override public void forDbUpdate(String tableName, String[] pKeys, Object[] ids, Record record, StringBuilder sql, List<Object> paras) { // 序列化JSONB字段 try { processJsonbFields(record); } catch (SQLException e) { throw new RuntimeException(e); } super.forDbUpdate(tableName, pKeys, ids, record, sql, paras); } // JsonbPostgreSqlDialect.java private void processJsonbFields(Record record) throws SQLException { Map<String, Object> columns = record.getColumns(); Set<String> modifyFlag = CPI.getModifyFlag(record); for (Map.Entry<String, Object> entry : columns.entrySet()) { Object value = entry.getValue(); PGobject pgObj = null; if (value instanceof Map) { pgObj = new PGobject(); pgObj.setType("jsonb"); pgObj.setValue(JSON.toJSONString(value)); } // 新增:处理JSON格式字符串 else if (value instanceof String && isJsonString((String) value)) { pgObj = new PGobject(); pgObj.setType("jsonb"); pgObj.setValue((String) value); } if (pgObj != null) { columns.put(entry.getKey(), pgObj); modifyFlag.add(entry.getKey()); } } } // 简单检查字符串是否是JSON格式 private boolean isJsonString(String str) { if (str == null) return false; String trimmed = str.trim(); return trimmed.startsWith("{") && trimmed.endsWith("}"); } }
JsonbRecordBuilder.java:
package com.jfinal.admin.common.pgsql; import com.jfinal.plugin.activerecord.*; import com.jfinal.plugin.activerecord.Record; import org.postgresql.util.PGobject; import com.alibaba.fastjson2.JSON; import java.sql.*; import java.util.*; import java.util.function.Function; public class JsonbRecordBuilder extends RecordBuilder { @Override public List<Record> build(Config config, ResultSet rs, Function<Record, Boolean> func) throws SQLException { List<Record> result = new ArrayList<>(); ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); String[] labelNames = new String[columnCount + 1]; int[] types = new int[columnCount + 1]; buildLabelNamesAndTypes(rsmd, labelNames, types); while (rs.next()) { Record record = new Record(); // ✅ 使用公共API设置配置 record.setContainerFactoryByConfigName(config.getName()); Map<String, Object> columns = record.getColumns(); for (int i = 1; i <= columnCount; i++) { Object value = getColumnValue(rs, i, types[i]); // JSONB 类型识别与转换 if (value instanceof PGobject) { PGobject pgObj = (PGobject) value; if ("jsonb".equals(pgObj.getType())) { value = JSON.parseObject(pgObj.getValue(), Map.class); } } columns.put(labelNames[i], value); } if (func == null) { result.add(record); } else if (!func.apply(record)) { break; } } return result; } // 安全获取列值(处理大对象类型) private Object getColumnValue(ResultSet rs, int index, int type) throws SQLException { if (type < Types.BLOB) { return rs.getObject(index); } switch (type) { case Types.CLOB: return ModelBuilder.me.handleClob(rs.getClob(index)); case Types.NCLOB: return ModelBuilder.me.handleClob(rs.getNClob(index)); case Types.BLOB: return ModelBuilder.me.handleBlob(rs.getBlob(index)); default: return rs.getObject(index); } } // 保留原生元数据处理逻辑 @Override public void buildLabelNamesAndTypes( ResultSetMetaData rsmd, String[] labelNames, int[] types) throws SQLException { for (int i = 1; i < labelNames.length; i++) { labelNames[i] = rsmd.getColumnLabel(i); types[i] = rsmd.getColumnType(i); } } }
AppConfig.java
// 配置 ActiveRecordPlugin ActiveRecordPlugin arp = new ActiveRecordPlugin(druidPlugin); arp.setDialect(new JsonbPostgreSqlDialect()); // 关键配置