mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#1150 - Refactor convert RawSql and RawSqlBuilder into interfaces and push the parsers into io.ebeaninternal
This commit is contained in:
@@ -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<T> query = createQuery(beanType);
|
||||
query.setRawSql(rawSql);
|
||||
|
||||
@@ -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<T> implements MetaBeanInfo, BeanType<T> {
|
||||
|
||||
private final ConcurrentHashMap<String, ElComparator<T>> comparatorCache = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, RawSql> namedRawSql;
|
||||
private final Map<String, SpiRawSql> namedRawSql;
|
||||
|
||||
private final Map<String, String> namedQuery;
|
||||
|
||||
@@ -1112,7 +1112,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
/**
|
||||
* 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<T> implements MetaBeanInfo, BeanType<T> {
|
||||
if (idProperty != null && !idProperty.isEmbedded()) {
|
||||
OrderBy<T> orderBy = query.getOrderBy();
|
||||
if (orderBy == null || orderBy.isEmpty()) {
|
||||
RawSql rawSql = query.getRawSql();
|
||||
SpiRawSql rawSql = query.getRawSql();
|
||||
if (rawSql != null) {
|
||||
query.order(rawSql.getSql().getOrderBy());
|
||||
}
|
||||
|
||||
@@ -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<T> {
|
||||
|
||||
private static final Map<String, String> EMPTY_NAMED_QUERY = new HashMap<>();
|
||||
|
||||
private static final Map<String, RawSql> EMPTY_RAW_MAP = new HashMap<>();
|
||||
private static final Map<String, SpiRawSql> EMPTY_RAW_MAP = new HashMap<>();
|
||||
|
||||
private static class PropOrder implements Comparator<DeployBeanProperty> {
|
||||
|
||||
@@ -75,7 +75,7 @@ public class DeployBeanDescriptor<T> {
|
||||
*/
|
||||
private LinkedHashMap<String, DeployBeanProperty> propMap = new LinkedHashMap<>();
|
||||
|
||||
private Map<String, RawSql> namedRawSql;
|
||||
private Map<String, SpiRawSql> namedRawSql;
|
||||
|
||||
private Map<String, String> namedQuery;
|
||||
|
||||
@@ -1100,14 +1100,14 @@ public class DeployBeanDescriptor<T> {
|
||||
/**
|
||||
* Return the named RawSql queries.
|
||||
*/
|
||||
public Map<String, RawSql> getNamedRawSql() {
|
||||
public Map<String, SpiRawSql> 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<>();
|
||||
}
|
||||
|
||||
@@ -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<T> {
|
||||
* Add named RawSql from ebean.xml.
|
||||
*/
|
||||
public void addRawSql(String name, RawSql rawSql) {
|
||||
descriptor.addRawSql(name, rawSql);
|
||||
descriptor.addRawSql(name, (SpiRawSql)rawSql);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<Column> 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<T> implements SpiQuery<T> {
|
||||
|
||||
private ManyWhereJoins manyWhereJoins;
|
||||
|
||||
private RawSql rawSql;
|
||||
private SpiRawSql rawSql;
|
||||
|
||||
private boolean useDocStore;
|
||||
|
||||
@@ -373,13 +349,13 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RawSql getRawSql() {
|
||||
public SpiRawSql getRawSql() {
|
||||
return rawSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setRawSql(RawSql rawSql) {
|
||||
this.rawSql = rawSql;
|
||||
this.rawSql = (SpiRawSql)rawSql;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user