使用Query Object 模式 基于jpql实例

Query Object

    编程语言可以包含sql语句,但许多开发者对此并不太熟悉。而且,你需要了解数据库设计方案以便形成查询。可以通过创建特殊的,隐藏的SQL内部参数化方法来避免这一点。但是这样难以构造更多的查询。而且,如果数据库设计方案改变,就会需要复制到SQL语句。

    查询对象是一个解析器(interpreter)【Gang of Four】,也就是说,它是由多个对象组成的结构,该结构可以自身转化为一个sql查询。你可以通过引用类和域而不是表和列来创建这个查询。采用这种方式,那些编写查询的人就能够设计独立数据库方案的查询,并且数据库方案的变化也可以被封装到局部

                                                                                             《企业应用架构模式》 - Martin Fower

    看到上面,我第一时间是想到Hibernate中的Criteria查询,他是Query Object很直观和跟经典的实现。但是就我个人使用来说,Criteria查询比较少,而且要记住几个英文很长的类,写出来巨繁琐(静态导入会好一些)。目前做项目都基于JPA的接口来开发。(但大多复杂查询还是使用原生sql查出来,然后自己转换对象!也有干脆使用mybatis)。就平时开发而言,简单快速好用的封装Query Object,然后配合链式编程(该实践中,良好的测试可以避免火车事故),使用起来是非常方便的。

    查询对象可以很好的简单的完美的:^)跟 查询通道(QueryChannel)或者直接活动记录(ActiveRecord)+资源库(Repository)这些模式结合,形成简单好用的查询接口。

    以下是我参考以前一些前辈,跟自己进行改进的一个Query Object实践,基本JPQL。 最终于我Repository 或DAO的查询接口可能是类似于这样的风格。

<T> T findEntity(Query q);
PageVo findPage(Query q);
List<T> findEntites(Query q);
然后这个Query的代码跟单元测试:


Query.java


package com.xz.marksix.repository;

import java.util.ArrayList;
import java.util.List;

import com.xz.marksix.infrastructure.AppContants;

/**
 * <pre>
 * 
 * 	JPA 查询对象	
 * 
 * @author XzhiF
 * 			Sep 25, 2013
 * </pre>
 */
public class Query
{
	private static final String SPACE = " ";
	private static final String SELECT = "SELECT";
	private static final String COUNT = "COUNT";
	private static final String FROM = "FROM";
	private static final String WHERE = "WHERE";
	private static final String AND = "AND";
	private static final String OR = "OR";
	private static final String ORDER_BY = "ORDER BY";
	private static final String ASC = "ASC";
	private static final String DESC = "DESC";
	private static final String JOIN = "JOIN";
	private static final String INNER_JOIN = "INNER JOIN";
	private static final String LEFT_JOIN = "LEFT JOIN";
	private static final String RIGHT_JOIN = "RIGHT JOIN";
	private static final String LEFT_OUTER_JOIN = "LEFT OUTER JOIN";
	private static final String RIGHT_OUT_JOIN = "RIGHT OUTER JOIN";
	private static final String LEFT_JOIN_FETCH = "LEFT JOIN FETCH";
	private static final String RIGHT_JOIN_FETCH = "RIGHT JOIN FETCH";

	private static final boolean ADD_PARAM = true;
	private static final boolean NOT_ADD_PARAM = false;

	private Class<?> entityClass;
	private String alias;
	private Integer pageNum;
	private Integer pageSize;
	private List<Object> paramters = new ArrayList<>();

	private StringBuilder selectClause = new StringBuilder();
	private StringBuilder fromClause = new StringBuilder();
	private StringBuilder joinClause = new StringBuilder();
	private StringBuilder whereClause = new StringBuilder();
	private StringBuilder orderByClause = new StringBuilder();
	private boolean fetchAllRecords = true;

	public Query( Class<?> entityClass, String alias )
	{
		this.entityClass = entityClass;
		this.alias = alias;

		/**
		 * FROM abc.Def alais
		 */
		fromClause//
		.append( SPACE )//
		.append( FROM )//
		.append( SPACE )//
		.append( entityClass.getName() )//
		.append( SPACE )//
		.append( this.alias );
	}

