简单的自定义java类反向生成sql表的注解及功能类

用于简单的指定表名,类名,外键的类反向生成mysql表的注解功能

java包下的com.xx文件夹才能用,不过项目一般都是com文件夹开头,问题不大

用于注解属性,指定属性名或者外键,也可以不写

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Field {
    public String value();//可以指定属性名 //不写就自己原名
    public String foreignKey() default "";//外键  格式是  表明-键
}

指定主键

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PrimaryKey {
    public String value() default "";//可以指定属性名
}

 注解在类上,标明要以哪个类为底

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 {
    public String value() default "";   //可以指定表名,没有就默认为类名
}

  1. 存储带有 @Table 注解的类

    • 使用 newC 列表存储找到的带有 @Table 注解的类。
  2. createTable 方法

    • 设置包名和包路径。
    • 调用 findPackageClasses 方法查找指定包路径下的所有类。
    • 遍历找到的类,如果类带有 @Table 注解,则生成对应的 SQL 创建表语句。
  3. findPackageClasses 方法

    • 递归查找指定包路径下的所有文件和目录。
    • 对于每个类文件,使用类加载器加载类,并检查是否带有 @Table 注解。
    • 将带有 @Table 注解的类添加到 newC 列表中。

生成 SQL 语句的逻辑

  • 对于每个带有 @Table 注解的类,获取其所有字段。
  • 检查字段是否带有 @Field 注解,如果没有,则使用字段名作为列名。
  • 根据字段类型(int, double, varchar 等)拼接 SQL 语句。
  • 如果字段带有 @PrimaryKey 注解,则将其设置为主键。
  • 如果字段带有外键,则生成外键约束。

执行 SQL 语句

  • 最后调用 db.doUpdate(sql) 方法执行生成的 SQL 创建表语句。

 NewTable

import com.newtable.annotation.PrimaryKey;
import com.newtable.annotation.Table;
import org.apache.log4j.Logger;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

public class NewTable {
    private static Logger log = Logger.getLogger(NewTable.class); // 日志记录器

    private static List<Class> newC = new ArrayList<>(); // 存储找到的带有 @Table 注解的类
    private static DBHelper db = new DBHelper(); // 数据库操作类实例

