栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

使用Java根据约定格式生成Oracle存储过程

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

使用Java根据约定格式生成Oracle存储过程

记录:285

场景:在实际开发中,例如,系统升级,数模变化差异大,就涉及数模全量字段转化。根据建表语句逐个摘取字段,可行,但相对比较慢。本例根据表名从数据库读取字段,统一组装插入(INSERT INTO)和查询(SELECt)等基础SQL语句,具体业务SQL在此基础上按需修改。本例基于Oracle存储过程方式实现数模转换,先定义好一个规范的存储过程模板,使用Java代码,根据表名批量生成Oracle存储过程。这样就只需集中开发具体业务SQL即可。本例使用Java根据约定格式生成Oracle存储过程。

版本:Spring Boot 2.6.3

版本:Oracle Database 11g

工具:PL/SQL Developer

一、案例场景

1.存储过程模板

文件名:存储过程模板.sql

-- 注释
PROCEDURE PRO_NAME(INPUT_PARA VARCHAR2,OUTPUT_MSG OUT VARCHAR2) IS
  BEGIN
    -- 声明变量
    DECLARE
      LOG_ID  NUMBER; -- 日志表主键
      EXECUTE_NUM NUMBER := 0; -- 操作表的记录条数
    
    BEGIN
      -- 1.插入日志
      PKG_LOG.PRO_LOG_INSERT('业务描述', '表名',INPUT_PARA,LOG_ID);
	  OUTPUT_MSG :='';
    
      -- 2.业务处理
    
      EXECUTE_NUM := EXECUTE_NUM + SQL%ROWCOUNT; -- 计算记录条数
      COMMIT;
      -- 3.更新成功日志
      PKG_LOG.PRO_LOG_UPDATE(LOG_ID, '1', '', EXECUTE_NUM);
    EXCEPTION
      WHEN OTHERS THEN
        -- 4.更新异常日志
        PKG_LOG.PRO_LOG_UPDATE(LOG_ID, '0', SQLERRM, EXECUTE_NUM);
		OUTPUT_MSG :='异常信息: ' || SQLERRM;
        ROLLBACK;
    END;
END PRO_NAME;

2.表清单

文件名:TableList.txt

表清单中,每行是一个表信息,使用##分割,Java代码以此分割字符串,再加工。

格式:表名(英文名称)##表注释

T_SENSOR_DATA##传感器数据
T_02##表T_02注释
T_03##表T_03注释
T_04##表T_04注释
T_05##表T_05注释
......

3.生成的存储过程文件

本例执行Java代码生成Oracle存储过程定义和Oracle存储过程实现两个文件如下。

存储过程定义文件:20220713_declare.sql

存储过程实现文件:20220713_procedure.sql

比如:T_SENSOR_V1、T_USER等多张表关联后,转换到T_SENSOR_DATA中。本例只取T_SENSOR_DATA字段组装SQL,关联表的各个字段即实际业务要开发的。

3.1 存储过程定义文件

文件名:20220713_declare.sql

-- 传感器数据
PROCEDURE PRO_01_T_SENSOR_DATA(INPUT_PARA VARCHAR2,OUTPUT_MSG OUT VARCHAR2);

3.2 存储过程实现文件

本例中的PKG_LOG.PRO_LOG_INSERT、PKG_LOG.PRO_LOG_UPDATE是PKG_LOG包下的已经封装好的2个统一操作日志的存储过程。

文件名:20220713_procedure.sql

--传感器数据
PROCEDURE PRO_01_T_SENSOR_DATA(INPUT_PARA VARCHAR2,OUTPUT_MSG OUT VARCHAR2) IS
  BEGIN
    -- 声明变量
    DECLARE
      LOG_ID  NUMBER; -- 日志表主键
      EXECUTE_NUM NUMBER := 0; -- 操作表的记录条数
    
    BEGIN
      -- 1.插入日志
      PKG_LOG.PRO_LOG_INSERT('业务描述', '表名',INPUT_PARA,LOG_ID);
	  OUTPUT_MSG :='';
    
      -- 2.业务处理
    INSERT INTO T_SENSOR_DATA
    (ID,
    REGION,
    VALUE1,
    VALUE2)
    SELECT ID,
        REGION,
        VALUE1,
        VALUE2
    FROM T_SENSOR_DATA;
    
      EXECUTE_NUM := EXECUTE_NUM + SQL%ROWCOUNT; -- 计算记录条数
      COMMIT;
      -- 3.更新成功日志
      PKG_LOG.PRO_LOG_UPDATe(LOG_ID, '1', '', EXECUTE_NUM);
    EXCEPTION
      WHEN OTHERS THEN
        -- 4.更新异常日志
        PKG_LOG.PRO_LOG_UPDATE(LOG_ID, '0', SQLERRM, EXECUTE_NUM);
		OUTPUT_MSG :='异常信息: ' || SQLERRM;
        ROLLBACK;
    END;