	/**
	 * evaluate 生成最后的JPQL语句
	 */
	public String evaluate()
	{
		StringBuilder result = new StringBuilder();

		/**
		 * 以下构造要保持顺序,若重构时请注意
		 */
		evlauteAppendSelectClause( result );
		evlauteAppendFromClause( result );
		evlauteAppendJoinClause( result );
		evlauteAppendWhereClause( result );
		evlauteAppendOrderByClause( result );

		return result.toString().trim();
	}

	/**
	 * evaluateCount 生成最后的查询总数的JPQL语句
	 */
	public String evaluateCount()
	{
		StringBuilder result = new StringBuilder();

		/**
		 * 以下构造要保持顺序,若重构时请注意
		 */
		evlauteAppendSelectCountClause( result );
		evlauteAppendFromClause( result );
		evlauteAppendWhereClause( result );

		return result.toString().trim();
	}

	private void evlauteAppendSelectClause( StringBuilder result )
	{
		if ( isNotBlank( selectClause ) )
		{
			result.append( selectClause );
		}
	}

	private void evlauteAppendSelectCountClause( StringBuilder result )
	{
		result.append( SELECT )//
		.append( SPACE )//
		.append( COUNT )//
		.append( "(" )//
		.append( alias )//
		.append( ")" );
	}

	private void evlauteAppendFromClause( StringBuilder result )
	{
		result.append( fromClause );
	}

	private void evlauteAppendJoinClause( StringBuilder result )
	{
		if ( isNotBlank( joinClause ) )
		{
			result.append( joinClause );
		}
	}

	private void evlauteAppendWhereClause( StringBuilder result )
	{
		if ( isNotBlank( whereClause ) )
		{
			result.append( whereClause );
		}
	}

	private void evlauteAppendOrderByClause( StringBuilder result )
	{
		if ( isNotBlank( orderByClause ) )
		{
			result.append( orderByClause );
		}
	}

	/**
	 * <pre>
	 * 例如返回一个实体
	 * 	addSelectClause("e") "e" 跟new Query() 第二个别名参数要一至。
	 * 
	 * 例如返回一个查询的VO对象
	 * 	addSelectClause("new VO(e.id,e.name)") "e" 跟new Query() 第二个别名参数要一至。
	 * 
	 * 注意些方法只能调用一次,同一实例调用多次会抛出RepositoryException异常
	 * </pre>
	 * 
	 * @param expression JPQL片段
	 * @return Query
	 */
	public Query addSelectClause( String expression )
	{
		checkSelectClauseIsBinded();
		selectClause.append( SELECT ).append( SPACE );

		if ( isBlank( expression ) )
		{
			selectClause.append( this.alias );
		}
		else
		{
			selectClause.append( expression );
		}
		return this;
	}

	private void checkSelectClauseIsBinded()
	{
		if ( isNotBlank( selectClause ) )
		{
			throw new RepositoryException( "selectClause已经绑定过不能再定绑定." );
		}
	}

	/**
	 * <pre>
	 * 	结果将添加如  WHERE e.id=? AND e.name=? 的 JPQL片段
	 * 	例子:
	 * 	addWhereAndClause( id!=null, "e.id=?", id )
	 * 
	 * </pre>
	 * 
	 * @param canAdd 条件,如果成立 ,JPQL片段将被添加
	 * @param expression JPQL片段
	 * @param value 设置的值
	 * @return Query
	 */
	public Query addWhereAndClause( boolean canAdd, String expression, Object value )
	{
		if ( canAdd )
		{
			return addWhereClause( AND, expression, value, ADD_PARAM );
		}
		return this;
	}

	public Query addWhereAndClause( boolean canAdd, String expression )
	{
		if ( canAdd )
		{
			return addWhereClause( AND, expression, null, NOT_ADD_PARAM );
		}
		return this;
	}

	/**
	 * 直接添加JPQL片段
	 * 
	 * @see Query#addWhereOrClause(boolean,
	 *      String, Object)
	 */
	public Query addWhereAndClause( String expression, Object value )
	{
		return addWhereClause( AND, expression, value, ADD_PARAM );
	}

