当前位置: 首页 > 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框架的基础组件。

相关文章:

  • BitsAndBytesConfig参数描述
  • RESTful风格
  • C++网络编程入门学习(四)-- GDB 调试 学习 笔记
  • 面试题 - 微服务相关的经典问题(33道)
  • 解决echarts图表legend文本太长;echarts图表的图例legend省略号显示
  • 第十节第四部分:常见API:秒杀案例、Calendar
  • SkyWalking 报错:sw_profile_task 索引缺失问题分析与解决
  • Javascript 编程基础(4)函数 | 4.4、bind() 方法
  • 重磅升级!Google Play商店改版上线
  • 13、自动配置【源码分析】-自动包规则原理
  • Postgres数据库配置用户读写权限(read_write)和只读权限(read_only):
  • 第23天-Python Flet 开发指南
  • Quasar 使用 Pinia 进行状态管理
  • 10.18 LangChain ToolMessage实战:多轮交互与状态管理全解析
  • 【PhysUnits】7 类型整数基本结构体(basic.rs)
  • xpath使用_结合python提取页面内容
  • 《AI工程技术栈》:三层结构解析,AI工程如何区别于ML工程与全栈工程
  • 《捕捉桌面存成jpg案例代码》调试中的注意事项
  • 网络 :网络基础【网络框架认识】
  • kml数据生成全球科学研究所地理标记
  • 舟山网站制作/推广普通话手抄报简单
  • 网站横幅怎做/可以免费发布广告的平台有哪些
  • 网站开发教程下载/推广公司
  • 中小企业网站制作广州网络服务公司找赛合/市场推广是做什么的
  • 网站建设市区/做关键词排名好的公司