java+mysql 实现实体的映射与翻译


完善《java高级程序设计》中第四章注解的4.5实例

通过扫描指定路径下的所有类,找到含有指定注解的实体,解析这些实体,并生成对应的SQL命令,在数据库中创建相应的表

一、定义注解

用于指明那些类需要映射成数据库的字段

/**
 * @Auther: Parsifal
 * @Date: 2021/03/30/20:12
 * @Description:  用于指明那些类需要映射成数据库的字段
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    //字段的名字
    public String value() default "";

 //约束条件:
    //是否可以为空
    public boolean nullable() default true;
    //字段长度
    public int length() default -1;

}

用于指明那些需要映射成数据库的表

/**
 * @Auther: Parsifal
 * @Date: 2021/03/30/20:10
 * @Description:
 *  用于指明那些需要映射成数据库的表
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {
    //数据库映射后表的名字
    public String value() default "";
}

作用与属性,表示属性作为映射表的主键

/**
 * @Auther: Parsifal
 * @Date: 2021/03/30/20:14
 * @Description:  作用与属性,表示属性作为映射表的主键
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ID {
    public String value() default "";
}

二、注解处理器

注解处理器接口

/**
 * @Auther: Parsifal
 * @Date: 2021/03/31/15:39
 * @Description: 注解处理器
 */
public interface IProcessor {
    public String process(String url)throws Exception;
}

处理器实现类

  • 通过扫描器获得类的信息,同过TableInfo 的toStirng方法获得该类的sql语句
/**
 * @Auther: Parsifal
 * @Date: 2021/03/31/15:40
 * @Description:
 */
public class TableProcessor implements IProcessor {
    @Override
    public String process(String url) throws Exception {
        //扫描 url下的实体
        Scanner scanner = new Scanner(url);
        //获得实体的Class<?>
        List<Class<?>> entities = scanner.getEntity();
        //获得创建表和属性的sql
        StringBuilder sql  = new StringBuilder();
        entities.forEach(entity->{
            TableInfo tableInfo = new TableInfo();
            TableInfo parse = tableInfo.parse(entity);
            if (parse!=null){
                sql.append(tableInfo.toString());

            }
        });
        return sql.toString();
    }
}

三、工具类

定义的常量

/**
 * @Auther: Parsifal
 * @Date: 2021/03/30/21:15
 * @Description:
 */
public class Symbol {
    public static final String BLANK=" ";
    public static final String TAB="\t";
    public static final String LINE="\n";
}

Scanner类

  • 扫描指定路径下的.java文件并获取该类信息
/**
 * @Auther: Parsifal
 * @Date: 2021/03/30/21:25
 * @Description: 扫描指定路径下的实体
 */
public class Scanner {
    private String path;
    private File file;

    public Scanner(String path) {
        this.path = path;
        file = new File(path);

    }

    /**
     * 获取src目录下的指定路径的实体类信息
     * 路径格式(src开头): src\EntityMapping\Entity
     * @return
     * @throws ClassNotFoundException
     */
    public List<Class<?>> getEntity() throws ClassNotFoundException {
//        获得该文件下的作用子文件对象
        String[] list = file.list();
        List<Class<?>> classes  =new ArrayList<>();
//       path 替换/为 .
        this.path=  path.replace('\\','.');
        //去除文件后的后缀名  '.java'
        this.path = path.substring(4);
        if (list == null){
            return null;
        }
        for (String s : list) {
            s =s.split("\\.")[0];
            //获得实体的类信息
            classes.add(Class.forName(path+"."+s));
        }

        return classes;
    }

	//测试
    public static void main(String[] args) throws ClassNotFoundException {
        Scanner scanner = new Scanner("src\\EntityMapping\\Entity");
        List<Class<?>> entity = scanner.getEntity();
        System.out.println(entity);
    }
}

ColumInfo类

  • 储存数据库中表的某一字段的信息,即是类中需要映射的属性信息
/**
 * @Auther: Parsifal
 * @Date: 2021/03/30/20:26
 * @Description:
 * 用于描述数据库中某一字段的信息
 */
public class ColumnInfo {
    //字段名称
    private String columnName;
    //字段类型
    private Class<?> type;
    //是否是主键
    private boolean isID =false;
    //是否可为空
    private boolean nullable=true;
    //字段长度
    private int length=32;
    //该字段是否需要保存到数据库中
    private boolean needPersist = false;


    /**
     * 根据Field对象,解析该字段信息
     * @param field 字段
     * @return
     */
    public ColumnInfo parse(Field field){
        this.columnName =field.getName();
        this.type = field.getType();
        Annotation[] annotations = field.getAnnotations();
        for (Annotation annotation : annotations) {
            //如果注解是@Column,则获取字段信息
            if (annotation.annotationType().equals(Column.class)){
                this.needPersist=true;
                Column column = (Column) annotation;
                if (!column.value().equals("")){
                    //如果字段的名字不为空,则获取该字段的名字
                    this.columnName=column.value();
                }
                this.nullable=column.nullable();
                if (column.length()!=-1){
                    //如果该字段的长度不为默认值-1,则获取该长度
                    this.length = column.length();
                }
            }
            //如果该注解是@ID
            else if (annotation.annotationType().equals(ID.class)){
                this.needPersist=true;
                ID id = (ID)annotation;
                this.isID = true;
                //如果用户设置了value值,则以value值作为字段
                if (!id.value().equals("")){
                    this.columnName =id.value();
                }
            }
        }
        if (this.needPersist){
            return this;
        }else {
            return null;
        }
    }

