那些高端、优雅的注解是怎么实现的<6> --自定义持久层框架-类 Hibernate

13 篇文章 0 订阅

概述

上一篇我们用apt,实现了一个自动生成工厂类的实例。这篇我们使用反射的方式去实现一个持久层框架,当然只是核心代码,并不是可以直接拿来使用的商业项目 ,类似于 Hibernate

自定义注解系列文章

需求

  • 有一张用户表(user),包括用户id、用户名、昵称、年龄、性别、邮箱、手机号等字段
  • 根据bean 类中包含的信息查询在 user 表中查询出符合条件的 user

定义实体类并和 user 表形成映射关系

定义beanCustomer

@Table("user")
public class Customer {
    //id
    @Column("id")
    private int id;

    //姓名
    @Column("user_name")
    private String userName;

    //年龄
    @Column("age")
    private int age;

    //城市
    @Column("city")
    private String city;

    //邮箱
    @Column("email")
    private String email;

    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 int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

我们需要定义@Table注解来让 user 表和 Customer 类之间形成映射关系。需要定义 @Columnuser 表 和 Customer 类的字段之间形成映射关系。

定义注解

  • 定义 @Table 注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Table {
    String value();
}
  • 定义 @Column 注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Column {
    String value();
}

解析注解并生成相应的逻辑代码

我们最后需要生成sql语句类似如下

select * from user where 1=1 and user_name='zhangsan' and age=18 and email in('xxx@163.com,xxx@qq.com,xxx@yahoo.com.cn')

下面我们定义query 方法,去解析注解,并拼接完成我们的 sql 语句 。

 public static String query(Object bean) {

        //因为最后我们会返回sql语句,所以这里我们需要拼接sql语句
        StringBuilder sb = new StringBuilder();

        //首先获取 bean 类的 class,本例就是 Customer
        Class<?> beanClass = bean.getClass();

        //判断该 class 上是否有 table 注解
        boolean present = beanClass.isAnnotationPresent(Table.class);
        if (!present) {
            return "";
        }

        //获取该 class上 table 注解
        Table table = beanClass.getAnnotation(Table.class);

        //获取表名
        String tableName = table.value();
        sb.append("select * from")
                .append(" ")
                .append(tableName)
                .append(" ")

                //防止没有查询条件而报错
                .append("where 1=1");

        //获取类里面的所有字段
        Field[] fields = beanClass.getDeclaredFields();

        //遍历所有字段
        for (Field field : fields) {

            //判断字段上是否有Colomn 注解
            boolean fieldPresent = field.isAnnotationPresent(Column.class);

            //如果不存在,跳过这个字段继续下一个循环
            if (!fieldPresent) {
                continue;
            }

            //获取到 Column
            Column column = field.getAnnotation(Column.class);

            //获取 Column 的名称,如(user_name)
            String columnName = column.value();

            //获取字段的名称,如 useName
            String fieldName = field.getName();

            //通过该字段的get方法名
            String fieldGetName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);


            Object value = null;

            try {
                //获取到get方法
                Method method = beanClass.getMethod(fieldGetName);

                //反射调用该字段的get方法,获取到该字段的值
                value = method.invoke(bean);

            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

            //如果不为空,则开始添加 and 语句
            if (value == null ||((value instanceof Integer)&& (Integer)value==0)) {
                continue;
            }

            //如果有值则添加 and 语句
            sb.append(" ")
                    .append("and").append(" ")
                    .append(columnName)

            ;

            //如果这个字段是int类型
            if (value instanceof Integer) {
                sb.append("=").append(value);
            } else if (value instanceof String) {

                //如果包含逗号,则是in查询
                if (((String) value).contains(",")) {
                    String[] values = ((String) value).split(",");

                    //添加 in 语句
                    sb.append(" ").append("in('");

                    //添加查询条件,并用逗号隔开
                    for (String item : values) {
                        sb.append(item).append(",");
                    }

                    //去除最后一个逗号
                    sb.deleteCharAt(sb.length() - 1);

                    sb.append("')");

                }

                //如果单个查询用 = 即可,但需要加入单引号
                else {


                    sb.append("='").append(value).append("'");
                }
            }


        }


        return sb.toString();
    }

调用 query 方法,获取sql语句

    public static void main(String[] args) {
        // new  Customer,并赋值
        Customer customer1 = new Customer();
        customer1.setUserName("zhangsan");
        customer1.setAge(18);
        customer1.setEmail("xxx@163.com,xxx@qq.com,xxx@yahoo.com.cn");
        
        
        //调用 query 方法,获取 sql 语句 
        String sql1 = Query.query(customer1);
        
        
        //打印sql语句
        System.out.println(sql1);
    }

总结

完整的代码我会上传到 github,如果仅仅上上面的代码片段无法看懂,那clone下来看看吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值