相关背景

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

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

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

举个简单例子如下:

       DynaProperty[] props = new DynaProperty[]{
new DynaProperty("address", java.util.Map.class),
new DynaProperty("subordinate", mypackage.Employee[].class),
new DynaProperty("firstName", String.class),
new DynaProperty("lastName", String.class)
};
BasicDynaClass dynaClass = new BasicDynaClass("employee", null, props); DynaBean employee = dynaClass.newInstance();
employee.set("address", new HashMap());
employee.set("subordinate", new Employee[]{...});
employee.set("firstName", "Fred");
employee.set("lastName", "Flintstone"); DynaBean employee = ...; // 具体的DynaBean实现类 String firstName = (String) employee.get("firstName");
Address homeAddress = (Address) employee.get("address", "home");
Object subordinate = employee.get("subordinate", 2);

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

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

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

基础实现类:BasicDynaBean和BasicDynaClass

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

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

        DynaProperty[] props = new DynaProperty[]
{
new DynaProperty("address", java.util.Map.class),
new DynaProperty("subordinate", Employee[].class),
new DynaProperty("firstName", String.class),
new DynaProperty("lastName", String.class)
};
BasicDynaClass dynaClass = new BasicDynaClass("employee", null, props);

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

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

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

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

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

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

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

            String sql = "SELECT id, name, address, state FROM user";
stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery(sql); while (rs.next())
{
Long id = rs.getLong("id");
String name = rs.getString("name");
String address = rs.getString("address");
boolean state = rs.getBoolean("state"); System.out.print("id: " + id);
System.out.print(", name: " + name);
System.out.print(", address: " + address);
System.out.println(", state: " + state);
}

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

            String sql = "SELECT id, name, address, state FROM user";
stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery(sql);
Iterator<DynaBean> rows = (new ResultSetDynaClass(rs)).iterator();
while (rows.hasNext())
{
DynaBean row = rows.next();
System.out.print("id: " + row.get("id"));
System.out.print(", name: " + row.get("name"));
System.out.print(", address: " + row.get("address"));
System.out.println(", state: " + row.get("state"));
}

完整示例:

/*
* File Name: ResultSetDyna.java
* Description:
* Author: PiChen
* Create Date: 2017年5月30日
*/
package apache.commons.beanutils.example.dynabeans; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator; import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.ResultSetDynaClass; /**
*
* @author PiChen
* @version 2017年5月30日
*/ public class ResultSetDyna
{
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost/demo"; static final String USER = "root";
static final String PASS = "root"; public static void main(String[] args)
{
Connection conn = null;
PreparedStatement stmt = null;
try
{
Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(DB_URL, USER, PASS); String sql = "SELECT id, name, address, state FROM user";
stmt = conn.prepareStatement(sql); ResultSet rs = stmt.executeQuery(sql); // while (rs.next())
// {
// Long id = rs.getLong("id");
// String name = rs.getString("name");
// String address = rs.getString("address");
// boolean state = rs.getBoolean("state");
//
// System.out.print("id: " + id);
// System.out.print(", name: " + name);
// System.out.print(", address: " + address);
// System.out.println(", state: " + state);
// } Iterator<DynaBean> rows = (new ResultSetDynaClass(rs)).iterator();
while (rows.hasNext())
{
DynaBean row = rows.next();
System.out.print("id: " + row.get("id"));
System.out.print(", name: " + row.get("name"));
System.out.print(", address: " + row.get("address"));
System.out.println(", state: " + row.get("state"));
} rs.close();
stmt.close();
conn.close();
}
catch (SQLException se)
{
se.printStackTrace();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try
{
if (stmt != null)
stmt.close();
}
catch (SQLException se2)
{
}
try
{
if (conn != null)
conn.close();
}
catch (SQLException se)
{
se.printStackTrace();
}
}
} }

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

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

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

如下是一个示例:

           Class.forName("com.mysql.jdbc.Driver");

            conn = DriverManager.getConnection(DB_URL, USER, PASS);

            String sql = "SELECT id, name, address, state FROM user";
stmt = conn.prepareStatement(sql); ResultSet rs = stmt.executeQuery(sql); RowSetDynaClass rowSet = new RowSetDynaClass(rs); rs.close();
stmt.close();
conn.close(); List<DynaBean> rowlist = rowSet.getRows();
for (DynaBean row : rowlist)
{
System.out.print("id: " + row.get("id"));
System.out.print(", name: " + row.get("name"));
System.out.print(", address: " + row.get("address"));
System.out.println(", state: " + row.get("state"));
}

完整示例:

