相关背景

上一篇介绍了PropertyUtils的用法,PropertyUtils主要是在不修改bean结构的前提下,动态访问bean的属性;

但是有时候,我们会经常希望能够在不定义一个Java类的前提下,动态决定这个类中包含哪些属性,并动态访问它们的属性值,比较典型的使用场景是作为SQL查询的结果集的bean;

为了支持以上特性,Apache Commons Beanutils包为我们提供了DynaBean接口、DynaClass接口;

举个简单例子如下:

  1. DynaProperty[] props = new DynaProperty[]{
  2. new DynaProperty("address", java.util.Map.class),
  3. new DynaProperty("subordinate", mypackage.Employee[].class),
  4. new DynaProperty("firstName", String.class),
  5. new DynaProperty("lastName", String.class)
  6. };
  7. BasicDynaClass dynaClass = new BasicDynaClass("employee", null, props);
  8.  
  9. DynaBean employee = dynaClass.newInstance();
  10. employee.set("address", new HashMap());
  11. employee.set("subordinate", new Employee[]{...});
  12. employee.set("firstName", "Fred");
  13. employee.set("lastName", "Flintstone");
  14.  
  15. DynaBean employee = ...; // 具体的DynaBean实现类
  16.  
  17. String firstName = (String) employee.get("firstName");
  18. Address homeAddress = (Address) employee.get("address", "home");
  19. Object subordinate = employee.get("subordinate", 2);

由于DynaBean和DynaClass都是接口,它们可以有多种实现形式,应用于多种场景。

接下来,会介绍在Apache Commons Beanutils包下,DynaBean和DynaClass接口不同的实现类;

当然,我们也可以自定义实现类来满足我们特定的需求;

基础实现类:BasicDynaBean和BasicDynaClass

先了解下这两个重要的实现,这两个类为DynaBean和DynaClass接口的基础实现类;

首先,我们可以这样创建一个DynaClass实例,其中类的成员属性是用DynaProperty类来描述的:

  1. DynaProperty[] props = new DynaProperty[]
  2. {
  3. new DynaProperty("address", java.util.Map.class),
  4. new DynaProperty("subordinate", Employee[].class),
  5. new DynaProperty("firstName", String.class),
  6. new DynaProperty("lastName", String.class)
  7. };
  8. BasicDynaClass dynaClass = new BasicDynaClass("employee", null, props);

注意这里的Class<?> dynaBeanClass参数为空,看下源码就发现,如果为null的话,默认会使用BasicDynaBean.class;

有了BasicDynaClass实例后,我们就可以开始创建DynaBean实例了,并且可以调用DynaBean接口中定义的方法,如get和set来读写属性值,如下所示:

  1. DynaBean employee = dynaClass.newInstance();
  2. employee.set("address", new HashMap<String, Object>());
  3. employee.set("subordinate", new Employee[0]);
  4. employee.set("firstName", "Fred");
  5. employee.set("lastName", "Flintstone");

      System.out.println(employee.get("firstName"));

实现类:ResultSetDynaClass,处理数据库查询结果集

ResultSetDynaClass主要用于包装java.sql.ResultSet,即SQL查询时候返回的结果集;

不使用DynaBean的话,通常我们是这样处理的:

  1. String sql = "SELECT id, name, address, state FROM user";
  2. stmt = conn.prepareStatement(sql);
  3. ResultSet rs = stmt.executeQuery(sql);
  4.  
  5. while (rs.next())
  6. {
  7. Long id = rs.getLong("id");
  8. String name = rs.getString("name");
  9. String address = rs.getString("address");
  10. boolean state = rs.getBoolean("state");
  11.  
  12. System.out.print("id: " + id);
  13. System.out.print(", name: " + name);
  14. System.out.print(", address: " + address);
  15. System.out.println(", state: " + state);
  16. }

使用ResultSetDynaClass的话,我们可以这样做:

  1. String sql = "SELECT id, name, address, state FROM user";
  2. stmt = conn.prepareStatement(sql);
  3. ResultSet rs = stmt.executeQuery(sql);
  4. Iterator<DynaBean> rows = (new ResultSetDynaClass(rs)).iterator();
  5. while (rows.hasNext())
  6. {
  7. DynaBean row = rows.next();
  8. System.out.print("id: " + row.get("id"));
  9. System.out.print(", name: " + row.get("name"));
  10. System.out.print(", address: " + row.get("address"));
  11. System.out.println(", state: " + row.get("state"));
  12. }

