項目中使用hibernate作為數據持久層框架,主要考慮hibernate在進行一些簡單的crud操作時非常便利,不需要和ibatis似的為每個sql操作都寫一堆文件,但是同時也帶來了一些局限性,如類似ibatis強大的動態查詢功能用不了了,但是這個功能在項目中的應用場景又很大,hibernate自身也支持將sql/hql語句寫在.hbm.xml映射文件中<sql-query>和<query>元素,但是這個功能只能對那些查詢限制條件固定的sql有用,對於需要動態拼接的sql語句,hibernate就顯得力不從心了,如何給hibernate插上ibatis動態查詢的翅膀,既保留crud的簡潔性,又能收獲ibatis的特性呢?接下來的文章將會重點介紹
2.設計思路
先看一下ibatis的動態查詢時怎么做的
<select id="getUserList" resultMap="user">
select * from user
<isGreaterThan prepend="and" property="id" compareValue="0">
where user_id = #userId#
</isGreaterThan>
order by createTime desc
</select>
ibatis在程序實現內部回去解析sql語句中的標簽,然后去解析計算,我們在ibatis在實現的時候也參考了這個解決思路,但是否是需要把ibatis里的解析sql的語法都抄到我們的dao框架中呢-顯然這樣太復雜了,而且ibatis自己的sql元素是和那些resultMap等是綁定在一起用的,而在hibernate是沒用這些東西的,要改造這些東西是一項非常浩大的工程,因此這個方案被放棄了
我們在實現的時候采取了一種非常簡潔又功能強大的方式-模板技術!對,就是利用freemarker把sql/hql中的動態拼接條件判斷語法都交給freemarker語法去處理,這樣既能復用freemarker框架,又保持了我們框架設計的簡潔性-不需要自己寫過多的處理邏輯,以下是我們需要進行動態處理的sql/hql語句的樣例
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE dynamic-hibernate-statement PUBLIC "-//Haier/HOP Hibernate Dynamic Statement DTD 1.0//EN" "http://www.haier.com/dtd/dynamic-hibernate-statement-1.0.dtd"> <dynamic-hibernate-statement> <!-- 查詢某個資源下的直接子節點 --> <hql-query name="resource.getChildren"> <![CDATA[ from Resource where parent.id=${parentId} and parent.id != id ]]> </hql-query> <!-- 查詢系統中所有的root資源 --> <hql-query name="resource.getRoots"> <![CDATA[ from Resource where parent.id = id order by orderIndex ]]> </hql-query> <!-- 獲取某個用戶可訪問的某個資源下的所有子資源 --> <sql-query name="resource.getDescendants"> <![CDATA[ select distinct t.id, t.name, t.description, t.url, t.type, t.status, t.code, t.configuration, t.module_name, t.gmt_create, t.gmt_modified, t.create_by, t.last_modified_by, t.order_index, t.parent_id from resource_info t inner join role_resource rr on t.id = rr.resource_id inner join user_role ur on rr.role_id = ur.role_id where ur.user_id = ${userId} <#if type == '1'> and t.type=1 <#else> and t.type=0 </#if> and t.type = ${type} and t.status = ${status} start with t.code = '${code}' connect by nocycle prior t.id = t.parent_id ]]> </sql-query> </dynamic-hibernate-statement>這個文件看起來非常類似ibatis的語句了,只是沒用ibatis的哪些標簽-改成了freemarker語法,沒錯,我們就是復用freemarker來幫我們解決這些煩雜的判斷操作的
這樣我們的動態sql程序就可以總結成以下流程
a.系統加載階段
這個階段程序負責將指定路徑下的動態sql文件加載到內存中,一次性緩存起來,沒錯,這些東西只需要加載一次,以后直接讀取就行了,沒必要每次去查找,緩存也非常簡單,一個Map<String,String>就搞定,key是sql-query或hql-query元素的name屬性,value就是與其對應的sql/hql語句
b.程序調用查詢階段
調用程序通過sql/hql語句的name屬性和傳入查詢參數來得到最終解析出來的語句
我們期望的方法可能是這樣的:
public <X> List<X> findByNamedQuery(final String queryName, final Map<String, ?> parameters)通過queryName從緩存中查找出其對應的sql/hql語句(最原始的,里面帶有freemarker語法)
然后通過freemarker模板和傳遞進去的parameters參數對模板進行解析,得到最終的語句(純sql/hql)
最后將解析后的sql/hql傳遞給底層api,返回查詢結果
3.實現
上面介紹了大致的思路,這里介紹具體的代碼實現
3.1DTD定義
我們是把動態的sql/hql語句放在單獨的xml配置文件里的,為了規范xml文檔,我們給文檔定義了dtd文件,這里我們只定義了兩個元素<sql-query>和<hql-query>分別表示sql查詢語句和hql查詢語句,這兩個元素目前自有一個name屬性用來唯一標示該語句,如下
<!-- HOP Hibernate Dynamic Statement Mapping DTD. <!DOCTYPE dynamic-hibernate-statement PUBLIC "-//Haier/HOP Hibernate Dynamic Statement DTD 1.0//EN" "http://www.haier.com/dtd/dynamic-hibernate-statement-1.0.dtd"> 這個文件時用來定義動態參數語句,類似itabis --> <!-- The document root. --> <!ELEMENT dynamic-hibernate-statement ( (hql-query|sql-query)* )> <!-- default: none --> <!-- The query element declares a named Hibernate query string --> <!ELEMENT hql-query (#PCDATA)> <!ATTLIST hql-query name CDATA #REQUIRED> <!-- The sql-query element declares a named SQL query string --> <!ELEMENT sql-query (#PCDATA)> <!ATTLIST sql-query name CDATA #REQUIRED>
然后將其保存為dynamic-hibernate-statement-1.0.dtd,放在classpath下
編寫DTD校驗器
/** * hibernate動態sql dtd解析器 * @author WangXuzheng * */ public class DynamicStatementDTDEntityResolver implements EntityResolver, Serializable{ private static final long serialVersionUID = 8123799007554762965L; private static final Logger LOGGER = LoggerFactory.getLogger( DynamicStatementDTDEntityResolver.class ); private static final String HOP_DYNAMIC_STATEMENT = "http://www.haier.com/dtd/"; public InputSource resolveEntity(String publicId, String systemId) { InputSource source = null; // returning null triggers default behavior if ( systemId != null ) { LOGGER.debug( "trying to resolve system-id [" + systemId + "]" ); if ( systemId.startsWith( HOP_DYNAMIC_STATEMENT ) ) { LOGGER.debug( "recognized hop dyanmic statement namespace; attempting to resolve on classpath under com/haier/openplatform/dao/hibernate/" ); source = resolveOnClassPath( publicId, systemId, HOP_DYNAMIC_STATEMENT ); } } return source; } private InputSource resolveOnClassPath(String publicId, String systemId, String namespace) { InputSource source = null; String path = "com/haier/openplatform/dao/hibernate/" + systemId.substring( namespace.length() ); InputStream dtdStream = resolveInHibernateNamespace( path ); if ( dtdStream == null ) { LOGGER.debug( "unable to locate [" + systemId + "] on classpath" ); if ( systemId.substring( namespace.length() ).indexOf( "2.0" ) > -1 ) { LOGGER.error( "Don't use old DTDs, read the Hibernate 3.x Migration Guide!" ); } } else { LOGGER.debug( "located [" + systemId + "] in classpath" ); source = new InputSource( dtdStream ); source.setPublicId( publicId ); source.setSystemId( systemId ); } return source; } protected InputStream resolveInHibernateNamespace(String path) { return this.getClass().getClassLoader().getResourceAsStream( path ); } protected InputStream resolveInLocalNamespace(String path) { try { return ConfigHelper.getUserResourceAsStream( path ); } catch ( Throwable t ) { return null; } } }
3.2編寫sql文件
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE dynamic-hibernate-statement PUBLIC "-//Haier/HOP Hibernate Dynamic Statement DTD 1.0//EN" "http://www.haier.com/dtd/dynamic-hibernate-statement-1.0.dtd"> <dynamic-hibernate-statement> <!-- 查詢某個資源下的直接子節點 --> <hql-query name="resource.getChildren"> <![CDATA[ from Resource where parent.id=${parentId} and parent.id != id ]]> </hql-query> <!-- 查詢系統中所有的root資源 --> <hql-query name="resource.getRoots"> <![CDATA[ from Resource where parent.id = id order by orderIndex ]]> </hql-query> <!-- 獲取某個用戶可訪問的某個資源下的所有子資源 --> <sql-query name="resource.getDescendants"> <![CDATA[ select distinct t.id, t.name, t.description, t.url, t.type, t.status, t.code, t.configuration, t.module_name, t.gmt_create, t.gmt_modified, t.create_by, t.last_modified_by, t.order_index, t.parent_id from resource_info t inner join role_resource rr on t.id = rr.resource_id inner join user_role ur on rr.role_id = ur.role_id where ur.user_id = ${userId} <#if type == '1'> and t.type=1 <#else> and t.type=0 </#if> and t.type = ${type} and t.status = ${status} start with t.code = '${code}' connect by nocycle prior t.id = t.parent_id ]]> </sql-query> </dynamic-hibernate-statement>3.3加載動態sql文件
這里我們將加載sql/hql語句的程序獨立到一個單獨的類中,以便獨立擴展
這里一共3個方法,分表標示獲取系統中sql/hql語句的map(key:語句名稱,value:具體的)
/** * 動態sql/hql語句組裝器 * @author WangXuzheng * */ public interface DynamicHibernateStatementBuilder { /** * hql語句map * @return */ public Map<String,String> getNamedHQLQueries(); /** * sql語句map * @return */ public Map<String,String> getNamedSQLQueries(); /** * 初始化 * @throws IOException */ public void init() throws IOException; }默認的加載器-將指定配置文件中的sql/hql語句加載到內存中
/** * @author WangXuzheng * */ public class DefaultDynamicHibernateStatementBuilder implements DynamicHibernateStatementBuilder, ResourceLoaderAware { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDynamicHibernateStatementBuilder.class); private Map<String, String> namedHQLQueries; private Map<String, String> namedSQLQueries; private String[] fileNames = new String[0]; private ResourceLoader resourceLoader; private EntityResolver entityResolver = new DynamicStatementDTDEntityResolver(); /** * 查詢語句名稱緩存,不允許重復 */ private Set<String> nameCache = new HashSet<String>(); public void setFileNames(String[] fileNames) { this.fileNames = fileNames; } @Override public Map<String, String> getNamedHQLQueries() { return namedHQLQueries; } @Override public Map<String, String> getNamedSQLQueries() { return namedSQLQueries; } @Override public void init() throws IOException { namedHQLQueries = new HashMap<String, String>(); namedSQLQueries = new HashMap<String, String>(); boolean flag = this.resourceLoader instanceof ResourcePatternResolver; for (String file : fileNames) { if (flag) { Resource[] resources = ((ResourcePatternResolver) this.resourceLoader).getResources(file); buildMap(resources); } else { Resource resource = resourceLoader.getResource(file); buildMap(resource); } } //clear name cache nameCache.clear(); } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } private void buildMap(Resource[] resources) throws IOException { if (resources == null) { return; } for (Resource resource : resources) { buildMap(resource); } } @SuppressWarnings({ "rawtypes" }) private void buildMap(Resource resource) { InputSource inputSource = null; try { inputSource = new InputSource(resource.getInputStream()); XmlDocument metadataXml = MappingReader.INSTANCE.readMappingDocument(entityResolver, inputSource, new OriginImpl("file", resource.getFilename())); if (isDynamicStatementXml(metadataXml)) { final Document doc = metadataXml.getDocumentTree(); final Element dynamicHibernateStatement = doc.getRootElement(); Iterator rootChildren = dynamicHibernateStatement.elementIterator(); while (rootChildren.hasNext()) { final Element element = (Element) rootChildren.next(); final String elementName = element.getName(); if ("sql-query".equals(elementName)) { putStatementToCacheMap(resource, element, namedSQLQueries); } else if ("hql-query".equals(elementName)) { putStatementToCacheMap(resource, element, namedHQLQueries); } } } } catch (Exception e) { LOGGER.error(e.toString()); throw new SysException(e); } finally { if (inputSource != null && inputSource.getByteStream() != null) { try { inputSource.getByteStream().close(); } catch (IOException e) { LOGGER.error(e.toString()); throw new SysException(e); } } } } private void putStatementToCacheMap(Resource resource, final Element element, Map<String, String> statementMap) throws IOException { String sqlQueryName = element.attribute("name").getText(); Validate.notEmpty(sqlQueryName); if (nameCache.contains(sqlQueryName)) { throw new SysException("重復的sql-query/hql-query語句定義在文件:" + resource.getURI() + "中,必須保證name的唯一."); } nameCache.add(sqlQueryName); String queryText = element.getText(); statementMap.put(sqlQueryName, queryText); } private static boolean isDynamicStatementXml(XmlDocument xmlDocument) { return "dynamic-hibernate-statement".equals(xmlDocument.getDocumentTree().getRootElement().getName()); } }配置一下
<bean id="dynamicStatementBuilder" class="com.haier.openplatform.dao.hibernate.support.DefaultDynamicHibernateStatementBuilder"> <property name="fileNames"> <list> <value>classpath*:/**/*-dynamic.xml</value><!--這里我們指定要加載某個文件夾下所有以-dynamic.xml結尾的文件 --> </list> </property> </bean> <bean id="baseHibernateDAO" class="com.haier.openplatform.dao.hibernate.BaseDAOHibernateImpl" abstract="true"> <property name="sessionFactory"> <ref bean="sessionFactory"/> </property> <property name="dynamicStatementBuilder" ref="dynamicStatementBuilder"/> </bean>dao層代碼
/** * Hibernate實現的DAO層 * @param <T> DAO操作的對象類型 * @param <ID> 主鍵類型 * @author WangXuzheng * */ public class SimpleHibernateDAO<T,ID extends Serializable> implements BaseDAO<T, ID>,InitializingBean{ private static final Logger LOGER = LoggerFactory.getLogger(SimpleHibernateDAO.class); protected SessionFactory sessionFactory; protected Class<T> entityClass; /** * 模板緩存 */ protected Map<String, StatementTemplate> templateCache; protected DynamicHibernateStatementBuilder dynamicStatementBuilder; /** * 通過子類的泛型定義取得對象類型Class. * eg. * public class UserDao extends SimpleHibernateDao<User, Long> */ public SimpleHibernateDAO() { this.entityClass = Reflections.getSuperClassGenricType(getClass()); } /** * 取得sessionFactory. */ public SessionFactory getSessionFactory() { return sessionFactory; } /** * 采用@Autowired按類型注入SessionFactory, 當有多個SesionFactory的時候在子類重載本函數. */ public void setSessionFactory(final SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } public void setDynamicStatementBuilder(DynamicHibernateStatementBuilder dynamicStatementBuilder) { this.dynamicStatementBuilder = dynamicStatementBuilder; } /** * 取得當前Session. */ public Session getSession() { return sessionFactory.getCurrentSession(); } /** * 保存新增或修改的對象. */ @Override public void save(final T entity) { Validate.notNull(entity, "entity不能為空"); getSession().save(entity); LOGER.debug("save entity: {}", entity); } /** * 刪除對象. * * @param entity 對象必須是session中的對象或含id屬性的transient對象. */ @Override public void delete(final T entity) { if(entity == null){ return; } getSession().delete(entity); LOGER.debug("delete entity: {}", entity); } /** * 按id刪除對象. */ @Override public void delete(final ID id) { Validate.notNull(id, "id不能為空"); delete(get(id)); LOGER.debug("delete entity {},id is {}", entityClass.getSimpleName(), id); } /** * 按id獲取對象. */ @SuppressWarnings("unchecked") @Override public T get(final ID id) { Validate.notNull(id, "id不能為空"); return (T) getSession().get(entityClass, id); } /** * 按id列表獲取對象列表. */ public List<T> get(final Collection<ID> ids) { return find(Restrictions.in(getIdName(), ids)); } /** * 獲取全部對象. */ @Override public List<T> getAll() { return find(); } /** * 獲取全部對象, 支持按屬性行序. */ @SuppressWarnings("unchecked") public List<T> getAll(String orderByProperty, boolean isAsc) { Criteria c = createCriteria(); if (isAsc) { c.addOrder(Order.asc(orderByProperty)); } else { c.addOrder(Order.desc(orderByProperty)); } return c.list(); } /** * 按屬性查找對象列表, 匹配方式為相等. */ public List<T> findBy(final String propertyName, final Object value) { Criterion criterion = Restrictions.eq(propertyName, value); return find(criterion); } /** * 按屬性查找唯一對象, 匹配方式為相等. */ @SuppressWarnings("unchecked") @Override public T findUniqueBy(final String propertyName, final Object value) { Criterion criterion = Restrictions.eq(propertyName, value); return ((T) createCriteria(criterion).uniqueResult()); } /** * 按HQL查詢對象列表. * * @param values 數量可變的參數,按順序綁定. */ @SuppressWarnings("unchecked") public <X> List<X> findByHQL(final String hql, final Object... values) { return createHQLQuery(hql, values).list(); } /** * 按HQL查詢對象列表,並將對象封裝成指定的對象 * * @param values 數量可變的參數,按順序綁定. */ @SuppressWarnings("unchecked") public <X> List<X> findByHQLRowMapper(RowMapper<X> rowMapper,final String hql, final Object... values) { Validate.notNull(rowMapper, "rowMapper不能為空!"); List<Object[]> result = createHQLQuery(hql, values).list(); return buildListResultFromRowMapper(rowMapper, result); } protected <X> List<X> buildListResultFromRowMapper(RowMapper<X> rowMapper, List<Object[]> result) { List<X> rs = new ArrayList<X>(result.size()); for(Object[] obj : result){ rs.add(rowMapper.fromColumn(obj)); } return rs; } /** * 按SQL查詢對象列表. * * @param values 數量可變的參數,按順序綁定. */ @SuppressWarnings("unchecked") public <X> List<X> findBySQLRowMapper(RowMapper<X> rowMapper,final String sql, final Object... values) { Validate.notNull(rowMapper, "rowMapper不能為空!"); List<Object[]> result = createSQLQuery(sql, values).list(); return buildListResultFromRowMapper(rowMapper, result); } /** * 按SQL查詢對象列表,並將結果集轉換成指定的對象列表 * * @param values 數量可變的參數,按順序綁定. */ @SuppressWarnings("unchecked") public <X> List<X> findBySQL(final String sql, final Object... values) { return createSQLQuery(sql, values).list(); } /** * 按HQL查詢對象列表. * * @param values 命名參數,按名稱綁定. */ @SuppressWarnings("unchecked") public <X> List<X> findByHQL(final String hql, final Map<String, ?> values) { return createHQLQuery(hql, values).list(); } /** * 按HQL查詢對象列表,並將結果集封裝成對象列表 * * @param values 命名參數,按名稱綁定. */ @SuppressWarnings("unchecked") public <X> List<X> findByHQLRowMapper(RowMapper<X> rowMapper,final String hql, final Map<String, ?> values) { Validate.notNull(rowMapper, "rowMapper不能為空!"); List<Object[]> result = createHQLQuery(hql, values).list(); return buildListResultFromRowMapper(rowMapper, result); } /** * 按SQL查詢對象列表. * @param sql SQL查詢語句 * @param values 命名參數,按名稱綁定. */ @SuppressWarnings("unchecked") public <X> List<X> findBySQL(final String sql, final Map<String, ?> values) { return createSQLQuery(sql, values).list(); } /** * 查詢在xxx.hbm.xml中配置的查詢語句 * @param queryName 查詢的名稱 * @param parameters 參數 * @return */ public <X> List<X> findByNamedQuery(final String queryName, final Map<String, ?> parameters) { StatementTemplate statementTemplate = templateCache.get(queryName); String statement = processTemplate(statementTemplate,parameters); if(statementTemplate.getType() == StatementTemplate.TYPE.HQL){ return this.findByHQL(statement); }else{ return this.findBySQL(statement); } } /** * 查詢在xxx.hbm.xml中配置的查詢語句 * @param rowMapper * @param queryName 查詢的名稱 * @param parameters 參數 * @return */ public <X> List<X> findByNamedQuery(RowMapper<X> rowMapper,final String queryName, final Map<String, ?> parameters) { StatementTemplate statementTemplate = templateCache.get(queryName); String statement = processTemplate(statementTemplate,parameters); if(statementTemplate.getType() == StatementTemplate.TYPE.HQL){ return this.findByHQLRowMapper(rowMapper,statement); }else{ return this.findBySQLRowMapper(rowMapper,statement); } } /** * 按SQL查詢對象列表,並將結果集封裝成對象列表 * @param sql SQL查詢語句 * @param values 命名參數,按名稱綁定. */ @SuppressWarnings("unchecked") public <X> List<X> findBySQLRowMapper(RowMapper<X> rowMapper,final String sql, final Map<String, ?> values) { Validate.notNull(rowMapper, "rowMapper不能為空!"); List<Object[]> result = createSQLQuery(sql, values).list(); return buildListResultFromRowMapper(rowMapper, result); } /** * 按HQL查詢唯一對象. * * @param values 數量可變的參數,按順序綁定. */ @SuppressWarnings("unchecked") public <X> X findUniqueByHQL(final String hql, final Object... values) { return (X) createHQLQuery(hql, values).uniqueResult(); } /** * 按SQL查詢唯一對象. * * @param values 數量可變的參數,按順序綁定. */ @SuppressWarnings("unchecked") public <X> X findUniqueBySQL(final String sql, final Object... values) { return (X) createSQLQuery(sql, values).uniqueResult(); } /** * 按HQL查詢唯一對象. * * @param values 命名參數,按名稱綁定. */ @SuppressWarnings("unchecked") public <X> X findUniqueByHQL(final String hql, final Map<String, ?> values) { return (X) createHQLQuery(hql, values).uniqueResult(); } /** * 按HQL查詢唯一對象. * @param sql sql語句 * @param values 命名參數,按名稱綁定. */ @SuppressWarnings("unchecked") public <X> X findUniqueBySQL(final String sql, final Map<String, ?> values) { return (X) createSQLQuery(sql, values).uniqueResult(); } /** * 執行HQL進行批量修改/刪除操作. * * @param values 數量可變的參數,按順序綁定. * @return 更新記錄數. */ public int batchExecuteHQL(final String hql, final Object... values) { return createHQLQuery(hql, values).executeUpdate(); } /** * 執行SQL進行批量修改/刪除操作. * * @param sql sql語句 * @param values 數量可變的參數,按順序綁定. * @return 更新記錄數. */ public int batchExecuteSQL(final String sql, final Object... values) { return createSQLQuery(sql, values).executeUpdate(); } /** * 執行HQL進行批量修改/刪除操作. * * @param values 命名參數,按名稱綁定. * @return 更新記錄數. */ public int batchExecuteHQL(final String hql, final Map<String, ?> values) { return createHQLQuery(hql, values).executeUpdate(); } /** * 執行SQL進行批量修改/刪除操作. * * @param values 命名參數,按名稱綁定. * @return 更新記錄數. */ public int batchExecuteSQL(final String sql, final Map<String, ?> values) { return createSQLQuery(sql, values).executeUpdate(); } /** * 根據查詢HQL與參數列表創建Query對象. * 與find()函數可進行更加靈活的操作. * * @param values 數量可變的參數,按順序綁定. */ public Query createHQLQuery(final String queryString, final Object... values) { Query query = getSession().createQuery(queryString); if (values != null) { for (int i = 0; i < values.length; i++) { query.setParameter(i, values[i]); } } return query; } /** * 根據查詢SQL與參數列表創建Query對象. * 與find()函數可進行更加靈活的操作. * @param sqlQueryString sql語句 * * @param values 數量可變的參數,按順序綁定. */ public Query createSQLQuery(final String sqlQueryString, final Object... values) { Query query = getSession().createSQLQuery(sqlQueryString); if (values != null) { for (int i = 0; i < values.length; i++) { query.setParameter(i, values[i]); } } return query; } /** * 根據查詢HQL與參數列表創建Query對象. * 與find()函數可進行更加靈活的操作. * * @param values 命名參數,按名稱綁定. */ public Query createHQLQuery(final String queryString, final Map<String, ?> values) { Query query = getSession().createQuery(queryString); if (values != null) { query.setProperties(values); } return query; } /** * 根據查詢SQL與參數列表創建Query對象. * 與find()函數可進行更加靈活的操作. * @param queryString SQL語句 * @param values 命名參數,按名稱綁定. */ public Query createSQLQuery(final String queryString, final Map<String, ?> values) { Query query = getSession().createSQLQuery(queryString); if (values != null) { query.setProperties(values); } return query; } /** * 按Criteria查詢對象列表. * * @param criterions 數量可變的Criterion. */ @SuppressWarnings("unchecked") public List<T> find(final Criterion... criterions) { return createCriteria(criterions).list(); } /** * 按Criteria查詢唯一對象. * * @param criterions 數量可變的Criterion. */ @SuppressWarnings("unchecked") public T findUnique(final Criterion... criterions) { return (T) createCriteria(criterions).uniqueResult(); } /** * 根據Criterion條件創建Criteria. * 與find()函數可進行更加靈活的操作. * * @param criterions 數量可變的Criterion. */ public Criteria createCriteria(final Criterion... criterions) { Criteria criteria = getSession().createCriteria(entityClass); for (Criterion c : criterions) { criteria.add(c); } return criteria; } /** * 初始化對象. * 使用load()方法得到的僅是對象Proxy, 在傳到View層前需要進行初始化. * 如果傳入entity, 則只初始化entity的直接屬性,但不會初始化延遲加載的關聯集合和屬性. * 如需初始化關聯屬性,需執行: * Hibernate.initialize(user.getRoles()),初始化User的直接屬性和關聯集合. * Hibernate.initialize(user.getDescription()),初始化User的直接屬性和延遲加載的Description屬性. */ public void initProxyObject(Object proxy) { Hibernate.initialize(proxy); } /** * Flush當前Session. */ public void flush() { getSession().flush(); } /** * 為Query添加distinct transformer. * 預加載關聯對象的HQL會引起主對象重復, 需要進行distinct處理. */ public Query distinct(Query query) { query.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY); return query; } /** * 為Criteria添加distinct transformer. * 預加載關聯對象的HQL會引起主對象重復, 需要進行distinct處理. */ public Criteria distinct(Criteria criteria) { criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY); return criteria; } /** * 取得對象的主鍵名. */ public String getIdName() { ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass); return meta.getIdentifierPropertyName(); } /** * 判斷對象的屬性值在數據庫內是否唯一. * * 在修改對象的情景下,如果屬性新修改的值(value)等於屬性原來的值(orgValue)則不作比較. */ public boolean isPropertyUnique(final String propertyName, final Object newValue, final Object oldValue) { if (newValue == null || newValue.equals(oldValue)) { return true; } Object object = findUniqueBy(propertyName, newValue); return (object == null); } @Override public void update(T object) { getSession().update(object); } @SuppressWarnings("unchecked") @Override public T load(ID id) { return (T) getSession().load(this.entityClass, id); } /** * 將list轉化為數組 * @param list * @return */ protected Criterion[] list2Array(List<Criterion> list){ if(list == null){ return new Criterion[0]; } Criterion[] result = new Criterion[list.size()]; for(int i = 0; i < list.size(); i++){ result[i] = list.get(i); } return result; } @Override public void afterPropertiesSet() throws Exception { templateCache = new HashMap<String, StatementTemplate>(); if(this.dynamicStatementBuilder == null){ this.dynamicStatementBuilder = new NoneDynamicHibernateStatementBuilder(); } dynamicStatementBuilder.init(); Map<String,String> namedHQLQueries = dynamicStatementBuilder.getNamedHQLQueries(); Map<String,String> namedSQLQueries = dynamicStatementBuilder.getNamedSQLQueries(); Configuration configuration = new Configuration(); configuration.setNumberFormat("#"); StringTemplateLoader stringLoader = new StringTemplateLoader(); for(Entry<String, String> entry : namedHQLQueries.entrySet()){ stringLoader.putTemplate(entry.getKey(), entry.getValue()); templateCache.put(entry.getKey(), new StatementTemplate(StatementTemplate.TYPE.HQL,new Template(entry.getKey(),new StringReader(entry.getValue()),configuration))); } for(Entry<String, String> entry : namedSQLQueries.entrySet()){ stringLoader.putTemplate(entry.getKey(), entry.getValue()); templateCache.put(entry.getKey(), new StatementTemplate(StatementTemplate.TYPE.SQL,new Template(entry.getKey(),new StringReader(entry.getValue()),configuration))); } configuration.setTemplateLoader(stringLoader); } protected String processTemplate(StatementTemplate statementTemplate,Map<String, ?> parameters){ StringWriter stringWriter = new StringWriter(); try { statementTemplate.getTemplate().process(parameters, stringWriter); } catch (Exception e) { LOGER.error("處理DAO查詢參數模板時發生錯誤:{}",e.toString()); throw new SysException(e); } return stringWriter.toString(); } }我們的SimpleHibernateDAO實現了InitializingBean,在其afterProperties方法中我們將調用DynamicHibernateStatementBuilder把語句緩存起來
上層方法調用示例-這個已經非常類似ibatis了
public List<Resource> getDescendants(Long userId,String code) { Map<String, Object> values = new HashMap<String, Object>(); values.put("userId", String.valueOf(userId)); values.put("code", code); values.put("type", String.valueOf(ResourceTypeEnum.URL_RESOURCE.getType())); values.put("status", String.valueOf(ResourceStatusEnum.ACTIVE.getStatus())); return this.findByNamedQuery(new ResourceRowMapper(),"resource.getDescendants", values); }