/*
* File Name: ResultSetDyna.java
* Description:
* Author: PiChen
* Create Date: 2017年5月30日
*/
package apache.commons.beanutils.example.dynabeans; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List; import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.RowSetDynaClass; /**
*
* @author PiChen
* @version 2017年5月30日
*/ public class RowSetDyna
{
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost/demo"; static final String USER = "root";
static final String PASS = "root"; public static void main(String[] args)
{
Connection conn = null;
PreparedStatement stmt = null;
try
{
Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(DB_URL, USER, PASS); String sql = "SELECT id, name, address, state FROM user";
stmt = conn.prepareStatement(sql); ResultSet rs = stmt.executeQuery(sql); RowSetDynaClass rowSet = new RowSetDynaClass(rs); rs.close();
stmt.close();
conn.close(); List<DynaBean> rowlist = rowSet.getRows();
for (DynaBean row : rowlist)
{
System.out.print("id: " + row.get("id"));
System.out.print(", name: " + row.get("name"));
System.out.print(", address: " + row.get("address"));
System.out.println(", state: " + row.get("state"));
}
}
catch (SQLException se)
{
se.printStackTrace();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try
{
if (stmt != null)
stmt.close();
}
catch (SQLException se2)
{
}
try
{
if (conn != null)
conn.close();
}
catch (SQLException se)
{
se.printStackTrace();
}
}
} }

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

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

示例:

        Employee e = new Employee();
e.setFirstName("hello"); DynaBean wrapper = new WrapDynaBean(e);
String firstName = (String) wrapper.get("firstName");
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例子:

        DynaBean dynaBean = new LazyDynaBean();

        dynaBean.set("foo", "bar");                   // simple

        dynaBean.set("customer", "title", "Mr");      // mapped
dynaBean.set("customer", "surname", "Smith"); // mapped dynaBean.set("users", 0, new User()); // indexed
dynaBean.set("users", 1, new User()); // indexed
dynaBean.set("users", 2, new User()); // indexed System.out.println(dynaBean.get("customer", "title"));

如下是一个LazyDynaMap例子:

       DynaBean dynaBean = new LazyDynaMap();

        dynaBean.set("foo", "bar");                   // simple

        dynaBean.set("customer", "title", "Mr");      // mapped
dynaBean.set("customer", "surname", "Smith"); // mapped dynaBean.set("users", 0, new User()); // indexed
dynaBean.set("users", 1, new User()); // indexed
dynaBean.set("users", 2, new User()); // indexed System.out.println(dynaBean.get("customer", "title")); //转成Map对象
Map<String, Object> myMap = ((LazyDynaBean) dynaBean).getMap();
System.out.println(myMap);

LazyDynaList例子,详见API文档

  LazyDynaList dynaBean = new LazyDynaList();
dynaBean.setElementType(User.class); User u = new User();
u.setName("hello"); dynaBean.add(1, u); System.out.println(dynaBean.size()); User[] users = (User[])dynaBean.toArray();//转化为数组
System.out.println(users[1].getName()); WrapDynaBean w = (WrapDynaBean) dynaBean.get(1);
System.out.println(w.get("name"));

LazyDynaClass示例:

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

        MutableDynaClass dynaClass = new LazyDynaClass();    // create DynaClass

        dynaClass.add("amount", java.lang.Integer.class);    // add property
dynaClass.add("users", User[].class); // add indexed property
dynaClass.add("orders", TreeMap.class); // add mapped property DynaBean dynaBean = new LazyDynaBean(dynaClass);
dynaBean.set("amount_", "s");
dynaBean.set("amount", "s");//报错,需要为整型
dynaBean.set("users", 1);//报错,需要维数组 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. onload、onpageshow、onpagehide、onbeforeunload、onunload的谣言纠正及特点介绍

    谣言一.chrome不支持unload.onbeforeunload 为什么说不支持呢?因为你使用alert,confirm,promot用来测试是否可用了!在unload和onbeforeunloa ...

  2. Django formset

    一 什么是formset Form组件或ModelForm用于做一个表单验证而formset是用于做多个表单的验证组件,用于做批量操作 二 formset的使用方法 1 设置form信息 class ...

  3. 服务器重新启动,oracle数据库重新连接问题

    最近服务器使用期限到了,用户无法登陆,联系了网络的同事,重新开启了服务器,用户可以重新登陆了,但是在使用oracle数据库的时候,发现报了以下的错误: 很明显是数据库服务没有启动,解决这个问题分两步: ...

  4. apache提示make_sock?

    [root@localhost apache]# /etc/init.d/*_apache restart 停止 *_apache: [失败] 正在启动 *_apache:(98)Address al ...

  5. linux下反弹shell

    01 前言 CTF中一些命令执行的题目需要反弹shell,于是solo一波. 02 环境 win10      192.168.43.151       监听端    装有nc kali        ...

  6. Monotonic Array LT896

    An array is monotonic if it is either monotone increasing or monotone decreasing. An array A is mono ...

  7. 在datasnap 中使用unidac 访问数据(服务器端)

    从delphi 6 开始,datasnap 作为delphi 自带的多层框架,一直更新到最新的delphi 10.3 .同时逐步增加了很多新的功能 ,比如支持REST 调用,支持 IIS ,apach ...

  8. ABP框架系列之十五:(Caching-缓存)

    Introduction ASP.NET Boilerplate provides an abstraction for caching. It internally uses this cache ...

  9. 秒杀系统-DAO

    DAO(Data Access Object) 数据访问对象 首先需要创建秒杀库存表和秒杀成功明细表,如下所示: CREATE DATABASE seckill; use seckill; CREAT ...

  10. 【转】Map 与 Unordered_map

    map和unordered_map的差别和使用 map和unordered_map的差别还不知道或者搞不清unordered_map和map是什么的,请见:http://blog.csdn.net/b ...