完整示例:

  1. /*
  2. * File Name: ResultSetDyna.java
  3. * Description:
  4. * Author: PiChen
  5. * Create Date: 2017年5月30日
  6. */
  7. package apache.commons.beanutils.example.dynabeans;
  8.  
  9. import java.sql.Connection;
  10. import java.sql.DriverManager;
  11. import java.sql.PreparedStatement;
  12. import java.sql.ResultSet;
  13. import java.sql.SQLException;
  14. import java.util.Iterator;
  15.  
  16. import org.apache.commons.beanutils.DynaBean;
  17. import org.apache.commons.beanutils.ResultSetDynaClass;
  18.  
  19. /**
  20. *
  21. * @author PiChen
  22. * @version 2017年5月30日
  23. */
  24.  
  25. public class ResultSetDyna
  26. {
  27. static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
  28. static final String DB_URL = "jdbc:mysql://localhost/demo";
  29.  
  30. static final String USER = "root";
  31. static final String PASS = "root";
  32.  
  33. public static void main(String[] args)
  34. {
  35. Connection conn = null;
  36. PreparedStatement stmt = null;
  37. try
  38. {
  39. Class.forName("com.mysql.jdbc.Driver");
  40.  
  41. conn = DriverManager.getConnection(DB_URL, USER, PASS);
  42.  
  43. String sql = "SELECT id, name, address, state FROM user";
  44. stmt = conn.prepareStatement(sql);
  45.  
  46. ResultSet rs = stmt.executeQuery(sql);
  47.  
  48. // while (rs.next())
  49. // {
  50. // Long id = rs.getLong("id");
  51. // String name = rs.getString("name");
  52. // String address = rs.getString("address");
  53. // boolean state = rs.getBoolean("state");
  54. //
  55. // System.out.print("id: " + id);
  56. // System.out.print(", name: " + name);
  57. // System.out.print(", address: " + address);
  58. // System.out.println(", state: " + state);
  59. // }
  60.  
  61. Iterator<DynaBean> rows = (new ResultSetDynaClass(rs)).iterator();
  62. while (rows.hasNext())
  63. {
  64. DynaBean row = rows.next();
  65. System.out.print("id: " + row.get("id"));
  66. System.out.print(", name: " + row.get("name"));
  67. System.out.print(", address: " + row.get("address"));
  68. System.out.println(", state: " + row.get("state"));
  69. }
  70.  
  71. rs.close();
  72. stmt.close();
  73. conn.close();
  74. }
  75. catch (SQLException se)
  76. {
  77. se.printStackTrace();
  78. }
  79. catch (Exception e)
  80. {
  81. e.printStackTrace();
  82. }
  83. finally
  84. {
  85. try
  86. {
  87. if (stmt != null)
  88. stmt.close();
  89. }
  90. catch (SQLException se2)
  91. {
  92. }
  93. try
  94. {
  95. if (conn != null)
  96. conn.close();
  97. }
  98. catch (SQLException se)
  99. {
  100. se.printStackTrace();
  101. }
  102. }
  103. }
  104.  
  105. }

实现类:RowSetDynaClass,处理数据库查询结果集,连接关闭后仍可使用

ResultSetDynaClass作为SQL查询结果集中的一个动态bean非常实用,但是仍然有一个严重的缺陷,就是使用ResultSetDynaClass的前提是要保证ResultSet一直处于打开状态,这对于分层结构的Web项目来说是非常不便的,因为我们经常需要将数据从dao层传到service层传到view层,而ResultSet在DAO层使用后往往会关闭掉;

为解决这个问题,引入了RowSetDynaClass实现类,与ResultSetDynaClass不同的是,它会自己在内存中拷贝一份数据,这样就保证了即使ResultSet关闭后,数据也能一直被访问到;不过同样也有缺点就是需要消耗性能用于拷贝数据以及占用堆内存空间;

