Spring JdbcTemplate 查询结果集Map反向生成Java实体

来源:互联网 发布:全力以赴歌曲网络歌手 编辑:程序博客网 时间:2024/06/09 19:18

以前写过一篇文章吐槽过Spring JdbcTemplate的queryForList方法(参见:http://blog.csdn.net/will_awoke/article/details/12617383),因为这个方法只支持单数据类型泛型实体,而想返回自定义实体时还得自己写callback方法,笔者不想在每个返回自定义实体的query方法中都去写callback处理返回的map,于是索性就自己造了个轮子,有点像hibernate的orm的赶脚。话说,现在用惯了SpringJDBC,反而又不喜欢hibernate了,感觉hibernate太重了,人就是这么奇怪。O(∩_∩)O~


回归正题,下面讲下造的这个轮子,轮子的应用场景如下:

1.已使用了 List<Map<String, Object>> org.springframework.jdbc.core.JdbcTemplate.queryForList(String sql, Object[] args, int[] argTypes) throws DataAccessException 方法,想将此方法返回的Map反向生成对应的实体Instance;

2.而你又不想在使用到上述方法的每处都override callback方法去处理map(和笔者一样懒,O(∩_∩)O~);

3.你的实体类字段和DBTable中的字段命名不一样,关于这一点下文会提到,其实,真正按着编程规范来的话,实体类是绝对不可能和DB中字段命名一样的。

如果你具备了上述应用场景,那么可以继续向下看轮子了。


我们知道jdbcTemplate.queryForList(sql, params, types)返回的是个List<Map<String,Object>>,如:{USER_ID=5438,LOGIN_NAME=admin,PASSWORD=admin123456},其中key是大写的。想根据map反向生成JavaBeanInstance,首先你得拥有一个实体类,下面是一个轮子中用到的Bean.java:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. import java.io.Serializable;  
  2.   
  3. import javax.persistence.Column;  
  4.   
  5. import com.alibaba.fastjson.JSON;  
  6.   
  7. /** 
  8.  * 系统用户 
  9.  *  
  10.  * column注解配置与hibernate一致,不过只需要name属性,其他属性不再需要 
  11.  * name属性值对应数据库中字段名称,忽略大小写 
  12.  * 该注解用于map反向生成modelbean 
  13.  * 另请参考:ReflectUtil.java 
  14.  *  
  15.  * @author will_awoke 
  16.  * @version 2014-5-29 
  17.  * @see SysUser 
  18.  * @since 
  19.  */  
  20. public class SysUser implements Serializable  
  21. {  
  22.   
  23.     /** 
  24.      * 序列号<br> 
  25.      */  
  26.     private static final long serialVersionUID = 7931705053661707847L;  
  27.   
  28.     @Column(name = "USER_ID")  
  29.     private Long userId;  
  30.   
  31.     @Column(name = "LOGIN_NAME")  
  32.     private String loginName;  
  33.   
  34.     @Column(name = "PASSWORD")  
  35.     private String password;  
  36.   
  37.     /** 
  38.      * 构造器 
  39.      */  
  40.     public SysUser()  
  41.     {  
  42.   
  43.     }  
  44.   
  45.     @Override  
  46.     public String toString()  
  47.     {  
  48.         return JSON.toJSONString(this);  
  49.     }  
  50.   
  51.     //setter getter  
  52.     public Long getUserId()  
  53.     {  
  54.         return userId;  
  55.     }  
  56.   
  57.     public void setUserId(Long userId)  
  58.     {  
  59.         this.userId = userId;  
  60.     }  
  61.   
  62.     public String getLoginName()  
  63.     {  
  64.         return loginName;  
  65.     }  
  66.   
  67.     public void setLoginName(String loginName)  
  68.     {  
  69.         this.loginName = loginName;  
  70.     }  
  71.   
  72.     public String getPassword()  
  73.     {  
  74.         return password;  
  75.     }  
  76.   
  77.     public void setPassword(String password)  
  78.     {  
  79.         this.password = password;  
  80.     }  
  81.   
  82. }  

实体类中使用了@Column注解,用于绑定实体field字段和DB中column字段,思想类似于Hibernate。

关于@Column 可以使用Hibernate提供的jar:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <dependency>  
  2.     <groupId>org.hibernate</groupId>  
  3.     <artifactId>ejb3-persistence</artifactId>  
  4.     <version>1.0.2.GA</version>  
  5. </dependency>  

或者是自己可以手写个注解:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * 注解映射 
  3.  *  
  4.  * @author will_awoke 
  5.  * @version  
  6.  * @see Column 
  7.  * @since 
  8.  */  
  9. @Target(java.lang.annotation.ElementType.FIELD)  
  10. @Retention(RetentionPolicy.RUNTIME)  
  11. public @interface Column {  
  12.   
  13.     String name() default "";  
  14.   
  15. }  


核心反射工具类如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. import java.lang.annotation.Annotation;  
  2. import java.lang.reflect.Field;  
  3. import java.util.HashMap;  
  4. import java.util.LinkedHashMap;  
  5. import java.util.Map;  
  6.   
  7. import javax.persistence.Column;  
  8.   
  9. import com.alibaba.fastjson.JSON;  
  10. import com.iss.rabc.bean.SysUser;  
  11.   
  12.   
  13. /** 
  14.  *  
  15.  * @author will_awoke 
  16.  * @version 2014-5-30 
  17.  * @see ReflectUtil 
  18.  * @since 
  19.  */  
  20. public class ReflectUtil  
  21. {  
  22.   
  23.     /** 
  24.      * 将jdbcTemplate查询的map结果集 反射生成对应的bean 
  25.      * @param clazz 意向反射的实体.clazz 
  26.      * @param jdbcMapResult 查询结果集  key is UpperCase 
  27.      * @return  
  28.      * @see 
  29.      */  
  30.     public static <T> T reflect(Class<T> clazz, Map<String, Object> jdbcMapResult)  
  31.     {  
  32.         //获得  
  33.         Field[] fields = clazz.getDeclaredFields();  
  34.   
  35.         //存放field和column对应关系,该关系来自于实体类的 @Column配置  
  36.         Map<String/*field name in modelBean*/, String/*column in db*/> fieldHasColumnAnnoMap = new LinkedHashMap<String, String>();  
  37.         Annotation[] annotations = null;  
  38.         for (Field field : fields)  
  39.         {  
  40.             annotations = field.getAnnotations();  
  41.             for (Annotation an : annotations)  
  42.             {  
  43.                 if (an instanceof Column)  
  44.                 {  
  45.                     Column column = (Column)an;  
  46.                     fieldHasColumnAnnoMap.put(field.getName(), column.name());  
  47.                 }  
  48.             }  
  49.         }  
  50.         //存放field name 和 对应的来自map的该field的属性值,用于后续reflect成ModelBean  
  51.         Map<String, Object> conCurrent = new LinkedHashMap<String, Object>();  
  52.         for (Map.Entry<String, String> en : fieldHasColumnAnnoMap.entrySet())  
  53.         {  
  54.             //将column大写。因为jdbcMapResult key is UpperCase  
  55.             String key = en.getValue().toUpperCase();  
  56.               
  57.             //获得map的该field的属性值  
  58.             Object value = jdbcMapResult.get(key);  
  59.               
  60.             //确保value有效性,防止JSON reflect时异常  
  61.             if (value != null)  
  62.             {  
  63.                 conCurrent.put(en.getKey(), jdbcMapResult.get(key));  
  64.             }  
  65.         }  
  66.         //fastjson reflect to modelbean  
  67.         return JSON.parseObject(JSON.toJSONString(conCurrent), clazz);  
  68.     }  
  69.   
  70.       
  71.     /** 
  72.      * test example 
  73.      * @param args 
  74.      * @throws Exception  
  75.      * @see 
  76.      */  
  77.     public static void main(String[] args)  
  78.         throws Exception  
  79.     {              
  80.         //call reflect testing  
  81.         Map<String, Object> jdbcMapResult = new HashMap<>();  
  82.         jdbcMapResult.put("LOGIN_NAME""reflect");  
  83.         jdbcMapResult.put("PASSWORD""reflect123456");  
  84.           
  85.         System.out.println(ReflectUtil.reflect(SysUser.class, jdbcMapResult));  
  86.     }  
  87. }  

工具类中,实现map的替换处理,然后利用fastjson将map反射成JavaBean即可,一如上述的场景条件3中提到,如果实体类字段和DBTable中的字段命名一样直接就可以用fastjson reflect成JavaBean,而就不需要这些轮子了。


应用层调用:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. import static com.iss.util.ReflectUtil.reflect;  
  2.   
  3. List<Map<String, Object>> mapList = userDao.queryNaviByUser(loginName);  
  4.   
  5. List<SysMenu> meunList = new LinkedList<SysMenu>();  
  6. for (Map<String, Object> jdbcMapResult : mapList)  
  7. {  
  8.      //利用工具类反向生成bean  
  9.      meunList.add(reflect(SysMenu.class, jdbcMapResult));  
  10. }  


综上,轮子完毕。

0 0
原创粉丝点击