mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-02 20:02:29 +01:00
Fix and test GroupBy SQL query creation
Signed-off-by: Ian Southam <isoutham@schubergphilis.com> Signed-off-by: Hugo Trippaers <htrippaers@schubergphilis.com>
This commit is contained in:
parent
8649fa005a
commit
cbea6d26e8
@ -28,31 +28,35 @@ public class GroupBy<J extends SearchBase<?, T, R>, T, R> {
|
||||
List<Pair<Func, Attribute>> _groupBys;
|
||||
Having _having;
|
||||
|
||||
public GroupBy(J builder) {
|
||||
public GroupBy(final J builder) {
|
||||
init(builder);
|
||||
}
|
||||
|
||||
protected void init(final J builder) {
|
||||
_builder = builder;
|
||||
_groupBys = new ArrayList<Pair<Func, Attribute>>();
|
||||
_having = null;
|
||||
for (Attribute attr : _builder.getSpecifiedAttributes()) {
|
||||
for (final Attribute attr : _builder.getSpecifiedAttributes()) {
|
||||
_groupBys.add(new Pair<Func, Attribute>(null, attr));
|
||||
}
|
||||
_builder.getSpecifiedAttributes().clear();
|
||||
}
|
||||
|
||||
public GroupBy<J, T, R> group(Object useless) {
|
||||
public GroupBy<J, T, R> group(final Object useless) {
|
||||
_groupBys.add(new Pair<Func, Attribute>(null, _builder.getSpecifiedAttributes().get(0)));
|
||||
_builder.getSpecifiedAttributes().clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
public GroupBy<J, T, R> group(Func func, Object useless) {
|
||||
public GroupBy<J, T, R> group(final Func func, final Object useless) {
|
||||
_groupBys.add(new Pair<Func, Attribute>(func, _builder.getSpecifiedAttributes().get(0)));
|
||||
_builder.getSpecifiedAttributes().clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
public J having(Func func, Object obj, Op op, Object value) {
|
||||
public J having(final Func func, final Object obj, final Op op, final Object value) {
|
||||
assert (_having == null) : "You can only specify one having in a group by";
|
||||
List<Attribute> attrs = _builder.getSpecifiedAttributes();
|
||||
final List<Attribute> attrs = _builder.getSpecifiedAttributes();
|
||||
assert attrs.size() == 1 : "You didn't specified an attribute";
|
||||
|
||||
_having = new Having(func, attrs.get(0), op, value);
|
||||
@ -60,12 +64,12 @@ public class GroupBy<J extends SearchBase<?, T, R>, T, R> {
|
||||
return _builder;
|
||||
}
|
||||
|
||||
public void toSql(StringBuilder builder) {
|
||||
public void toSql(final StringBuilder builder) {
|
||||
builder.append(" GROUP BY ");
|
||||
for (Pair<Func, Attribute> groupBy : _groupBys) {
|
||||
for (final Pair<Func, Attribute> groupBy : _groupBys) {
|
||||
if (groupBy.first() != null) {
|
||||
String func = groupBy.first().toString();
|
||||
func.replaceFirst("@", groupBy.second().table + "." + groupBy.second().columnName);
|
||||
func = func.replaceFirst("@", groupBy.second().table + "." + groupBy.second().columnName);
|
||||
builder.append(func);
|
||||
} else {
|
||||
builder.append(groupBy.second().table + "." + groupBy.second().columnName);
|
||||
@ -86,18 +90,19 @@ public class GroupBy<J extends SearchBase<?, T, R>, T, R> {
|
||||
public Op op;
|
||||
public Object value;
|
||||
|
||||
public Having(Func func, Attribute attr, Op op, Object value) {
|
||||
public Having(final Func func, final Attribute attr, final Op op, final Object value) {
|
||||
this.func = func;
|
||||
this.attr = attr;
|
||||
this.op = op;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void toSql(StringBuilder builder) {
|
||||
public void toSql(final StringBuilder builder) {
|
||||
builder.append(" HAVING ");
|
||||
if (func != null) {
|
||||
String f = func.toString();
|
||||
f.replaceFirst("@", attr.toString());
|
||||
builder.append(func);
|
||||
f = f.replaceFirst("@", attr.toString());
|
||||
builder.append(f);
|
||||
} else {
|
||||
builder.append(attr.toString());
|
||||
}
|
||||
|
||||
@ -52,13 +52,13 @@ import com.cloud.utils.exception.CloudRuntimeException;
|
||||
*/
|
||||
public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
|
||||
final Map<String, Attribute> _attrs;
|
||||
final Class<T> _entityBeanType;
|
||||
final Class<K> _resultType;
|
||||
final GenericDaoBase<? extends T, ? extends Serializable> _dao;
|
||||
Map<String, Attribute> _attrs;
|
||||
Class<T> _entityBeanType;
|
||||
Class<K> _resultType;
|
||||
GenericDaoBase<? extends T, ? extends Serializable> _dao;
|
||||
|
||||
final ArrayList<Condition> _conditions;
|
||||
final ArrayList<Attribute> _specifiedAttrs;
|
||||
ArrayList<Condition> _conditions;
|
||||
ArrayList<Attribute> _specifiedAttrs;
|
||||
|
||||
protected HashMap<String, JoinBuilder<SearchBase<?, ?, ?>>> _joins;
|
||||
protected ArrayList<Select> _selects;
|
||||
@ -66,7 +66,11 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
protected SelectType _selectType;
|
||||
T _entity;
|
||||
|
||||
SearchBase(Class<T> entityType, Class<K> resultType) {
|
||||
SearchBase(final Class<T> entityType, final Class<K> resultType) {
|
||||
init(entityType, resultType);
|
||||
}
|
||||
|
||||
protected void init(final Class<T> entityType, final Class<K> resultType) {
|
||||
_dao = (GenericDaoBase<? extends T, ? extends Serializable>)GenericDaoBase.getDao(entityType);
|
||||
if (_dao == null) {
|
||||
throw new CloudRuntimeException("Unable to find DAO for " + entityType);
|
||||
@ -81,7 +85,6 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
_joins = null;
|
||||
_specifiedAttrs = new ArrayList<Attribute>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies how the search query should be grouped
|
||||
*
|
||||
@ -90,7 +93,7 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
* @see GroupBy
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public GroupBy<J, T, K> groupBy(Object... fields) {
|
||||
public GroupBy<J, T, K> groupBy(final Object... fields) {
|
||||
assert _groupBy == null : "Can't do more than one group bys";
|
||||
_groupBy = new GroupBy<J, T, K>((J)this);
|
||||
return _groupBy;
|
||||
@ -106,7 +109,7 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
* @return itself to build more search parts.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public J select(String fieldName, Func func, Object field, Object... params) {
|
||||
public J select(final String fieldName, final Func func, final Object field, final Object... params) {
|
||||
if (_entity == null) {
|
||||
throw new RuntimeException("SearchBuilder cannot be modified once it has been setup");
|
||||
}
|
||||
@ -126,9 +129,9 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
try {
|
||||
declaredField = _resultType.getDeclaredField(fieldName);
|
||||
declaredField.setAccessible(true);
|
||||
} catch (SecurityException e) {
|
||||
} catch (final SecurityException e) {
|
||||
throw new RuntimeException("Unable to find " + fieldName, e);
|
||||
} catch (NoSuchFieldException e) {
|
||||
} catch (final NoSuchFieldException e) {
|
||||
throw new RuntimeException("Unable to find " + fieldName, e);
|
||||
}
|
||||
} else {
|
||||
@ -138,7 +141,7 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
}
|
||||
}
|
||||
|
||||
Select select = new Select(func, _specifiedAttrs.size() == 0 ? null : _specifiedAttrs.get(0), declaredField, params);
|
||||
final Select select = new Select(func, _specifiedAttrs.size() == 0 ? null : _specifiedAttrs.get(0), declaredField, params);
|
||||
_selects.add(select);
|
||||
|
||||
_specifiedAttrs.clear();
|
||||
@ -153,7 +156,7 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
* @return itself
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public J selectFields(Object... fields) {
|
||||
public J selectFields(final Object... fields) {
|
||||
if (_entity == null) {
|
||||
throw new RuntimeException("SearchBuilder cannot be modified once it has been setup");
|
||||
}
|
||||
@ -165,13 +168,13 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
_selects = new ArrayList<Select>();
|
||||
}
|
||||
|
||||
for (Attribute attr : _specifiedAttrs) {
|
||||
for (final Attribute attr : _specifiedAttrs) {
|
||||
Field field = null;
|
||||
try {
|
||||
field = _resultType.getDeclaredField(attr.field.getName());
|
||||
field.setAccessible(true);
|
||||
} catch (SecurityException e) {
|
||||
} catch (NoSuchFieldException e) {
|
||||
} catch (final SecurityException e) {
|
||||
} catch (final NoSuchFieldException e) {
|
||||
}
|
||||
_selects.add(new Select(Func.NATIVE, attr, field, null));
|
||||
}
|
||||
@ -192,14 +195,14 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
* @return itself
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public J join(String name, SearchBase<?, ?, ?> builder, Object joinField1, Object joinField2, JoinBuilder.JoinType joinType) {
|
||||
public J join(final String name, final SearchBase<?, ?, ?> builder, final Object joinField1, final Object joinField2, final JoinBuilder.JoinType joinType) {
|
||||
assert _entity != null : "SearchBuilder cannot be modified once it has been setup";
|
||||
assert _specifiedAttrs.size() == 1 : "You didn't select the attribute.";
|
||||
assert builder._entity != null : "SearchBuilder cannot be modified once it has been setup";
|
||||
assert builder._specifiedAttrs.size() == 1 : "You didn't select the attribute.";
|
||||
assert builder != this : "You can't add yourself, can you? Really think about it!";
|
||||
|
||||
JoinBuilder<SearchBase<?, ?, ?>> t = new JoinBuilder<SearchBase<?, ?, ?>>(builder, _specifiedAttrs.get(0), builder._specifiedAttrs.get(0), joinType);
|
||||
final JoinBuilder<SearchBase<?, ?, ?>> t = new JoinBuilder<SearchBase<?, ?, ?>>(builder, _specifiedAttrs.get(0), builder._specifiedAttrs.get(0), joinType);
|
||||
if (_joins == null) {
|
||||
_joins = new HashMap<String, JoinBuilder<SearchBase<?, ?, ?>>>();
|
||||
}
|
||||
@ -214,8 +217,8 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
return _selectType;
|
||||
}
|
||||
|
||||
protected void set(String name) {
|
||||
Attribute attr = _attrs.get(name);
|
||||
protected void set(final String name) {
|
||||
final Attribute attr = _attrs.get(name);
|
||||
assert (attr != null) : "Searching for a field that's not there: " + name;
|
||||
_specifiedAttrs.add(attr);
|
||||
}
|
||||
@ -239,12 +242,12 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
return _specifiedAttrs;
|
||||
}
|
||||
|
||||
protected Condition constructCondition(String conditionName, String cond, Attribute attr, Op op) {
|
||||
protected Condition constructCondition(final String conditionName, final String cond, final Attribute attr, final Op op) {
|
||||
assert _entity != null : "SearchBuilder cannot be modified once it has been setup";
|
||||
assert op == null || _specifiedAttrs.size() == 1 : "You didn't select the attribute.";
|
||||
assert op != Op.SC : "Call join";
|
||||
|
||||
Condition condition = new Condition(conditionName, cond, attr, op);
|
||||
final Condition condition = new Condition(conditionName, cond, attr, op);
|
||||
_conditions.add(condition);
|
||||
_specifiedAttrs.clear();
|
||||
return condition;
|
||||
@ -304,7 +307,7 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public J cp() {
|
||||
Condition condition = new Condition(null, " ) ", null, Op.RP);
|
||||
final Condition condition = new Condition(null, " ) ", null, Op.RP);
|
||||
_conditions.add(condition);
|
||||
return (J)this;
|
||||
}
|
||||
@ -315,7 +318,7 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public J op() {
|
||||
Condition condition = new Condition(null, " ( ", null, Op.RP);
|
||||
final Condition condition = new Condition(null, " ( ", null, Op.RP);
|
||||
_conditions.add(condition);
|
||||
return (J)this;
|
||||
}
|
||||
@ -326,13 +329,13 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
@Override
|
||||
protected synchronized void finalize() {
|
||||
if (_entity != null) {
|
||||
Factory factory = (Factory)_entity;
|
||||
final Factory factory = (Factory)_entity;
|
||||
factory.setCallback(0, null);
|
||||
_entity = null;
|
||||
}
|
||||
|
||||
if (_joins != null) {
|
||||
for (JoinBuilder<SearchBase<?, ?, ?>> join : _joins.values()) {
|
||||
for (final JoinBuilder<SearchBase<?, ?, ?>> join : _joins.values()) {
|
||||
join.getT().finalize();
|
||||
}
|
||||
}
|
||||
@ -343,7 +346,7 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Select select : _selects) {
|
||||
for (final Select select : _selects) {
|
||||
if (select.field == null) {
|
||||
assert (_selects.size() == 1) : "You didn't specify any fields to put the result in but you're specifying more than one select so where should I put the selects?";
|
||||
_selectType = SelectType.Single;
|
||||
@ -365,11 +368,11 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
protected final Attribute attr;
|
||||
protected Object[] presets;
|
||||
|
||||
protected Condition(String name) {
|
||||
protected Condition(final String name) {
|
||||
this(name, null, null, null);
|
||||
}
|
||||
|
||||
public Condition(String name, String cond, Attribute attr, Op op) {
|
||||
public Condition(final String name, final String cond, final Attribute attr, final Op op) {
|
||||
this.name = name;
|
||||
this.attr = attr;
|
||||
this.cond = cond;
|
||||
@ -381,7 +384,7 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
return presets != null;
|
||||
}
|
||||
|
||||
public void setPresets(Object... presets) {
|
||||
public void setPresets(final Object... presets) {
|
||||
this.presets = presets;
|
||||
}
|
||||
|
||||
@ -389,7 +392,7 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
return presets;
|
||||
}
|
||||
|
||||
public void toSql(StringBuilder sql, Object[] params, int count) {
|
||||
public void toSql(final StringBuilder sql, final Object[] params, final int count) {
|
||||
if (count > 0) {
|
||||
sql.append(cond);
|
||||
}
|
||||
@ -438,12 +441,12 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(final Object obj) {
|
||||
if (!(obj instanceof Condition)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Condition condition = (Condition)obj;
|
||||
final Condition condition = (Condition)obj;
|
||||
return name.equals(condition.name);
|
||||
}
|
||||
}
|
||||
@ -457,7 +460,7 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
protected Select() {
|
||||
}
|
||||
|
||||
public Select(Func func, Attribute attr, Field field, Object[] params) {
|
||||
public Select(final Func func, final Attribute attr, final Field field, final Object[] params) {
|
||||
this.func = func;
|
||||
this.attr = attr;
|
||||
this.params = params;
|
||||
@ -467,22 +470,22 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
|
||||
|
||||
protected class Interceptor implements MethodInterceptor {
|
||||
@Override
|
||||
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
|
||||
String name = method.getName();
|
||||
public Object intercept(final Object object, final Method method, final Object[] args, final MethodProxy methodProxy) throws Throwable {
|
||||
final String name = method.getName();
|
||||
if (method.getAnnotation(Transient.class) == null) {
|
||||
if (name.startsWith("get")) {
|
||||
String fieldName = Character.toLowerCase(name.charAt(3)) + name.substring(4);
|
||||
final String fieldName = Character.toLowerCase(name.charAt(3)) + name.substring(4);
|
||||
set(fieldName);
|
||||
return null;
|
||||
} else if (name.startsWith("is")) {
|
||||
String fieldName = Character.toLowerCase(name.charAt(2)) + name.substring(3);
|
||||
final String fieldName = Character.toLowerCase(name.charAt(2)) + name.substring(3);
|
||||
set(fieldName);
|
||||
return null;
|
||||
} else {
|
||||
Column ann = method.getAnnotation(Column.class);
|
||||
final Column ann = method.getAnnotation(Column.class);
|
||||
if (ann != null) {
|
||||
String colName = ann.name();
|
||||
for (Map.Entry<String, Attribute> attr : _attrs.entrySet()) {
|
||||
final String colName = ann.name();
|
||||
for (final Map.Entry<String, Attribute> attr : _attrs.entrySet()) {
|
||||
if (colName.equals(attr.getValue().columnName)) {
|
||||
set(attr.getKey());
|
||||
return null;
|
||||
|
||||
62
framework/db/test/com/cloud/utils/db/GroupByTest.java
Normal file
62
framework/db/test/com/cloud/utils/db/GroupByTest.java
Normal file
@ -0,0 +1,62 @@
|
||||
package com.cloud.utils.db;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.SearchCriteria.Func;
|
||||
import com.cloud.utils.db.SearchCriteria.Op;
|
||||
|
||||
|
||||
public class GroupByTest {
|
||||
|
||||
protected static final String EXPECTED_QUERY = "BASE GROUP BY FIRST(TEST_TABLE.TEST_COLUMN), MAX(TEST_TABLE.TEST_COLUMN) HAVING COUNT(TEST_TABLE2.TEST_COLUMN2) > ? ";
|
||||
@Test
|
||||
public void testToSql() {
|
||||
// Prepare
|
||||
final StringBuilder sb = new StringBuilder("BASE");
|
||||
final GroupByExtension groupBy = new GroupByExtension(new SearchBaseExtension(String.class, String.class));
|
||||
|
||||
final Attribute att = new Attribute("TEST_TABLE", "TEST_COLUMN");
|
||||
final Pair<Func, Attribute> pair1 = new Pair<SearchCriteria.Func, Attribute>(SearchCriteria.Func.FIRST, att);
|
||||
final Pair<Func, Attribute> pair2 = new Pair<SearchCriteria.Func, Attribute>(SearchCriteria.Func.MAX, att);
|
||||
groupBy._groupBys = new ArrayList<Pair<Func, Attribute>>();
|
||||
groupBy._groupBys.add(pair1);
|
||||
groupBy._groupBys.add(pair2);
|
||||
groupBy.having(SearchCriteria.Func.COUNT, att, Op.GT, "SOME_VALUE");
|
||||
|
||||
// Execute
|
||||
groupBy.toSql(sb);
|
||||
|
||||
// Assert
|
||||
assertTrue("It didn't create the expected SQL query.", sb.toString().equals(EXPECTED_QUERY));
|
||||
}
|
||||
}
|
||||
|
||||
class GroupByExtension extends GroupBy<SearchBaseExtension, String, String> {
|
||||
|
||||
public GroupByExtension(final SearchBaseExtension builder) {
|
||||
super(builder);
|
||||
_builder = builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init(final SearchBaseExtension builder) {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
class SearchBaseExtension extends SearchBase<SearchBaseExtension, String, String>{
|
||||
|
||||
SearchBaseExtension(final Class entityType, final Class resultType) {
|
||||
super(entityType, resultType);
|
||||
_specifiedAttrs = new ArrayList<Attribute>();
|
||||
_specifiedAttrs.add(new Attribute("TEST_TABLE2", "TEST_COLUMN2"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init(final Class<String> entityType, final Class<String> resultType) {
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user