Apache Commons Beanutils 二 (动态Bean - DynaBeans)
相关背景
上一篇介绍了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"));
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"));
参考资料
源码
https://github.com/peterchenhdu/apache-commons-beanutils-example
Apache Commons Beanutils 二 (动态Bean - DynaBeans)的更多相关文章
- Apache Commons Beanutils 一 (使用PropertyUtils访问Bean属性)
BeanUtils简要描述 beanutils,顾名思义,是java bean的一个工具类,可以帮助我们方便的读取(get)和设置(set)bean属性值.动态定义和访问bean属性: 细心的话,会发 ...
- 对于Java Bean的类型转换问题()使用 org.apache.commons.beanutils.ConvertUtils)
在进行与数据库的交互过程中,由数据库查询到的数据放在 map 中,由 map 到 JavaBean 的过程中可以使用 BeanUtils.populate(map,bean)来进行转换 这里要处理的问 ...
- Apache Commons BeanUtils
http://commons.apache.org/proper/commons-beanutils/javadocs/v1.9.2/apidocs/org/apache/commons/beanut ...
- Apache Commons Beanutils 三 (BeanUtils、ConvertUtils、CollectionUtils...)
前言 前面已经学习了Apache Commons Beanutils包里的PropertyUtils和动态bean,接下来将学习剩下的几个工具类,个人觉得还是非常实用的,特别是CollectionUt ...
- org.springframework.beans.BeanUtils与org.apache.commons.beanutils.BeanUtils的copyProperties用法区别
知识点 org.springframework.beans.BeanUtils与org.apache.commons.beanutils.BeanUtils都提供了copyProperties方法,作 ...
- 再续前缘-apache.commons.beanutils的补充
title: 再续前缘-apache.commons.beanutils的补充 toc: true date: 2016-05-32 02:29:32 categories: 实在技巧 tags: 插 ...
- 关闭log4j 输出 DEBUG org.apache.commons.beanutils.*
2016-03-23 10:52:26,860 DEBUG org.apache.commons.beanutils.MethodUtils - Matching name=getEPort on c ...
- Apache Commons Beanutils对象属性批量复制(pseudo-singleton)
Apache Commons Beanutils为开源软件,可在Apache官网http://commons.apache.org/proper/commons-beanutils/download_ ...
- org.apache.commons.beanutils.BeanMap简单使用例子
一.org.apache.commons.beanutils.BeanMap; 将一个java bean允许通过map的api进行调用, 几个支持的操作接口: Object get(Object ke ...
随机推荐
- 现代编译原理--第六章(中间树 IR Tree 含源码)
(转载请表明出处 http://www.cnblogs.com/BlackWalnut/p/4559717.html ) 这一章,就虎书而言,理论知识点是及其少的,就介绍了为什么要有一个中间表示树 ...
- Netsharp配置文件
一.总体说明 netsharp下需要配置的项目一般是需要独立启动的项目,主要有四个 netsharp-web netsharp-test netsharp-elephant netsharp-donk ...
- Python(四) 列表元组
- RNQOJ 4 数列
把N化成二进制是关键,比如把序号10化成二进制就是1010,对于K=2来说第10个数就是2^3+2^1,对于k=3来说第10个数就是3^3+3^1;这里只需要把K替代一下就可以解决了 #include ...
- SpringMVC学习 十三 拦截器栈
拦截器栈:就是有多个拦截器同时拦截相同的控制器(controller)请求,这写拦截器就构成了拦截器栈. 栈的特点是先进后出,在拦截器栈中也是如此,如果先执行了preHandle方法,也就是意味着先进 ...
- # 2019-2020.3 《java程序设计》第一周学习总结
2019-2020-3 <Java 程序设计>第一周学习总结 在本周的学习中,学习到了好多也收获了好多,从最基础的安装虚拟机开始,根据老师的博客中的教程一步一步的进行,在这过程中也遇到了好 ...
- QT中报错collect2:ld returned 1 exit status的可能原因。
参考:https://blog.csdn.net/u014546553/article/details/78781547 1.编译成功的例子在后台执行,有时一闪而过,如果再次build ,则会提示上述 ...
- MFC字体样式和颜色设置
在编写MFC界面程序时,可能会使用不同大小或者颜色的字体,这里稍做记录. 使用方法 CFont *m_pFont;//创建新的字体 m_pFont = new CFont; m_pFont->C ...
- windows下angularJs环境搭建和遇到的问题解决
搭建本地开发环境 angular官网社区上说:你应该在自己的电脑上本地开发... 你也应该在本地环境学习 Angular. 本人也认为在本地搭建学习环境--靠谱.所以决定尝试一下. 安照中文社区给的步 ...
- 【pycharm 密钥】pycharm 2017 密钥
server选项里边输入: http://idea.liyang.io 亲测可用!!!