	/**
	 * 直接添加JPQL片段不带有占位参数
	 * 
	 * @see Query#addWhereOrClause(boolean,
	 *      String, Object)
	 */
	public Query addWhereAndClause( String expression )
	{
		return addWhereClause( AND, expression, null, NOT_ADD_PARAM );
	}

	/**
	 * <pre>
	 * 	结果将添加如  WHERE e.id=? OR e.name=? 的 JPQL片段
	 * 	例子:
	 * 	addWhereAndClause( id!=null, "e.id=?", id )
	 * 
	 * </pre>
	 * 
	 * @param canAdd 条件,如果成立 ,JPQL片段将被添加
	 * @param expression JPQL片段
	 * @param value 设置的值
	 * @return Query
	 */
	public Query addWhereOrClause( boolean canAdd, String expression, Object value )
	{
		if ( canAdd )
		{
			return addWhereClause( OR, expression, value, ADD_PARAM );
		}
		return this;
	}

	public Query addWhereOrClause( boolean canAdd, String expression )
	{
		if ( canAdd )
		{
			return addWhereClause( OR, expression, null, NOT_ADD_PARAM );
		}
		return this;
	}

	/**
	 * 直接添加JPQL片段
	 * 
	 * @see Query#addWhereOrClause(boolean,
	 *      String, Object)
	 */
	public Query addWhereOrClause( String expression, Object value )
	{
		return addWhereClause( OR, expression, value, ADD_PARAM );
	}

	/**
	 * 直接添加JPQL片段, 不添加占位符号查询参数
	 * 
	 * @see Query#addWhereOrClause(boolean,
	 *      String, Object)
	 */
	public Query addWhereOrClause( String expression )
	{
		return addWhereClause( OR, expression, null, NOT_ADD_PARAM );
	}

	/**
	 * addWhereClause
	 */

	private Query addWhereClause( String operation, String expression, Object value, boolean addParam )
	{
		prepareWhereClause( operation );
		whereClause.append( SPACE )//
		.append( expression.trim() );//

		if ( addParam )
		{
			paramters.add( value );
		}
		return this;
	}

	/**
	 * <pre>
	 * 
	 * 	结果将添加如  ORDER BY e.id ASC, e.name ASC 的 JPQL片段
	 * 	例子:
	 * 	addOrderByAscClause( true, "e.id" )
	 * 	addOrderByAscClause( true, "e.name" )
	 * </pre>
	 * 
	 * @param canAdd 条件,如果成立 ,JPQL片段将被添加
	 * @param expression JPQL片段
	 * @return {@link Query}
	 */
	public Query addOrderByAscClause( boolean canAdd, String expression )
	{
		if ( canAdd )
		{
			return addOrderByAscClause( expression );
		}
		return this;
	}

	/**
	 * 直接添加JPQL语句
	 * 
	 * @see Query#addOrderByAscClause(boolean,
	 *      String)
	 */
	public Query addOrderByAscClause( String expression )
	{
		return addOrderByClause( ASC, expression );
	}

	/**
	 * <pre>
	 * 
	 * 	结果将添加如  ORDER BY e.id DESC, e.name DESC 的 JPQL片段
	 * 	例子:
	 * 	addOrderByAscClause( true, "e.id" )
	 * 	addOrderByAscClause( true, "e.name" )
	 * </pre>
	 * 
	 * @param canAdd 条件,如果成立 ,JPQL片段将被添加
	 * @param expression JPQL片段
	 * @return {@link Query}
	 */
	public Query addOrderByDescClause( boolean canAdd, String expression )
	{
		if ( canAdd )
		{
			return addOrderByClause( DESC, expression );
		}
		return this;
	}

	/**
	 * 直接添加JPQL语句
	 * 
	 * @see Query#addOrderByDescClause(boolean,
	 *      String)
	 */
	public Query addOrderByDescClause( String expression )
	{
		return addOrderByClause( DESC, expression );
	}

	private Query addOrderByClause( String opration, String expression )
	{
		prepareOrderByClause();
		orderByClause.append( SPACE ).append( expression )//
		.append( SPACE )//
		.append( opration );
		return this;
	}

