#1150 - Refactor convert RawSql and RawSqlBuilder into interfaces and push the parsers into io.ebeaninternal

This commit is contained in:
rob bygrave
2017-10-04 23:03:12 +13:00
parent 5a5cb7ffbd
commit f02a552cdf
33 changed files with 994 additions and 906 deletions
@@ -0,0 +1,65 @@
package io.ebeaninternal.server.rawsql;
import java.sql.ResultSet;
/**
* Default implementation of SpiRawSql.
*/
public final class DRawSql implements SpiRawSql {
private final ResultSet resultSet;
private final Sql sql;
private final ColumnMapping columnMapping;
/**
* Construct with a ResultSet and properties that the columns map to.
*/
public DRawSql(ResultSet resultSet, String... propertyNames) {
this.resultSet = resultSet;
this.sql = null;
this.columnMapping = new ColumnMapping(propertyNames);
}
protected DRawSql(ResultSet resultSet, Sql sql, ColumnMapping columnMapping) {
this.resultSet = resultSet;
this.sql = sql;
this.columnMapping = columnMapping;
}
/**
* Return the Sql either unparsed or in parsed (broken up) form.
*/
@Override
public Sql getSql() {
return sql;
}
/**
* Return the key;
*/
@Override
public Key getKey() {
boolean parsed = sql != null && sql.isParsed();
String unParsedSql = (sql == null) ? "" : sql.getUnparsedSql();
return new Key(parsed, unParsedSql, columnMapping);
}
/**
* Return the resultSet if this is a ResultSet based RawSql.
*/
@Override
public ResultSet getResultSet() {
return resultSet;
}
/**
* Return the column mapping for the SQL columns to bean properties.
*/
@Override
public ColumnMapping getColumnMapping() {
return columnMapping;
}
}
@@ -0,0 +1,43 @@
package io.ebeaninternal.server.rawsql;
import io.ebean.RawSql;
import io.ebean.RawSqlBuilder;
import java.sql.ResultSet;
public class DRawSqlBuilder implements RawSqlBuilder {
private final ResultSet resultSet;
private final SpiRawSql.Sql sql;
private final SpiRawSql.ColumnMapping columnMapping;
DRawSqlBuilder(SpiRawSql.Sql sql, SpiRawSql.ColumnMapping columnMapping) {
this.sql = sql;
this.columnMapping = columnMapping;
this.resultSet = null;
}
@Override
public RawSqlBuilder columnMapping(String dbColumn, String propertyName) {
columnMapping.columnMapping(dbColumn, propertyName);
return this;
}
@Override
public RawSqlBuilder columnMappingIgnore(String dbColumn) {
return columnMapping(dbColumn, SpiRawSql.IGNORE_COLUMN);
}
@Override
public RawSqlBuilder tableAliasMapping(String tableAlias, String path) {
columnMapping.tableAliasMapping(tableAlias, path);
return this;
}
@Override
public RawSql create() {
return new DRawSql(resultSet, sql, columnMapping.createImmutableCopy());
}
}
@@ -0,0 +1,99 @@
package io.ebeaninternal.server.rawsql;
import io.ebeaninternal.server.rawsql.SpiRawSql.ColumnMapping;
import java.util.regex.Pattern;
import javax.persistence.PersistenceException;
import java.util.ArrayList;
/**
* Parses columnMapping (select clause) mapping columns to bean properties.
*/
final class DRawSqlColumnsParser {
private static final Pattern COLINFO_SPLIT = Pattern.compile("\\s(?=[^\\)]*(?:\\(|$))");
private final int end;
private final String sqlSelect;
private int pos;
private int indexPos;
public static ColumnMapping parse(String sqlSelect) {
return new DRawSqlColumnsParser(sqlSelect).parse();
}
private DRawSqlColumnsParser(String sqlSelect) {
this.sqlSelect = sqlSelect;
this.end = sqlSelect.length();
}
private ColumnMapping parse() {
ArrayList<ColumnMapping.Column> columns = new ArrayList<>();
while (pos <= end) {
ColumnMapping.Column c = nextColumnInfo();
columns.add(c);
}
return new ColumnMapping(columns);
}
private ColumnMapping.Column nextColumnInfo() {
int start = pos;
nextComma();
String colInfo = sqlSelect.substring(start, pos++);
colInfo = colInfo.trim();
String[] split = COLINFO_SPLIT.split(colInfo);
if (split.length > 1) {
ArrayList<String> tmp = new ArrayList<>(split.length);
for (String aSplit : split) {
if (!aSplit.trim().isEmpty()) {
tmp.add(aSplit.trim());
}
}
split = tmp.toArray(new String[tmp.size()]);
}
if (split.length == 0) {
throw new PersistenceException("Huh? Not expecting length=0 when parsing column " + colInfo);
}
if (split.length == 1) {
// default to column the same name as the property
return new ColumnMapping.Column(indexPos++, split[0], null);
}
if (split.length == 2) {
return new ColumnMapping.Column(indexPos++, split[0], split[1]);
}
// Ok, we now expect/require the AS keyword and it should be the
// second to last word in the colInfo content
if (!split[split.length - 2].equalsIgnoreCase("as")) {
throw new PersistenceException("Expecting AS keyword as second to last word when parsing column " + colInfo);
}
// build back the 'column formula' that precedes the AS keyword
StringBuilder sb = new StringBuilder();
sb.append(split[0]);
for (int i = 1; i < split.length - 2; i++) {
sb.append(" ").append(split[i]);
}
return new ColumnMapping.Column(indexPos++, sb.toString(), split[split.length - 1]);
}
private void nextComma() {
boolean inQuote = false;
int inbrackets = 0;
while (pos < end) {
char c = sqlSelect.charAt(pos);
if (c == '\'') inQuote = !inQuote;
else if (c == '(') inbrackets++;
else if (c == ')') inbrackets--;
else if (!inQuote && inbrackets == 0 && c == ',') {
return;
}
pos++;
}
}
}
@@ -0,0 +1,291 @@
package io.ebeaninternal.server.rawsql;
import io.ebeaninternal.server.rawsql.SpiRawSql.Sql;
import io.ebeaninternal.server.querydefn.SimpleTextParser;
/**
* Parses sql-select queries to try and determine the location where WHERE and
* HAVING clauses can be added dynamically to the sql.
*/
class DRawSqlParser {
private static final String $_AND_HAVING = "${andHaving}";
private static final String $_HAVING = "${having}";
private static final String $_AND_WHERE = "${andWhere}";
private static final String $_WHERE = "${where}";
private final SimpleTextParser textParser;
private String sql;
private int placeHolderWhere;
private int placeHolderAndWhere;
private int placeHolderHaving;
private int placeHolderAndHaving;
private final boolean hasPlaceHolders;
private int selectPos = -1;
private int distinctPos = -1;
private int fromPos = -1;
private int wherePos = -1;
private int groupByPos = -1;
private int havingPos = -1;
private int orderByPos = -1;
private int orderByStmtPos = -1;
private boolean whereExprAnd;
private int whereExprPos = -1;
private boolean havingExprAnd;
private int havingExprPos = -1;
public static Sql parse(String sql) {
return new DRawSqlParser(sql).parse();
}
private DRawSqlParser(String sqlString) {
sqlString = sqlString.trim();
sqlString = sqlString.replace('\n', ' ');
this.sql = sqlString;
this.hasPlaceHolders = findAndRemovePlaceHolders();
this.textParser = new SimpleTextParser(sqlString);
}
private Sql parse() {
if (!hasPlaceHolders()) {
// parse the sql for the keywords...
// select, from, where, having, group by, order by
parseSqlFindKeywords(true);
}
whereExprPos = findWhereExprPosition();
havingExprPos = findHavingExprPosition();
String preFrom = removeWhitespace(findPreFromSql());
String preWhere = removeWhitespace(findPreWhereSql());
String preHaving = removeWhitespace(findPreHavingSql());
String orderByPrefix = findOrderByPrefixSql();
String orderBySql = findOrderBySql();
preFrom = trimSelectKeyword(preFrom);
return new Sql(sql, preFrom, preWhere, whereExprAnd, preHaving, havingExprAnd, orderByPrefix, orderBySql, (distinctPos > -1));
}
/**
* Find and remove the known place holders such as ${where}.
*/
private boolean findAndRemovePlaceHolders() {
placeHolderWhere = removePlaceHolder($_WHERE);
placeHolderAndWhere = removePlaceHolder($_AND_WHERE);
placeHolderHaving = removePlaceHolder($_HAVING);
placeHolderAndHaving = removePlaceHolder($_AND_HAVING);
return hasPlaceHolders();
}
private int removePlaceHolder(String placeHolder) {
int pos = sql.indexOf(placeHolder);
if (pos > -1) {
int after = pos + placeHolder.length() + 1;
if (after > sql.length()) {
sql = sql.substring(0, pos);
} else {
sql = sql.substring(0, pos) + sql.substring(after);
}
}
return pos;
}
private boolean hasPlaceHolders() {
return placeHolderWhere > -1 || placeHolderAndWhere > -1 || placeHolderHaving > -1 || placeHolderAndHaving > -1;
}
/**
* Trim off the select keyword (to support row_number() limit function).
*/
private String trimSelectKeyword(String preWhereExprSql) {
if (selectPos < 0) {
throw new IllegalStateException("select keyword not found?");
}
// trim of select keyword
preWhereExprSql = preWhereExprSql.trim();
String select = preWhereExprSql.substring(0, 7);
if (!select.equalsIgnoreCase("select ")) {
throw new RuntimeException("Expecting [" + preWhereExprSql + "] to start with \"select\"");
}
preWhereExprSql = preWhereExprSql.substring(7).trim();
if (distinctPos > -1) {
// trim of distinct keyword
String distinct = preWhereExprSql.substring(0, 9);
if (!distinct.equalsIgnoreCase("distinct ")) {
throw new RuntimeException("Expecting [" + preWhereExprSql + "] to start with \"select distinct\"");
}
preWhereExprSql = preWhereExprSql.substring(9);
}
return preWhereExprSql;
}
private String findOrderByPrefixSql() {
return (orderByPos < 1) ? null : sql.substring(orderByPos, orderByStmtPos);
}
private String findOrderBySql() {
return (orderByStmtPos < 1) ? null : sql.substring(orderByStmtPos).trim();
}
private String findPreHavingSql() {
if (havingExprPos > whereExprPos) {
// an order by clause follows...
return sql.substring(whereExprPos, havingExprPos - 1);
}
if (whereExprPos > -1) {
if (orderByPos == -1) {
return sql.substring(whereExprPos);
} else if (whereExprPos == orderByPos) {
return "";
} else {
return sql.substring(whereExprPos, orderByPos - 1);
}
}
return null;
}
private String findPreFromSql() {
return sql.substring(0, fromPos - 1);
}
private String findPreWhereSql() {
if (whereExprPos > -1) {
return sql.substring(fromPos, whereExprPos - 1);
} else {
return sql.substring(fromPos);
}
}
private void parseSqlFindKeywords(boolean allKeywords) {
selectPos = textParser.findWordLower("select");
if (selectPos == -1) {
String msg = "Error parsing sql, can not find SELECT keyword in:";
throw new RuntimeException(msg + sql);
}
String possibleDistinct = textParser.nextWord();
if ("distinct".equals(possibleDistinct)) {
distinctPos = textParser.getPos() - 8;
}
fromPos = textParser.findWordLower("from");
if (fromPos == -1) {
String msg = "Error parsing sql, can not find FROM keyword in:";
throw new RuntimeException(msg + sql);
}
if (!allKeywords) {
return;
}
wherePos = textParser.findWordLower("where");
if (wherePos == -1) {
groupByPos = textParser.findWordLower("group", fromPos + 5);
} else {
groupByPos = textParser.findWordLower("group");
}
if (groupByPos > -1) {
havingPos = textParser.findWordLower("having");
}
int startOrderBy = havingPos;
if (startOrderBy == -1) {
startOrderBy = groupByPos;
}
if (startOrderBy == -1) {
startOrderBy = wherePos;
}
if (startOrderBy == -1) {
startOrderBy = fromPos;
}
orderByPos = textParser.findWordLower("order", startOrderBy);
if (orderByPos > 1) {
// there might be keywords like siblings in between the order
// and by so search for the by keyword explicitly
orderByStmtPos = 2 + textParser.findWordLower("by", orderByPos);
}
}
private int findWhereExprPosition() {
if (hasPlaceHolders) {
if (placeHolderWhere > -1) {
return placeHolderWhere;
} else {
whereExprAnd = true;
return placeHolderAndWhere;
}
}
whereExprAnd = wherePos > 0;
if (groupByPos > 0) {
return groupByPos;
}
if (havingPos > 0) {
return havingPos;
}
if (orderByPos > 0) {
return orderByPos;
}
return -1;
}
private int findHavingExprPosition() {
if (hasPlaceHolders) {
if (placeHolderHaving > -1) {
return placeHolderHaving;
} else {
havingExprAnd = true;
return placeHolderAndHaving;
}
}
havingExprAnd = havingPos > 0;
if (orderByPos > 0) {
return orderByPos;
}
return -1;
}
private String removeWhitespace(String sql) {
if (sql == null) {
return "";
}
boolean removeWhitespace = false;
int length = sql.length();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
char c = sql.charAt(i);
if (removeWhitespace) {
if (!Character.isWhitespace(c)) {
sb.append(c);
removeWhitespace = false;
}
} else {
if (c == '\r' || c == '\n') {
sb.append('\n');
removeWhitespace = true;
} else {
sb.append(c);
}
}
}
String s = sb.toString();
return s.trim();
}
}
@@ -0,0 +1,31 @@
package io.ebeaninternal.server.rawsql;
import io.ebean.RawSql;
import io.ebean.RawSqlBuilder;
import io.ebean.plugin.SpiRawSqlService;
import java.sql.ResultSet;
public class DRawSqlService implements SpiRawSqlService {
@Override
public RawSql resultSet(ResultSet resultSet, String... propertyNames) {
return new DRawSql(resultSet, propertyNames);
}
@Override
public RawSqlBuilder parsed(String sql) {
SpiRawSql.Sql sql2 = DRawSqlParser.parse(sql);
String select = sql2.getPreFrom();
SpiRawSql.ColumnMapping mapping = DRawSqlColumnsParser.parse(select);
return new DRawSqlBuilder(sql2, mapping);
}
@Override
public RawSqlBuilder unparsed(String sql) {
SpiRawSql.Sql s = new SpiRawSql.Sql(sql);
return new DRawSqlBuilder(s, new SpiRawSql.ColumnMapping());
}
}
@@ -0,0 +1,536 @@
package io.ebeaninternal.server.rawsql;
import io.ebean.RawSql;
import io.ebean.util.CamelCaseHelper;
import java.io.Serializable;
import java.sql.ResultSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Internal service API for Raw Sql.
*/
public interface SpiRawSql extends RawSql {
/**
* Special property name assigned to a DB column that should be ignored.
*/
String IGNORE_COLUMN = "$$_IGNORE_COLUMN_$$";
SpiRawSql.Sql getSql();
SpiRawSql.Key getKey();
ResultSet getResultSet();
SpiRawSql.ColumnMapping getColumnMapping();
/**
* Represents the sql part of the query. For parsed RawSql the sql is broken
* up so that Ebean can insert extra WHERE and HAVING expressions into the
* SQL.
*/
final class Sql implements Serializable {
private static final long serialVersionUID = 1L;
private final boolean parsed;
private final String unparsedSql;
private final String preFrom;
private final String preWhere;
private final boolean andWhereExpr;
private final String preHaving;
private final boolean andHavingExpr;
private final String orderByPrefix;
private final String orderBy;
private final boolean distinct;
/**
* Construct for unparsed SQL.
*/
protected Sql(String unparsedSql) {
this.parsed = false;
this.unparsedSql = unparsedSql;
this.preFrom = null;
this.preHaving = null;
this.preWhere = null;
this.andHavingExpr = false;
this.andWhereExpr = false;
this.orderByPrefix = null;
this.orderBy = null;
this.distinct = false;
}
/**
* Construct for parsed SQL.
*/
protected Sql(String unparsedSql, String preFrom, String preWhere, boolean andWhereExpr,
String preHaving, boolean andHavingExpr, String orderByPrefix, String orderBy, boolean distinct) {
this.unparsedSql = unparsedSql;
this.parsed = true;
this.preFrom = preFrom;
this.preHaving = preHaving;
this.preWhere = preWhere;
this.andHavingExpr = andHavingExpr;
this.andWhereExpr = andWhereExpr;
this.orderByPrefix = orderByPrefix;
this.orderBy = orderBy;
this.distinct = distinct;
}
@Override
public String toString() {
if (!parsed) {
return "unparsed[" + unparsedSql + "]";
}
return "select[" + preFrom + "] preWhere[" + preWhere + "] preHaving[" + preHaving + "] orderBy[" + orderBy + "]";
}
public boolean isDistinct() {
return distinct;
}
/**
* Return true if the SQL is left completely unmodified.
* <p>
* This means Ebean can't add WHERE or HAVING expressions into the query -
* it will be left completely unmodified.
* </p>
*/
public boolean isParsed() {
return parsed;
}
/**
* Return the SQL when it is unparsed.
*/
public String getUnparsedSql() {
return unparsedSql;
}
/**
* Return the SQL prior to FROM clause.
*/
public String getPreFrom() {
return preFrom;
}
/**
* Return the SQL prior to WHERE clause.
*/
public String getPreWhere() {
return preWhere;
}
/**
* Return true if there is already a WHERE clause and any extra where
* expressions start with AND.
*/
public boolean isAndWhereExpr() {
return andWhereExpr;
}
/**
* Return the SQL prior to HAVING clause.
*/
public String getPreHaving() {
return preHaving;
}
/**
* Return true if there is already a HAVING clause and any extra having
* expressions start with AND.
*/
public boolean isAndHavingExpr() {
return andHavingExpr;
}
/**
* Return the 'order by' keywords.
* This can contain additional keywords, for example 'order siblings by' as Oracle syntax.
*/
public String getOrderByPrefix() {
return (orderByPrefix == null) ? "order by" : orderByPrefix;
}
/**
* Return the SQL ORDER BY clause.
*/
public String getOrderBy() {
return orderBy;
}
}
/**
* Defines the column mapping for raw sql DB columns to bean properties.
*/
final class ColumnMapping implements Serializable {
private static final long serialVersionUID = 1L;
private final LinkedHashMap<String, Column> dbColumnMap;
private final Map<String, String> propertyMap;
private final Map<String, Column> propertyColumnMap;
private final boolean parsed;
private final boolean immutable;
/**
* Construct from parsed sql where the columns have been identified.
*/
protected ColumnMapping(List<Column> columns) {
this.immutable = false;
this.parsed = true;
this.propertyMap = null;
this.propertyColumnMap = null;
this.dbColumnMap = new LinkedHashMap<>();
for (Column c : columns) {
dbColumnMap.put(c.getDbColumnKey(), c);
}
}
/**
* Construct for unparsed sql.
*/
protected ColumnMapping() {
this.immutable = false;
this.parsed = false;
this.propertyMap = null;
this.propertyColumnMap = null;
this.dbColumnMap = new LinkedHashMap<>();
}
/**
* Construct for ResultSet use.
*/
protected ColumnMapping(String... propertyNames) {
this.immutable = false;
this.parsed = false;
this.propertyMap = null;
this.dbColumnMap = new LinkedHashMap<>();
int pos = 0;
for (String prop : propertyNames) {
dbColumnMap.put(prop, new Column(pos++, prop, null, prop));
}
propertyColumnMap = dbColumnMap;
}
/**
* Construct an immutable ColumnMapping based on collected information.
*/
protected ColumnMapping(boolean parsed, LinkedHashMap<String, Column> dbColumnMap) {
this.immutable = true;
this.parsed = parsed;
this.dbColumnMap = dbColumnMap;
HashMap<String, Column> pcMap = new HashMap<>();
HashMap<String, String> pMap = new HashMap<>();
for (Column c : dbColumnMap.values()) {
pMap.put(c.getPropertyName(), c.getDbColumn());
pcMap.put(c.getPropertyName(), c);
}
this.propertyMap = Collections.unmodifiableMap(pMap);
this.propertyColumnMap = Collections.unmodifiableMap(pcMap);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ColumnMapping that = (ColumnMapping) o;
return dbColumnMap.equals(that.dbColumnMap);
}
@Override
public int hashCode() {
return dbColumnMap.hashCode();
}
/**
* Return true if the property is mapped.
*/
public boolean contains(String property) {
return this.propertyColumnMap.containsKey(property);
}
/**
* Creates an immutable copy of this ColumnMapping.
*
* @throws IllegalStateException when a propertyName has not been defined for a column.
*/
protected ColumnMapping createImmutableCopy() {
for (Column c : dbColumnMap.values()) {
c.checkMapping();
}
return new ColumnMapping(parsed, dbColumnMap);
}
protected void columnMapping(String dbColumn, String propertyName) {
if (immutable) {
throw new IllegalStateException("Should never happen");
}
if (!parsed) {
int pos = dbColumnMap.size();
dbColumnMap.put(dbColumn, new Column(pos, dbColumn, null, propertyName));
} else {
Column column = dbColumnMap.get(dbColumn);
if (column == null) {
String msg = "DB Column [" + dbColumn + "] not found in mapping. Expecting one of [" + dbColumnMap.keySet() + "]";
throw new IllegalArgumentException(msg);
}
column.setPropertyName(propertyName);
}
}
/**
* Returns true if the Columns where supplied by parsing the sql select
* clause.
* <p>
* In the case where the columns where parsed then we can do extra checks on
* the column mapping such as, is the column a valid one in the sql and
* whether all the columns in the sql have been mapped.
* </p>
*/
public boolean isParsed() {
return parsed;
}
/**
* Return the number of columns in this column mapping.
*/
public int size() {
return dbColumnMap.size();
}
/**
* Return the column mapping.
*/
protected Map<String, Column> mapping() {
return dbColumnMap;
}
/**
* Return the mapping by DB column.
*/
public Map<String, String> getMapping() {
return propertyMap;
}
/**
* Return the index position by bean property name.
*/
public int getIndexPosition(String property) {
Column c = propertyColumnMap.get(property);
return c == null ? -1 : c.getIndexPos();
}
/**
* Return an iterator of the Columns.
*/
public Iterator<Column> getColumns() {
return dbColumnMap.values().iterator();
}
/**
* Modify any column mappings with the given table alias to have the path prefix.
* <p>
* For example modify all mappings with table alias "c" to have the path prefix "customer".
* </p>
* <p>
* For the "Root type" you don't need to specify a tableAliasMapping.
* </p>
*/
public void tableAliasMapping(String tableAlias, String path) {
String startMatch = tableAlias + ".";
for (Map.Entry<String, Column> entry : dbColumnMap.entrySet()) {
if (entry.getKey().startsWith(startMatch)) {
entry.getValue().tableAliasMapping(path);
}
}
}
/**
* A Column of the RawSql that is mapped to a bean property (or ignored).
*/
public static class Column implements Serializable {
private static final long serialVersionUID = 1L;
private final int indexPos;
private final String dbColumn;
private final String dbAlias;
private String propertyName;
/**
* Construct a Column.
*/
public Column(int indexPos, String dbColumn, String dbAlias) {
this(indexPos, dbColumn, dbAlias, derivePropertyName(dbAlias, dbColumn));
}
private Column(int indexPos, String dbColumn, String dbAlias, String propertyName) {
this.indexPos = indexPos;
this.dbColumn = dbColumn;
this.dbAlias = dbAlias;
if (propertyName == null && dbAlias != null) {
this.propertyName = dbAlias;
} else {
this.propertyName = propertyName;
}
}
protected static String derivePropertyName(String dbAlias, String dbColumn) {
if (dbAlias != null) {
return CamelCaseHelper.toCamelFromUnderscore(dbAlias);
}
int dotPos = dbColumn.indexOf('.');
if (dotPos > -1) {
dbColumn = dbColumn.substring(dotPos + 1);
}
return CamelCaseHelper.toCamelFromUnderscore(dbColumn);
}
private void checkMapping() {
if (propertyName == null) {
String msg = "No propertyName defined (Column mapping) for dbColumn [" + dbColumn + "]";
throw new IllegalStateException(msg);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Column that = (Column) o;
if (indexPos != that.indexPos) return false;
if (!dbColumn.equals(that.dbColumn)) return false;
if (dbAlias != null ? !dbAlias.equals(that.dbAlias) : that.dbAlias != null) return false;
return propertyName != null ? propertyName.equals(that.propertyName) : that.propertyName == null;
}
@Override
public int hashCode() {
int result = indexPos;
result = 92821 * result + dbColumn.hashCode();
result = 92821 * result + (dbAlias != null ? dbAlias.hashCode() : 0);
result = 92821 * result + (propertyName != null ? propertyName.hashCode() : 0);
return result;
}
@Override
public String toString() {
return dbColumn + "->" + propertyName;
}
/**
* Return the index position of this column.
*/
public int getIndexPos() {
return indexPos;
}
/**
* Return the DB column alias if specified otherwise DB column.
* This is used as the key for mapping a column to a logical property.
*/
public String getDbColumnKey() {
return (dbAlias != null) ? dbAlias : dbColumn;
}
/**
* Return the DB column name including table alias (if it has one).
*/
public String getDbColumn() {
return dbColumn;
}
/**
* Return the bean property this column is mapped to.
*/
public String getPropertyName() {
return propertyName;
}
/**
* Set the property name mapped to this db column.
*/
private void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
/**
* Prepend the path to the property name.
* <p/>
* For example if path is "customer" then "name" becomes "customer.name".
*/
public void tableAliasMapping(String path) {
if (path != null) {
propertyName = path + "." + propertyName;
}
}
}
}
/**
* A key for the RawSql object using for the query plan.
*/
final class Key {
private final boolean parsed;
private final ColumnMapping columnMapping;
private final String unParsedSql;
Key(boolean parsed, String unParsedSql, ColumnMapping columnMapping) {
this.parsed = parsed;
this.unParsedSql = unParsedSql;
this.columnMapping = columnMapping;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Key that = (Key) o;
return parsed == that.parsed
&& columnMapping.equals(that.columnMapping)
&& unParsedSql.equals(that.unParsedSql);
}
@Override
public int hashCode() {
int result = (parsed ? 1 : 0);
result = 92821 * result + columnMapping.hashCode();
result = 92821 * result + unParsedSql.hashCode();
return result;
}
}
}