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

一、注解

注解(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')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值