END PRO_01_T_SENSOR_DATA;

4.业务举例

Java代码在读取到存储过程模板中的“-- 2.业务处理”后,会添加组装的基础SQL.例如,操作的目标表T_SENSOR_DATA,就会到数据库读取T_SENSOR_DATA的所有字段,组成插入和查询的基础SQL。在做业务开发时,就在此基础上修改具体where条件逻辑和字段对应关系。

Java代码生成的基础SQL。

INSERT INTO T_SENSOR_DATA
    (ID,
    REGION,
    VALUE1,
    VALUE2)
    SELECT ID,
        REGION,
        VALUE1,
        VALUE2
    FROM T_SENSOR_DATA;

在基础SQL上开发的业务SQL。

INSERT INTO T_SENSOR_DATA
    (ID,
    REGION,
    VALUE1,
    VALUE2)
    SELECt AA.DATA_ID AS ID,
        BB.REGION AS REGION,
        AA.D1 AS VALUE1,
        AA.D2 AS VALUE2
FROM T_SENSOR AA 
JOIN T_USER BB
ON AA.ID = BB.ID;

5.加载字段SQL

从Oracle数据库中加载指定表名的所有字段SQL。

select aa.COLUMN_ID AS columnId, 
       aa.COLUMN_NAME AS columnName
  from user_tab_columns aa
 where aa.TABLE_NAME = ?
 order by aa.COLUMN_ID asc;

二、代码

1.读文件

从文件中读取数据,一次读取一行,存放在List,函数的参数Boolean isTrim,当为true时,读取一行数据会使用String的trim()函数去掉首位空格,即不保持原有格式。当为false时,读取的字符串不做操作,保持原有格式。

public static ArrayList readFromFile(String fileName, Boolean isTrim) {
  ArrayList listAll = new ArrayList<>();
  try {
    BufferedReader br = new BufferedReader(new FileReader(fileName));
    String oneLine = null;
    while ((oneLine = br.readLine()) != null) {
      if (isTrim) {
          listAll.add(oneLine.trim());
      } else {
          listAll.add(oneLine);
      }
    }
    br.close();
  } catch (Exception e) {
    System.out.println("读异常:");
    e.printStackTrace();
  }
  return listAll;
}

2.写文件

逐行写文件,以追加方式写入,不覆盖已经写入的内容。

public static void writeFile(String fileName, String content) {
  try {
    BufferedWriter bw = new BufferedWriter(new FileWriter(fileName, true));
    bw.write(content);
    bw.close();
  } catch (IOException e) {
    System.out.println("写异常:");
    e.printStackTrace();
  }
}

3.操作数据源

使用org.springframework.jdbc.core.JdbcTemplate操作数据源。

public static JdbcTemplate getJdbcTemplate() {
    DruidDataSource dataSource = new DruidDataSource();
    
    String username = "***数据库用户名**";
    String password = "***数据库口令**";
    String jdbcUrl = "***数据库连接URL**";
    String driverName = "oracle.jdbc.OracleDriver";
    
    dataSource.setPassword(password);
    dataSource.setUrl(jdbcUrl);
    dataSource.setUsername(username);
    dataSource.setDriverClassName(driverName);
    
    JdbcTemplate jdbcTemplate = new JdbcTemplate();
    
    jdbcTemplate.setDataSource(dataSource);
    return jdbcTemplate;
}

4.处理表清单信息

处理从文件名:TableList.txt读取的表清单信息。最终加工成List 。

public static List getTableInfo(List fromTxtStr) {
  ArrayList result = new ArrayList<>();
  for (String oneLine : fromTxtStr) {
      ArrayList one = getOneLine(oneLine, "##");
      TableBO tableBO = new TableBO();
      tableBO.setTableName(one.get(0));
      tableBO.setComment(one.get(1));
      result.add(tableBO);
  }
  return result;
}

public static ArrayList getOneLine(String content, String split) {
  String[] strArr = content.split(split);
  ArrayList oneLine = new ArrayList(strArr.length);
  Collections.addAll(oneLine, strArr);
  return oneLine;
}

5.生成存储过程定义文件

按照规则生成存储过程定义文件。

public static void generateDeclare(String file, String procedure,
                                 String comment) {
  String declare = "-- " + comment + "n" + "PROCEDURE "
     + procedure + "(INPUT_PARA VARCHAR2,OUTPUT_MSG OUT VARCHAR2)" + ";"
     + "n";
  writeFile(file, declare + "n");
}

6.生成存储过程实现文件

按照规则生成生成存储过程实现文件。

