使用基于注解的mybatis时,利用反射和注解生成sql语句
在開(kāi)發(fā)時(shí)遇到一個(gè)問(wèn)題,在使用基于注解的mybatis插入一個(gè)對(duì)象到mysql時(shí),在寫(xiě)sql語(yǔ)句時(shí)需要列出對(duì)象的所有屬性,所以在插入一個(gè)擁有10個(gè)以上屬性的對(duì)象時(shí)sql語(yǔ)句就會(huì)變得很長(zhǎng),寫(xiě)起來(lái)也很不方便,也很容易拼錯(cuò)。google了一下也沒(méi)有找到什么解決方式(可能是姿勢(shì)不對(duì)),在stackoverflow上提的問(wèn)題截止目前還沒(méi)有人回答。所以自己想了一個(gè)基于反射和注解的解決辦法
git地址:
giraffe0813
spring-mybatis-utils
a555c9?, up-to-date下面是之前的代碼片段:
| 1 2 3 | @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); |
第一版(利用反射)
首先想到的是可以利用反射獲得對(duì)象的所有屬性,然后拼接成sql語(yǔ)句。所以寫(xiě)了一個(gè)基于反射拼裝sql語(yǔ)句的方法,然后基于mybatis動(dòng)態(tài)獲得sql語(yǔ)句的方式 獲得完整的sql 具體的代碼如下:
接口層改為下面的樣子,sql語(yǔ)句的生成放到PoiSqlProvider的insertPoiBo方法中
| 1 2 | @InsertProvider(type = PoiSqlProvider.class, method = "insertPoiBo") public Long insertPoiInfo(@Param("poiBo")PoiBo poiBo); |
PoiSqlProvider.class
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | ??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(); ???} //根據(jù)傳入的對(duì)象 基于反射生成兩部分sql語(yǔ)句 ???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() + "},"); ???????} ???????//remove last ',' ???????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語(yǔ)句和最后拼接的語(yǔ)句
| 1 2 3 4 5 6 7 8 9 | 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}) |
要注意的是如果數(shù)據(jù)庫(kù)的字段名和插入對(duì)象的屬性名不一致,那么不能使用生成的database filed sql。
最終版(加入注解)
上面的getAllPropertiesForSql方法有個(gè)缺點(diǎn),如果數(shù)據(jù)庫(kù)的字段名和類(lèi)的屬性名不一致,就不能依靠反射獲得sql了。所以借鑒老大的ORM框架也寫(xiě)了一個(gè)注解Column,用于model類(lèi)的屬性上,表明屬性所對(duì)應(yīng)數(shù)據(jù)庫(kù)字段。下面是Column注解的snippet。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 { ????/** ?????* 用來(lái)存放字段的名字 如果未指定列名,默認(rèn)列名使用成員變量名 ?????* ?????* @return ?????*/ ????String name() default ""; ????} |
之后在model類(lèi)屬性上加入對(duì)應(yīng)的注解,省略getter和setter。Column的name為空時(shí),代表屬性名和字段名一致。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | public class PoiBo { ????@Column ????private Long id; ????@Column(name = "poi_name") ????private String name;//表示name屬性對(duì)應(yīng)數(shù)據(jù)庫(kù)poi_name字段 ????@Column(name = "poi_brand") ????private String brand;//表示brand屬性對(duì)應(yīng)數(shù)據(jù)庫(kù)poi_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方法,通過(guò)獲取類(lèi)屬性上的注解獲得數(shù)據(jù)庫(kù)字段名。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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類(lèi)型的注解 ????????????????if (!field.isAnnotationPresent(Column.class)) { ????????????????????continue; ????????????????} ????????????????Column c = field.getAnnotation(Column.class);// 獲取實(shí)例 ????????????????// 獲取元素值 ????????????????String columnName = c.name(); ????????????????// 如果未指定列名,默認(rèn)列名使用成員變量名 ????????????????if ("".equals(columnName.trim())) { ????????????????????columnName = field.getName(); ????????????????} ????????????filedSql.append(columnName + ","); ????????????valueSql.append("#{" + objName + "." + field.getName() + "},"); ????????} ????????//remove last ',' ????????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; ????} |
利用反射+注解之后的輸出結(jié)果,可以看到sql語(yǔ)句正確按照name的Column注解的輸出了name屬性對(duì)應(yīng)的數(shù)據(jù)庫(kù)字段是poi_name.
| 1 2 3 4 5 6 7 8 9 | 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}) |
原文出處:?Giraffe from:?http://www.importnew.com/22918.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專(zhuān)家共同創(chuàng)作,文字、視頻、音頻交互閱讀
總結(jié)
以上是生活随笔為你收集整理的使用基于注解的mybatis时,利用反射和注解生成sql语句的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 锁Lock 那点事儿
- 下一篇: BigDecimal使用时的参数详解