	/**
	 * <pre>
	 * 
	 * 添加JOIN语句,例如:
	 * 	.addJoinClause("d.sub s")  s为join的对象别名
	 * 
	 * </pre>
	 */
	public Query addJoinClause( String expression )
	{
		return addCaseedJoinClause( JOIN, expression );
	}

	/**
	 * <pre>
	 * 
	 * 添加INNER JOIN语句,例如:
	 * 	.addInnerJoinClause("d.sub s")  s为join的对象别名
	 * 
	 * </pre>
	 */
	public Query addInnerJoinClause( String expression )
	{
		return addCaseedJoinClause( INNER_JOIN, expression );
	}

	/**
	 * <pre>
	 * 
	 * 添加LEFT JOIN语句,例如:
	 * 	.addLeftJoinClause("d.sub s")  s为join对象后别名
	 * 
	 * </pre>
	 */
	public Query addLeftJoinClause( String expression )
	{
		return addCaseedJoinClause( LEFT_JOIN, expression );
	}

	/**
	 * <pre>
	 * 
	 * 添加RIGHT JOIN语句,例如:
	 * 	.addRightJoinClause("d.sub s")  s为join的对象别名
	 * 
	 * </pre>
	 */
	public Query addRightJoinClause( String expression )
	{
		return addCaseedJoinClause( RIGHT_JOIN, expression );
	}

	/**
	 * <pre>
	 * 
	 * 添加LEFT OUTER JOIN语句,例如:
	 * 	.addLeftOuterJoinClause("d.sub s")  s为join的对象别名
	 * 
	 * </pre>
	 */
	public Query addLeftOuterJoinClause( String expression )
	{
		return addCaseedJoinClause( LEFT_OUTER_JOIN, expression );
	}

	/**
	 * <pre>
	 * 
	 * 添加RIGHT OUTER JOIN语句,例如:
	 * 	.addRightOuterJoinClause("d.sub s")  s为join的对象别名
	 * 
	 * </pre>
	 */
	public Query addRightOuterJoinClause( String expression )
	{
		return addCaseedJoinClause( RIGHT_OUT_JOIN, expression );
	}

	/**
	 * <pre>
	 * 注意fetch也不应该与setMaxResults() 或 setFirstResult()共用,
	 * 这是因为这些操作是基于结果集的,而在预先抓取集合类时可能包含重复的数据,也就是说无法预先知道精确的行数。
	 * 
	 * 添加LEFT JOIN FETCH 语句,例如:
	 * 	.addLeftJoinFetchClause("d.sub s")  s为join的对象别名
	 * 
	 * </pre>
	 */
	public Query addLeftJoinFetchClause( String expression )
	{
		return addCaseedJoinClause( LEFT_JOIN_FETCH, expression );
	}

	/**
	 * <pre>
	 * 注意fetch也不应该与setMaxResults() 或 setFirstResult()共用,
	 * 这是因为这些操作是基于结果集的,而在预先抓取集合类时可能包含重复的数据,也就是说无法预先知道精确的行数。
	 * 
	 * 添加LEFT JOIN FETCH 语句,例如:
	 * 	.addRightJoinFetchClause("d.sub s")  s为join的对象别名
	 * 
	 * </pre>
	 */
	public Query addRightJoinFetchClause( String expression )
	{
		return addCaseedJoinClause( RIGHT_JOIN_FETCH, expression );
	}

	private Query addCaseedJoinClause( String whatJoin, String expression )
	{
		joinClause.append( SPACE ).append( whatJoin ).append( SPACE ).append( expression );
		return this;
	}

	private void prepareOrderByClause()
	{
		if ( isBlank( orderByClause ) )
		{
			orderByClause.append( SPACE ).append( ORDER_BY );
		}
		prepareOrderByClauseIfHasCondition();
	}

	private void prepareOrderByClauseIfHasCondition()
	{
		if ( orderByClause.length() != new String( SPACE + ORDER_BY ).length() )
		{
			orderByClause.append( "," );
		}
	}

	private void prepareWhereClause( String operation )
	{
		if ( isBlank( whereClause ) )
		{
			whereClause.append( SPACE ).append( WHERE );
		}
		prepareWhereClauseIfHasCondition( operation );
	}

