hibernate根據條件動態組裝sql/hql語句(仿ibatis動態查詢語句功能)


1.功能需求背景
  項目中使用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);
	}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM