_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()); // 关键配置