Commits

Danny van Bruggen committed 88dcd56

* Initial code import

Comments (0)

Files changed (17)

+bin
+.manager
+target
+.settings
+*~
+/target
+.project
+.classpath

hql-queryprutser/pom.xml

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.laamella.queryprutser</groupId>
+		<artifactId>queryprutser</artifactId>
+		<version>1.0-SNAPSHOT</version>
+	</parent>
+	<groupId>com.laamella.queryprutser.hql</groupId>
+	<artifactId>hql-queryprutser</artifactId>
+	<dependencies>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+			<version>1.6.6</version>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.10</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.mockito</groupId>
+			<artifactId>mockito-core</artifactId>
+			<version>1.9.5-rc1</version>
+		</dependency>
+		<dependency>
+			<groupId>org.hibernate</groupId>
+			<artifactId>hibernate-core</artifactId>
+			<version>4.1.7.Final</version>
+			<scope>compile</scope>
+		</dependency>
+	</dependencies>
+
+</project>

hql-queryprutser/src/main/java/com/laamella/queryprutser/Assert.java

+package com.laamella.queryprutser;
+
+public final class Assert {
+	private Assert(){
+		// Singleton.
+	}
+	public static void notNull(Object value) {
+		if (value == null) {
+			throw new AssertionError("Value was null");
+		}
+	}
+}

hql-queryprutser/src/main/java/com/laamella/queryprutser/Clause.java

+package com.laamella.queryprutser;
+
+/**
+ * Builds up a string with the correct prefix, infix and postfix insertions.
+ */
+public class Clause {
+	private final StringBuilder builder;
+	private final String infix;
+	private final String postfix;
+	private final String prefix;
+
+	private boolean firstAppend;
+
+	/**
+	 * "Normal" constructor.
+	 * 
+	 * @param prefix a piece of text to put at the start of the eventual string.
+	 * @param infix a piece of text to put inbetween each piece of text added.
+	 * @param postfix a piece of text to put at the end of the eventual string, but only if the eventual string isn't empty.
+	 */
+	Clause(String prefix, String infix, String postfix) {
+		this(prefix, infix, postfix, true, new StringBuilder());
+	}
+
+	/**
+	 * Makes this {@link Clause} a copy of source.
+	 */
+	Clause(Clause source) {
+		this(source.prefix, source.infix, source.postfix, source.firstAppend, new StringBuilder(source.builder));
+	}
+
+	private Clause(String prefix, String infix, String postfix, boolean firstAppend, StringBuilder builder) {
+		Assert.notNull(prefix);
+		Assert.notNull(infix);
+		Assert.notNull(postfix);
+		Assert.notNull(builder);
+		this.prefix = prefix;
+		this.infix = infix;
+		this.postfix = postfix;
+		this.firstAppend = firstAppend;
+		this.builder = builder;
+	}
+
+	/**
+	 * Adds one piece of text, taking care to insert pre, in, and postfixes.
+	 * 
+	 * @return this
+	 */
+	public Clause add(String text) {
+		return addWithSpecialInfix(infix, text);
+	}
+
+	/**
+	 * Calls add for each supplied text.
+	 * 
+	 * @return this
+	 */
+	public Clause add(String... texts) {
+		return addWithSpecialInfix(infix, texts);
+	}
+
+	/**
+	 * Adds one piece of text, taking care to insert pre, in, and postfixes, overriding the infix for just this once.
+	 * 
+	 * @return this
+	 */
+	public Clause addWithSpecialInfix(String specialInfix, String text) {
+		Assert.notNull(specialInfix);
+		Assert.notNull(text);
+		if (!firstAppend) {
+			builder.append(specialInfix);
+		} else {
+			builder.append(prefix);
+		}
+		builder.append(text);
+		firstAppend = false;
+		return this;
+	}
+
+	/**
+	 * Calls addWithSpecialInfix for each supplied text.
+	 * 
+	 * @return this
+	 */
+	public Clause addWithSpecialInfix(String specialInfix, String... texts) {
+		for (String text : texts) {
+			addWithSpecialInfix(specialInfix, text);
+		}
+		return this;
+	}
+
+	/**
+	 * @return the string as it was built up to now.
+	 */
+	@Override
+	public String toString() {
+		if (firstAppend) {
+			/* Don't even return the postfix */
+			return "";
+		}
+		return builder.toString() + postfix;
+	}
+}