6.1 存储过程实现文件主流程

主要包括:从数据库获取字段信息、读取存储过程模板文件、生成存储过程。

public static void generateProcedure(String file, String procedure,
                      String table, String comment,
                      JdbcTemplate jt,
                      String templateFile) {
  String sql = "select aa.COLUMN_ID AS columnId, aa.COLUMN_NAME AS columnNamen" +
          " from user_tab_columns aan" +
          " where aa.TABLE_NAME = ?  " +
          " order by aa.COLUMN_ID asc";
  // 根据表名从数据库读取表字段信息
  List columnList = jt.query(sql,
          new BeanPropertyRowMapper<>(TableColumnPO.class), table);
  // 从模板文件中读取模板
  List template = readFromFile(templateFile, false);
  // 生成SQL
  String oneSQL = comeTrue(procedure, table, 
                   columnList, comment, template);
  writeFile(file, oneSQL + "n");
}
//组装存储过程实现
public static String comeTrue(String procedure, String table, 
                    List columnList,
                    String comment, List template) {
  StringBuffer sb = new StringBuffer();
  //遍历模板生成数据
  for (String oneLine : template) {
      oneLine = oneLine+"n";
      if (oneLine.contains("业务处理")) {
          sb.append(oneLine);
          sb.append(getBizSql(table, columnList));
      } else if (oneLine.contains("PRO_NAME")) {
          oneLine = oneLine.replace("PRO_NAME", procedure);
          sb.append(oneLine);
      } else if (oneLine.contains("注释")) {
          String partComment = "--" + comment + "n";
          sb.append(partComment);
      } else {
          sb.append(oneLine);
      }
  }
  return sb.toString();
}

6.2 存储过程实现文件生成业务SQL

// 组装基础业务SQL
public static String getBizSql(String table, List columnList) {
  String part01 = "  INSERT INTO " + table + "n";
  String sbColumns1 = getColumns(columnList, "    ");
  String sbColumns2 = getColumns(columnList, "        ");
  String part02 = "    (" + sbColumns1 + ")" + "n";
  String part03 = "    SELECT " + sbColumns2.toString().toString() + "n";
  String part04 = "    FROM " + table + ";" + "n";
  String bizSQL = part01 + part02 + part03 + part04;
  return bizSQL;
}
// 获取一张表的所有字段以逗号分割,根据space来简单排版
public static String getColumns(List columnList, String space) {
  StringBuffer sbColumns = new StringBuffer();
  int size = columnList.size();
  int index = 1;
  for (TableColumnPO po : columnList) {
    String column = "";
    if (index == size) {
        column = po.getColumnName();
    } else {
        column = po.getColumnName() + "," + "n";
    }
    if (index == 1) {
        sbColumns.append("" + column);
    } else {
        sbColumns.append(space + column);
    }
    index++;
  }
  return sbColumns.toString();
}

7.生成存储过程主入口

生成存储过程主入口,核心逻辑。

public static void generate(String baseDir, String tableFile,
                          String templateFile) {
  List tableList = readFromFile(tableFile, true);
  List tableInfoList = getTableInfo(tableList);
  int index = 1;
  String indexN = "";
  JdbcTemplate jt = getJdbcTemplate();
  // 遍历tableInfoList,即遍历取出每张表
  for (TableBO tableBO : tableInfoList) {
   // 表名
   String table = tableBO.getTableName();
   // 表注释
   String comment = tableBO.getComment();
   if (index < 10) {
       indexN = "0" + index;
   } else {
       indexN = "" + index;
   }
   // 存储过程定义文件路径
   String declareFile = baseDir + "20220713_declare" + ".sql";
   // 存储过程实现文件路径
   String procedureFile = baseDir + "20220713_procedure" + ".sql";
   // 存储过程名称
   String procedure = "PRO_" + indexN + "_" + table;
   // 生成存储过程定义文件
   generateDeclare(declareFile, procedure, comment);
   // 生成存储过程实现文件
   generateProcedure(procedureFile, procedure, table, comment, jt,templateFile);
   index++;
  }
}

8.main函数入口

main函数入口。

public static void main(String[] args) {
  System.out.println("开始...");
  String baseDir = "D:\example\";
  String tableFile = baseDir + "TableList" + ".txt";
  String templateFile = baseDir + "存储过程模板" + ".sql";
  generate(baseDir, tableFile, templateFile);
  System.out.println("结束...");
}

9.辅助实体类

TableBO和TableColumnPO两个辅助JavaBean。

//表信息
public class TableBO {
  private String tableName;
  private String comment;
}
//表字段信息
@Data
@NoArgsConstructor
public class TableColumnPO {
  private String columnId;
  private String columnName;
}

以上,感谢。

2022年7月13日

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1013527.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号