如下是一个示例:

  1. Class.forName("com.mysql.jdbc.Driver");
  2.  
  3. conn = DriverManager.getConnection(DB_URL, USER, PASS);
  4.  
  5. String sql = "SELECT id, name, address, state FROM user";
  6. stmt = conn.prepareStatement(sql);
  7.  
  8. ResultSet rs = stmt.executeQuery(sql);
  9.  
  10. RowSetDynaClass rowSet = new RowSetDynaClass(rs);
  11.  
  12. rs.close();
  13. stmt.close();
  14. conn.close();
  15.  
  16. List<DynaBean> rowlist = rowSet.getRows();
  17. for (DynaBean row : rowlist)
  18. {
  19. System.out.print("id: " + row.get("id"));
  20. System.out.print(", name: " + row.get("name"));
  21. System.out.print(", address: " + row.get("address"));
  22. System.out.println(", state: " + row.get("state"));
  23. }

完整示例:

  1. /*
  2. * File Name: ResultSetDyna.java
  3. * Description:
  4. * Author: PiChen
  5. * Create Date: 2017年5月30日
  6. */
  7. package apache.commons.beanutils.example.dynabeans;
  8.  
  9. import java.sql.Connection;
  10. import java.sql.DriverManager;
  11. import java.sql.PreparedStatement;
  12. import java.sql.ResultSet;
  13. import java.sql.SQLException;
  14. import java.util.List;
  15.  
  16. import org.apache.commons.beanutils.DynaBean;
  17. import org.apache.commons.beanutils.RowSetDynaClass;
  18.  
  19. /**
  20. *
  21. * @author PiChen
  22. * @version 2017年5月30日
  23. */
  24.  
  25. public class RowSetDyna
  26. {
  27. static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
  28. static final String DB_URL = "jdbc:mysql://localhost/demo";
  29.  
  30. static final String USER = "root";
  31. static final String PASS = "root";
  32.  
  33. public static void main(String[] args)
  34. {
  35. Connection conn = null;
  36. PreparedStatement stmt = null;
  37. try
  38. {
  39. Class.forName("com.mysql.jdbc.Driver");
  40.  
  41. conn = DriverManager.getConnection(DB_URL, USER, PASS);
  42.  
  43. String sql = "SELECT id, name, address, state FROM user";
  44. stmt = conn.prepareStatement(sql);
  45.  
  46. ResultSet rs = stmt.executeQuery(sql);
  47.  
  48. RowSetDynaClass rowSet = new RowSetDynaClass(rs);
  49.  
  50. rs.close();
  51. stmt.close();
  52. conn.close();
  53.  
  54. List<DynaBean> rowlist = rowSet.getRows();
  55. for (DynaBean row : rowlist)
  56. {
  57. System.out.print("id: " + row.get("id"));
  58. System.out.print(", name: " + row.get("name"));
  59. System.out.print(", address: " + row.get("address"));
  60. System.out.println(", state: " + row.get("state"));
  61. }
  62. }
  63. catch (SQLException se)
  64. {
  65. se.printStackTrace();
  66. }
  67. catch (Exception e)
  68. {
  69. e.printStackTrace();
  70. }
  71. finally
  72. {
  73. try
  74. {
  75. if (stmt != null)
  76. stmt.close();
  77. }
  78. catch (SQLException se2)
  79. {
  80. }
  81. try
  82. {
  83. if (conn != null)
  84. conn.close();
  85. }
  86. catch (SQLException se)
  87. {
  88. se.printStackTrace();
  89. }
  90. }
  91. }
  92.  
  93. }

实现类:WrapDynaBean和WrapDynaClass,包装普通bean

使用WrapDynaBean,我们可以将普通的javabean包装成DynaBean,并非常简便的使用DynaBean提供的API方法来访问bean成员属性

示例:

  1. Employee e = new Employee();
  2. e.setFirstName("hello");
  3.  
  4. DynaBean wrapper = new WrapDynaBean(e);
  5. String firstName = (String) wrapper.get("firstName");
  6. System.out.println(firstName);

注意,以上代码中,会间接的创建了WrapDynaClass实例,我们不需要直接处理它;

实现类:Lazy DynaBeans,简单易用的DynaBean实现