    public static void createTable() throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException {
        String packageName = "com";
        String packagePath = System.getProperty("user.dir") + "\\src\\main\\java\\com";
        packagePath = packagePath.replaceAll("\\\\", "/");


        //查找此包下的文件
        findPackageClasses(packagePath, packageName);

        //目前我的规则是必须有@Table,才能创建表
        //@Field是用来指定名字的,没有就用默认名
        //@PrimaryKey用来指定主键
        String sql = "";

        for (Class<?> c : newC) {
            // 已经找到所有带@Table的类
            // 现在开始遍历每个类的每个属性并做好拼接
            Object o = c.newInstance();

            // 表名
            String tableName = ((Table) c.getAnnotation(Table.class)).value();
            if (tableName == null || "".equals(tableName)) {
                tableName = c.getSimpleName();
            }

            Field[] fields = c.getDeclaredFields();
            StringBuilder tableSql = new StringBuilder("CREATE TABLE " + tableName + " (");

            StringBuilder foreignKeys = new StringBuilder();

            for (Field field : fields) {
                // 判断是否有注解,没有就默认值
                Class<?> type = field.getType();

                // 属性名
                String fieldName = "";
                if (field.getAnnotation(com.newtable.annotation.Field.class) == null || "".equals(field.getAnnotation(com.newtable.annotation.Field.class).value())) {
                    // 说明没有指定值
                    fieldName = field.getName();
                } else {
                    fieldName = field.getAnnotation(com.newtable.annotation.Field.class).value();
                }

                String foreignTable = ""; // 对应的表
                String foreign = ""; // 外键字段名
                // 现在是外键
                if (field.getAnnotation(com.newtable.annotation.Field.class) != null && !"".equals(field.getAnnotation(com.newtable.annotation.Field.class).foreignKey())) {
                    // 说明有外键
                    String temp = field.getAnnotation(com.newtable.annotation.Field.class).foreignKey();
                    String[] split = temp.split("-");
                    foreignTable = split[0];
                    foreign = split[1];
                }

                if ("int".equals(type.getName()) || "java.lang.Integer".equals(type.getName()) || "byte".equals(type.getName()) || "java.lang.Byte".equals(type.getName()) || "short".equals(type.getName()) || "java.lang.Short".equals(type.getName())) {
                    if (field.getAnnotation(PrimaryKey.class) != null) {
                        tableSql.append(fieldName).append(" INT PRIMARY KEY AUTO_INCREMENT,");
                    } else {
                        tableSql.append(fieldName).append(" INT,");
                        if (!foreign.equals("")) {
                            foreignKeys.append("FOREIGN KEY (").append(fieldName).append(") REFERENCES ").append(foreignTable).append("(").append(foreign).append("),");
                        }
                    }
                } else if ("double".equals(type.getName()) || "java.lang.Double".equals(type.getName()) || "float".equals(type.getName()) || "java.lang.Float".equals(type.getName())) {
                    if (field.getAnnotation(PrimaryKey.class) != null) {
                        tableSql.append(fieldName).append(" DOUBLE PRIMARY KEY AUTO_INCREMENT,");
                    } else {
                        tableSql.append(fieldName).append(" DOUBLE,");
                        if (!foreign.equals("")) {
                            foreignKeys.append("FOREIGN KEY (").append(fieldName).append(") REFERENCES ").append(foreignTable).append("(").append(foreign).append("),");
                        }
                    }
                } else {
                    if (field.getAnnotation(PrimaryKey.class) != null) {
                        tableSql.append(fieldName).append(" VARCHAR(255) PRIMARY KEY AUTO_INCREMENT,");
                    } else {
                        tableSql.append(fieldName).append(" VARCHAR(255),");
                        if (!foreign.equals("")) {
                            foreignKeys.append("FOREIGN KEY (").append(fieldName).append(") REFERENCES ").append(foreignTable).append("(").append(foreign).append("),");
                        }
                    }
                }
            }

            // 移除最后一个逗号
            tableSql.setLength(tableSql.length() - 1);

            // 添加外键约束
            if (foreignKeys.length() > 0) {
                tableSql.append(", ").append(foreignKeys.toString().substring(0, foreignKeys.length() - 1));
            }

            tableSql.append(")");

            sql = tableSql.toString();
            System.out.println(sql);
            db.doUpdate(sql);
        }


    }


    private static void findPackageClasses(String packagePath, String packageName) throws UnsupportedEncodingException {
        if (packagePath.startsWith("/")) {
            packagePath = packagePath.substring(1); // 去掉路径开头的斜杠
        }
        packagePath = URLDecoder.decode(packagePath, "utf-8"); // 防止路径中文,统一转utf-8
        // 获取路径下所有的文件
        File file = new File(packagePath);
        File[] classFiles = file.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {//过滤文件,只保留文件夹和java类
                return pathname.isDirectory() || pathname.getName().endsWith(".java"); // 过滤目录和 .java 文件
            }
        });

        if (classFiles != null && classFiles.length > 0) {
            for (File classFile : classFiles) {
                if (classFile.isDirectory()) {
                    findPackageClasses(classFile.getAbsolutePath(), packageName + "." + classFile.getName()); // 递归查找目录
                } else {
                    if (!classFile.getName().endsWith(".java")) {
                        continue; // 跳过非 .java 文件
                    }
                    // 使用类加载器加载 class 文件
                    URLClassLoader uc = new URLClassLoader(new URL[]{});
                    try {
                        //loadClass只能用java下面的包.xx来找想要的java类
                        Class cls = uc.loadClass(packageName + "." + classFile.getName().replace(".java", ""));
                        if (cls.getAnnotation(Table.class) != null) {
                            log.info(cls); // 记录找到的类
                            newC.add(cls); // 添加到列表
                        }
                    } catch (Exception e) {
                        e.printStackTrace(); // 处理异常
                    }
                }
            }
        }
    }
}

 以下是基于jdbc的简单工具类,封装了统一执行mysql语句的代码,避免重复写

