使用基于Mybatis注解,利用反射和注解生成SQL语句

来源:互联网 发布:淘宝联盟可以赚钱吗 编辑:程序博客网 时间:2024/06/09 22:02

今天是手伤后的第3天,感谢大家的关心!缴了6针现伤口已经愈合不疼了,但今天依然还是坚持写博客。。。好了,不说废话,正式进入今天的话题:
我们在使用SSM整合开发的时候,常会遇到这样一个问题(在这里以我平时做项目遇到的问题来说:Maven项目、数据库采用的mysql5.7.16):使用注解的mybatis插入一个对象到mysql中,在写sql语句时需要列出对象的所有属性,如果要插入一个10个以及多个以上属性的对象时sql语句会变得越来越长,写起来也就很不方便,也很容易拼错。因此,上网搜索了一下,也有关于这方面注解的博客文章,我们可以利用反射和注解来解决这个问题,因此我学习了它,并且总结…到我自己的博客和感兴趣的朋友们一起学习和分享!
下面我们来看一段代码片段,如下(嘿嘿,这是我粘贴的人家的代码片):

@Insert("insert into poi_shop(name,brand,tags,status,phone,mobile,business_time,address,city,lng,lat,business_type,attribute_json) values(#{name},#{brand},#{tags},#{status},#{phone},#{mobile},#{business_time},#{address},#{city},#{lng},#{lat},#{business_type},#{attribute_json})")@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")public Long insertPoiInfo(PoiBo poiBo);

哈哈,是不是注解太长了?
是的,我觉得都长,那么我们来学习通过反射和注解生成SQL语句:
第一、利用反射:
首先想到的是可以利用反射获得对象的所有属性,然后拼接成sql语句。所以写了一个基于反射拼装sql语句的方法,然后基于mybatis动态获得sql语句的方式 获得完整的sql具体的代码如下:

接口层改为下面的样子,sql语句的生成放到PoiSqlProvider的insertPoiBo方法中@InsertProvider(type = PoiSqlProvider.class, method = "insertPoiBo")public Long insertPoiInfo(@Param("poiBo")PoiBo poiBo);