Lazy DynaBeans,正如其名,可以让我们省去很多工作,更加人性化的去使用DynaBean,

Lazy DynaBeans有如下特性:

1、自动添加bean属性,当我们调用set(name, value)方法时,如果属性不存在,会自动添加该属性;

2、List、Array属性自动扩容,

3、List、Array属性里的内部元素可以自动创建,实例化

4、Map属性也可以自动创建,实例化

5、...

简单的说,使用Lazy DynaBeans的话,你可以大胆调用DynaBean的set、get方法,而不必担心没有属性不存在,集合数组空间不够等问题,Lazy DynaBeans会帮我们自动处理;

如下是一个LazyDynaBean例子:

  1. DynaBean dynaBean = new LazyDynaBean();
  2.  
  3. dynaBean.set("foo", "bar"); // simple
  4.  
  5. dynaBean.set("customer", "title", "Mr"); // mapped
  6. dynaBean.set("customer", "surname", "Smith"); // mapped
  7.  
  8. dynaBean.set("users", 0, new User()); // indexed
  9. dynaBean.set("users", 1, new User()); // indexed
  10. dynaBean.set("users", 2, new User()); // indexed
  11.  
  12. System.out.println(dynaBean.get("customer", "title"));

如下是一个LazyDynaMap例子:

  1. DynaBean dynaBean = new LazyDynaMap();
  2.  
  3. dynaBean.set("foo", "bar"); // simple
  4.  
  5. dynaBean.set("customer", "title", "Mr"); // mapped
  6. dynaBean.set("customer", "surname", "Smith"); // mapped
  7.  
  8. dynaBean.set("users", 0, new User()); // indexed
  9. dynaBean.set("users", 1, new User()); // indexed
  10. dynaBean.set("users", 2, new User()); // indexed
  11.  
  12. System.out.println(dynaBean.get("customer", "title"));
  13.  
  14. //转成Map对象
  15. Map<String, Object> myMap = ((LazyDynaBean) dynaBean).getMap();
  16. System.out.println(myMap);

LazyDynaList例子,详见API文档

  1. LazyDynaList dynaBean = new LazyDynaList();
  2. dynaBean.setElementType(User.class);
  3.  
  4. User u = new User();
  5. u.setName("hello");
  6.  
  7. dynaBean.add(1, u);
  8.  
  9. System.out.println(dynaBean.size());
  10.  
  11. User[] users = (User[])dynaBean.toArray();//转化为数组
  12. System.out.println(users[1].getName());
  13.  
  14. WrapDynaBean w = (WrapDynaBean) dynaBean.get(1);
  15. System.out.println(w.get("name"));

LazyDynaClass示例:

Lazy DynaBeans可以让我们不受控制的添加任意类型的bean属性,但是有时候,我们还是希望能控制某个bean属性的数据类型,如下,是一个示例:

  1. MutableDynaClass dynaClass = new LazyDynaClass(); // create DynaClass
  2.  
  3. dynaClass.add("amount", java.lang.Integer.class); // add property
  4. dynaClass.add("users", User[].class); // add indexed property
  5. dynaClass.add("orders", TreeMap.class); // add mapped property
  6.  
  7. DynaBean dynaBean = new LazyDynaBean(dynaClass);
  8. dynaBean.set("amount_", "s");
  9. dynaBean.set("amount", "s");//报错,需要为整型
  10. dynaBean.set("users", 1);//报错,需要维数组
  11.  
  12. System.out.println(dynaBean.get("amount"));

参考资料

http://commons.apache.org/proper/commons-beanutils/javadocs/v1.9.3/apidocs/org/apache/commons/beanutils/package-summary.html

源码

https://github.com/peterchenhdu/apache-commons-beanutils-example

