使用基于注解的mybatis時,利用反射和注解生成sql語句
在開發時遇到一個問題,在使用基于注解的mybatis插入一個對象到mysql時,在寫sql語句時需要列出對象的所有屬性,所以在插入一個擁有10個以上屬性的對象時sql語句就會變得很長,寫起來也很不方便,也很容易拼錯。google了一下也沒有找到什么解決方式(可能是姿勢不對),在stackoverflow上提的 問題 截止目前還沒有人回答。所以自己想了一個基于反射和注解的解決辦法
下面是之前的代碼片段:
@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語句的方法,然后基于mybatis動態獲得sql語句的方式 獲得完整的sql 具體的代碼如下:
接口層改為下面的樣子,sql語句的生成放到PoiSqlProvider的insertPoiBo方法中
@InsertProvider(type = PoiSqlProvider.class, method = "insertPoiBo")
public Long insertPoiInfo(@Param("poiBo")PoiBo poiBo);
PoiSqlProvider.class
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() + "},");
}
//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語句和最后拼接的語句
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")
private String name;//表示name屬性對應數據庫poi_name字段
@Column(name = "poi_brand")
private String brand;//表示brand屬性對應數據庫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方法,通過獲取類屬性上的注解獲得數據庫字段名。
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() + "},");
}
//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;
}
利用反射+注解之后的輸出結果,可以看到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})
來自:http://www.importnew.com/22918.html