当前位置: 首页 > news >正文

递归+反射+注解(动态拼接建表语句)

编写程序扫描一个包下所有的类,凡是被 @Table 注解标注的类都要生成一条建表语句,表名在 @Table 注解中指定。被@Table 标注的类中的属性被 @Column 注解标注,在 @Column注解中描述字段的名称和字段的数据类型。

// 包路径声明:Vip类位于a.b子包下,用于测试反射和注解处理
package com.xie.javase.reflect.test.a.b;// 导入自定义注解
import com.xie.javase.reflect.test.anotation.Column;
import com.xie.javase.reflect.test.anotation.Table;@Table("t_vip") // 类级别的Table注解,指定对应数据库表名
public class Vip {@Column(name="id")    // 字段注解,指定数据库列名(使用默认varchar类型)private String id;@Column(name="name")  // 字段注解,指定数据库列名private String name;@Column(name="grade")  // 字段注解,指定数据库列名private String grade;
}// Customer类位于c子包下,用于对比测试(没有使用任何注解)
package com.xie.javase.reflect.test.c;public class Customer {private String cid;    // 没有注解的字段private String name;private int age;private String address;
}// 主测试类位于根包下
package com.xie.javase.reflect.test;// 导入各种依赖
import com.xie.javase.reflect.test.anotation.Column;
import com.xie.javase.reflect.test.anotation.Table;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;public class test {// 类路径根目录(编译后的class文件存放位置)private static String classpathRoot;// 用于拼接SQL语句的StringBuilderprivate static StringBuilder sb = new StringBuilder();public static void main(String[] args) {// 获取当前线程的类加载器,并获取类路径根目录classpathRoot = Thread.currentThread().getContextClassLoader().getResource(".").getPath();// 创建文件对象指向类路径根目录File file = new File(classpathRoot);// 开始递归处理目录/文件generateCreateStatement(file);// 输出最终生成的SQL语句System.out.println(sb);}/*** 递归生成建表SQL语句的核心方法* @param file 当前处理的文件/目录对象*/private static void generateCreateStatement(File file) {// 如果是文件(非目录)if (file.isFile()) {String classFileAbsolutePath = file.getAbsolutePath();// 检查是否是.class文件if (classFileAbsolutePath.endsWith(".class")) {/* 转换文件路径为完整类名:1. 截取从类路径根目录开始的部分(注意-1处理Windows路径问题)2. 移除.class后缀3. 将文件分隔符转换为包分隔符 */String className = classFileAbsolutePath.substring(classpathRoot.length() - 1, classFileAbsolutePath.length() - ".class".length()).replace("\\", ".");try {// 通过反射加载类Class<?> clazz = Class.forName(className);// 检查类是否包含Table注解if (clazz.isAnnotationPresent(Table.class)) {// 获取Table注解实例Table tableAnnotation = clazz.getAnnotation(Table.class);// 获取注解中定义的表名String tableName = tableAnnotation.value();// 开始拼接建表语句sb.append("create table ");sb.append(tableName);sb.append(" (");// 获取类的所有声明字段(包括私有字段)Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {// 检查字段是否包含Column注解if (field.isAnnotationPresent(Column.class)) {Column columnAnnotation = field.getAnnotation(Column.class);// 获取列名和类型(默认varchar)String columnName = columnAnnotation.name();String columnType = columnAnnotation.type();// 拼接列定义sb.append(columnName).append(" ").append(columnType).append(",");}}// 移除最后一个逗号并闭合语句sb.deleteCharAt(sb.length()-1);sb.append(");\n");}} catch (ClassNotFoundException e) {e.printStackTrace();}}return; // 文件处理完成返回}// 如果是目录,递归处理子文件File[] files = file.listFiles();for (File f : files) {generateCreateStatement(f);}}
}// Table注解定义(包路径:com.xie.javase.reflect.test.anotation)
package com.xie.javase.reflect.test.anotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.TYPE) // 限定注解只能用于类/接口
@Retention(RetentionPolicy.RUNTIME) // 注解保留到运行时
public @interface Table {String value(); // 必填属性,用于指定表名
}// Column注解定义
package com.xie.javase.reflect.test.anotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD) // 限定注解只能用于字段
public @interface Column {String name();         // 必填属性:列名String type() default "varchar"; // 可选属性,默认varchar类型
}// User类位于a子包下,用于测试不同类型字段
package com.xie.javase.reflect.test.a;import com.xie.javase.reflect.test.anotation.Column;
import com.xie.javase.reflect.test.anotation.Table;@Table("t_user") // 指定对应数据库表名
public class User {@Column(name="uid")  // 指定列名(使用默认varchar类型)private String userid;@Column(name="uname")private String username;@Column(name="pwd")private String password;@Column(name="age", type="int") // 显式指定int类型private int age;private String email; // 没有注解的字段不会被处理
}

关键点说明:

  1. 代码结构:
  • 使用自定义注解@Table和@Column标记需要处理的类和字段
  • 主程序通过递归扫描class文件+反射机制解析注解
  • 自动生成建表SQL语句
  1. 路径处理细节:
  • 使用classpathRoot.length() - 1处理Windows系统路径问题
  • 路径转换时使用replace("\\", ".")确保包名正确性
  1. 反射处理:
  • 使用Class.forName()动态加载类
  • 通过isAnnotationPresent判断注解存在性
  • getAnnotation获取注解实例读取配置值
  1. SQL生成:
  • 使用StringBuilder高效拼接字符串
  • 处理最后一个逗号的删除逻辑
  • 支持字段类型指定(默认varchar)
  1. 扩展性:
  • 通过添加新注解属性可以轻松扩展功能(如字段长度约束)
  • 递归文件处理机制自动扫描整个类路径

这个实现展示了注解处理器+反射机制的典型应用,可以自动将Java实体类转换为数据库建表语句,非常适合作为ORM框架的基础组件。

http://www.xdnf.cn/news/570655.html

相关文章:

  • 机动车授权签字人有哪些权利和义务?
  • 【Element UI排序】JavaScript 的表格排序sortable=“custom“和 @sort-change
  • 欢乐熊大话蓝牙知识7:如何用蓝牙芯片实现一个 BLE 传感器节点?
  • SAR ADC 是选择先置位再比较,还是先比较再置位
  • 【聚合MQ管理 第一章】一个项目管理多种MQ 之 ActiveMq
  • Mac安装redis
  • epoll_wait未触发的小Bug
  • adb抓包
  • 元宇宙数字人设计大赛:往届获奖作品赏析
  • 公司OA系统中金格iWebOffice2015智能文档中间件不能用了怎么办?
  • 深入解析C++静态成员变量与函数
  • ABC 354
  • Linux上运行程序加载动态库失败
  • Redis语法大全
  • 【Flutter】创建BMI计算器应用并添加依赖和打包
  • 【HTML-5】HTML 实体:完整指南与最佳实践
  • DSP定时器的计算
  • Spring Boot集成Spring AI与Milvus实现智能问答系统
  • dali本地安装和使用
  • WSD3043 MOSFET 在吸黑头仪中的应用
  • 小数第n位--快速幂+数学
  • 软件设计师“数据流图”真题考点分析——求三连
  • System.in 的本质:输入流的方向​
  • 高效能、高可靠性——SILM94112/08-AQ可编程半桥电机驱动器
  • WordPress_Madara 本地文件包含漏洞复现(CVE-2025-4524)
  • 深入浅出:线程安全问题的原因与解决方案
  • 5月21日直播安排
  • Taro 安全区域
  • React-改变当前页class默认的样式
  • PHP 扇形的面积(Area of a Circular Sector)