我们来看一下PoiSqlProvider.java的代码:

  public String insertPoiBo(Map<String,Object> map) {       PoiBo poiBo = (PoiBo)map.get("poiBo");       StringBuilder sql = new StringBuilder("insert into poi_shop ");       //get sql via reflection       Map<String,String> sqlMap = getAllPropertiesForSql(poiBo, "poiBo");       sql.append(sqlMap.get("field")).append(sqlMap.get("value"));       System.out.println(sql.toString());       return sql.toString();   }   //根据传入的对象 基于反射生成两部分sql语句   private  Map<String,String> getAllPropertiesForSql(Object obj, String objName) {       Map<String,String> map = new HashMap<String,String>();       if(null == obj) return map;       StringBuilder filedSql = new StringBuilder("(");       StringBuilder valueSql = new StringBuilder("value (");       Field[] fields = obj.getClass().getDeclaredFields();       for (int i = 0; i < fields.length; i++) {           filedSql.append(fields[i].getName() + ",");           valueSql.append("#{" + objName + "." + fields[i].getName() + "},");       }       valueSql.deleteCharAt(valueSql.length() - 1);       filedSql.deleteCharAt(filedSql.length() - 1);       valueSql.append(") ");       filedSql.append(") ");       map.put("field",filedSql.toString());       map.put("value", valueSql.toString());       System.out.println("database filed sql: " + filedSql.toString());       System.out.println("value sql:" + valueSql.toString());       return map;   }

下面是基于反射生成的两部分sq语句和最后拼接的语句:

database filed sql: (id,name,brand,tags,status,phone,mobile,business_time,address,city,lng,lat,business_type,attribute_json,updated_at,created_at) value sql:value(#{poiBo.id},#{poiBo.name},#{poiBo.brand},#{poiBo.tags},#{poiBo.status},#{poiBo.phone},#{poiBo.mobile},#{poiBo.business_time},#{poiBo.address},#{poiBo.city},#{poiBo.lng},#{poiBo.lat},#{poiBo.business_type},#{poiBo.attribute_json},#{poiBo.updated_at},#{poiBo.created_at}) insert into poi_shop (id,name,brand,tags,status,phone,mobile,business_time,address,city,lng,lat,business_type,attribute_json,updated_at,created_at) value (#{poiBo.id},#{poiBo.name},#{poiBo.brand},#{poiBo.tags},#{poiBo.status},#{poiBo.phone},#{poiBo.mobile},#{poiBo.business_time},#{poiBo.address},#{poiBo.city},#{poiBo.lng},#{poiBo.lat},#{poiBo.business_type},#{poiBo.attribute_json},#{poiBo.updated_at},#{poiBo.created_at})

我们需要注意的是如果数据库的字段名和插入对象的属性名不一致,那么不能使用生成的database filed sql。
第二、利用注解:
上面的getAllPropertiesForSql方法有个缺点,如果数据库的字段名和类的属性名不一致,就不能依靠反射获得sql了。所以借鉴老大的ORM框架也写了一个注解Column,用于model类的属性上,表明属性所对应数据库字段。下面是Column注解的snippet。如下代码:

import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/* 定义字段的注解*/@Retention(RetentionPolicy.RUNTIME)/*该注解只能用在成员变量上*/@Target(ElementType.FIELD)public @interface Column {    /**     * 用来存放字段的名字 如果未指定列名,默认列名使用成员变量名     *     * @return     */    String name() default "";}

之后在model类属性上加入对应的注解,省略getter和setter。Column的name为空时,代表属性名和字段名一致。如下代码:

public class PoiBo {    @Column    private Long id;    @Column(name = "poi_name")    //表示name属性对应数据库poi_name字段    private String name;    @Column(name = "poi_brand")    //表示brand属性对应数据库poi_brand字段    private String brand;    @Column    private String tags;    @Column    private Integer status;    @Column    private String phone;    @Column    private String mobile;    @Column    private String business_time;    @Column    private Float average_price;    @Column    private String address;    @Column    private String city;    @Column    private Double lng;    @Column    private Double lat;    @Column    private String business_type;    @Column    private String attribute_json;    @Column    private Timestamp updated_at;    @Column    private Timestamp created_at;}

修改getAllPropertiesForSql方法,通过获取类属性上的注解获得数据库字段名。如下代码:

private  Map<String,String> getAllPropertiesForSql(Object obj, String objName) {        Map<String,String> map = new HashMap<String,String>();        if(null == obj) return map;        StringBuilder filedSql = new StringBuilder("(");        StringBuilder valueSql = new StringBuilder("value (");        Field[] fields = obj.getClass().getDeclaredFields();        for (Field field : fields) {                // 判断该成员变量上是不是存在Column类型的注解                if (!field.isAnnotationPresent(Column.class)) {                    continue;                }                Column c = field.getAnnotation(Column.class);// 获取实例                // 获取元素值                String columnName = c.name();                // 如果未指定列名,默认列名使用成员变量名                if ("".equals(columnName.trim())) {                    columnName = field.getName();                }            filedSql.append(columnName + ",");            valueSql.append("#{" + objName + "." + field.getName() + "},");        }        valueSql.deleteCharAt(valueSql.length() - 1);        filedSql.deleteCharAt(filedSql.length() - 1);        valueSql.append(") ");        filedSql.append(") ");        map.put("field",filedSql.toString());        map.put("value", valueSql.toString());        System.out.println("database filed sql: " + filedSql.toString());        System.out.println("value sql:" + valueSql.toString());        return map;}

利用反射+注解之后的输出结果,可以看到sql语句正确按照name的Column注解的输出了name属性对应的数据库字段是poi_name。如下:

database filed sql: (id,poi_name,poi_brand,tags,status,phone,mobile,business_time,average_price,address,city,lng,lat,business_type,attribute_json,updated_at,created_at) value sql:value(#{poiBo.id},#{poiBo.name},#{poiBo.brand},#{poiBo.tags},#{poiBo.status},#{poiBo.phone},#{poiBo.mobile},#{poiBo.business_time},#{poiBo.average_price},#{poiBo.address},#{poiBo.city},#{poiBo.lng},#{poiBo.lat},#{poiBo.business_type},#{poiBo.attribute_json},#{poiBo.updated_at},#{poiBo.created_at}) insert into poi_shop (id,poi_name,poi_brand,tags,status,phone,mobile,business_time,average_price,address,city,lng,lat,business_type,attribute_json,updated_at,created_at) value (#{poiBo.id},#{poiBo.name},#{poiBo.brand},#{poiBo.tags},#{poiBo.status},#{poiBo.phone},#{poiBo.mobile},#{poiBo.business_time},#{poiBo.average_price},#{poiBo.address},#{poiBo.city},#{poiBo.lng},#{poiBo.lat},#{poiBo.business_type},#{poiBo.attribute_json},#{poiBo.updated_at},#{poiBo.created_at})

写完了试试吧,总之,总这种方法,利用反射和注解生成的SQL语句还是很方便的。
GOOD LUCK!

1 0
原创粉丝点击