	private void prepareWhereClauseIfHasCondition( String operation )
	{
		if ( whereClause.length() != new String( SPACE + WHERE ).length() )
		{
			whereClause.append( SPACE ).append( operation );
		}
	}

	private boolean isBlank( CharSequence c )
	{
		if ( c == null || c.length() == 0 )
		{
			return true;
		}
		return false;
	}

	private boolean isNotBlank( CharSequence c )
	{
		return !isBlank( c );
	}

	public Class<?> getEntityClass()
	{
		return entityClass;
	}

	public List<Object> getParameters()
	{
		return paramters;
	}

	/**
	 * <pre>
	 * 
	 * 设置该参数抓取数据库所有数据
	 * 设置的分页数将无效,改方法不能与setPageNum 跟 setPageSize 同时使用
	 * 
	 * 设置为true为全部抓取
	 * 默认为false
	 * 
	 * </pre>
	 * 
	 * @param fetch 是否抓取全部数据
	 * @return
	 */
	public Query setFetchAllRecords( boolean fetch )
	{
		this.fetchAllRecords = fetch;
		return this;
	}

	public boolean isFetchAllRecords()
	{
		return fetchAllRecords;
	}

	/**
	 * - page
	 */
	public Query setPageNum( Integer pageNum )
	{
		this.fetchAllRecords = false;
		this.pageNum = pageNum;
		return this;
	}

	public Query setPageSize( int pageSize )
	{
		this.fetchAllRecords = false;
		this.pageSize = pageSize;
		return this;
	}

	public Integer getPageNum()
	{
		return ( pageNum == null || pageNum < 1 ) ? 1 : pageNum;
	}

	public Integer getPageSize()
	{
		return ( pageSize == null || pageSize < 1 ) ? AppContants.DEFAULT_PAGE_SIZE : pageSize;
	}

	public Integer getFirstResult()
	{
		return ( getPageNum() - 1 ) * getPageSize();
	}

	public Integer getMaxResults()
	{
		return getPageSize();
	}

}

对应的单元测试代码:TestQuery.java



package com.xz.marksix.repository;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.util.List;

import org.junit.Test;

/**
 * <pre>
 * 
 * 	测试Query生成jpql查询语子
 * 
 * @author XzhiF
 * 			Sep 25, 2013
 * </pre>
 */
public class TestQuery
{

	private static final String ENTITY_NAME = Object.class.getName();

	@Test
	public void testMultiEvaluate() throws Exception
	{
		String expected = "FROM " + ENTITY_NAME + " d WHERE d.name=?";

		Query query = new Query( Object.class, "d" )//
		.addWhereAndClause( "d.name=?", "abc" );

		String result = query.evaluate();
		assertEquals( expected, result );
		/*
		 * second evaluate
		 */
		assertEquals( result, query.evaluate() );
	}

	@Test
	public void testAndOrWhereClause() throws Exception
	{
		String expected = "FROM " + ENTITY_NAME + " d WHERE d.name=? AND d.id=? OR d.id=? AND d.parent IS NULL";

		Query query = new Query( Object.class, "d" )//
		.addWhereAndClause( "d.name=?", "abc" )//
		.addWhereAndClause( "d.id=?", "abc" )//
		.addWhereOrClause( "d.id=?", "aa" )//
		.addWhereAndClause( "d.parent IS NULL" );
		String result = query.evaluate();
		assertEquals( expected, result );
		assertEquals( result, query.evaluate() );
		assertEquals( new Integer(3), new Integer(query.getParameters().size()) );
	}

	@Test
	public void testOrderByClause() throws Exception
	{
		String expected = "FROM " + ENTITY_NAME + " d WHERE d.name=? ORDER BY d.id DESC, d.name DESC, d.age ASC";

		Query query = new Query( Object.class, "d" )//
		.addWhereAndClause( "d.name=?", "abc" )//
		.addOrderByDescClause( "d.id" )//
		.addOrderByDescClause( "d.name" ).addOrderByAscClause( "d.age" );

		String result = query.evaluate();
		// System.out.println(result);
		assertEquals( expected, result );
	}