    /**
     * 重写toString方法返回字段信息的sql语句
     * @return
     */
    @Override
    public String toString() {
       StringBuilder sql = new StringBuilder(columnName);
       if (this.type.equals(String.class)){
           sql.append(Symbol.BLANK + "VARCHAR(").append(this.length).append(")");
       }else if(this.type.equals(Integer.class)){
           sql.append(Symbol.BLANK+"INT");
       }
      if (this.isID){
          sql.append(Symbol.BLANK+"PRIMARY KEY");
      }
      if(!this.nullable){
          sql.append(Symbol.BLANK+"NOT NULL");
      }
      return sql.toString();
    }
}

ColumnInfo 类解析解析的流程

  • 获取Field对象的名称,作为字段名称
  • 获取Field对象的类型,作为字段的类型
  • 获取该属性上声明的注解集合,并遍历这些注解
  • 如果注解是@Column,则表明该属性应映射成数据库中的字段
  • 如果注解是@ID,则表明该属性作为数据库中表的主键
  • 判断该属性是否需要持久化,如需要返回解析后的字段信息对象,否则返回nul1

TableInfo 类

  • 储存数据库中的表的信息
/**
 * @Auther: Parsifal
 * @Date: 2021/03/30/22:29
 * @Description: 储存数据库中表的信息
 */
public class TableInfo {
    //表的名字
    private String tableName;
    //表对应的实体的信息
    private Class<?> clazz;
    //是否需要持久化储存
    private boolean needPersist = false;
    //该表的所有字段信息
    private Map<String,ColumnInfo> columns = new HashMap<>();




    public  TableInfo parse(Class<?> clazz){
        this.clazz = clazz;
        this.tableName=this.clazz.getSimpleName();
        Annotation[] annotations = this.clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            if (annotation.annotationType().equals(Entity.class)){
                //如果包含@Entity注解,则表明实体需要持久化储存
                this.needPersist =true;
                Entity entity = (Entity) annotation;
                if (!entity.value().equals("")){
                    this.tableName = entity.value();
                }
                break;
            }
        }
        if (this.needPersist){
            //如果需要永久化储存
            Field[] fields = this.clazz.getDeclaredFields();
            for (Field field : fields) {
                ColumnInfo column = new ColumnInfo();
                column=column.parse(field);
                if (column!=null){
                    this.columns.put(field.getName(),column);
                }
            }
            return this;
        }else {
            return null;
        }
    }

    @Override
    public String toString() {
       StringBuilder sql  =new StringBuilder();
       sql.append(Symbol.LINE)
               .append("CREATE TABLE")
               .append(Symbol.BLANK)
               .append(this.tableName)
               .append(Symbol.BLANK)
               .append("(");
        for (ColumnInfo value : this.columns.values()) {
            sql.append(Symbol.LINE).append(Symbol.TAB).append(value.toString()).append(",");
        }
       sql.delete(sql.length()-1,sql.length());
       sql.append(Symbol.LINE).append(");");
        return sql.toString();
    }
}

TableInfo 类解析实体的主要流程:

  • 根据类型信息,获取实体类的简单名称作为表名。
  • 获取在该类上使用的注解集合。
  • 遍历这些集合。
  • @ Entity注解的参数value.
  • 如果参数不为空,则将表名设为此参数值,跳出循环。
  • 如果没有找到该注解,则说明此实体不需要持久化存储,则返回null.
  • 如果该实体需要持久化存储,则遍历该实体类型信息的所有属性描述对象列表,并将它们转换成表的字段信息对象,添加到字段信息map中。
  • 返回解析好的表信息实体。

四、测试类

/**
 * @Auther: Parsifal
 * @Date: 2021/03/31/15:38
 * @Description:
 */
public class MappingTest {
    public static void main(String[] args)  {
        TableProcessor tp = new TableProcessor();
        Connection connection = null;
        PreparedStatement ps = null;
        try {
            String sql = tp.process("src\\EntityMapping\\Entity");
            System.out.println(sql);
            connection = JDBCUtil.getConnection();
            if (connection != null) {
                ps = connection.prepareStatement(sql);
            }
            if (ps != null) {
                int i = ps.executeUpdate();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.close(ps, connection);
        }

    }
}

文件路径
在这里插入图片描述

Person实体


@Entity("Person")
public class Person{
    @ID
    @Column(nullable = false)
    Integer id;
    @Column(nullable = false,length = 16)
    String name;
    
}

JDBCUtil类是我封装的一个工具类
详情请见: https://blog.csdn.net/m0_49647974/article/details/115285852

结果

  • sql语句
    在这里插入图片描述
  • 数据库
    创建成功
  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值