以下是对配置文件的解析过程
Student.jpa.xml配置文件主要描述dao接口方法的hql语句:
<?xml version="1.0" encoding="UTF-8"?> <!-- 对StudentDaoi接口中的所有方法进行外置命名实现 --> <hqlMap namespace="Student"> <!-- jpa查询语句 --> <select id="findAllStudent"> SELECT mode FROM Student mode </select> <!-- jpa根据id查询 ,参数类型为Integer,如果不写parameterClass,则默认为语句里面只有一个参数--> <select id="findByIdStudent"> SELECT mode FROM Student mode WHERE mode.stuId=? </select> <!-- jpa根据name查询,参数类型为String --> <select id="findByNameStudent"> SELECT mode FROM Student mode WHERE mode.stuName=? </select> <!-- 多个参数封装成map形式的 --> <select id="findByMap"> SELECT mode FROM Student mode WHERE mode.stuName=:stuName AND mode.stuSex=:stuSex </select> <!-- 多个参数封装成list形式的 --> <select id="findByList"> SELECT mode FROM Student mode WHERE mode.stuName=? AND mode.stuSex=? </select> <!-- 保存单个实体方法 --> <insert id="saveStudent"></insert> <!-- 更新单个实体 --> <update id="updateStudent"></update> <!-- 根据id删除数据 --> <delete id="deleteStudent"> DELETE Student mode WHERE mode.stuId=? </delete> <!-- 批量删除操作 --> <delete id="batchDelete"> <![CDATA[ DELETE Student mode WHERE 1=1 where mode.stuId IN ]]> <!-- 使用iterate标签,目前参数只支持list类型 当迭代的时候,里面自动获取具体的参数对象 下面解析出来的结果是(?,?,?,?),里面支持无限级嵌套 --> <iterate conjunction="," open="(" close=")"> #value# </iterate> </delete> <!-- 统计操作(不带条件的) --> <select id="count"> SELECT COUNT(mode) FROM Student mode </select> <!-- 处理条件的模板ql语句 --> <hql id="condition_hql"> <iterate property="conditions" prepend=" AND " conjunction=" AND "> <isEqual property="operation" compareValue="EQ"> <![CDATA[ ($propertyName$ = #propertyValue#) ]]> </isEqual> <isEqual property="operation" compareValue="GE"> <![CDATA[ ($propertyName$ >= #propertyValue#) ]]> </isEqual> <isEqual property="operation" compareValue="LE"> <![CDATA[ ($propertyName$ <= #propertyValue#) ]]> </isEqual> <isEqual property="operation" compareValue="LIKE"> <![CDATA[ ($propertyName$ LIKE #propertyValue#) ]]> </isEqual> <isEqual property="operation" compareValue="BETWEEN"> <![CDATA[ ($propertyName$ BETWEEN #propertyValue[0]# AND #propertyValue[1]#) ]]> </isEqual> <isEqual property="operation" compareValue="IN"> <![CDATA[ ($propertyName$ IN ]]> <iterate open="(" close="))" conjunction="," property="propertyValue"> #value# </iterate> </isEqual> </iterate> </hql> <!--多属性排序模板--> <hql id="order_hql"> <iterate property="orders" prepend=" ORDER BY " conjunction=","> <isEqual property="orderType" compareValue="ASC"> <![CDATA[$propertyName$ ASC]]> </isEqual> <isEqual property="orderType" compareValue="DESC"> <![CDATA[$propertyName$ DESC]]> </isEqual> </iterate> </hql> <!-- 统计操作(带条件的) --> <select id="countByCondition"> <![CDATA[ SELECT COUNT(mode) FROM Student mode WHERE 1=1 ]]> <!--支持模板hql语句--> <include refid="condition_hql"/> </select> <!-- 分页结果集查询 --> <select id="findByPageStudent"> <![CDATA[ SELECT mode FROM Student mode WHERE 1=1 ]]> <include refid="condition_hql"/> <include refid="order_hql"/> </select> </hqlMap>
StudentJpaXml.java是模拟解析配置文件全过程:
package org.forever.xml;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.dom4j.tree.DefaultCDATA;
import org.dom4j.tree.DefaultText;
import org.forever.pagination.Condition;
import org.forever.pagination.Operation;
import org.forever.pagination.Order;
import org.forever.pagination.OrderType;
import org.forever.xxxx.domain.Student;
public class StudentJpaXml {
private static Log log = LogFactory.getLog(StudentJpaXml.class);
private static Document doc;
private static StringBuffer hql;
private static List<Object> param_list;
public static void main(String[] args) throws Exception {
SAXReader reader = new SAXReader();
InputStream in = StudentJpaXml.class.getClassLoader()
.getResourceAsStream("org/forever/xxxx/domain/Student.jpa.xml");
doc = reader.read(in);
param_list = new ArrayList<Object>();//搜集参数值的list
Object param = null;//模拟dao接口方法的参数
//findAllStudent();
Element element = getSingleNode("/hqlMap/select[@id='findAllStudent']");
//findByIdStudent(int id);
element = getSingleNode("/hqlMap/select[@id='findByIdStudent']");
param = 2;
//findByNameStudent(String name);
element = getSingleNode("/hqlMap/select[@id='findByNameStudent']");
param = "陈均";
//findByMap(Map map);
element = getSingleNode("/hqlMap/select[@id='findByMap']");
Map<String, Object> param_map = new HashMap<String, Object>();
param_map.put("stuName", "chenjun");
param_map.put("stuSex", "男");
param = param_map;
//findByList(List list);
element = getSingleNode("/hqlMap/select[@id='findByList']");
List<Object> list = new ArrayList<Object>();
list.add("chenjun");
list.add("男");
param = list;
//countByCondition(Student student);
element = getSingleNode("/hqlMap/select[@id='countByCondition']");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date startTime = dateFormat.parse("2010-02-01");
Date endTime = dateFormat.parse("2010-03-02");
List<Condition> conditions = Arrays.asList(
new Condition("stuGrade", "一年级",Operation.EQ),
new Condition("stuAge",12,Operation.GE),
new Condition("stuAge",19,Operation.LE),
new Condition("stuClass", "二班", Operation.EQ),
new Condition("stuName", "%stor%", Operation.LIKE),
new Condition("stuAge", new Object[] { 14, 18 }, Operation.BETWEEN),
new Condition("stuSex", Arrays.asList("男","女"),Operation.IN),
new Condition("stuTime",new Object[]{startTime,endTime},Operation.BETWEEN)
);
Student student = new Student();
student.setConditions(conditions);
param = student;
//findByPageStudent(Student student);
element = getSingleNode("hqlMap/select[@id='findByPageStudent']");
List<Order> orders = Arrays.asList(
new Order("stuName", OrderType.ASC),
new Order("stuAge", OrderType.DESC)
);
student.setOrders(orders);
hql = new StringBuffer();
//动态hql解析
parseHQL(element,param);
//解析参数
parseParam(param);
log.info("解析后的hql语句:" + hql.toString());
log.info("参数个数:"+param_list.size()+"个------>");
for (int i = 0; i < param_list.size(); i++) {
log.info("第" + (i+1) + "个参数值:" + param_list.get(i));
}
}
//解析非引用类型参数
private static void parseParam(Object param) {
if (isPrimitive(param)) {
param_list.add(param);
} else if (param instanceof List<?>) {
param_list = (List) param;
} else if (param instanceof Map<?, ?>) {
Map<String,Object> map = (Map) param;
for (String key : map.keySet()) {
String name = ":" + key;
int start = hql.indexOf(name);
int end = start + name.length();
hql.replace(start, end, "?");
param_list.add(map.get(key));
}
}
}
/**
* 解析元素
* 该内容可能是以下几种情况:文本、元素、文档注释,CDATA
* 元素可能又会有上面的情况,所以是递归的解析
* 参数有以下3种:基本类型,集合类型(Map,List),引用类型
* 该递归方法只考虑引用类型
* @param element 待解析的标签元素
* @param param 接口方法的参数
* @param hql hql字符串
* @param param_list 存放参数集合
* @throws Exception
*/
public static void parseHQL(Element element,Object param) throws Exception{
for (Object item : element.content()) {
if(item instanceof DefaultText){
parseText(item,param);
}else if(item instanceof DefaultCDATA){
parseCDATA(item,param);
}else if(item instanceof Element){
parseElement(item,param);
}
}
}
//解析元素标签
private static void parseElement(Object item, Object param) throws Exception {
//元素有以下几种:
//include,isEqual,iterate
Element element = (Element)item;
String name = element.getName();
if("include".equals(name)){//模板标签
String refid = element.attributeValue("refid");//获取引用的sqlid
//找到该元素的模板进行解析,递归操作
Element hqlE = getSingleNode("/hqlMap/hql[@id='"+refid+"']");
if(hqlE==null){
throw new RuntimeException("配置文件不存在id="+refid+"的标签!");
}
parseHQL(hqlE, param);
}else if("iterate".equals(name)){
String property = element.attributeValue("property");//参数的属性
String prepend = element.attributeValue("prepend");//开始字符串
String conjunction = element.attributeValue("conjunction");//迭代一次所加字符串
String open = element.attributeValue("open");
String close = element.attributeValue("close");
if(null != prepend){
hql.append(prepend);
}
if(null != open){
hql.append(open);
}
//目前迭代标签只支持List类型的
Field field = getField(param, property);
field.setAccessible(true);
List list = new ArrayList<Object>();
try {
list = (List)field.get(param);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("获取迭代标签属性值失败!");
}
if(conjunction==null){
conjunction = " ";
}
for (Object object : list) {
parseHQL(element, object);
hql.append(conjunction);
}
hql.delete(hql.lastIndexOf(conjunction), hql.length());
if(null != close){
hql.append(close);
}
} else if("isEqual".equals(name)){
String property = element.attributeValue("property");//属性
String compareValue = element.attributeValue("compareValue");//比较的值
Field field = getField(param, property);
try {
field.setAccessible(true);
Object value = field.get(param);
if(compareValue!=null && compareValue.equals(value.toString())){//满足条件
parseHQL(element, param);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("获取迭代标签属性值失败!");
}
}
}
//反射获取属性字段
private static Field getField(Object param, String property){
Field field = null;
try {
field = param.getClass().getDeclaredField(property);
} catch (Exception e1) {
// 去父类中找寻,在此支持一级父类找寻,如果多级,那就递归呗
try {
field = param.getClass().getSuperclass().getDeclaredField(property);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("反射获取属性字段失败!");
}
}
return field;
}
//解析元素据类型
private static void parseCDATA(Object element, Object param) throws Exception {
DefaultCDATA cdata = (DefaultCDATA)element;
String text = cdata.getText().trim();
log.info("CDATA文本内容:" + text);
if("".equals(text)){
return;
}
hql.append(" " + text + " ");
parseJH(param);
parse$(param);
}
/**
* 是否是基本类型(8种基本类型和包装类型)
* @param param
* @return
*/
public static boolean isPrimitive(Object param) {
// 等等....
if (param == null)
return false;
if (param.getClass().isPrimitive() || param instanceof Integer
|| param instanceof String || param instanceof Long
|| param instanceof Short || param instanceof Double
|| param instanceof Float || param instanceof Byte
|| param instanceof Boolean) {
return true;
}
return false;
}
//解析文本类型
public static void parseText(Object element,Object param) throws Exception{
DefaultText dt = (DefaultText)element;
String text = dt.getText().trim();
log.info("text文本内容:" + text);
if("".equals(text)){
return;
}
hql.append(" " + text + " ");
parseJH(param);
parse$(param);
}
//解析$符号的表达式
private static void parse$(Object param) throws Exception {
log.info("解析$号.........");
String str = "$";// $?$形式的解析
int start = hql.indexOf(str,0);// 第一次$号出现的起始位置
int end = hql.indexOf(str,start+1);// 第一次$号出现的结束位置
while(start !=-1 && end != -1){// 可能有多个$号
String property = hql.substring(start+1, end);
//propertyValue[0]这种情况
int start_ = property.indexOf("[");
Object value = null;
if(start_!=-1){//数组的情况
int index = Integer.parseInt(property.substring(start_+1, start_+2));//获取下标
Field field = getField(param, property);
field.setAccessible(true);
value = field.get(param);
value = ((Object[])value)[index];
} else if(isPrimitive(param)){
value = param;
}
else{// 普通属性
Field field = getField(param, property);
field.setAccessible(true);
value = field.get(param);
}
hql.replace(start, end + 1, value.toString());// 把语句中的$?$符号替换成?号
start = hql.indexOf(str, start);// 获取下一次$号出现的起始位置
end = hql.indexOf(str, start + 1);// 获取下一次$号出现的结束位置
}//end while
}
//解析#符号的表达式
public static void parseJH(Object param) throws Exception{
log.info("解析#号.........");
String str = "#";// #?#形式的解析
int start = hql.indexOf(str,0);// 第一次#号出现的起始位置
int end = hql.indexOf(str,start+1);// 第一次#号出现的结束位置
while(start !=-1 && end != -1){// 可能有多个#号
String property = hql.substring(start+1, end);
//propertyValue[0]这种情况
int start_ = property.indexOf("[");
Object value = null;
if(start_!=-1){//数组的情况
int index = Integer.parseInt(property.substring(start_+1, start_+2));//获取下标
Field field = getField(param, property.replaceFirst("\\["+index+"\\]", ""));
field.setAccessible(true);
value = field.get(param);
param_list.add(((Object[])value)[index]);
} else if(isPrimitive(param)){
param_list.add(param);
}
else{// 普通属性
Field field = getField(param, property);
field.setAccessible(true);
value = field.get(param);
param_list.add(value);
}
hql.replace(start, end + 1, "?");// 把语句中的#?#符号替换成?号
start = hql.indexOf(str, start);// 获取下一次#号出现的起始位置
end = hql.indexOf(str, start + 1);// 获取下一次#号出现的结束位置
}//end while
}
//根据xpath获取单节点元素
public static Element getSingleNode(String xpathExpression){
return (Element)doc.selectSingleNode(xpathExpression);
}
}
涵盖了对单对象的所有情况的一个操作。当然这种通用写法肯定会降低效率的哈.
以上依赖的jar包:dom4j-1.6.1.jar,jaxen-1.1.1.jar,pagination.jar,commons-logging.jar
log4j.properties:
##LOGGERS##
log4j.rootLogger=INFO,console
##APPENDERS##
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=d\:\\log.txt
log4j.appender.file.MaxFileSize=1024KB
##LAYOUTS##
log4j.appender.console.layout=org.apache.log4j.SimpleLayout
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm} %t %p- %m%n