美文网首页Java核心基础
Java核心基础--基于注解&反射生成SQL语句

Java核心基础--基于注解&反射生成SQL语句

作者: Ramos007 | 来源:发表于2020-05-14 23:13 被阅读0次

    一、注解

    注解(Annotation)Java5引入的一种代码辅助工具,其核心作用是对类、方法、变量、参数和包进行标注,然后通过反射来访问这些标注信息,并在运行时改变被注解对象的行为。

    1. 基本概念

    Java注解又名Java标注,是Java5开始支持加入源代码的特殊语法元数据,其附加的信息根据需要可以保存到class文件中,甚至运行期加载到Class对象中;其与普通的注释不同在于:普通的注释在编译后的class文件中是不存在的。
    Java注解其本身没有任何功能,不会执行,只是起到标注的作用,所以往往需要配合反射去解析并处理被注解标记的代码。

    2. JDK内置标准注解

    Java SE5内置了三种定义在java.lang中的注解

    • @Override: 一个标记注解,表示被它标注的方法将覆盖父类的方法。如果写代码过程中拼写错误或方法签名对不上父类的方法,编译器就会发出错误提示。
    • @Deprecated: 一个标记注解,用于修饰一个方法。它表示此方法不推荐使用。无论是继承、覆盖或直接使用此方法,编译器都会给出警告
    • @SuppressWarnings: 关闭不当的编译器警告信息,其有一些参数用于表示特定的警告:
      • deprecation: 不要给出“不赞成使用的类或方法的警告”;
      • unchecked: 不要给出“类型转换时警告”;
      • fallthrough: 不要给出”switch语句块没有break的警告”;
      • path: 不要给出“不存在的路径”的警告;
      • serial: 不要给出“可序列化类缺少serialVersionUID”的警告;
      • finally: 不要给出“finally语句块不能正常完成”的警告;
      • all: 不要给出以上所有情况的警告

    3. 自定义注解

    定义注解格式:

    public @interface 注解名 {定义体}
    

    看一个定义好的注解:

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Column {
        String value() default "";
    }
    

    上面这个Column注解主要由三部分组成:

    3.1. @interface

    @interface自定义注解时,自动继承了 java.lang.annotation.Annotation 接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。

    3.2. 元注解

    Java允许我们自定义注解,并为我们提供四种元注解来负责注解我们自定义的注解。四种元注解分布时:

    • @Retention(RetentionPolicy.RUNTIME):元注解,定义注解的生命周期

      • RetentionPolicy.SOURCE:在源文件中有效
      • RetentionPolicy.CLASS:在class文件中有效
      • RetentionPolicy.RUNTIME: 在运行时有效,也是我们最常用的
    • @Documented:文档注解, 表示自定义注解可以被javadoc之类的工具文档化

    • @Inherited:是否让子类继承该注解

    • @Target({ElementType.TYPE, ElementType.FIELD}):元注解,定义注解的修饰范围,可以设置多个

      • TYPE:表示可以用来修饰类、接口、注解类型或枚举类型
      • PACKAGE:表示可用来修饰包
      • PARAMETER:表示可用来修饰参数
      • ANNOTATION_TYPE:可用来修饰注解类型
      • METHOD:可用来修饰方法
      • FIELD:可用来修饰属性(包括枚举常量)
      • CONSTRUCTOR:可用来修饰构造器
      • LOCAL_VARIABLE:可用来修饰局部变量

    3.3. 注解参数

    @interface用来声明一个注解,其中的每一个方法实际上是声明了一个注解的参数。方法的名称就是参数的名称,返回值类型就是参数的类型,可以通过default来声明参数的默认值。

    • 注解参数的可支持数据类型:
      • 所有基本数据类型(int,float,boolean,byte,double,char,long,short)
      • String类型
      • Class类型
      • enum类型
      • Annotation类型
      • 以上所有类型的数组

    4. 注解使用

    @Table(value = "person")
    public class Person {
        @Column
        private int id;
        @Column("name")
        private String name;
    }
    

    二、反射

    反射(Reflection):指的是程序在运行期间借助反射 API 取得任何类的内部信息,并通过这些内部信息去操作对应对象的内部属性和方法。

    任何一个类,在第一次使用时,就会被 JVM 加载到堆内存的方法区中。JVM 加载类成功后,就会在方法区中产生一个对应的 Class 对象(一个类只要一个 Class 对象),这个 Class 对象包含被加载类的全部结构信息。

    1. 获取 Class 对象的常用方式

    1.1. 类的 class 属性

    Class<Person> cl1 = Person.class; 
    

    1.2. Object 对象 的 getClass() 方法

    Person person= new Person();
    Class<Person> cl2 = (Class<Person>) person.getClass(); 
    

    1.3. 通过 Class 类的 forName() 方法(最常用)

    try {
        Class cl3 = Class.forName("com.dkfrog.sqlgenerator.model.Person");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    

    1.4. 通过 ClassLoader 类(不常用)

    ClassLoader cl = Person.class.getClassLoader();
    try {
        Class cl4 = cl.loadClass("com.dkfrog.sqlgenerator.model.Person");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    

    2. 反射常用的API

    方法名 返回值 参数描述
    clz.getDeclaredFields() 获取当前类中所有的属性
    setAccessible(boolean) 设置当前属性可见
    getMethods 获取类所有方法
    invoke(obj) 通过反射执行方法 类的元信息
    getAnnotation(class) 获取类注解 需要获取注解的Class

    Class 对象中大多数 get 方法有 Declared 和无 Declared,他们的区别是:

    • 无 Declared:只能获取到 public 修饰的,包括当前类和所有父类。
    • 有 Declared:获取到当前类所有的(含有 private),但不包括其父类。

    3. 实例

    public class ReflectionDemo {
    
        public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
            Person person = new Person("dkfrog", 20);
            // 获取类的元信息
            Class<? extends Person> aClass = person.getClass();
            // Spring XML配置Bean常用
            //        Class<?> aClass1 = Class.forName("com.dkfrog.model.Person");
    
            // 实例2:通过反射获取类名、包名
            // 包名+类名: com.dkfrog.model.Person
            System.out.println("-----------  实例2:通过反射获取类名、包名");
            System.out.println(aClass.getName());
            // 类名: Person
            System.out.println(aClass.getSimpleName());
    
            // 实例3:获取类属性
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                System.out.println(declaredField);
            }
    
            // 实例4:获取类属性具体值
            System.out.println("----------- 实例4:获取类属性具体值");
            for (Field declaredField : declaredFields) {
                // 设置私有属性可见
                declaredField.setAccessible(true);
                // 通过get获取值,需传入类
                System.out.println(declaredField.get(person));
            }
    
            // 实例4的另一种写法
            // 相当于在反射中的实例化
            Object p = aClass.newInstance();
            for (Field declaredField : declaredFields) {
                declaredField.setAccessible(true);
                if ("name".equals(declaredField.getName())) {
                    declaredField.set(p, "ramos");
                } else {
                    declaredField.set(p, 19);
                }
                System.out.println(declaredField.get(p));
            }
    
            // 实例5:反射获取当前类的方法
            System.out.println("-----------  实例5:反射获取当前类的方法 ");
            Method[] methods = aClass.getMethods();
            for (Method method : methods) {
                System.out.println(method.getName());
            }
            // 反射执行方法
            Method getString = aClass.getMethod("getString");
            Object invoke = getString.invoke(p);
            System.out.println(invoke);
    
            // 实例6:获得注解
            System.out.println("----------- 实例6:获得注解 ");
            Study annotation = aClass.getAnnotation(Study.class);
            String[] mores = annotation.mores();
            for (String more : mores) {
                System.out.println(more);
            }
            String name = annotation.name();
            System.out.println("name: " + name );
            // 从方法上获取注解
            for (Method method : methods) {
                Study annotation1 = method.getAnnotation(Study.class);
                if (annotation1 == null) {
                    continue;
                }
                System.out.println(annotation1.mores());
            }
            // 从属性上获取注解, 类似于从方法上获取注解
    
        }
    }
    

    三、手写简单查询SQL生成器

    1. 定义描述用户表的注解:

    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();
    }
    

    2. 定义描述用户属性的注解:

    import java.lang.annotation.*;
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Column {
        String value() default "";
    }
    

    3. 定义映射Bean类User:

    @Table(value = "user")
    public class User {
        @Column
        private int id;
        @Column("username")
        private String username;
        @Column("password")
        private String password;
    
        public User() {
        }
    
        public User(int id, String username, String password) {
            this.id = id;
            this.username = username;
            this.password = password;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    }
    

    4. 定义SQL生成器:

    import com.dkfrog.sqlgenerator.annotation.Column;
    import com.dkfrog.sqlgenerator.annotation.Table;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    /**
     * @author ramos_javax@163.com
     * @date 2020-05-12 21:02
     * @since
     */
    public class GenerateSqlUtil {
    
        public String query(Object obj) throws Exception {
            StringBuilder str = new StringBuilder();
            // 1.获取一个类class
            Class c = obj.getClass();
            // 2.获取Table的名字
            boolean exists = c.isAnnotationPresent(Table.class);
            if (!exists) {
                return null;
            }
            Table t = (Table) c.getAnnotation(Table.class);
            String tableName = t.value();
            str.append("select * from ").append(tableName).append(" where 1=1");
            // 3.遍历所有的 字段
            Field fArray[] = c.getDeclaredFields();
            for (Field field : fArray) {
                // 4.处理每个字段对应的sql
                // 4.1取到字段名
                // 判断是否包含Column类型的注解
                boolean fExists = field.isAnnotationPresent(Column.class);
                if (!fExists) {
                    continue;
                }
                Column column = field.getAnnotation(Column.class);
                String columnName = column.value();
                // 4.2取到字段的值
                String fieldName = field.getName();
                // 获取相应字段的getXXX()方法
                String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                Object fieldValue=null;
                try {
                    Method getMethod = c.getMethod(getMethodName);
                    fieldValue = getMethod.invoke(obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //4.3拼接Sql
                if (fieldValue==null||fieldValue instanceof Integer &&(Integer)fieldValue==0) {
                    continue;
                }
                str.append(" and ").append(fieldName);
                if (fieldValue instanceof String) {
                    if (((String) fieldValue).contains(",")) {
                        String[] values=((String) fieldValue).split(",");
                        str.append(" in (");
                        for (String s : values) {
                            str.append("'").append(s).append("'").append(",");
                        }
                        str.deleteCharAt(str.length()-1);
                        str.append(")");
                    }else{
                        str.append("=").append("'").append(fieldValue).append("' ");
                    }
                }else {
                    str.append("=").append(fieldValue);
                }
            }
            return str.toString();
        }
    }
    

    5. 测试类

    import com.dkfrog.sqlgenerator.model.User;
    import com.dkfrog.sqlgenerator.util.GenerateSqlUtil;
    
    public class Main {
        public static void main(String[] args) throws Exception {
            GenerateSqlUtil util = new GenerateSqlUtil();
    
            // 查询id
            User user1 = new User();
            user1.setId(100);
            // 模糊查询用户名
            User user2 = new User();
            user2.setUsername("dkfrog");
            user2.setPassword("******");
    
            // 查询邮箱有任意一个的用户
            User user3 = new User();
            user3.setEmail("test1@163.com,test2@qq.com");
    
            System.out.println(util.query(user1));
            System.out.println(util.query(user2));
            System.out.println(util.query(user3));
        }
    }
    

    输出结果:

    select * from user where 1=1 and id=100
    select * from user where 1=1 and username='dkfrog'  and password='******' 
    select * from user where 1=1 and email in ('test1@163.com','test2@qq.com')
    

    相关文章

      网友评论

        本文标题:Java核心基础--基于注解&反射生成SQL语句

        本文链接:https://www.haomeiwen.com/subject/emvfohtx.html