hql-queryprutser/src/main/java/com/laamella/queryprutser/ClauseWithSubselects.java

+package com.laamella.queryprutser;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class ClauseWithSubselects extends Clause {
+	private final List<Subquery> subqueries = new ArrayList<Subquery>();
+
+	ClauseWithSubselects(String prefix, String infix, String postfix) {
+		super(prefix, infix, postfix);
+	}
+
+	public Subquery subselect(Class<?> rootClass, String alias) {
+		Subquery subquery = new Subquery(rootClass, alias);
+		subqueries.add(subquery);
+		return subquery;
+	}
+
+	public Subquery subselect(Class<?> rootClass) {
+		return subselect(rootClass, null);
+	}
+
+	@Override
+	public String toString() {
+		Clause privateClause = new Clause(this);
+		for (Subquery builder : subqueries) {
+			privateClause.add(" " + builder.queryString() + " ");
+		}
+		return privateClause.toString();
+	}
+
+	void putAllParametersInThisMap(Map<String, Object> inHere) {
+		for (Subquery subquery : subqueries) {
+			inHere.putAll(subquery.allParameters());
+		}
+	}
+}

hql-queryprutser/src/main/java/com/laamella/queryprutser/FromClause.java

+package com.laamella.queryprutser;
+
+public class FromClause extends Clause {
+	FromClause(Class<?> rootClass, String alias) {
+		super("from ", " ", " ");
+		Assert.notNull(rootClass);
+		if (alias != null) {
+			add(rootClass.getCanonicalName() + " " + alias);
+		} else {
+			add(rootClass.getCanonicalName());
+		}
+	}
+
+	public FromClause joinFetch(String relationship, String alias) {
+		add("join fetch " + relationship + " " + alias);
+		return this;
+	}
+
+}

hql-queryprutser/src/main/java/com/laamella/queryprutser/GroupByClause.java

+package com.laamella.queryprutser;
+
+public class GroupByClause extends Clause {
+	GroupByClause() {
+		super("\ngroup by ", ", ", " ");
+	}
+
+}

hql-queryprutser/src/main/java/com/laamella/queryprutser/HavingClause.java

+package com.laamella.queryprutser;
+
+public class HavingClause extends WhereClause {
+	HavingClause() {
+		super("having");
+	}
+
+}

hql-queryprutser/src/main/java/com/laamella/queryprutser/OrderByClause.java

+package com.laamella.queryprutser;
+
+public class OrderByClause extends Clause {
+	OrderByClause() {
+		super("\norder by ", ", ", " ");
+	}
+
+}

hql-queryprutser/src/main/java/com/laamella/queryprutser/QueryBuilder.java