DBHelper

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

public class DBHelper {

    // 如何来获取一个Connection
    public Connection getConnection() throws SQLException, ClassNotFoundException {
        DbProperties p = DbProperties.getInstance();
//        Class.forName("oracle.jdbc.driver.OracleDriver");
        Class.forName("com.mysql.cj.jdbc.Driver");
//        Connection con = DriverManager.getConnection(p.getProperty("oracleurl") ,
//                                                p.getProperty("oracleuname"),
//                                                p.getProperty("oraclepwd"));
        Connection con = DriverManager.getConnection(p.getProperty("mysqlurl"),
                p.getProperty("mysqluname"),
                p.getProperty("mysqlpwd"));
        return con;
    }

    //  设置参数的方法
    private void setParams(PreparedStatement pstmt, Object... params) throws SQLException {
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                pstmt.setObject(i + 1, params[i]);
            }
        }
    }


    /**
     * 基于模板设计模式的查询方法
     *
     * @param rowMapper: 对一行结果集的处理,返回一个对应的对象
     * @param sql
     * @param params
     * @param <T>
     * @return
     * @throws SQLException
     */
    public <T> List<T> select(RowMapper<T> rowMapper, String sql, Object... params) throws SQLException, ClassNotFoundException {
        List<T> list = new ArrayList<>();

        // 查询步骤的模板
        try (
                Connection con = getConnection();
                PreparedStatement pstmt = con.prepareStatement(sql);
        ) {
            this.setParams(pstmt, params);
            ResultSet rs = pstmt.executeQuery();
            int num = 0;
            while (rs.next()) {
                // 结果集的每一行的处理,由 RowMapper 接口的实现决定
                T t = rowMapper.mapRow(rs, num);
                num++;
                list.add(t);
            }
        } catch (Exception e) {
            throw e;
        }
        return list;
    }

    /**
     * 封装(insert, update, delete)
     * sql:是要执行的 更新语句  这里面可能有 n 个 ? 占位符,及对应的n个参数
     * Object...: 动态数组,长度不确定,这种参数只能加在一个方法参数列表的最后
     * 例:update emp set ename = ?, mgr = ? where empno = ?
     * params: '张三', '李四', '1101'
     */
    public int doUpdate(String sql, Object... params) {
        // 返回成功执行的条数
        int result = -1;
        try (
                Connection con = getConnection();  //获取连接
                PreparedStatement pstmt = con.prepareStatement(sql)
        ) {

//        问题一: ?对应的参数类型是什么, 这个类型是什么,则 setXxxx() ???
//          解决:将所有的参数类型指定为 Object, 则可以 setObject()
//        问题二:有多少个参数??? params到底有几个
//          params 是动态数组, 则有length
            setParams(pstmt, params);
            result = pstmt.executeUpdate();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return result;
    }

    /**
     * 方法名相同,参数不同  -> 重载
     * 查询返回值是一个List<T> T代表任意的类的对象
     * T类的标准JavaBean:属性封装,对外提供get/set
     *
     * @param c:代表   T 类的反射类的对象
     * @param sql:
     * @param params
     * @param <T>
     * @return
     */
    public <T> List<T> select(Class<T> c, String sql, Object... params) throws IllegalAccessException, InstantiationException, InvocationTargetException {
//        System.out.println(sql);
        // 1. sql, params => 查询得到数据表数据
        List result = new ArrayList<>();
        List<Map<String, Object>> list = this.select(sql, params);


        for (Map<String, Object> map : list) {
            T t = c.newInstance();  // 调用了这个T类的无参构造方法
            // 2. 将Map<String, Object> 转换成 T 对象
            //      a、循环map中所有的键值  entrySet()
            Set<Map.Entry<String, Object>> set = map.entrySet();
            Iterator<Map.Entry<String, Object>> iterator = set.iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, Object> entry = iterator.next();
                String key = entry.getKey().substring(0, 1).toUpperCase() + entry.getKey().substring(1).toLowerCase();
                Object value = entry.getValue();
                if (value==null){
                    continue;
                }
                //System.out.println(value);
                // 拼接为 setXxx 方法的名字
                String methodName = "set" + key;
                //System.out.println(methodName);
                //      b、找出 set 方法
                Method setMethod = findSetMethod(methodName, c);
                //      c、激活这个方法,就是设置值进去
                Class parameterTypeClass = setMethod.getParameterTypes()[0];
                String parameterTypeName = parameterTypeClass.getName();

                if ("int".equals(parameterTypeName) || "java.lang.Integer".equals(parameterTypeName)) {
                    setMethod.invoke(t, Integer.parseInt(value.toString()));
                } else if ("double".equals(parameterTypeName) || "java.lang.Double".equals(parameterTypeName)) {
                    setMethod.invoke(t, Double.parseDouble(value.toString()));
                } else if ("short".equals(parameterTypeName) || "java.lang.Short".equals(parameterTypeName)) {
                    setMethod.invoke(t, Short.parseShort(value.toString()));
                } else if ("byte".equals(parameterTypeName) || "java.lang.Byte".equals(parameterTypeName)) {
                    setMethod.invoke(t, Byte.parseByte(value.toString()));
                } else if ("boolean".equals(parameterTypeName) || "java.lang.Boolean".equals(parameterTypeName)) {
                    setMethod.invoke(t, Boolean.parseBoolean(value.toString()));
                } else if ("float".equals(parameterTypeName) || "java.lang.Float".equals(parameterTypeName)) {
                    setMethod.invoke(t, Float.parseFloat(value.toString()));
                } else if ("long".equals(parameterTypeName) || "java.lang.Long".equals(parameterTypeName)) {
                    setMethod.invoke(t, Long.parseLong(value.toString()));
                } else if("java.time.LocalDateTime".equals(parameterTypeName)){
                    // 定义日期时间字符串
                    String dateTimeString = value.toString();

                    // 定义日期时间格式器
                    DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;

                    // 将字符串转换为LocalDateTime
                    LocalDateTime localDateTime = LocalDateTime.parse(dateTimeString, formatter);

                    // 输出结果
                    //System.out.println("Converted LocalDateTime: " + localDateTime);
                    setMethod.invoke(t, localDateTime);
                }else{
                    setMethod.invoke(t, value.toString());
                }

            }

            // 3. 将 T 对象存在 List 中
            result.add(t);
        }
        return result;
    }

    private <T> Method findSetMethod(String methodName, Class<T> c) {
        // 找出所有方法
        Method[] ms = c.getDeclaredMethods();

        for (Method m : ms) {
//            如果我要找的方法名在这个反射对象返回的方法中找到了
            if (methodName.equals(m.getName())) {
                return m;
            }
        }
        return null;

    }


