日期:2014-05-16  浏览次数:20723 次

给spring jdbctemplate加上一层“华丽外衣”-动态SQL&&SQL语句以文件存放

??? 用hibernate用得多了,忽然怀念起自己直接写sql 的轻巧类库了。

??? 开源的类库中,需要写sql常用的有spring jdbc?,apache的dbutil?, MyBatis。看过MyBatis文档,写过demo, 相对于hibernate这个强大的ORM工具,MyBatis可控性更好,然而今天它不主角,今天的主角是spring jdbc.

???

??? spring jdbc 当我一接触到的时候,从此深深爱上了这个小巧的家伙:简单而优雅的API,配上spring本身的声名式事务!这就是一把小巧的神器!????

??? 然而随着我深入的了解,让我不爽的是SQL语句写在JAVA代码中,想要动态SQL自己拼字符串吧!

??? 但是这又有什么的?我把SQL语句放在外部文件,然后把它读出来就行了。

?? 有这想法第一步想到的是:

?? 利用Spring注入功能,把SQL注入到bean中去,但是想想又不合适,不喜欢用xml方式配置spring bean ,如果每个?? bean都注入几条SQL,不是所有要用SQL的bean都要写在xml文件吗?

?? 这个办法不是一个优雅的解决办法,那创建一个类,利用spring注入功能,将相关的值注入到map属性中,尝试了一下,所有的sql都丢到一个文件多不优雅!还是自己动手来读吧,dom4j读xml很简单,上代码:

?

?

public class SQLBuilder {

	private static Logger logger = LoggerFactory.getLogger(SQLBuilder.class);

	private Map<String, String> sqlContainer = null;

	private String sqlFilePath = "systemSql.xml";

	public SQLBuilder() {

		initSqlContainer();
	}

	public SQLBuilder(String sqlFilePath) {

		this.sqlFilePath = sqlFilePath;
		initSqlContainer();
	}

	public String getSql(String key) {
		String sql = sqlContainer.get(key);
		if (sql == null || "".equals(sql))
			logger.warn("不存在该SQL语句");
		if (logger.isDebugEnabled()) {
			logger.debug("SQL:" + sql);
		}
		return sql;
	}

	public String getDynamicalSql(String key, Map<String, ?> param) {
		String sql = sqlContainer.get(key);

		return VelocityUtils.render(sql, param);
	}


	@SuppressWarnings("unchecked")
	private void initSqlContainer() {
		sqlContainer = new ConcurrentHashMap<String, String>();
		if (sqlFilePath == null || "".equals(sqlFilePath)) {
            throw new NullPointerException("sql语句文件不能为空!");
		}
		String[] files = sqlFilePath.split(";");
		for (String file : files) {
			readSQLFromFile(file);
		}
	}

	private void readSQLFromFile(String fileName) {
		InputStream ips = Thread.currentThread().getContextClassLoader()
				.getResourceAsStream(fileName);
		Document document = null;
		SAXReader saxReader = new SAXReader();
		try {
			document = saxReader.read(ips);
		} catch (DocumentException e) {
			logger.error("读取系统中用到的SQL 语句XML出错");
			throw new RuntimeException("读取sql语句XML文件出错:" + e.getMessage());
		}
		Element root = document.getRootElement();
		List<Element> sqlElements = root.selectNodes("//sqlElement");
		String key;
		for (Element sql : sqlElements) {
			key=sql.attribute("key").getValue();
			if(sqlContainer.containsKey(key)){
				logger.warn("key值:"+key+"重复");
			}
			sqlContainer.put(key, sql.getText());

		}

		if (ips != null) {
			try {
				ips.close();
			} catch (IOException e) {
				logger.error("关闭输入流出错:" + e.getMessage());
			}
		}
	}

	public void setSqlFilePath(String sqlFilePath) {
		this.sqlFilePath = sqlFilePath;
	}

	@Override
	protected void finalize() throws Throwable {
		super.finalize();
		if (sqlContainer != null) {
			sqlContainer.clear();
			sqlContainer = null;
		}

	}

}

?

??? 模板技术用过freemarker ,velocity 我喜欢velocity多点,直接拿springside 里面工具类生成动态SQL

?

??

/**
 * 使用Velocity生成内容的工具类.
 * 
 * @author calvin
 */
public class VelocityUtils {

	static {
		try {
			Velocity.init();
		} catch (Exception e) {
			throw new RuntimeException("Exception occurs while initialize the velociy.", e);
		}
	}

	/**
	 * 渲染内容.
	 * 
	 * @param template 模板内容.
	 * @param model 变量Map.
	 */
	public static String render(String template, Map<String, ?> model) {
		try {
			VelocityContext velocityContext = new VelocityContext(model);
			StringWriter result = new StringWriter();
			Velocity.evaluate(velocityContext, result, "", template);
			return result.toString();
		} catch (Exception e) {
			throw new RuntimeException("Parse template failed.", e);
		}
	}
}

?

<?xml version="1.0" encoding="UTF-8"?>
<sqls>
	<sqlElement key="queryUser">
		<![CDATA[
		
		s