+package com.laamella.queryprutser;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Simple helper for building dynamic HQL select queries, as opposed to Criteria, which are more helpful, complex, and contain many
+ * gotcha's, and to plain HQL, which is not dynamic.
+ */
+public class QueryBuilder {
+	private final Logger log = LoggerFactory.getLogger(QueryBuilder.class);
+
+	public final SelectClause select = new SelectClause();
+	public final FromClause from;
+	public final WhereClause where = new WhereClause();
+	private final GroupByClause groupBy = new GroupByClause();
+	public final HavingClause having = new HavingClause();
+	private final OrderByClause orderBy = new OrderByClause();
+
+	private final Map<String, Object> parameters = new HashMap<String, Object>();
+
+	public QueryBuilder(Class<?> rootClass, String alias) {
+		from = new FromClause(rootClass, alias);
+	}
+
+	public QueryBuilder(Class<?> rootClass) {
+		this(rootClass, null);
+	}
+
+	public QueryBuilder orderBy(String... fields) {
+		orderBy.add(fields);
+		return this;
+	}
+
+	public QueryBuilder groupBy(String... fields) {
+		groupBy.add(fields);
+		return this;
+	}
+
+	public QueryBuilder select(String... fields) {
+		select.add(fields);
+		return this;
+	}
+
+	/**
+	 * Set a named parameter for this query.
+	 * 
+	 * @return
+	 */
+	public QueryBuilder parameter(String name, Object value) {
+		Assert.notNull(name);
+		parameters.put(name, value);
+		return this;
+	}
+
+	/**
+	 * @return create the final query.
+	 */
+	public Query query(Session session) {
+		String hql = queryString().toString();
+		log.debug(hql);
+		Query query = session.createQuery(hql);
+		Map<String, Object> allParameters = allParameters();
+
+		for (Map.Entry<String, Object> entry : allParameters.entrySet()) {
+			log.debug(entry.getKey() + "=" + entry.getValue());
+			query.setParameter(entry.getKey(), entry.getValue());
+		}
+
+		return query;
+	}
+
+	/**
+	 * @return all parameters that occur in the query and its subqueries.
+	 */
+	public Map<String, Object> allParameters() {
+		final Map<String, Object> allParameters = new HashMap<String, Object>();
+		allParameters.putAll(parameters);
+		where.putAllParametersInThisMap(allParameters);
+		select.putAllParametersInThisMap(allParameters);
+		having.putAllParametersInThisMap(allParameters);
+		return allParameters;
+	}
+
+	/**
+	 * @return the current HQL for the query.
+	 */
+	public CharSequence queryString() {
+		final StringBuilder hql = new StringBuilder();
+		hql.append(select);
+		hql.append(from);
+		hql.append(where);
+		hql.append(groupBy);
+		hql.append(having);
+		hql.append(orderBy);
+		return hql;
+	}
+
+	@Override
+	public String toString() {
+		return queryString().toString();
+	}
+}

hql-queryprutser/src/main/java/com/laamella/queryprutser/SelectClause.java

+package com.laamella.queryprutser;
+
+public class SelectClause extends ClauseWithSubselects {
+	SelectClause() {
+		super("select ", ", ", " ");
+	}
+
+	public void min(String string) {
+		add("min(" + string + ")");
+	}
+
+	public void max(String string) {
+		add("max(" + string + ")");
+	}
+}

hql-queryprutser/src/main/java/com/laamella/queryprutser/Subquery.java

+package com.laamella.queryprutser;
+
+public class Subquery extends QueryBuilder {
+	private String before = "";
+	private String after = "";
+
+	Subquery(Class<?> rootClass, String alias) {
+		super(rootClass, alias);
+	}
+
+	public Subquery before(String before) {
+		Assert.notNull(before);
+		this.before = before;
+		return this;
+	}
+
+	public Subquery after(String after) {
+		Assert.notNull(after);
+		this.after = after;
+		return this;
+	}
+
+	@Override
+	public String queryString() {
+		final StringBuffer hql = new StringBuffer();
+		hql.append(before);
+		hql.append("(");
+		hql.append(super.queryString());
+		hql.append(") ");
+		hql.append(after);
+		return hql.toString();
+	}
+}

hql-queryprutser/src/main/java/com/laamella/queryprutser/WhereClause.java

+package com.laamella.queryprutser;
+
+public class WhereClause extends ClauseWithSubselects {
+	WhereClause() {
+		this("where");
+	}
+
+	protected WhereClause(String keyword) {
+		super(" \n" + keyword + " ", "\n", " ");
+	}
+
+	public void and(String... values) {
+		addWithSpecialInfix("\nand ", values);
+	}
+}

