iBatis开发详解(8)---使用XML
在实际应用中,很多时候我们使用到了XML格式的数据,比如FushionChart,iBatis允许使用XML作为数据库查询操作的参数,也允许数据库查询结果返回一个XML格式的数据。如果不是必须,使用XML并不会给带来什么价值,甚至不如直接使用POJO,因为它是强类型的。下面先来看看使用XML作为参数的情况,做参数时,可以使用String,也可以使用DOM来传递,此时的XML结构并不严格要求,只要格式良好即可。结合实例,比如有如下的XML片段:
<parameter><userId>1</userId></parameter>
这样,已映射的语句将获得名为userId的参数,且值为1,那么,我们来看看映射语句该怎么来写: <select id="getUserByXMLId" parameterClass="xml" resultClass="User">
select * from users where userId=#userId#
</select>
把parameterClass属性写成xml就行了,resultClass可以是你定义的Bean类型或Map,下面来看程序:
String parameter = "<parameter><userId>1</userId></parameter>";
User user = (User) sqlMap.queryForObject("User.getUserByXMLId",
parameter);
System.out.println(user);
这样就能得到结果了,这是String方式的XML传递,下面来看看DOM方式的写法,就需要使用到DOM生成的API了:public static void main(String[] args) throws SQLException,
ParserConfigurationException {
//创建XML文档
org.w3c.dom.Document parameterDocument = DocumentBuilderFactory
.newInstance().newDocumentBuilder().newDocument();
//创建根元素
org.w3c.dom.Element paramElement = parameterDocument
.createElement("parameterDocument");
//创建userId元素
org.w3c.dom.Element userIdElement = parameterDocument
.createElement("userId");
//设置userId元素的值
userIdElement.setTextContent("1");
//元素关系添加
paramElement.appendChild(userIdElement);
parameterDocument.appendChild(paramElement);
//数据查询
User user = (User) sqlMap.queryForObject("User.getUserByXMLId",
parameterDocument);
System.out.println(user);
}
这种方式就是DOM方式,代码比较复杂,但是结构很清晰。如果要引入第三方API,那么Dom4j是个不错的选择,写一个工具类来帮助我们简化开发,如下: package ibatis.util;
import java.io.IOException;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
/**
* 使用dom4j生成XML工具类
*
* @author Sarin
*
*/
public class XMLUtil {
private Document document = null;
public Document getDocument() {
return document;
}
/**
* 构造方法,初始化Document
*/
public XMLUtil() {
document = DocumentHelper.createDocument();
}
/**
* 生成根节点
*
* @param rootName
* @return
*/
public Element addRoot(String rootName) {
Element root = document.addElement(rootName);
return root;
}
/**
* 生成节点
*
* @param parentElement
* @param elementName
* @return
*/
public Element addNode(Element parentElement, String elementName) {
Element node = parentElement.addElement(elementName);
return node;
}
/**
* 为节点增加一个属性
*
* @param thisElement
* @param attributeName
* @param attributeValue
*/
public void addAttribute(Element thisElement, String attributeName,
String attributeValue) {
thisElement.addAttribute(attributeName, attributeValue);
}
/**
* 为节点增加多个属性
*
* @param thisElement
* @param attributeNames
* @param attributeValues
*/
public void addAttributes(Element thisElement, String[] attributeNames,
String[] attributeValues) {
for (int i = 0; i < attributeNames.length; i++) {
thisElement.addAttribute(attributeNames[i], attributeValues[i]);
}
}
/**
* 增加节点的值
*
* @param thisElement
* @param text
*/
public void addText(Element thisElement, String text) {
thisElement.addText(text);
}
/**
* 获取最终的XML
*
* @return
* @throws IOException
*/
public String getXML() {
return document.asXML().substring(39);
}
}
那么程序代码就简单很多了,如下: XMLUtil xmlParameter = new XMLUtil();
Element parameter = xmlParameter.addRoot("parameter");
Element userId = xmlParameter.addNode(parameter, "userId");
userId.addText("1");
System.out.println(xmlParameter.getXML());
User user = (User) sqlMap.queryForObject("User.getUserByXMLId",
xmlParameter.getXML());
System.out.println(user);
XML参数就说到这里,下面来看看XML结果,由上面的示例不难得出,下面我们先来看一个简单示例: <select id="getXMLValueByUserId" resultClass="xml" xmlResultName="User" parameterClass="int">
select * from users where userId=#userId#
</select>
这是映射语句,很简单,参数都是见名知意了。xmlResultName参数的含义就是返回XML格式数据的跟元素名称,那么程序中可以这样来写: String xmlUser = (String) sqlMap.queryForObject(
"User.getXMLValueByUserId", new Integer(1));
System.out.println(xmlUser);
我们得到如下结果(排版格式已经调整): <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<User>
<userId>1</userId>
<userName>Sarin</userName>
<password>123</password>
<age>23</age>
<mobile>15940912345</mobile>
<email>gmail@gmail.com</email>
</User>
这是获取一个结果的时候,那么如果要获取多条结果,XML结果会是什么样的呢?我们来看一下: <select id="getUserXMLValue" resultClass="xml" xmlResultName="User">
select * from users
</select>
程序也做相应的修改: List xmlUserList = sqlMap.queryForList("User.getUserXMLValue");
System.out.println(xmlUserList);
打印,得到集合形式的XML文档,每个元素都是独立的XML文档,这显然不是我们想要的形式,那么又该如何处理呢?把这些独立的XML手工拼接成一个XML文档,这显然不可取啊。要解决这个问题,就不能使用iBatis的XML结果了,返回Bean的集合,对Bean进行操作显然更为方法,下面给出一个简单的方法。 我们先来修改User类,加一个方法:
public String toXML() {
StringBuffer xmlValue = new StringBuffer();
xmlValue.append("<user>");
xmlValue.append("<userId>").append(getUserId()).append("</userId>");
xmlValue.append("<userName>").append(getUserName()).append("</userName>");
xmlValue.append("<password>").append(getPassword()).append("</password>");
xmlValue.append("<mobile>").append(getMobile()).append("</mobile>");
xmlValue.append("<email>").append(getEmail()).append("</email>");
xmlValue.append("<age>").append(getAge()).append("</age>");
xmlValue.append("</user>");
return xmlValue.toString();
}
那么我们就可以这样来写程序了: List<User> userList = sqlMap.queryForList("User.getAllUsers");
StringBuffer xmlUserList = new StringBuffer("<users>");
for (User user : userList) {
xmlUserList.append(user.toXML());
}
xmlUserList.append("</users>");
System.out.println(xmlUserList.toString());
这样我们就得到格式良好的XML文档了。下面给出一个工具类,利用反射将Bean中的属性转换为XML格式:package ibatis.util;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
public class BeanToXML {
private Class beanClass;
private BeanInfo beanInfo;
private String name;
public BeanToXML(Class beanClass, String name) throws Exception {
this.beanClass = beanClass;
this.name = name;
beanInfo = Introspector.getBeanInfo(beanClass);
}
public String convertToXML(Object obj) throws Exception {
StringBuffer xmlValue = new StringBuffer();
if (obj.getClass().isAssignableFrom(beanClass)) {
PropertyDescriptor[] pd = beanInfo.getPropertyDescriptors();
if (pd.length > 0) {
xmlValue.append("<").append(name).append(">");
for (int i = 0; i < pd.length; i++) {
xmlValue.append(getProperty(obj, pd[i]));
}
xmlValue.append("</").append(name).append(">");
} else {
xmlValue.append("<").append(name).append("/>");
}
} else {
throw new ClassCastException("Class " + obj.getClass().getName()
+ " is not compatible with " + beanClass.getName());
}
return xmlValue.toString();
}
private String getProperty(Object obj, PropertyDescriptor pd)
throws Exception {
StringBuffer propertyValue = new StringBuffer();
Method method = pd.getReadMethod();
Object bodyValue = method.invoke(obj);
if (null == bodyValue) { propertyValue.append("<").append(pd.getName()).append("/>");
} else { propertyValue.append("<").append(pd.getName()).append(">");
propertyValue.append(bodyValue.toString()); propertyValue.append("</").append(pd.getName()).append(">");
}
return propertyValue.toString();
}
}
而在测试程序中,这么写就行了: BeanToXML btx = new BeanToXML(User.class, "user");
List<User> userList = sqlMap.queryForList("User.getAllUsers");
StringBuffer xmlUserList = new StringBuffer("<users>");
for (User user : userList) {
xmlUserList.append(btx.convertToXML(user));
}
xmlUserList.append("</users>");
System.out.println(xmlUserList.toString());
也就得到了结果,只是在每个user元素中会多一个class子元素,如果不想要还要修改工具类。但这也并不是最佳实践,如果处理数据量太大,就要消耗大量的内存。