zhaojp0 2012-08-29
本文系iBatis开发详解系列文章之在iBatis中使用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>[email protected]</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子元素,如果不想要还要修改工具类。但这也并不是最佳实践,如果处理数据量太大,就要消耗大量的内存。
关于此示例的代码,请参考上一篇文章中的附件。