hql-queryprutser/src/test/java/com/laamella/queryprutser/AssertTest.java

+package com.laamella.queryprutser;
+
+import org.junit.Test;
+
+public class AssertTest {
+	@Test(expected = AssertionError.class)
+	public void whenAssertNotNullOnNullThenGetAnAssertionError() {
+		Assert.notNull(null);
+	}
+
+	@Test
+	public void whenAssertNotNullOnNotNullThenNothingHappens() {
+		Assert.notNull("");
+	}
+}

hql-queryprutser/src/test/java/com/laamella/queryprutser/ClauseTest.java

+package com.laamella.queryprutser;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import com.laamella.queryprutser.Clause;
+
+public class ClauseTest {
+
+	@Test
+	public void whenNotAppendingAnythingThenResultIsEmpty() {
+		Clause string = new Clause("pre", "in", "post");
+		assertEquals("", string.toString());
+	}
+
+	@Test
+	public void whenAppendingOneThingThenPreAndPostfixAreAdded() {
+		Clause string = new Clause("pre", "in", "post");
+		string.add("text");
+		assertEquals("pretextpost", string.toString());
+	}
+
+	@Test
+	public void whenAppendingSeveralThingsThenPreInAndPostfixAreAdded() {
+		Clause string = new Clause("pre", "in", "post");
+		string.add("text1", "text2", "text3");
+		assertEquals("pretext1intext2intext3post", string.toString());
+	}
+
+}

hql-queryprutser/src/test/java/com/laamella/queryprutser/QueryBuilderTest.java