	@Test
	public void testJoinsClause() throws Exception
	{
		String expected = "FROM " + ENTITY_NAME + " d " + //
		"JOIN d.sub s" + //
		" INNER JOIN d.sub s" + //
		" LEFT JOIN d.sub s" + //
		" RIGHT JOIN d.sub s" + //
		" LEFT OUTER JOIN d.sub s" + //
		" RIGHT OUTER JOIN d.sub s" + //
		" LEFT JOIN FETCH d.sub s" + //
		" RIGHT JOIN FETCH d.sub s";

		Query query = new Query( Object.class, "d" )//
		.addJoinClause( "d.sub s" )//
		.addInnerJoinClause( "d.sub s" )//
		.addLeftJoinClause( "d.sub s" )//
		.addRightJoinClause( "d.sub s" )//
		.addLeftOuterJoinClause( "d.sub s" )//
		.addRightOuterJoinClause( "d.sub s" )//
		.addLeftJoinFetchClause( "d.sub s" )//
		.addRightJoinFetchClause( "d.sub s" );

		String result = query.evaluate();
		// System.out.println(result);
		assertEquals( expected, result );
	}

	@Test
	public void testClauseHasCondition() throws Exception
	{
		String expected = "FROM " + ENTITY_NAME + " d INNER JOIN d.sub s WHERE d.id=? OR d.name=? ORDER BY o.id ASC, o.name DESC";

		Query query = new Query( Object.class, "d" )//
		.addWhereAndClause( true, "d.id=?", "1" )//
		.addWhereAndClause( false, "d.id=?", "1" )//
		.addWhereOrClause( true, "d.name=?", "a" )//
		.addWhereOrClause( false, "d.name=?", "b" )//
		.addOrderByAscClause( true, "o.id" )//
		.addOrderByAscClause( false, "o.id" )//
		.addOrderByDescClause( true, "o.name" )//
		.addOrderByDescClause( false, "o.name" )//
		.addInnerJoinClause( "d.sub s" );

		String result = query.evaluate();
		// System.out.println(result);
		assertEquals( expected, result );
	}

	@Test
	public void testSelectClause() throws Exception
	{
		String expected = "SELECT d FROM " + ENTITY_NAME + " d";

		Query query = new Query( Object.class, "d" )//
		.addSelectClause( "d" );//

		String result = query.evaluate();
		// System.out.println(result);
		assertEquals( expected, result );

		expected = "SELECT new EntityVO(id,name) FROM " + ENTITY_NAME + " d";
		query = new Query( Object.class, "d" )//
		.addSelectClause( "new EntityVO(id,name)" );//
		result = query.evaluate();
		assertEquals( expected, result );

	}

	@Test
	public void testMultiAddSelectClause() throws Exception
	{
		Query query = new Query( Object.class, "d" )//
		.addSelectClause( "d" );
		try
		{
			query.addSelectClause( "a" );
			fail( "retry addSelectCluase should throw a exception" );
		}
		catch ( RepositoryException e )
		{
		}
	}

	@Test
	public void testWithinParams() throws Exception
	{

		Query query = new Query( Object.class, "d" )//
		.addWhereAndClause( "d.id=?", "1" )//
		.addWhereAndClause( "d.id=?", "2" )//
		.addWhereOrClause( "d.name=?", "a" )//
		.addWhereOrClause( "d.name=?", "b" );//

		List<Object> paramters = query.getParameters();
		assertEquals( "1", paramters.get( 0 ) );
		assertEquals( "2", paramters.get( 1 ) );
		assertEquals( "a", paramters.get( 2 ) );
		assertEquals( "b", paramters.get( 3 ) );
	}

	@Test
	public void testEvaluateCount() throws Exception
	{
		String expected = "SELECT COUNT(d) FROM " + ENTITY_NAME + " d WHERE d.id=? AND d.name=?";
		Query query = new Query( Object.class, "d" )//
		.addWhereAndClause( "d.id=?", "1" )//
		.addWhereAndClause( "d.name=?", "a" )//
		.addOrderByAscClause( "d.id" )//
		.addInnerJoinClause( "d.sub c" )//
		.addSelectClause( "d" );

		String result = query.evaluateCount();
		// System.out.println(result);
		assertEquals( expected, result );

	}

}




你可能感兴趣的:(object,JPQL,query)