標籤:sql配置在xml中 j2ee架構搭建 hibernate hibernate轉化成指定對象 transformers
為了避免sql編寫在Java代碼裡面,所以實作類別型mybaits的功能,把sql語句編寫在xml檔案中,這樣可以統一管理sql語句,維護更加容易。
1. 首先編寫配置sql語句的xml的dtd檔案,dtd檔案主要是規範xml的,在編寫sql語句的設定檔中主要有五個標籤:select , update , insert , delete , import
其中select有兩個屬性id(保證所有的sql語句id唯一),resultClass(查詢語句返回的對象,可以使具體的實體類,也可以是Map,List);import只有一個resource屬性,指定匯入的xml的位置,其他的標籤都只有一個id屬性。dtd檔案如下,存放在hqhop-framework-common項目的/src/main/resources/META-INF下:
</pre><pre name="code" class="html"><!ELEMENT sqls-configs (import | insert | delete | update | select )*><!ELEMENT insert (#PCDATA)><!ATTLIST insert id ID #REQUIRED><!ELEMENT delete (#PCDATA)><!ATTLIST delete id ID #REQUIRED><!ELEMENT update (#PCDATA)><!ATTLIST update id ID #REQUIRED><!ELEMENT select (#PCDATA)><!ATTLIST select resultClass CDATA #REQUIRED><!ATTLIST select id ID #REQUIRED><!ELEMENT import EMPTY><!ATTLIST import resource CDATA #REQUIRED>
2. sql設定檔的參考案例:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE sqls-configs PUBLIC "-//SQLS//MAIN CONFIG DTD//EN" "http://kf.hqhop.com:8888/hqhop-framework-sqls.dtd"><sqls-configs><select <span style="color:#ff0000;">id="select1"</span> resultClass="<span style="color:#ff0000;">com.hqhop.framework.shiro.entity.User</span>">select * from t_base_user where id =:id</select><pre name="code" class="html"><span style="white-space:pre"></span><select <span style="color:#ff0000;">id="select2"</span> resultClass="<span style="color:#ff0000;">java.util.Map</span>">select * from t_base_user where id = ?</select>
<pre name="code" class="html"><span style="white-space:pre"></span><select <span style="color:#ff0000;">id="select3" </span>resultClass="<span style="color:#ff0000;">java.util.List</span>">select * from t_base_user where id = ?</select>
<update id="update"></update><insert id="insert"></insert><delete id="delete"></delete><import resource="classpath:sql/test.xml" /></sqls-configs>
3. 解析sql的設定檔,類目錄結構
實現主要就用到了這4個類,首先把每一條sql語句的資訊存放到SQL對象中,然後在把SQL對象和配置的sql的id對應存放到SQLContext,當我們在使用的時候就直接去SQLContext中去取。
SQL.java
public class SQL {private String id; //sql的idprivate String sql; //具體的sqlprivate Class resultClass; //返回的實體類,只對select標籤有效public SQL(String id, String sql, String resultClass) {this.id = id;this.sql = sql;try {if (Utils.isNotEmpty(resultClass))this.resultClass = Class.forName(resultClass);elsethis.resultClass = null;} catch (ClassNotFoundException e) {e.printStackTrace();}}<pre name="code" class="java"> //get set....
}
SQLContext.java
public class SQLContext {<span style="white-space:pre"></span>//僅僅一個屬性,使用map存放每一個sql語句,使用的時候更加key來找到sqlprivate Map<String, SQL> sqls = new HashMap<>();public SQL getSQL(String sqlKey) {return sqls.get(sqlKey);}public Set<String> getKeys() {return sqls.keySet();}public void put(String sqlKey, SQL sql) {this.sqls.put(sqlKey, sql);}public boolean checkKeyIsExist(String key) {return this.sqls.containsKey(key);}}
4. 解析sql的xml檔案主要是在SQLContextFileBuilder中完成的,是一個的單例模式
1) 首先解析import標籤,使用遞迴的方式把所有import的xml載入到一個document,實現的方法是public Document loadFullConfigFile(Resource resource, String encoding)
// 解析import包含的子設定檔public Document loadFullConfigFile(Resource resource, String encoding) throws UnsupportedEncodingException, IOException, DocumentException {SAXReader reader = null;Document document = null;reader = new SAXReader();InputStreamReader isr = null;try {isr = new InputStreamReader(resource.getInputStream(), encoding);document = reader.read(isr);} finally {CloseUtil.close(isr);}final Element root = document.getRootElement();List list = document.selectNodes("//import");for (int i = 0; i < list.size(); i++) {Element n = (Element) list.get(i);String file = n.attribute("resource").getValue();Resource fr = SpringUtils.getResource(file);<span style="color:#ff0000;">Document includedDoc = loadFullConfigFile(fr, encoding);</span>List content = root.content();int indexOfPos = content.indexOf(n);content.remove(indexOfPos);Element ie = includedDoc.getRootElement();List ie_children = ie.content();for (int k = ie_children.size() - 1; k >= 0; k--) {content.add(indexOfPos, ie_children.get(k));}}this.rootDoc = document.getRootElement();return document;}
2) 再從document中解析出所有的sql語句,建立SQL對象,存放到SQLContext中
public SQLContext loadSQLContext() {SQLContext sqlContext = new SQLContext();List els = rootDoc.elements();for (Object o : els) {Element element = (Element) o;String idKey = element.attribute("id").getValue();String sql = element.getTextTrim();Attribute attribute = element.attribute("resultClass");String resultClass = Utils.isNotEmpty(attribute) ? attribute.getValue() : null; <span style="color:#ff0000;">//判斷sql的id是否已經存在 ,若存在就拋出異常</span> if (<span style="color:#ff0000;">!sqlContext.checkKeyIsExist(idKey)</span>) {sqlContext.put(idKey, new SQL(idKey, sql, resultClass));} else {throw new RuntimeException("請檢查sql的設定檔,sql的key已經存在(key=" + idKey + ")!");}}return sqlContext;}
5. 讓spring容器啟動就去載入sql的xml檔案:SQLContextFactory.java 只有一個方法,
public static SQLContext createSQLContext(Resource springResource){......} 完成解析xml檔案,返回sqlContext
在spring的xml設定檔中添加如下代碼來完成SQLContext的建立:
<bean id="sqlContext" class="com.hqhop.framework.common.orm.sql.SQLContextFactory" factory-method="createSQLContext"> <constructor-arg> <!-- 指定sql檔案的位置 --> <value><span style="color:#ff0000;">classpath:sqls/sql.xml</span></value></constructor-arg></bean>
6. 到此為止sqlContext已經存在了spring容器中了,接下來就在 BaseRepoitoryImpl中來使用sqlContext,在BaseRepoitory中定義了幾個使用sql的xml的介面,這裡就只說明一個介面就行了:
public List<T> findAll(String sqlKey, Object... params) {....}
@Autowiredprivate SQLContext sqlContext;public List<T> findAll(String sqlKey, Object... params) {SQL sql = this.sqlContext.getSQL(sqlKey);SQLQuery sqlQuery = this.getSession().createSQLQuery(sql.getSql());RepositoryHelper.setValues(sqlQuery, params);RepositoryHelper.setResultTransformer(sqlQuery, sql.getResultClass());return sqlQuery.list();}
1) 傳入一個sql的id,然後通過sqlContext來擷取對應的SQL對象,通過RepoitoryHelper.setValues(...)來設定參數的值,這裡傳入的params可以使多個參數值,也可以是一個map對象,具體使用什麼就要根據編寫的sql來決定
select * from t_base_user where id =:id 這種方式只能傳入map對象
select * from t_base_user where id = ? 這種方式只能傳入對個參數值
RepositoryHelper.setValues(.....)代碼如下:
public static void setValues(Query query, Object... params) {if (params[0] instanceof Map) {Map mapParams = (Map) params[0];Set<String> keys = mapParams.keySet();for (String key : keys) {query.setParameter(key, mapParams.get(key));}} else {int paramIndex = 0;for (Object o : params) {query.setParameter(paramIndex++, o);}}}
2) 綁定查詢出來的資料應該轉化成什麼對象,使用RepositoryHelper.setResultTransformer(...)來完成的,這裡的resultClass是在sql的xml檔案中指定的,只對select標籤有效:
public static void setResultTransformer(SQLQuery query, Class resultClass) {if (Utils.isNotEmpty(resultClass)) {if (<span style="color:#ff0000;">resultClass.equals(List.class)</span>) {query.setResultTransformer(Transformers.TO_LIST);} else if (<span style="color:#ff0000;">resultClass.equals(Map.class)</span>) {query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);} else {query.addEntity(resultClass);}}}
其他使用sql的xml介面的實現方式都類似。
J2EE開發架構搭建(7) - 用hibernate實作類別似mybaits把sql寫在配置文字中