Skip to content

Commit e241a39

Browse files
committed
Merge remote-tracking branch 'origin/maintenance' into maintenance
2 parents a74ab07 + f6a1498 commit e241a39

File tree

10 files changed

+534
-43
lines changed

10 files changed

+534
-43
lines changed

src/main/java/org/javawebstack/orm/query/Query.java

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ public class Query<T extends Model> {
2424
private final QueryGroup<T> where;
2525
private Integer offset;
2626
private Integer limit;
27-
private QueryColumn order;
28-
private boolean desc = false;
27+
private QueryOrderBy order;
2928
private boolean withDeleted = false;
3029
private final List<QueryWith> withs = new ArrayList<>();
3130

@@ -37,6 +36,7 @@ public Query(Repo<T> repo, Class<T> model) {
3736
this.repo = repo;
3837
this.model = model;
3938
this.where = new QueryGroup<>();
39+
this.order = new QueryOrderBy();
4040
}
4141

4242
public boolean isWithDeleted() {
@@ -59,14 +59,10 @@ public Integer getOffset() {
5959
return offset;
6060
}
6161

62-
public QueryColumn getOrder() {
62+
public QueryOrderBy getOrder() {
6363
return order;
6464
}
6565

66-
public boolean isDescOrder() {
67-
return desc;
68-
}
69-
7066
public Repo<T> getRepo() {
7167
return repo;
7268
}
@@ -300,20 +296,50 @@ public Query<T> search(String search) {
300296
return this;
301297
}
302298

303-
public Query<T> order(String orderBy, boolean desc) {
304-
return order(new QueryColumn(orderBy), desc);
305-
}
299+
/**
300+
* Sorts the results by the given column name ascendingly.
301+
*
302+
* @param columnName The name of the column to sort ascendingly by.
303+
* @return The Query object with the given order by information added.
304+
* @throws ORMQueryException if the order operation is called twice on a column specification with the same name.
305+
*/
306+
public Query<T> order(String columnName) throws ORMQueryException {
307+
return order(columnName, false);
308+
}
309+
310+
/**
311+
* Sorts the results by the given column name with the given order direction.
312+
*
313+
* @param columnName The name of the column to sort ascendingly by.
314+
* @param desc If true it will order descendingly, if false it will order ascendingly.
315+
* @return The Query object with the given order by information added.
316+
* @throws ORMQueryException if the order operation is called twice on a column specification with the same name.
317+
*/
318+
public Query<T> order(String columnName, boolean desc) throws ORMQueryException {
319+
return order(new QueryColumn(columnName), desc);
320+
}
321+
322+
/**
323+
* Sorts the results by the given column with the given order direction.
324+
*
325+
* @param column The column encoded as QueryColumn object.
326+
* @param desc If true it will order descendingly, if false it will order ascendingly.
327+
* @return The Query object with the given order by information added.
328+
* @throws ORMQueryException if the order operation is called twice on a column specification with the same name.
329+
*/
330+
public Query<T> order(QueryColumn column, boolean desc) throws ORMQueryException{
331+
boolean success = this.order.add(column, desc);
332+
if(!success) {
333+
throw new ORMQueryException(String.format(
334+
"The column %s could not be ordered %s. This is probably caused by calling .order() on this column twice.",
335+
column.toString(),
336+
desc ? "descendingly" : "ascendingly"
337+
));
338+
}
306339

307-
public Query<T> order(QueryColumn orderBy, boolean desc) {
308-
this.order = orderBy;
309-
this.desc = desc;
310340
return this;
311341
}
312342

313-
public Query<T> order(String orderBy) {
314-
return order(orderBy, false);
315-
}
316-
317343
public Query<T> limit(int offset, int limit) {
318344
return offset(offset).limit(limit);
319345
}

src/main/java/org/javawebstack/orm/query/QueryColumn.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.javawebstack.orm.exception.ORMQueryException;
55

66
import java.util.Arrays;
7+
import java.util.Objects;
78
import java.util.regex.Pattern;
89
import java.util.stream.Collectors;
910

@@ -48,4 +49,16 @@ private static void validateName(String name) {
4849
throw new ORMQueryException("Invalid column name '" + name + "' (Use raw in case you know what you're doing)");
4950
}
5051

52+
@Override
53+
public boolean equals(Object o) {
54+
if (this == o) return true;
55+
if (o == null || getClass() != o.getClass()) return false;
56+
QueryColumn that = (QueryColumn) o;
57+
return toString().equals(that.toString());
58+
}
59+
60+
@Override
61+
public int hashCode() {
62+
return Objects.hash(toString());
63+
}
5164
}

src/main/java/org/javawebstack/orm/query/QueryGroup.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@
1010
import java.util.List;
1111
import java.util.function.Function;
1212

13+
/**
14+
* Queries grouped via the QueryGroup class will be put inside parenthesis.
15+
* This makes expressions as the following possible (MySQL example):
16+
* ... `column_a` = 'A' OR (`column_b` = 'B' AND `column_c´ = 'C') ...
17+
*
18+
* In the above example `column_b` = 'B' AND `column_c´ = 'C' would be in a QueryGroup.
19+
*
20+
* @param <T> The model under which the QueryGroups functions. Currently purely semantic without functionality.
21+
*/
1322
public class QueryGroup<T extends Model> implements QueryElement {
1423

1524
private final List<QueryElement> queryElements = new ArrayList<>();
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.javawebstack.orm.query;
2+
3+
import org.javawebstack.orm.TableInfo;
4+
5+
import java.util.LinkedList;
6+
import java.util.stream.Collectors;
7+
8+
/**
9+
* The QueryOrderBy class serves as an aggregation of order by elements. It extends a list, because the order of the
10+
* order by statements is of relevance.
11+
*/
12+
public class QueryOrderBy extends LinkedList<QueryOrderByElement>{
13+
14+
/**
15+
* Add a new order by statement. If a statement with the same column name already exists it will not add the
16+
* statement.
17+
*
18+
* @param columnName The column name to order by.
19+
* @param desc If the column should be order descendingly.
20+
* @return True if adding the statement was successful. False otherwise.
21+
*/
22+
public boolean add(String columnName, boolean desc) {
23+
return this.add(new QueryColumn(columnName), desc);
24+
}
25+
26+
/**
27+
* Add a new order by statement. If a statement with the same column name already exists it will not add the
28+
* statement.
29+
*
30+
* @param column The column to be ordered by. It will retrieve the name from the QueryColumn.
31+
* @param desc If the column should be order descendingly.
32+
* @return True if adding the statement was successful. False otherwise.
33+
*/
34+
public boolean add(QueryColumn column, boolean desc) {
35+
return this.add(new QueryOrderByElement(column, desc));
36+
}
37+
38+
@Override
39+
/**
40+
* Add a new order by statement. If a statement with the same column name already exists it will not add the
41+
* statement.
42+
*
43+
* @param element The direct QueryOrderByElement which encodes the order by statement.
44+
* @return True if adding the statement was successful. False otherwise.
45+
*/
46+
public boolean add(QueryOrderByElement element) {
47+
boolean hasBeenAdded = false;
48+
if(!willOverwrite(element))
49+
hasBeenAdded = super.add(element);
50+
51+
return hasBeenAdded;
52+
}
53+
54+
private boolean willOverwrite(QueryOrderByElement element) {
55+
return this.stream().anyMatch(element::hasEqualColumn);
56+
}
57+
58+
59+
// The toString methods are specific to MySQL so they might have to be replaced later on.
60+
@Override
61+
public String toString() {
62+
return toString(null);
63+
}
64+
65+
public String toString(TableInfo info) {
66+
return this.stream()
67+
.map(QueryOrderByElement::toString)
68+
.collect(Collectors.joining(","));
69+
}
70+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package org.javawebstack.orm.query;
2+
3+
import org.javawebstack.orm.TableInfo;
4+
5+
import java.util.Objects;
6+
7+
/**
8+
* The QueryOrderByElement class encodes an Order By Statement.
9+
*/
10+
public class QueryOrderByElement {
11+
private final QueryColumn queryColumn;
12+
private final boolean desc;
13+
14+
QueryOrderByElement(String columnName, boolean desc) {
15+
queryColumn = new QueryColumn(columnName);
16+
this.desc = desc;
17+
}
18+
19+
QueryOrderByElement(QueryColumn column, boolean desc) {
20+
this.queryColumn = column;
21+
this.desc = desc;
22+
}
23+
24+
/**
25+
* Retrieves the QueryColumn of the statement which encodes the column name.
26+
*
27+
* @return The encoding QueryColumn object.
28+
*/
29+
public QueryColumn getQueryColumn() {
30+
return queryColumn;
31+
}
32+
33+
/**
34+
* Retrieves the information if this column is ordered ascendingly or descendingly.
35+
*
36+
* @return false if ascending, true if descending.
37+
*/
38+
public boolean isDesc() {
39+
return desc;
40+
}
41+
42+
/**
43+
* Compares the encoded column name.
44+
*
45+
* @param o An object to compare to.
46+
* @return True if the object is a QueryOrderByElement with a QueryColumn with generates the same identifier.
47+
*/
48+
public boolean hasEqualColumn(Object o) {
49+
if (this == o) return true;
50+
if (o == null || getClass() != o.getClass()) return false;
51+
QueryOrderByElement that = (QueryOrderByElement) o;
52+
return getQueryColumn().equals(that.getQueryColumn());
53+
}
54+
55+
@Override
56+
public boolean equals(Object o) {
57+
if (this == o) return true;
58+
if (o == null || getClass() != o.getClass()) return false;
59+
QueryOrderByElement that = (QueryOrderByElement) o;
60+
return isDesc() == that.isDesc() && getQueryColumn().equals(that.getQueryColumn());
61+
}
62+
63+
@Override
64+
public int hashCode() {
65+
return Objects.hash(getQueryColumn(), isDesc());
66+
}
67+
68+
@Override
69+
public String toString() {
70+
return this.toString(null);
71+
}
72+
73+
public String toString(TableInfo info) {
74+
String stringifiedOrderBy = getQueryColumn().toString(info);
75+
if (isDesc())
76+
stringifiedOrderBy += " DESC";
77+
78+
return stringifiedOrderBy;
79+
}
80+
}

src/main/java/org/javawebstack/orm/wrapper/builder/MySQLQueryStringBuilder.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,13 @@ public SQLQueryString buildQuery(Query<?> query, boolean count) {
5050
sb.append(" WHERE ").append(qs.getQuery());
5151
parameters.addAll(qs.getParameters());
5252
}
53-
if (query.getOrder() != null) {
54-
sb.append(" ORDER BY ").append(query.getOrder().toString(repo.getInfo()));
55-
if (query.isDescOrder())
56-
sb.append(" DESC");
53+
54+
QueryOrderBy orderBy = query.getOrder();
55+
if (!orderBy.isEmpty()) {
56+
sb.append(" ORDER BY ")
57+
.append(orderBy.toString());
5758
}
59+
5860
Integer offset = query.getOffset();
5961
Integer limit = query.getLimit();
6062
if (offset != null && limit == null)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.javawebstack.orm.test.exception;
2+
3+
import lombok.Getter;
4+
import lombok.Setter;
5+
6+
@Getter
7+
@Setter
8+
/**
9+
* Only to be used for tests.
10+
* This exception should be thrown when a SQL Query String is manually parsed and sections and section types are defined, and
11+
* a type of section is attempted to be retrieved which does not exist in this number.
12+
*/
13+
public class SectionIndexOutOfBoundException extends Exception {
14+
private int sectionCount;
15+
private int attemptedIndex;
16+
private String topLevelKeyword;
17+
}

0 commit comments

Comments
 (0)