+package com.laamella.queryprutser;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Map;
+
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.junit.Test;
+
+import com.laamella.queryprutser.QueryBuilder;
+import com.laamella.queryprutser.Subquery;
+
+public class QueryBuilderTest {
+	@Test
+	public void whenNothingIsDoneWithQueryBuilderThenWeGetASelectEverything() {
+		QueryBuilder queryBuilder = new QueryBuilder(Object.class);
+		assertEquals(0, queryBuilder.allParameters().size());
+		assertFuzzyString("from java.lang.Object", queryBuilder.queryString());
+	}
+
+	@Test
+	public void whenTheRootObjectIsAliasedThenItShowsInTheHql() {
+		QueryBuilder queryBuilder = new QueryBuilder(Object.class, "o");
+		assertFuzzyString("from java.lang.Object o", queryBuilder.queryString());
+	}
+
+	@Test
+	public void whenDoingAnAliasedJoinFetchThenWeGetThat() {
+		QueryBuilder queryBuilder = new QueryBuilder(Object.class);
+		queryBuilder.from.joinFetch("relationship", "rel");
+		assertFuzzyString("from java.lang.Object join fetch relationship rel", queryBuilder.queryString());
+	}
+
+	@Test
+	public void whenPuttingASubqueryIntoTheSelectClauseThenItIsThere(){
+		QueryBuilder builder = new QueryBuilder(Object.class);
+		builder.select.subselect(String.class);
+		assertFuzzyString("select (from java.lang.String ) from java.lang.Object", builder.queryString());
+	}
+
+	@Test
+	public void whenPuttingASubqueryIntoTheWhereClauseThenItIsThere(){
+		QueryBuilder builder = new QueryBuilder(Object.class);
+		builder.where.subselect(String.class);
+		assertFuzzyString("from java.lang.Object where (from java.lang.String )", builder.queryString());
+	}
+	@Test
+	public void whenGivingASubqueryABeforeAndAfterStringThenTheyAreThere(){
+		QueryBuilder builder = new QueryBuilder(Object.class);
+		Subquery subselect = builder.where.subselect(String.class);
+		subselect.before(">>>");
+		subselect.after("<<<");
+		assertFuzzyString("from java.lang.Object where >>>(from java.lang.String ) <<<", builder.queryString());
+	}
+
+	@Test
+	public void whenUsingAllClausesThenTheyAreInTheRightOrder(){
+		QueryBuilder builder = new QueryBuilder(Object.class);
+		builder.select.add("[select clause]");
+		builder.from.add("[from clause]");
+		builder.where.add("[where clause]");
+		builder.orderBy("[order by clause]");
+		builder.groupBy("[group by clause]");
+		builder.having.add("[having clause]");
+		assertFuzzyString("select [select clause] from java.lang.Object [from clause] where [where clause] group by [group by clause] having [having clause] order by [order by clause]", builder.queryString());
+	}
+	
+	@Test
+	public void whenUsingMinMaxThenThoseAreAdded(){
+		QueryBuilder builder = new QueryBuilder(Object.class);
+		builder.select.max("a");
+		builder.select.min("b");
+		assertFuzzyString("select max(a), min(b) from java.lang.Object", builder.queryString());
+	}
+	
+	@Test
+	public void whenSelectingMoreThanOneThingThenTheRightInfixesAreUsed(){
+		QueryBuilder builder = new QueryBuilder(Object.class);
+		builder.select("abc", "def", "ghi");
+		assertFuzzyString("select abc, def, ghi from java.lang.Object", builder.queryString());
+	}
+
+	@Test
+	public void whenWhereingMoreThanOneThingThenTheRightInfixesAreUsed(){
+		QueryBuilder builder = new QueryBuilder(Object.class);
+		builder.where.and("abc", "def", "ghi");
+		assertFuzzyString("from java.lang.Object where abc and def and ghi", builder.queryString());
+	}
+	
+	@Test
+	public void whenSettingParametersAllOverThePlaceThenTheyAreAllCollected(){
+		QueryBuilder builder = new QueryBuilder(Object.class);
+		builder.parameter("a", "a");
+		builder.select.subselect(String.class).parameter("b", "b");
+		builder.where.subselect(Integer.class).having.subselect(Float.class).parameter("c", "c");
+		Map<String, Object> allParameters = builder.allParameters();
+		
+		assertEquals(3, allParameters.size());
+		assertEquals("a", allParameters.get("a"));
+		assertEquals("b", allParameters.get("b"));
+		assertEquals("c", allParameters.get("c"));
+	}
+	
+	@Test
+	public void whenCallingToStringThenTheHqlIsReturned(){
+		QueryBuilder builder = new QueryBuilder(Object.class);
+		assertFuzzyString("from java.lang.Object", builder.toString());
+	}
+	
+	@Test
+	public void whenCreatingAHibernateQueryThenTheRightHqlAndAllParametersAreSet(){
+		QueryBuilder builder = new QueryBuilder(Object.class);
+		builder.parameter("a", "a");
+		Session sessionMock=mock(Session.class);
+		Query queryMock=mock(Query.class);
+		when(sessionMock.createQuery("from java.lang.Object ")).thenReturn(queryMock);
+
+		Query query = builder.query(sessionMock);
+		assertEquals(queryMock, query);
+		verify(queryMock).setParameter("a", "a");
+	}
+
+	private void assertFuzzyString(CharSequence expected, CharSequence actual) {
+		assertEquals(cleanString(expected), cleanString(actual));
+	}
+	
+	private String cleanString(CharSequence actual){
+		String[] split = actual.toString().split("\\s+");
+		StringBuffer stringBuffer = new StringBuffer();
+		for(String s: split){
+			stringBuffer.append(s);
+			stringBuffer.append(" ");
+		}
+		return stringBuffer.toString().trim();
+	}
+}
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>com.laamella.queryprutser</groupId>
+	<artifactId>queryprutser</artifactId>
+	<version>1.0-SNAPSHOT</version>
+	<name>Laamella Gad's Query Prutser</name>
+	<packaging>pom</packaging>
+	<modules>
+		<module>hql-queryprutser</module>
+	</modules>
+</project>