diff --git a/src/main/java/io/ebean/Query.java b/src/main/java/io/ebean/Query.java
index 9d383ed8d..60a0c341f 100644
--- a/src/main/java/io/ebean/Query.java
+++ b/src/main/java/io/ebean/Query.java
@@ -215,11 +215,6 @@ public interface Query {
SKIPLOCKED
}
- /**
- * Return the RawSql that was set to use for this query.
- */
- RawSql getRawSql();
-
/**
* Set RawSql to use for this query.
*/
@@ -1328,14 +1323,14 @@ public interface Query {
* Set the {@link CacheMode} to use the query for executing this query.
*/
Query setUseQueryCache(CacheMode useQueryCache);
-
+
/**
* Calls {@link #setUseQueryCache(CacheMode)} with ON or OFF.
*/
default Query setUseQueryCache(boolean enabled) {
return setUseQueryCache(enabled ? CacheMode.ON : CacheMode.OFF);
}
-
+
/**
* Set to true if this query should execute against the doc store.
*
diff --git a/src/main/java/io/ebean/RawSql.java b/src/main/java/io/ebean/RawSql.java
index 937161e2a..0c52bb83e 100644
--- a/src/main/java/io/ebean/RawSql.java
+++ b/src/main/java/io/ebean/RawSql.java
@@ -169,566 +169,6 @@ import java.util.Map;
* Note that lazy loading also works with object graphs built with RawSql.
*
*/
-public final class RawSql implements Serializable {
+public interface RawSql {
- private static final long serialVersionUID = 1L;
-
- private final ResultSet resultSet;
-
- private final Sql sql;
-
- private final ColumnMapping columnMapping;
-
- /**
- * Construct with a ResultSet and properties that the columns map to.
- *
- * The properties listed in the propertyNames must be in the same order as the columns in the
- * resultSet.
- *
- * When a query executes this RawSql object then it will close the resultSet.
- */
- public RawSql(ResultSet resultSet, String... propertyNames) {
- this.resultSet = resultSet;
- this.sql = null;
- this.columnMapping = new ColumnMapping(propertyNames);
- }
-
- protected RawSql(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.
- */
- public Sql getSql() {
- return sql;
- }
-
- /**
- * Return the key;
- */
- public Key getKey() {
- boolean parsed = sql != null && sql.parsed;
- String unParsedSql = (sql == null) ? "" : sql.unparsedSql;
- return new Key(parsed, unParsedSql, columnMapping);
- }
-
- /**
- * Return the resultSet if this is a ResultSet based RawSql.
- */
- public ResultSet getResultSet() {
- return resultSet;
- }
-
- /**
- * Return the column mapping for the SQL columns to bean properties.
- */
- public ColumnMapping getColumnMapping() {
- return columnMapping;
- }
-
- /**
- * 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.
- */
- public static 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.
- *
- * This means Ebean can't add WHERE or HAVING expressions into the query -
- * it will be left completely unmodified.
- *
- */
- 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.
- */
- public static final class ColumnMapping implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- private final LinkedHashMap dbColumnMap;
-
- private final Map propertyMap;
-
- private final Map propertyColumnMap;
-
- private final boolean parsed;
-
- private final boolean immutable;
-
- /**
- * Construct from parsed sql where the columns have been identified.
- */
- protected ColumnMapping(List 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 dbColumnMap) {
- this.immutable = true;
- this.parsed = parsed;
- this.dbColumnMap = dbColumnMap;
-
- HashMap pcMap = new HashMap<>();
- HashMap 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.
- *
- * 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.
- *
- */
- 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 mapping() {
- return dbColumnMap;
- }
-
- /**
- * Return the mapping by DB column.
- */
- public Map 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 getColumns() {
- return dbColumnMap.values().iterator();
- }
-
- /**
- * Modify any column mappings with the given table alias to have the path prefix.
- *
- * For example modify all mappings with table alias "c" to have the path prefix "customer".
- *
- *
- * For the "Root type" you don't need to specify a tableAliasMapping.
- *
- */
- public void tableAliasMapping(String tableAlias, String path) {
-
- String startMatch = tableAlias + ".";
- for (Map.Entry 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.
- *
- * 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.
- */
- public static 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;
- }
- }
}
diff --git a/src/main/java/io/ebean/RawSqlBuilder.java b/src/main/java/io/ebean/RawSqlBuilder.java
index cc05bdfdb..4cebe5832 100644
--- a/src/main/java/io/ebean/RawSqlBuilder.java
+++ b/src/main/java/io/ebean/RawSqlBuilder.java
@@ -1,8 +1,5 @@
package io.ebean;
-import io.ebean.RawSql.ColumnMapping;
-import io.ebean.RawSql.Sql;
-
import java.sql.ResultSet;
/**
@@ -14,18 +11,7 @@ import java.sql.ResultSet;
*
* @see RawSql
*/
-public class RawSqlBuilder {
-
- /**
- * Special property name assigned to a DB column that should be ignored.
- */
- public static final String IGNORE_COLUMN = "$$_IGNORE_COLUMN_$$";
-
- private final ResultSet resultSet;
-
- private final Sql sql;
-
- private final ColumnMapping columnMapping;
+public interface RawSqlBuilder {
/**
* Create and return a RawSql object based on the resultSet and list of properties the columns in
@@ -34,8 +20,8 @@ public class RawSqlBuilder {
* The properties listed in the propertyNames must be in the same order as the columns in the
* resultSet.
*/
- public static RawSql resultSet(ResultSet resultSet, String... propertyNames) {
- return new RawSql(resultSet, propertyNames);
+ static RawSql resultSet(ResultSet resultSet, String... propertyNames) {
+ return XServiceProvider.get().resultSet(resultSet, propertyNames);
}
/**
@@ -43,10 +29,8 @@ public class RawSqlBuilder {
* modified - so no additional WHERE or HAVING expressions can be added to
* this query.
*/
- public static RawSqlBuilder unparsed(String sql) {
-
- Sql s = new Sql(sql);
- return new RawSqlBuilder(s, new ColumnMapping());
+ static RawSqlBuilder unparsed(String sql) {
+ return XServiceProvider.get().unparsed(sql);
}
/**
@@ -62,19 +46,8 @@ public class RawSqlBuilder {
* correct column names are entered into the mapping.
*
*/
- public static RawSqlBuilder parse(String sql) {
-
- Sql sql2 = DRawSqlParser.parse(sql);
- String select = sql2.getPreFrom();
-
- ColumnMapping mapping = DRawSqlColumnsParser.parse(select);
- return new RawSqlBuilder(sql2, mapping);
- }
-
- private RawSqlBuilder(Sql sql, ColumnMapping columnMapping) {
- this.sql = sql;
- this.columnMapping = columnMapping;
- this.resultSet = null;
+ static RawSqlBuilder parse(String sql) {
+ return XServiceProvider.get().parsed(sql);
}
/**
@@ -87,17 +60,12 @@ public class RawSqlBuilder {
* @param dbColumn the DB column that we are mapping to a bean property
* @param propertyName the bean property that we are mapping the DB column to.
*/
- public RawSqlBuilder columnMapping(String dbColumn, String propertyName) {
- columnMapping.columnMapping(dbColumn, propertyName);
- return this;
- }
+ RawSqlBuilder columnMapping(String dbColumn, String propertyName);
/**
* Ignore this DB column. It is not mapped to any bean property.
*/
- public RawSqlBuilder columnMappingIgnore(String dbColumn) {
- return columnMapping(dbColumn, IGNORE_COLUMN);
- }
+ RawSqlBuilder columnMappingIgnore(String dbColumn);
/**
* Modify any column mappings with the given table alias to have the path prefix.
@@ -108,24 +76,12 @@ public class RawSqlBuilder {
* For the "Root type" you don't need to specify a tableAliasMapping.
*
*/
- public RawSqlBuilder tableAliasMapping(String tableAlias, String path) {
- columnMapping.tableAliasMapping(tableAlias, path);
- return this;
- }
+ RawSqlBuilder tableAliasMapping(String tableAlias, String path);
/**
* Create the immutable RawSql object. Do this after all the column mapping
* has been defined.
*/
- public RawSql create() {
- return new RawSql(resultSet, sql, columnMapping.createImmutableCopy());
- }
-
- /**
- * Return the internal parsed Sql object (for testing).
- */
- protected Sql getSql() {
- return sql;
- }
+ RawSql create();
}
diff --git a/src/main/java/io/ebean/XServiceProvider.java b/src/main/java/io/ebean/XServiceProvider.java
new file mode 100644
index 000000000..6e7ce4d98
--- /dev/null
+++ b/src/main/java/io/ebean/XServiceProvider.java
@@ -0,0 +1,30 @@
+package io.ebean;
+
+import io.ebean.plugin.SpiRawSqlService;
+
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+/**
+ * Lookup internal services.
+ */
+class XServiceProvider {
+
+ private static SpiRawSqlService builder = init();
+
+ private static SpiRawSqlService init() {
+
+ Iterator loader = ServiceLoader.load(SpiRawSqlService.class).iterator();
+ if (loader.hasNext()) {
+ return loader.next();
+ }
+ throw new IllegalStateException("No service implementation found for SpiRawSqlService?");
+ }
+
+ /**
+ * Return the RawSqlService implementation.
+ */
+ static SpiRawSqlService get() {
+ return builder;
+ }
+}
diff --git a/src/main/java/io/ebean/plugin/SpiRawSqlService.java b/src/main/java/io/ebean/plugin/SpiRawSqlService.java
new file mode 100644
index 000000000..67254a6f2
--- /dev/null
+++ b/src/main/java/io/ebean/plugin/SpiRawSqlService.java
@@ -0,0 +1,27 @@
+package io.ebean.plugin;
+
+import io.ebean.RawSql;
+import io.ebean.RawSqlBuilder;
+
+import java.sql.ResultSet;
+
+/**
+ * Service provided for parsing and column mapping raw SQL queries.
+ */
+public interface SpiRawSqlService {
+
+ /**
+ * Create based on a JDBC ResultSet.
+ */
+ RawSql resultSet(ResultSet resultSet, String... propertyNames);
+
+ /**
+ * Parse the SQL determining column mapping.
+ */
+ RawSqlBuilder parsed(String sql);
+
+ /**
+ * Unparsed SQL so explicit column mapping expected.
+ */
+ RawSqlBuilder unparsed(String sql);
+}
diff --git a/src/main/java/io/ebeaninternal/api/SpiQuery.java b/src/main/java/io/ebeaninternal/api/SpiQuery.java
index 8a4022096..69123520d 100644
--- a/src/main/java/io/ebeaninternal/api/SpiQuery.java
+++ b/src/main/java/io/ebeaninternal/api/SpiQuery.java
@@ -20,6 +20,7 @@ import io.ebeaninternal.server.query.CancelableQuery;
import io.ebeaninternal.server.querydefn.NaturalKeyBindParam;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
import io.ebeaninternal.server.querydefn.OrmUpdateProperties;
+import io.ebeaninternal.server.rawsql.SpiRawSql;
import java.sql.Timestamp;
import java.util.List;
@@ -170,6 +171,11 @@ public interface SpiQuery extends Query {
*/
BeanDescriptor getBeanDescriptor();
+ /**
+ * Return the RawSql that was set to use for this query.
+ */
+ SpiRawSql getRawSql();
+
/**
* Return true if this query should be executed against the doc store.
*/
diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java
index 34087d0f9..3a5013b24 100644
--- a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java
+++ b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java
@@ -1,30 +1,8 @@
package io.ebeaninternal.server.core;
-import io.ebean.AutoTune;
-import io.ebean.BackgroundExecutor;
-import io.ebean.BeanState;
-import io.ebean.CallableSql;
-import io.ebean.DocumentStore;
-import io.ebean.ExpressionFactory;
-import io.ebean.Filter;
-import io.ebean.FutureIds;
-import io.ebean.FutureList;
-import io.ebean.FutureRowCount;
-import io.ebean.PagedList;
-import io.ebean.PersistenceContextScope;
-import io.ebean.Query;
-import io.ebean.QueryIterator;
-import io.ebean.RawSql;
-import io.ebean.SqlQuery;
-import io.ebean.SqlRow;
-import io.ebean.SqlUpdate;
-import io.ebean.Transaction;
-import io.ebean.TransactionCallback;
-import io.ebean.TxScope;
-import io.ebean.Update;
-import io.ebean.UpdateQuery;
-import io.ebean.ValuePair;
-import io.ebean.Version;
+import io.ebean.*;
+import io.ebean.annotation.TxIsolation;
+import io.ebean.annotation.TxType;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.CallStack;
import io.ebean.bean.EntityBean;
@@ -37,12 +15,11 @@ import io.ebean.common.CopyOnFirstWriteList;
import io.ebean.config.CurrentTenantProvider;
import io.ebean.config.EncryptKeyManager;
import io.ebean.config.ServerConfig;
-import io.ebean.config.TenantMode;
-import io.ebean.config.dbplatform.DatabasePlatform;
-import io.ebeaninternal.dbmigration.DdlGenerator;
-import io.ebean.event.BeanPersistController;
import io.ebean.config.SlowQueryEvent;
import io.ebean.config.SlowQueryListener;
+import io.ebean.config.TenantMode;
+import io.ebean.config.dbplatform.DatabasePlatform;
+import io.ebean.event.BeanPersistController;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.meta.MetaInfoManager;
@@ -53,6 +30,7 @@ import io.ebean.text.csv.CsvReader;
import io.ebean.text.json.JsonContext;
import io.ebeaninternal.api.*;
import io.ebeaninternal.api.SpiQuery.Type;
+import io.ebeaninternal.dbmigration.DdlGenerator;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
import io.ebeaninternal.server.autotune.AutoTuneService;
import io.ebeaninternal.server.core.timezone.DataTimeZone;
@@ -65,9 +43,9 @@ import io.ebeaninternal.server.grammer.EqlParser;
import io.ebeaninternal.server.lib.ShutdownManager;
import io.ebeaninternal.server.query.CQuery;
import io.ebeaninternal.server.query.CQueryEngine;
+import io.ebeaninternal.server.query.CallableQueryCount;
import io.ebeaninternal.server.query.CallableQueryIds;
import io.ebeaninternal.server.query.CallableQueryList;
-import io.ebeaninternal.server.query.CallableQueryCount;
import io.ebeaninternal.server.query.LimitOffsetPagedList;
import io.ebeaninternal.server.query.QueryFutureIds;
import io.ebeaninternal.server.query.QueryFutureList;
@@ -76,6 +54,7 @@ import io.ebeaninternal.server.querydefn.DefaultOrmQuery;
import io.ebeaninternal.server.querydefn.DefaultOrmUpdate;
import io.ebeaninternal.server.querydefn.DefaultRelationalQuery;
import io.ebeaninternal.server.querydefn.DefaultUpdateQuery;
+import io.ebeaninternal.server.rawsql.SpiRawSql;
import io.ebeaninternal.server.text.csv.TCsvReader;
import io.ebeaninternal.server.transaction.DefaultPersistenceContext;
import io.ebeaninternal.server.transaction.RemoteTransactionEvent;
@@ -84,8 +63,6 @@ import io.ebeaninternal.server.transaction.TransactionScopeManager;
import io.ebeaninternal.util.ParamTypeHelper;
import io.ebeaninternal.util.ParamTypeHelper.TypeInfo;
import io.ebeanservice.docstore.api.DocStoreIntegration;
-import io.ebean.annotation.TxIsolation;
-import io.ebean.annotation.TxType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -93,7 +70,6 @@ import javax.persistence.NonUniqueResultException;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
-
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
@@ -1003,7 +979,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
if (named != null) {
return createQuery(beanType, named);
}
- RawSql rawSql = desc.getNamedRawSql(namedQuery);
+ SpiRawSql rawSql = desc.getNamedRawSql(namedQuery);
if (rawSql != null) {
DefaultOrmQuery query = createQuery(beanType);
query.setRawSql(rawSql);
diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java
index 606409061..858ae67a5 100644
--- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java
+++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java
@@ -3,7 +3,6 @@ package io.ebeaninternal.server.deploy;
import io.ebean.OrderBy;
import io.ebean.PersistenceContextScope;
import io.ebean.Query;
-import io.ebean.RawSql;
import io.ebean.SqlUpdate;
import io.ebean.Transaction;
import io.ebean.ValuePair;
@@ -64,6 +63,7 @@ import io.ebeaninternal.server.query.CQueryPlan;
import io.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
import io.ebeaninternal.server.query.SplitName;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
+import io.ebeaninternal.server.rawsql.SpiRawSql;
import io.ebeaninternal.server.text.json.ReadJson;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import io.ebeaninternal.server.type.DataBind;
@@ -110,7 +110,7 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType {
private final ConcurrentHashMap> comparatorCache = new ConcurrentHashMap<>();
- private final Map namedRawSql;
+ private final Map namedRawSql;
private final Map namedQuery;
@@ -1112,7 +1112,7 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType {
/**
* Return the named RawSql query.
*/
- public RawSql getNamedRawSql(String named) {
+ public SpiRawSql getNamedRawSql(String named) {
return namedRawSql.get(named);
}
@@ -2932,7 +2932,7 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType {
if (idProperty != null && !idProperty.isEmbedded()) {
OrderBy orderBy = query.getOrderBy();
if (orderBy == null || orderBy.isEmpty()) {
- RawSql rawSql = query.getRawSql();
+ SpiRawSql rawSql = query.getRawSql();
if (rawSql != null) {
query.order(rawSql.getSql().getOrderBy());
}
diff --git a/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java b/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java
index ef378a1ba..87420edf7 100644
--- a/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java
+++ b/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.deploy.meta;
-import io.ebean.RawSql;
import io.ebean.annotation.Cache;
import io.ebean.annotation.DocStore;
import io.ebean.annotation.DocStoreMode;
@@ -30,6 +29,7 @@ import io.ebeaninternal.server.deploy.IndexDefinition;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.parse.DeployBeanInfo;
import io.ebeaninternal.server.idgen.UuidIdGenerator;
+import io.ebeaninternal.server.rawsql.SpiRawSql;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
@@ -49,7 +49,7 @@ public class DeployBeanDescriptor {
private static final Map EMPTY_NAMED_QUERY = new HashMap<>();
- private static final Map EMPTY_RAW_MAP = new HashMap<>();
+ private static final Map EMPTY_RAW_MAP = new HashMap<>();
private static class PropOrder implements Comparator {
@@ -75,7 +75,7 @@ public class DeployBeanDescriptor {
*/
private LinkedHashMap propMap = new LinkedHashMap<>();
- private Map namedRawSql;
+ private Map namedRawSql;
private Map namedQuery;
@@ -1100,14 +1100,14 @@ public class DeployBeanDescriptor {
/**
* Return the named RawSql queries.
*/
- public Map getNamedRawSql() {
+ public Map getNamedRawSql() {
return (namedRawSql != null) ? namedRawSql : EMPTY_RAW_MAP;
}
/**
* Add a named RawSql from ebean.xml file.
*/
- public void addRawSql(String name, RawSql rawSql) {
+ public void addRawSql(String name, SpiRawSql rawSql) {
if (namedRawSql == null) {
namedRawSql = new HashMap<>();
}
diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/DeployBeanInfo.java b/src/main/java/io/ebeaninternal/server/deploy/parse/DeployBeanInfo.java
index ebffb6f3c..f84a9dd69 100644
--- a/src/main/java/io/ebeaninternal/server/deploy/parse/DeployBeanInfo.java
+++ b/src/main/java/io/ebeaninternal/server/deploy/parse/DeployBeanInfo.java
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.RawSql;
+import io.ebeaninternal.server.rawsql.SpiRawSql;
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
@@ -81,7 +82,7 @@ public class DeployBeanInfo {
* Add named RawSql from ebean.xml.
*/
public void addRawSql(String name, RawSql rawSql) {
- descriptor.addRawSql(name, rawSql);
+ descriptor.addRawSql(name, (SpiRawSql)rawSql);
}
/**
diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java b/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java
index 144ab495c..9af904c6a 100644
--- a/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java
+++ b/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java
@@ -1,10 +1,8 @@
package io.ebeaninternal.server.query;
-import io.ebean.annotation.Platform;
import io.ebean.RawSql;
-import io.ebean.RawSql.ColumnMapping;
-import io.ebean.RawSql.ColumnMapping.Column;
import io.ebean.RawSqlBuilder;
+import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.SqlLimitRequest;
import io.ebean.config.dbplatform.SqlLimitResponse;
@@ -23,6 +21,9 @@ import io.ebeaninternal.server.el.ElPropertyValue;
import io.ebeaninternal.server.persist.Binder;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
import io.ebeaninternal.server.querydefn.OrmQueryLimitRequest;
+import io.ebeaninternal.server.rawsql.SpiRawSql;
+import io.ebeaninternal.server.rawsql.SpiRawSql.ColumnMapping;
+import io.ebeaninternal.server.rawsql.SpiRawSql.ColumnMapping.Column;
import javax.persistence.PersistenceException;
import java.sql.Connection;
@@ -395,7 +396,7 @@ class CQueryBuilder {
if (path != null) {
propertyNames.add(path);
} else {
- propertyNames.add(RawSqlBuilder.IGNORE_COLUMN);
+ propertyNames.add(SpiRawSql.IGNORE_COLUMN);
}
}
@@ -418,9 +419,9 @@ class CQueryBuilder {
// convert list of columns into (tree like) PathProperties
Iterator it = columnMapping.getColumns();
while (it.hasNext()) {
- RawSql.ColumnMapping.Column column = it.next();
+ SpiRawSql.ColumnMapping.Column column = it.next();
String propertyName = column.getPropertyName();
- if (!RawSqlBuilder.IGNORE_COLUMN.equals(propertyName)) {
+ if (!SpiRawSql.IGNORE_COLUMN.equals(propertyName)) {
ElPropertyValue el = descriptor.getElGetValue(propertyName);
if (el == null && propertyName.endsWith("Id")) {
// try default naming convention for foreign key columns
diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryBuilderRawSql.java b/src/main/java/io/ebeaninternal/server/query/CQueryBuilderRawSql.java
index 10b69eba1..2a84760b6 100644
--- a/src/main/java/io/ebeaninternal/server/query/CQueryBuilderRawSql.java
+++ b/src/main/java/io/ebeaninternal/server/query/CQueryBuilderRawSql.java
@@ -1,6 +1,6 @@
package io.ebeaninternal.server.query;
-import io.ebean.RawSql;
+import io.ebeaninternal.server.rawsql.SpiRawSql;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.SqlLimitResponse;
import io.ebean.config.dbplatform.SqlLimiter;
@@ -24,7 +24,7 @@ class CQueryBuilderRawSql {
/**
* Build the full SQL Select statement for the request.
*/
- SqlLimitResponse buildSql(OrmQueryRequest> request, CQueryPredicates predicates, RawSql.Sql rsql) {
+ SqlLimitResponse buildSql(OrmQueryRequest> request, CQueryPredicates predicates, SpiRawSql.Sql rsql) {
if (rsql == null) {
// this is a ResultSet based RawSql query - just use some placeholder for the SQL
@@ -60,7 +60,7 @@ class CQueryBuilderRawSql {
}
}
- private String buildMainQuery(String orderBy, OrmQueryRequest> request, CQueryPredicates predicates, RawSql.Sql sql) {
+ private String buildMainQuery(String orderBy, OrmQueryRequest> request, CQueryPredicates predicates, SpiRawSql.Sql sql) {
StringBuilder sb = new StringBuilder();
sb.append(sql.getPreFrom());
@@ -137,7 +137,7 @@ class CQueryBuilderRawSql {
return s == null || s.isEmpty();
}
- private String getOrderBy(CQueryPredicates predicates, RawSql.Sql sql) {
+ private String getOrderBy(CQueryPredicates predicates, SpiRawSql.Sql sql) {
String orderBy = predicates.getDbOrderBy();
if (orderBy != null) {
return orderBy;
diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryPlanRawSql.java b/src/main/java/io/ebeaninternal/server/query/CQueryPlanRawSql.java
index 43b7ff92b..0c7e8c3a0 100644
--- a/src/main/java/io/ebeaninternal/server/query/CQueryPlanRawSql.java
+++ b/src/main/java/io/ebeaninternal/server/query/CQueryPlanRawSql.java
@@ -1,6 +1,6 @@
package io.ebeaninternal.server.query;
-import io.ebean.RawSql.ColumnMapping;
+import io.ebeaninternal.server.rawsql.SpiRawSql.ColumnMapping;
import io.ebean.config.dbplatform.SqlLimitResponse;
import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.type.DataReader;
diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryPredicates.java b/src/main/java/io/ebeaninternal/server/query/CQueryPredicates.java
index 4ca81ddcd..b49f65a59 100644
--- a/src/main/java/io/ebeaninternal/server/query/CQueryPredicates.java
+++ b/src/main/java/io/ebeaninternal/server/query/CQueryPredicates.java
@@ -1,6 +1,6 @@
package io.ebeaninternal.server.query;
-import io.ebean.RawSql;
+import io.ebeaninternal.server.rawsql.SpiRawSql;
import io.ebeaninternal.api.BindParams;
import io.ebeaninternal.api.SpiExpressionList;
import io.ebeaninternal.api.SpiQuery;
@@ -210,7 +210,7 @@ public class CQueryPredicates {
// RawSql query hit cached query plan. Need to convert
// named parameters into positioned parameters so that
// the named parameters are bound
- RawSql.Sql sql = query.getRawSql().getSql();
+ SpiRawSql.Sql sql = query.getRawSql().getSql();
String s = sql.isParsed() ? sql.getPreWhere() : sql.getUnparsedSql();
BindParamsParser.parse(bindParams, s);
}
diff --git a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java
index 11cd893d0..34548edd5 100644
--- a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java
+++ b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java
@@ -1,23 +1,7 @@
package io.ebeaninternal.server.querydefn;
-import io.ebean.CacheMode;
-import io.ebean.EbeanServer;
-import io.ebean.Expression;
-import io.ebean.ExpressionFactory;
-import io.ebean.ExpressionList;
-import io.ebean.FetchConfig;
-import io.ebean.FetchPath;
-import io.ebean.FutureIds;
-import io.ebean.FutureList;
-import io.ebean.FutureRowCount;
-import io.ebean.OrderBy;
+import io.ebean.*;
import io.ebean.OrderBy.Property;
-import io.ebean.PagedList;
-import io.ebean.PersistenceContextScope;
-import io.ebean.Query;
-import io.ebean.QueryIterator;
-import io.ebean.RawSql;
-import io.ebean.Version;
import io.ebean.bean.CallStack;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.bean.ObjectGraphOrigin;
@@ -25,16 +9,7 @@ import io.ebean.bean.PersistenceContext;
import io.ebean.event.BeanQueryRequest;
import io.ebean.event.readaudit.ReadEvent;
import io.ebean.plugin.BeanType;
-import io.ebeaninternal.api.BindParams;
-import io.ebeaninternal.api.CQueryPlanKey;
-import io.ebeaninternal.api.HashQuery;
-import io.ebeaninternal.api.ManyWhereJoins;
-import io.ebeaninternal.api.SpiExpression;
-import io.ebeaninternal.api.SpiExpressionList;
-import io.ebeaninternal.api.SpiExpressionValidation;
-import io.ebeaninternal.api.SpiNamedParam;
-import io.ebeaninternal.api.SpiQuery;
-import io.ebeaninternal.api.SpiQuerySecondary;
+import io.ebeaninternal.api.*;
import io.ebeaninternal.server.autotune.ProfilingListener;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
@@ -43,6 +18,7 @@ import io.ebeaninternal.server.expression.DefaultExpressionList;
import io.ebeaninternal.server.expression.SimpleExpression;
import io.ebeaninternal.server.query.CancelableQuery;
import io.ebeaninternal.server.query.NativeSqlQueryPlanKey;
+import io.ebeaninternal.server.rawsql.SpiRawSql;
import java.sql.Timestamp;
import java.util.ArrayList;
@@ -244,7 +220,7 @@ public class DefaultOrmQuery implements SpiQuery {
private ManyWhereJoins manyWhereJoins;
- private RawSql rawSql;
+ private SpiRawSql rawSql;
private boolean useDocStore;
@@ -373,13 +349,13 @@ public class DefaultOrmQuery implements SpiQuery {
}
@Override
- public RawSql getRawSql() {
+ public SpiRawSql getRawSql() {
return rawSql;
}
@Override
public DefaultOrmQuery setRawSql(RawSql rawSql) {
- this.rawSql = rawSql;
+ this.rawSql = (SpiRawSql)rawSql;
return this;
}
diff --git a/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryPlanKey.java b/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryPlanKey.java
index 721d21b1e..88e060e85 100644
--- a/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryPlanKey.java
+++ b/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryPlanKey.java
@@ -2,7 +2,7 @@ package io.ebeaninternal.server.querydefn;
import io.ebean.OrderBy;
import io.ebean.Query;
-import io.ebean.RawSql;
+import io.ebeaninternal.server.rawsql.SpiRawSql;
import io.ebeaninternal.api.BindParams;
import io.ebeaninternal.api.CQueryPlanKey;
import io.ebeaninternal.api.SpiExpression;
@@ -14,7 +14,7 @@ import io.ebeaninternal.server.deploy.TableJoin;
*/
class OrmQueryPlanKey implements CQueryPlanKey {
- private final RawSql.Key rawSqlKey;
+ private final SpiRawSql.Key rawSqlKey;
private final int maxRows;
private final int firstRow;
private final int planHash;
@@ -23,7 +23,7 @@ class OrmQueryPlanKey implements CQueryPlanKey {
OrmQueryPlanKey(String discValue, TableJoin m2mIncludeTable, SpiQuery.Type type, OrmQueryDetail detail, int maxRows, int firstRow, boolean disableLazyLoading,
OrderBy> orderBy, boolean distinct, boolean sqlDistinct, String mapKey, Object id, BindParams bindParams,
SpiExpression whereExpressions, SpiExpression havingExpressions, SpiQuery.TemporalMode temporalMode,
- Query.ForUpdate forUpdate, String rootTableAlias, RawSql rawSql, OrmUpdateProperties updateProperties) {
+ Query.ForUpdate forUpdate, String rootTableAlias, SpiRawSql rawSql, OrmUpdateProperties updateProperties) {
StringBuilder sb = new StringBuilder(300);
if (type != null) {
diff --git a/src/main/java/io/ebeaninternal/server/rawsql/DRawSql.java b/src/main/java/io/ebeaninternal/server/rawsql/DRawSql.java
new file mode 100644
index 000000000..9f7e6627d
--- /dev/null
+++ b/src/main/java/io/ebeaninternal/server/rawsql/DRawSql.java
@@ -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;
+ }
+
+}
diff --git a/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlBuilder.java b/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlBuilder.java
new file mode 100644
index 000000000..40160e873
--- /dev/null
+++ b/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlBuilder.java
@@ -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());
+ }
+}
diff --git a/src/main/java/io/ebean/DRawSqlColumnsParser.java b/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlColumnsParser.java
similarity index 96%
rename from src/main/java/io/ebean/DRawSqlColumnsParser.java
rename to src/main/java/io/ebeaninternal/server/rawsql/DRawSqlColumnsParser.java
index 4439585fe..6e17ed5a9 100644
--- a/src/main/java/io/ebean/DRawSqlColumnsParser.java
+++ b/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlColumnsParser.java
@@ -1,6 +1,6 @@
-package io.ebean;
+package io.ebeaninternal.server.rawsql;
-import io.ebean.RawSql.ColumnMapping;
+import io.ebeaninternal.server.rawsql.SpiRawSql.ColumnMapping;
import java.util.regex.Pattern;
import javax.persistence.PersistenceException;
diff --git a/src/main/java/io/ebean/DRawSqlParser.java b/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlParser.java
similarity index 98%
rename from src/main/java/io/ebean/DRawSqlParser.java
rename to src/main/java/io/ebeaninternal/server/rawsql/DRawSqlParser.java
index 83325f6b7..a653a074e 100644
--- a/src/main/java/io/ebean/DRawSqlParser.java
+++ b/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlParser.java
@@ -1,6 +1,6 @@
-package io.ebean;
+package io.ebeaninternal.server.rawsql;
-import io.ebean.RawSql.Sql;
+import io.ebeaninternal.server.rawsql.SpiRawSql.Sql;
import io.ebeaninternal.server.querydefn.SimpleTextParser;
/**
diff --git a/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java b/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java
new file mode 100644
index 000000000..12207fc1a
--- /dev/null
+++ b/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java
@@ -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());
+ }
+}
diff --git a/src/main/java/io/ebeaninternal/server/rawsql/SpiRawSql.java b/src/main/java/io/ebeaninternal/server/rawsql/SpiRawSql.java
new file mode 100644
index 000000000..e84ab7d94
--- /dev/null
+++ b/src/main/java/io/ebeaninternal/server/rawsql/SpiRawSql.java
@@ -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.
+ *
+ * This means Ebean can't add WHERE or HAVING expressions into the query -
+ * it will be left completely unmodified.
+ *
+ */
+ 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 dbColumnMap;
+
+ private final Map propertyMap;
+
+ private final Map propertyColumnMap;
+
+ private final boolean parsed;
+
+ private final boolean immutable;
+
+ /**
+ * Construct from parsed sql where the columns have been identified.
+ */
+ protected ColumnMapping(List 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 dbColumnMap) {
+ this.immutable = true;
+ this.parsed = parsed;
+ this.dbColumnMap = dbColumnMap;
+
+ HashMap pcMap = new HashMap<>();
+ HashMap 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.
+ *
+ * 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.
+ *
+ */
+ 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 mapping() {
+ return dbColumnMap;
+ }
+
+ /**
+ * Return the mapping by DB column.
+ */
+ public Map 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 getColumns() {
+ return dbColumnMap.values().iterator();
+ }
+
+ /**
+ * Modify any column mappings with the given table alias to have the path prefix.
+ *
+ * For example modify all mappings with table alias "c" to have the path prefix "customer".
+ *
+ *
+ * For the "Root type" you don't need to specify a tableAliasMapping.
+ *
+ */
+ public void tableAliasMapping(String tableAlias, String path) {
+
+ String startMatch = tableAlias + ".";
+ for (Map.Entry 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.
+ *
+ * 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;
+ }
+ }
+}
diff --git a/src/main/resources/META-INF/services/io.ebean.plugin.SpiRawSqlService b/src/main/resources/META-INF/services/io.ebean.plugin.SpiRawSqlService
new file mode 100644
index 000000000..f505dbcf5
--- /dev/null
+++ b/src/main/resources/META-INF/services/io.ebean.plugin.SpiRawSqlService
@@ -0,0 +1 @@
+io.ebeaninternal.server.rawsql.DRawSqlService
diff --git a/src/test/java/io/ebean/ColumnMappingTest.java b/src/test/java/io/ebean/ColumnMappingTest.java
deleted file mode 100644
index d8c44c5d2..000000000
--- a/src/test/java/io/ebean/ColumnMappingTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package io.ebean;
-
-import io.ebean.RawSql;
-import org.junit.Test;
-
-import java.util.Arrays;
-
-import static org.assertj.core.api.StrictAssertions.assertThat;
-
-public class ColumnMappingTest {
-
- RawSql.ColumnMapping.Column col(int indexPos, String dbColumn, String dbAlias) {
- return new RawSql.ColumnMapping.Column(indexPos, dbColumn, dbAlias);
- }
-
- RawSql.ColumnMapping mapping(RawSql.ColumnMapping.Column... cols) {
- return new RawSql.ColumnMapping(Arrays.asList(cols));
- }
-
- @Test
- public void equals_same() {
-
- RawSql.ColumnMapping mapping1 = mapping(col(1, "id", null), col(2, "name", null));
- RawSql.ColumnMapping mapping2 = mapping(col(1, "id", null), col(2, "name", null));
-
- assertSame(mapping1, mapping2);
- }
-
- @Test
- public void equals_diffPropertyName() {
-
- RawSql.ColumnMapping mapping1 = mapping(col(1, "id", null), col(2, "name", null));
- RawSql.ColumnMapping mapping2 = mapping(col(1, "id", null), col(2, "diff", null));
-
- assertDifferent(mapping1, mapping2);
- }
-
- @Test
- public void equals_moreColumns() {
-
- RawSql.ColumnMapping mapping1 = mapping(col(1, "id", null), col(2, "name", null));
- RawSql.ColumnMapping mapping2 = mapping(col(1, "id", null), col(2, "name", null), col(2, "diff", null));
-
- assertDifferent(mapping1, mapping2);
- }
-
-
- @Test
- public void equals_lessColumns() {
-
- RawSql.ColumnMapping mapping1 = mapping(col(1, "id", null), col(2, "name", null));
- RawSql.ColumnMapping mapping2 = mapping(col(1, "id", null));
-
- assertDifferent(mapping1, mapping2);
- }
-
- private void assertSame(Object key, Object key1) {
- assertThat(key).isEqualTo(key1);
- assertThat(key.hashCode()).isEqualTo(key1.hashCode());
- }
-
- private void assertDifferent(Object key, Object key1) {
- assertThat(key).isNotEqualTo(key1);
- assertThat(key.hashCode()).isNotEqualTo(key1.hashCode());
- }
-}
diff --git a/src/test/java/io/ebean/ColumnTest.java b/src/test/java/io/ebean/ColumnTest.java
index 57e7ed694..c8889896e 100644
--- a/src/test/java/io/ebean/ColumnTest.java
+++ b/src/test/java/io/ebean/ColumnTest.java
@@ -1,15 +1,15 @@
package io.ebean;
-import io.ebean.RawSql;
+import io.ebeaninternal.server.rawsql.SpiRawSql;
import org.junit.Test;
import static org.assertj.core.api.StrictAssertions.assertThat;
public class ColumnTest {
- RawSql.ColumnMapping.Column col(int indexPos, String dbColumn, String dbAlias) {
- return new RawSql.ColumnMapping.Column(indexPos, dbColumn, dbAlias);
+ SpiRawSql.ColumnMapping.Column col(int indexPos, String dbColumn, String dbAlias) {
+ return new SpiRawSql.ColumnMapping.Column(indexPos, dbColumn, dbAlias);
}
@Test
diff --git a/src/test/java/io/ebean/RawSqlKeyTest.java b/src/test/java/io/ebean/RawSqlKeyTest.java
deleted file mode 100644
index c798ecd3f..000000000
--- a/src/test/java/io/ebean/RawSqlKeyTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package io.ebean;
-
-
-import io.ebean.RawSql;
-import io.ebean.RawSqlBuilder;
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class RawSqlKeyTest {
-
- @Test
- public void equals_when_sameParsedSql() {
-
- RawSql.Key key = RawSqlBuilder.parse("select id from customer").create().getKey();
- RawSql.Key key1 = RawSqlBuilder.parse("select id from customer").create().getKey();
-
- assertSame(key, key1);
- }
-
- @Test
- public void equals_when_diffParsedSql() {
-
- RawSql.Key key = RawSqlBuilder.parse("select id from customer").create().getKey();
- RawSql.Key key1 = RawSqlBuilder.parse("select name from customer").create().getKey();
-
- assertDifferent(key, key1);
- }
-
- @Test
- public void equals_when_sameColumnMapping() {
-
- RawSql.Key key = RawSqlBuilder.parse("select id from customer").columnMapping("id", "b").create().getKey();
- RawSql.Key key1 = RawSqlBuilder.parse("select id from customer").columnMapping("id", "b").create().getKey();
-
- assertSame(key, key1);
- }
-
- @Test
- public void equals_when_diffColumnMapping() {
-
- RawSql.Key key = RawSqlBuilder.parse("select a from customer").columnMapping("a", "b").create().getKey();
- RawSql.Key key1 = RawSqlBuilder.parse("select a from customer").columnMapping("a", "c").create().getKey();
-
- assertDifferent(key, key1);
- }
-
- @Test
- public void equals_when_parseToUnpased() {
-
- RawSql.Key key = RawSqlBuilder.parse("select a from customer").columnMapping("a", "b").create().getKey();
- RawSql.Key key1 = RawSqlBuilder.unparsed("select a from customer").columnMapping("a", "c").create().getKey();
-
- assertDifferent(key, key1);
- }
-
- private void assertSame(RawSql.Key key, RawSql.Key key1) {
- assertThat(key).isEqualTo(key1);
- assertThat(key.hashCode()).isEqualTo(key1.hashCode());
- }
-
- private void assertDifferent(RawSql.Key key, RawSql.Key key1) {
- assertThat(key).isNotEqualTo(key1);
- assertThat(key.hashCode()).isNotEqualTo(key1.hashCode());
- }
-}
diff --git a/src/test/java/io/ebeaninternal/server/rawsql/ColumnMappingTest.java b/src/test/java/io/ebeaninternal/server/rawsql/ColumnMappingTest.java
new file mode 100644
index 000000000..527c1cf93
--- /dev/null
+++ b/src/test/java/io/ebeaninternal/server/rawsql/ColumnMappingTest.java
@@ -0,0 +1,65 @@
+package io.ebeaninternal.server.rawsql;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.assertj.core.api.StrictAssertions.assertThat;
+
+public class ColumnMappingTest {
+
+ SpiRawSql.ColumnMapping.Column col(int indexPos, String dbColumn, String dbAlias) {
+ return new SpiRawSql.ColumnMapping.Column(indexPos, dbColumn, dbAlias);
+ }
+
+ SpiRawSql.ColumnMapping mapping(SpiRawSql.ColumnMapping.Column... cols) {
+ return new SpiRawSql.ColumnMapping(Arrays.asList(cols));
+ }
+
+ @Test
+ public void equals_same() {
+
+ SpiRawSql.ColumnMapping mapping1 = mapping(col(1, "id", null), col(2, "name", null));
+ SpiRawSql.ColumnMapping mapping2 = mapping(col(1, "id", null), col(2, "name", null));
+
+ assertSame(mapping1, mapping2);
+ }
+
+ @Test
+ public void equals_diffPropertyName() {
+
+ SpiRawSql.ColumnMapping mapping1 = mapping(col(1, "id", null), col(2, "name", null));
+ SpiRawSql.ColumnMapping mapping2 = mapping(col(1, "id", null), col(2, "diff", null));
+
+ assertDifferent(mapping1, mapping2);
+ }
+
+ @Test
+ public void equals_moreColumns() {
+
+ SpiRawSql.ColumnMapping mapping1 = mapping(col(1, "id", null), col(2, "name", null));
+ SpiRawSql.ColumnMapping mapping2 = mapping(col(1, "id", null), col(2, "name", null), col(2, "diff", null));
+
+ assertDifferent(mapping1, mapping2);
+ }
+
+
+ @Test
+ public void equals_lessColumns() {
+
+ SpiRawSql.ColumnMapping mapping1 = mapping(col(1, "id", null), col(2, "name", null));
+ SpiRawSql.ColumnMapping mapping2 = mapping(col(1, "id", null));
+
+ assertDifferent(mapping1, mapping2);
+ }
+
+ private void assertSame(Object key, Object key1) {
+ assertThat(key).isEqualTo(key1);
+ assertThat(key.hashCode()).isEqualTo(key1.hashCode());
+ }
+
+ private void assertDifferent(Object key, Object key1) {
+ assertThat(key).isNotEqualTo(key1);
+ assertThat(key.hashCode()).isNotEqualTo(key1.hashCode());
+ }
+}
diff --git a/src/test/java/io/ebeaninternal/server/rawsql/RawSqlKeyTest.java b/src/test/java/io/ebeaninternal/server/rawsql/RawSqlKeyTest.java
new file mode 100644
index 000000000..54efa28c5
--- /dev/null
+++ b/src/test/java/io/ebeaninternal/server/rawsql/RawSqlKeyTest.java
@@ -0,0 +1,74 @@
+package io.ebeaninternal.server.rawsql;
+
+
+import io.ebean.RawSql;
+import io.ebean.RawSqlBuilder;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RawSqlKeyTest {
+
+ private SpiRawSql.Key key(String sqlStatement) {
+ return ((SpiRawSql) RawSqlBuilder.parse(sqlStatement).create()).getKey();
+ }
+
+ private SpiRawSql.Key key(RawSql rawSql) {
+ return ((SpiRawSql)rawSql).getKey();
+ }
+
+ @Test
+ public void equals_when_sameParsedSql() {
+
+ SpiRawSql.Key key = key("select id from customer");
+ SpiRawSql.Key key1 = key("select id from customer");
+
+ assertSame(key, key1);
+ }
+
+ @Test
+ public void equals_when_diffParsedSql() {
+
+ SpiRawSql.Key key = key("select id from customer");
+ SpiRawSql.Key key1 = key("select name from customer");
+
+ assertDifferent(key, key1);
+ }
+
+ @Test
+ public void equals_when_sameColumnMapping() {
+
+ SpiRawSql.Key key = key(RawSqlBuilder.parse("select id from customer").columnMapping("id", "b").create());
+ SpiRawSql.Key key1 = key(RawSqlBuilder.parse("select id from customer").columnMapping("id", "b").create());
+
+ assertSame(key, key1);
+ }
+
+ @Test
+ public void equals_when_diffColumnMapping() {
+
+ SpiRawSql.Key key = key(RawSqlBuilder.parse("select a from customer").columnMapping("a", "b").create());
+ SpiRawSql.Key key1 = key(RawSqlBuilder.parse("select a from customer").columnMapping("a", "c").create());
+
+ assertDifferent(key, key1);
+ }
+
+ @Test
+ public void equals_when_parseToUnpased() {
+
+ SpiRawSql.Key key = key(RawSqlBuilder.parse("select a from customer").columnMapping("a", "b").create());
+ SpiRawSql.Key key1 = key(RawSqlBuilder.unparsed("select a from customer").columnMapping("a", "c").create());
+
+ assertDifferent(key, key1);
+ }
+
+ private void assertSame(SpiRawSql.Key key, SpiRawSql.Key key1) {
+ assertThat(key).isEqualTo(key1);
+ assertThat(key.hashCode()).isEqualTo(key1.hashCode());
+ }
+
+ private void assertDifferent(SpiRawSql.Key key, SpiRawSql.Key key1) {
+ assertThat(key).isNotEqualTo(key1);
+ assertThat(key.hashCode()).isNotEqualTo(key1.hashCode());
+ }
+}
diff --git a/src/test/java/io/ebean/TestRawSqlBuilder.java b/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilder.java
similarity index 73%
rename from src/test/java/io/ebean/TestRawSqlBuilder.java
rename to src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilder.java
index 62a02615a..f8f0bd0f0 100644
--- a/src/test/java/io/ebean/TestRawSqlBuilder.java
+++ b/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilder.java
@@ -1,6 +1,12 @@
-package io.ebean;
+package io.ebeaninternal.server.rawsql;
-import io.ebean.RawSql.Sql;
+import io.ebean.BaseTestCase;
+import io.ebean.Ebean;
+import io.ebean.Query;
+import io.ebean.RawSql;
+import io.ebean.RawSqlBuilder;
+import io.ebeaninternal.server.rawsql.SpiRawSql.Sql;
+import org.assertj.core.api.StrictAssertions;
import org.tests.model.basic.Customer;
import org.tests.model.basic.ResetBasicData;
import org.tests.model.rawsql.ERawSqlAggBean;
@@ -14,19 +20,23 @@ public class TestRawSqlBuilder extends BaseTestCase {
@Test
public void testDeriveProperty() {
- assertThat(RawSql.ColumnMapping.Column.derivePropertyName("item_total", "some_other")).isEqualTo("itemTotal");
- assertThat(RawSql.ColumnMapping.Column.derivePropertyName(null, "some_other")).isEqualTo("someOther");
- assertThat(RawSql.ColumnMapping.Column.derivePropertyName(null, "alias.some_other")).isEqualTo("someOther");
- assertThat(RawSql.ColumnMapping.Column.derivePropertyName(null, "alias.someOther")).isEqualTo("someOther");
- assertThat(RawSql.ColumnMapping.Column.derivePropertyName(null, "some")).isEqualTo("some");
- assertThat(RawSql.ColumnMapping.Column.derivePropertyName(null, "someOther")).isEqualTo("someOther");
+ StrictAssertions.assertThat(SpiRawSql.ColumnMapping.Column.derivePropertyName("item_total", "some_other")).isEqualTo("itemTotal");
+ assertThat(SpiRawSql.ColumnMapping.Column.derivePropertyName(null, "some_other")).isEqualTo("someOther");
+ assertThat(SpiRawSql.ColumnMapping.Column.derivePropertyName(null, "alias.some_other")).isEqualTo("someOther");
+ assertThat(SpiRawSql.ColumnMapping.Column.derivePropertyName(null, "alias.someOther")).isEqualTo("someOther");
+ assertThat(SpiRawSql.ColumnMapping.Column.derivePropertyName(null, "some")).isEqualTo("some");
+ assertThat(SpiRawSql.ColumnMapping.Column.derivePropertyName(null, "someOther")).isEqualTo("someOther");
+ }
+
+ private Sql getSql(String sqlStatement) {
+ RawSql r = RawSqlBuilder.parse(sqlStatement).create();
+ return ((SpiRawSql)r).getSql();
}
@Test
public void testSimple() {
- RawSqlBuilder r = RawSqlBuilder.parse("select id from t_cust");
- Sql sql = r.getSql();
+ Sql sql = getSql("select id from t_cust");
assertEquals("id", sql.getPreFrom());
assertEquals("from t_cust", sql.getPreWhere());
assertEquals("", sql.getPreHaving());
@@ -36,8 +46,7 @@ public class TestRawSqlBuilder extends BaseTestCase {
@Test
public void testWithNewLineCharacters() {
- RawSqlBuilder r = RawSqlBuilder.parse("select\n id from\n o_customer");
- Sql sql = r.getSql();
+ Sql sql = getSql("select\n id from\n o_customer");
assertEquals("id", sql.getPreFrom());
assertEquals("from o_customer", sql.getPreWhere());
@@ -46,7 +55,7 @@ public class TestRawSqlBuilder extends BaseTestCase {
ResetBasicData.reset();
- RawSql rawSql = r.create();
+ RawSql rawSql = RawSqlBuilder.parse("select\n id from\n o_customer").create();
Ebean.find(Customer.class)
.setRawSql(rawSql)
@@ -56,8 +65,8 @@ public class TestRawSqlBuilder extends BaseTestCase {
@Test
public void testWithWhere() {
- RawSqlBuilder r = RawSqlBuilder.parse("select id from t_cust where id > ?");
- Sql sql = r.getSql();
+ Sql sql = getSql("select id from t_cust where id > ?");
+
assertEquals("id", sql.getPreFrom());
assertEquals("from t_cust where id > ?", sql.getPreWhere());
assertEquals("", sql.getPreHaving());
@@ -67,24 +76,21 @@ public class TestRawSqlBuilder extends BaseTestCase {
@Test
public void testWithOrder() {
- RawSqlBuilder r = RawSqlBuilder.parse("select id from t_cust where id > ? order by id desc");
- Sql sql = r.getSql();
+ Sql sql = getSql("select id from t_cust where id > ? order by id desc");
+
assertEquals("id", sql.getPreFrom());
assertEquals("from t_cust where id > ?", sql.getPreWhere());
assertEquals("", sql.getPreHaving());
assertEquals("order by", sql.getOrderByPrefix());
assertEquals("id desc", sql.getOrderBy());
- r = RawSqlBuilder.parse("select id from t_cust order by id desc");
- sql = r.getSql();
+ sql = getSql("select id from t_cust order by id desc");
assertEquals("id", sql.getPreFrom());
assertEquals("from t_cust", sql.getPreWhere());
assertEquals("", sql.getPreHaving());
assertEquals("id desc", sql.getOrderBy());
- r = RawSqlBuilder
- .parse("select id, sum(x) from t_cust where id > ? group by id order by id desc");
- sql = r.getSql();
+ sql = getSql("select id, sum(x) from t_cust where id > ? group by id order by id desc");
assertEquals("id, sum(x)", sql.getPreFrom());
assertEquals("from t_cust where id > ?", sql.getPreWhere());
assertEquals("group by id", sql.getPreHaving());
@@ -94,9 +100,7 @@ public class TestRawSqlBuilder extends BaseTestCase {
@Test
public void testWithHaving() {
- RawSqlBuilder r = RawSqlBuilder
- .parse("select id, sum(x) from t_cust where id > ? group by id having sum(x) > ? order by id desc");
- Sql sql = r.getSql();
+ Sql sql = getSql("select id, sum(x) from t_cust where id > ? group by id having sum(x) > ? order by id desc");
assertEquals("id, sum(x)", sql.getPreFrom());
assertEquals("from t_cust where id > ?", sql.getPreWhere());
assertEquals("group by id having sum(x) > ?", sql.getPreHaving());
@@ -104,9 +108,7 @@ public class TestRawSqlBuilder extends BaseTestCase {
assertEquals("id desc", sql.getOrderBy());
// no where
- r = RawSqlBuilder
- .parse("select id, sum(x) from t_cust group by id having sum(x) > ? order by id desc");
- sql = r.getSql();
+ sql = getSql("select id, sum(x) from t_cust group by id having sum(x) > ? order by id desc");
assertEquals("id, sum(x)", sql.getPreFrom());
assertEquals("from t_cust", sql.getPreWhere());
assertEquals("group by id having sum(x) > ?", sql.getPreHaving());
@@ -114,8 +116,7 @@ public class TestRawSqlBuilder extends BaseTestCase {
assertEquals("id desc", sql.getOrderBy());
// no where, no order by
- r = RawSqlBuilder.parse("select id, sum(x) from t_cust group by id having sum(x) > ?");
- sql = r.getSql();
+ sql = getSql("select id, sum(x) from t_cust group by id having sum(x) > ?");
assertEquals("id, sum(x)", sql.getPreFrom());
assertEquals("from t_cust", sql.getPreWhere());
assertEquals("group by id having sum(x) > ?", sql.getPreHaving());
@@ -123,9 +124,7 @@ public class TestRawSqlBuilder extends BaseTestCase {
assertEquals("order by", sql.getOrderByPrefix());
// no order by
- r = RawSqlBuilder
- .parse("select id, sum(x) from t_cust where id > ? group by id having sum(x) > ?");
- sql = r.getSql();
+ sql = getSql("select id, sum(x) from t_cust where id > ? group by id having sum(x) > ?");
assertEquals("id, sum(x)", sql.getPreFrom());
assertEquals("from t_cust where id > ?", sql.getPreWhere());
assertEquals("group by id having sum(x) > ?", sql.getPreHaving());
@@ -140,15 +139,11 @@ public class TestRawSqlBuilder extends BaseTestCase {
public void testWithOrderSiblingsByName() {
String s = "SELECT ID, DESCRIPTION, NAME, PARENT_ID FROM SOME_TABLE WHERE lower(NAME) like :name START WITH ID = :parentId CONNECT BY PRIOR ID = PARENT_ID order siblings by NAME";
-
- RawSql rawSql = RawSqlBuilder.parse(s).create();
-
- Sql sql = rawSql.getSql();
+ Sql sql = getSql(s);
assertEquals("ID, DESCRIPTION, NAME, PARENT_ID", sql.getPreFrom());
assertEquals("order siblings by", sql.getOrderByPrefix());
assertEquals("NAME", sql.getOrderBy());
assertEquals("FROM SOME_TABLE WHERE lower(NAME) like :name START WITH ID = :parentId CONNECT BY PRIOR ID = PARENT_ID", sql.getPreWhere());
-
}
@@ -164,13 +159,13 @@ public class TestRawSqlBuilder extends BaseTestCase {
"order by o.id, d.id asc";
- RawSql rawSql = RawSqlBuilder.parse(rs)
+ SpiRawSql rawSql = (SpiRawSql)RawSqlBuilder.parse(rs)
.tableAliasMapping("c", "customer")
.tableAliasMapping("d", "details")
.tableAliasMapping("p", "details.product")
.create();
- RawSql.ColumnMapping columnMapping = rawSql.getColumnMapping();
+ SpiRawSql.ColumnMapping columnMapping = rawSql.getColumnMapping();
assertEquals(0, columnMapping.getIndexPosition("id"));
assertEquals(1, columnMapping.getIndexPosition("status"));
assertEquals(2, columnMapping.getIndexPosition("customer.id"));
@@ -194,8 +189,8 @@ public class TestRawSqlBuilder extends BaseTestCase {
RawSqlBuilder builder = RawSqlBuilder.parse(rs);
- RawSql rawSql = builder.create();
- RawSql.ColumnMapping columnMapping = rawSql.getColumnMapping();
+ SpiRawSql rawSql = (SpiRawSql)builder.create();
+ SpiRawSql.ColumnMapping columnMapping = rawSql.getColumnMapping();
assertEquals(0, columnMapping.getIndexPosition("id"));
assertEquals(1, columnMapping.getIndexPosition("status"));
@@ -221,9 +216,9 @@ public class TestRawSqlBuilder extends BaseTestCase {
" from o_order d" +
" group by DATE_TRUNC('DAY', d.order_date)";
- RawSql rawSql = RawSqlBuilder.parse(sql).create();
+ SpiRawSql rawSql = (SpiRawSql)RawSqlBuilder.parse(sql).create();
- RawSql.ColumnMapping columnMapping = rawSql.getColumnMapping();
+ SpiRawSql.ColumnMapping columnMapping = rawSql.getColumnMapping();
assertEquals(0, columnMapping.getIndexPosition("day"));
assertEquals(1, columnMapping.getIndexPosition("total"));
diff --git a/src/test/java/io/ebean/TestRawSqlBuilderDistinct.java b/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilderDistinct.java
similarity index 62%
rename from src/test/java/io/ebean/TestRawSqlBuilderDistinct.java
rename to src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilderDistinct.java
index e11526efb..146b6d5e0 100644
--- a/src/test/java/io/ebean/TestRawSqlBuilderDistinct.java
+++ b/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilderDistinct.java
@@ -1,7 +1,8 @@
-package io.ebean;
+package io.ebeaninternal.server.rawsql;
-import io.ebean.RawSql.Sql;
+import io.ebean.RawSql;
import io.ebean.RawSqlBuilder;
+import io.ebeaninternal.server.rawsql.SpiRawSql.Sql;
import junit.framework.TestCase;
import org.junit.Assert;
@@ -9,8 +10,8 @@ public class TestRawSqlBuilderDistinct extends TestCase {
public void testDistinct() {
- RawSqlBuilder r = RawSqlBuilder.parse("select distinct id, name from t_cust");
- Sql sql = r.getSql();
+ RawSql r = RawSqlBuilder.parse("select distinct id, name from t_cust").create();
+ Sql sql = ((SpiRawSql)r).getSql();
Assert.assertEquals("id, name", sql.getPreFrom());
Assert.assertEquals("from t_cust", sql.getPreWhere());
Assert.assertEquals("", sql.getPreHaving());
diff --git a/src/test/java/io/ebean/TestRawSqlColumnParsing.java b/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlColumnParsing.java
similarity index 96%
rename from src/test/java/io/ebean/TestRawSqlColumnParsing.java
rename to src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlColumnParsing.java
index 7faa92e26..47c74ebd7 100644
--- a/src/test/java/io/ebean/TestRawSqlColumnParsing.java
+++ b/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlColumnParsing.java
@@ -1,7 +1,7 @@
-package io.ebean;
+package io.ebeaninternal.server.rawsql;
-import io.ebean.RawSql.ColumnMapping;
-import io.ebean.RawSql.ColumnMapping.Column;
+import io.ebeaninternal.server.rawsql.SpiRawSql.ColumnMapping;
+import io.ebeaninternal.server.rawsql.SpiRawSql.ColumnMapping.Column;
import junit.framework.TestCase;
import java.util.Map;
diff --git a/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlParsing.java b/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlParsing.java
index 814aa66f9..3d59d2033 100644
--- a/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlParsing.java
+++ b/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlParsing.java
@@ -1,7 +1,7 @@
package io.ebeaninternal.server.rawsql;
import io.ebean.RawSql;
-import io.ebean.RawSql.Sql;
+import io.ebeaninternal.server.rawsql.SpiRawSql.Sql;
import io.ebean.RawSqlBuilder;
import junit.framework.TestCase;
@@ -20,7 +20,7 @@ public class TestRawSqlParsing extends TestCase {
//.columnMapping("sum(order_qty*unit_price)","totalAmount")
.create();
- Sql rs = rawSql.getSql();
+ Sql rs = ((SpiRawSql)rawSql).getSql();
String s = rs.toString();
assertTrue(s, s.contains("[order_id, sum"));
diff --git a/src/test/java/org/tests/rawsql/TestRawSqlWithResultSet.java b/src/test/java/org/tests/rawsql/TestRawSqlWithResultSet.java
index eea1eb313..238d203a2 100644
--- a/src/test/java/org/tests/rawsql/TestRawSqlWithResultSet.java
+++ b/src/test/java/org/tests/rawsql/TestRawSqlWithResultSet.java
@@ -4,6 +4,7 @@ import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.FetchConfig;
import io.ebean.RawSql;
+import io.ebean.RawSqlBuilder;
import io.ebean.Transaction;
import org.tests.model.basic.Customer;
import org.tests.model.basic.ResetBasicData;
@@ -33,7 +34,7 @@ public class TestRawSqlWithResultSet extends BaseTestCase {
// ResultSet will be closed by Ebean
ResultSet resultSet = pstmt.executeQuery();
- RawSql rawSql = new RawSql(resultSet, "id", "name", "billingAddress.id");
+ RawSql rawSql = RawSqlBuilder.resultSet(resultSet, "id", "name", "billingAddress.id");
List list = Ebean.find(Customer.class)
.setRawSql(rawSql)