Apache Commons Beanutils 二 (动态Bean - DynaBeans)的更多相关文章

  1. Apache Commons Beanutils 一 (使用PropertyUtils访问Bean属性)

    BeanUtils简要描述 beanutils,顾名思义,是java bean的一个工具类,可以帮助我们方便的读取(get)和设置(set)bean属性值.动态定义和访问bean属性: 细心的话,会发 ...

  2. 对于Java Bean的类型转换问题()使用 org.apache.commons.beanutils.ConvertUtils)

    在进行与数据库的交互过程中,由数据库查询到的数据放在 map 中,由 map 到 JavaBean 的过程中可以使用 BeanUtils.populate(map,bean)来进行转换 这里要处理的问 ...

  3. Apache Commons BeanUtils

    http://commons.apache.org/proper/commons-beanutils/javadocs/v1.9.2/apidocs/org/apache/commons/beanut ...

  4. Apache Commons Beanutils 三 (BeanUtils、ConvertUtils、CollectionUtils...)

    前言 前面已经学习了Apache Commons Beanutils包里的PropertyUtils和动态bean,接下来将学习剩下的几个工具类,个人觉得还是非常实用的,特别是CollectionUt ...

  5. org.springframework.beans.BeanUtils与org.apache.commons.beanutils.BeanUtils的copyProperties用法区别

    知识点 org.springframework.beans.BeanUtils与org.apache.commons.beanutils.BeanUtils都提供了copyProperties方法,作 ...

  6. 再续前缘-apache.commons.beanutils的补充

    title: 再续前缘-apache.commons.beanutils的补充 toc: true date: 2016-05-32 02:29:32 categories: 实在技巧 tags: 插 ...

  7. 关闭log4j 输出 DEBUG org.apache.commons.beanutils.*

    2016-03-23 10:52:26,860 DEBUG org.apache.commons.beanutils.MethodUtils - Matching name=getEPort on c ...

  8. Apache Commons Beanutils对象属性批量复制(pseudo-singleton)

    Apache Commons Beanutils为开源软件,可在Apache官网http://commons.apache.org/proper/commons-beanutils/download_ ...

  9. org.apache.commons.beanutils.BeanMap简单使用例子

    一.org.apache.commons.beanutils.BeanMap; 将一个java bean允许通过map的api进行调用, 几个支持的操作接口: Object get(Object ke ...

随机推荐

  1. (十)创建ROS消息和ROS服务

    ROS总教程(中文版) 110.创建ROS消息和ROS服务

  2. 微信小程序之----获取设备信息

    1. 获取系统信息        wx.getSystemInfo(OBJECT)        wx.getSystemInfoSync() 同步获取系统信息                回调常用 ...

  3. node.js 调试问题

    最近打算在项目过程中使用node.js辅助解决一些问题,需要用到node.js的调试技术. 通常而言,大家都会提到debugger或者node-inspector方法. debugger方法谁用谁知道 ...

  4. tiny4412 --uboot移植(1)

    开发环境:win10 64位 + VMware12 + Ubuntu14.04 32位 工具链:linaro提供的gcc-linaro-6.1.1-2016.08-x86_64_arm-linux-g ...

  5. Python开发——数据类型【元祖】

    元祖的定义 tu = (11,22,33,44,) print(tu) # (11, 22, 33, 44) tu = tuple((11,22,33,44,)) print(tu) # (11, 2 ...

  6. RNA提取和建库流程对mRNA-Seq的影响

    RNA提取和建库流程对mRNA-Seq的影响 已有 10460 次阅读 2014-8-14 14:21 |个人分类:转录组测序|系统分类:科研笔记|关键词:转录组测序,RNA-Seq,,链特异性RNA ...

  7. C#实现局部峰值查找,功能对应Matlab中的findpeaks.m(转)

    相关算法的原理参考Ronny,地址:图像分析:投影曲线的波峰查找,这里感谢下原作者. 参照C++的代码实现,我用C#翻译了下,其实原理也很简单的,下面放相关实现代码: private double[] ...

  8. RQNOJ 3 Jam的计数法

    一道模拟题,用的vector比用链表要方便很多,毕竟不需要自己写,因为是递增的,所以每一次你都要去检查最后一位加1之后有没有越界,如果没越界你就可以把他当前的字符删掉替换成他下一位的字符就可以了,如果 ...

  9. SpringBoot2.x配置JsonRedisSerializer

    @Configurationpublic class SpringCacheRedisConfig { @Bean public RedisCacheManager cacheManager(Redi ...

  10. 把router-link标签渲染成指定的标签

    <router-link>标签默认渲染成 <a>标签,可以通过tag属性把router-link渲染成指定的标签,如: <router-link to="/&q ...