//    private static ArrayList allSetMethods( Method[] methods ) {
//        ArrayList setMethods = new ArrayList();
//        for (Method method : methods) {
//
//        }
//    }


    public List<Map<String, Object>> select(String sql, Object... params) {
        List<Map<String, Object>> list = new ArrayList<>();  // 设置一个List集合
        try (
                Connection con = getConnection();  //获取连接
                PreparedStatement pstmt = con.prepareStatement(sql)  // 预编译语句对象
        ) {
            setParams(pstmt, params);
            ResultSet rs = pstmt.executeQuery();
            // ResultSet 中有结果集中所有的信息
            ResultSetMetaData rsmd = rs.getMetaData(); // 结果集元数据  =》有多少个列, 每个列叫什么名字
            int columnCount = rsmd.getColumnCount(); // 列的数量


            // 循环结果集将数据放入map中,然后map放入List中
            while (rs.next()) {
                HashMap<String, Object> map = new HashMap<>(); // 创建一个map对象
                for (int i = 0; i < columnCount; i++) {
                    map.put(rsmd.getColumnName(i + 1), rs.getObject(i + 1)); // 存每一列
                }
                list.add(map);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return list;

    }


}
RowMapper接口
import java.sql.ResultSet;
import java.sql.SQLException;

public interface RowMapper<T> {
    /**
     * 由最终用户决定如何处理 ResultSet 中的第 rowNum 行
     * @param rs
     * @param rowNum
     * @return
     * @throws SQLException
     */

    T mapRow(ResultSet rs, int rowNum) throws SQLException;
}

用于读取mysql配置的

Dbporperties

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * 此DbProperties继承 自  Properties,所以它也是  Map,也是一个键值对
 * 但增的功能是 ,此DbProperties必须是单例
 */
public class DbProperties extends Properties {
    private static DbProperties instance;
    private DbProperties(){
        //读取配置文件
        InputStream iis= DbProperties.class.getClassLoader().getResourceAsStream("db.properties");
        //Properties类的load方法加载
        try {
            this.load(    iis );    //   this就是 DbProperties  对象,
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


    }
    public static DbProperties getInstance(){
        if(   instance==null){
            instance=new DbProperties();
        }



        return instance;
    }
}

配上自己的mysql账号和密码,放在resources包下方便类加载器在target包中读取配置

db.properties
driverClassName = com.mysql.cj.jdbc.Driver
mysqlurl = jdbc:mysql://localhost:3306/shop?serverTimezone=Asia/Shanghai
mysqluname = 
mysqlpwd = 

log4j配置

log4j.properties
# rootLogger\u9ED8\u8BA4\u60C5\u51B5\u65E5\u5FD7\u914D\u7F6E
#   debug \u7EA7\u522B  #  debug<info<warn<error<fatal<none
# \u65E5\u5FD7\u5728\u54EA\u91CC\u663E\u793A  console,file\u5BF9\u5E94\u4E0B\u9762\u7684 \u4E24\u90E8\u5206
log4j.rootLogger=info, console, file  
#  debug<info<warn<error<fatal<all

#\u65E5\u5FD7\u663E\u793A\u5230\u63A7\u5236\u53F0
log4j.appender.console=org.apache.log4j.ConsoleAppender 
#\u65E5\u5FD7\u7684\u683C\u5F0F
log4j.appender.console.layout=org.apache.log4j.PatternLayout   
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n  
#                                               \u6570\u5B57(\u5E74-\u6708-\u65E5  \u5C0F\u65F6:\u5206:\u79D2)       \u7C7B\u540D:\u7EA7\u522B   \u4FE1\u606F  \u6362\u884C

#\u6EDA\u52A8\u6587\u4EF6
log4j.appender.file=org.apache.log4j.RollingFileAppender 
#\u6587\u4EF6\u540D
log4j.appender.file.File=logs/app.log 
#\u6BCF10m\u751F\u6210\u4E00\u4E2A\u65B0\u6587\u4EF6 
log4j.appender.file.MaxFileSize=10MB   
log4j.appender.file.MaxBackupIndex=5    
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

maven要导入的依赖包

    <dependency>
      <groupId>com.mysql</groupId>
      <artifactId>mysql-connector-j</artifactId>
      <version>8.3.0</version>
    </dependency>

    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>

测试bean

import com.newtable.annotation.Field;
import com.newtable.annotation.PrimaryKey;
import com.newtable.annotation.Table;

@Table
public class Address {

    @PrimaryKey
    private Integer id;
    @Field(value = "heiheihei")
    private Integer userId;    //用户id
    private String province;    //省
    private String city;        //市
    private String town;        //区
    private String notes;   //备注
}
import com.newtable.annotation.Field;
import com.newtable.annotation.PrimaryKey;
import com.newtable.annotation.Table;

@Table
public class Address2 {

    @PrimaryKey
    private Integer id;
    @Field(value = "heiheihei",foreignKey = "address-id")
    private Integer userId;    //用户id
    private String province;    //省
    private String city;        //市
    private String town;        //区
    private String notes;   //备注
}

启动类

/**
 * 启动的时候对于已经存在的类进行建表操作
 */

public class App {
    public static void main(String[] args) throws Exception {
        NewTable.createTable();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值