Merge remote-tracking branch 'upstream/master' into subquery-with-fetch-path

This commit is contained in:
Roland Praml committed 2021-08-11 07:42:11 +02:00
commit 12b8b4b955
245 files changed
+3591 -2143

No files matched your search

@@ -4,11 +4,7 @@ import io.ebeaninternal.server.persist.MultiValueWrapper;
import io.ebeaninternal.server.querydefn.NaturalKeyBindParam;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
/**
@@ -54,12 +50,11 @@ public class BindParams implements Serializable {
positionedParameters.clear();
}
public int queryBindHash() {
int hc = namedParameters.hashCode();
for (Param positionedParameter : positionedParameters) {
hc = hc * 92821 + positionedParameter.hashCode();
public void queryBindHash(BindValuesKey key) {
key.add(positionedParameters.size());
for (Param param : positionedParameters) {
param.queryBindHash(key);
}
return hc;
}
/**
@@ -424,7 +419,14 @@ public class BindParams implements Serializable {
@Override
public boolean equals(Object o) {
return o != null && (o == this || (o instanceof Param) && hashCode() == o.hashCode());
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Param param = (Param) o;
return isInParam == param.isInParam && isOutParam == param.isOutParam && type == param.type && Objects.equals(inValue, param.inValue);
}
void queryBindHash(BindValuesKey key) {
key.add(isInParam).add(isOutParam).add(type).add(inValue);
}
/**
@@ -0,0 +1,35 @@
package io.ebeaninternal.api;
import java.util.ArrayList;
import java.util.List;
/**
* BindValues used for L2 query cache key matching.
* <p>
* The equals/hashCode implementation must meet the requirement that the query bind values
* match for L2 query cache hit (given the query plan hash is already a match).
*/
public class BindValuesKey {
private final List<Object> values = new ArrayList<>();
/**
* Add a bind value.
*/
public BindValuesKey add(Object value) {
values.add(value);
return this;
}
@Override
public boolean equals(Object obj) {
return obj instanceof BindValuesKey && ((BindValuesKey) obj).values.equals(values);
}
@Override
public int hashCode() {
return values.hashCode();
}
}
@@ -6,15 +6,14 @@ package io.ebeaninternal.api;
public class HashQuery {
private final CQueryPlanKey planHash;
private final int bindHash;
private final BindValuesKey bindValuesKey;
/**
* Create the HashQuery.
*/
public HashQuery(CQueryPlanKey planHash, int bindHash) {
public HashQuery(CQueryPlanKey planHash, BindValuesKey bindValuesKey) {
this.planHash = planHash;
this.bindHash = bindHash;
this.bindValuesKey = bindValuesKey;
}
@Override
@@ -25,7 +24,7 @@ public class HashQuery {
@Override
public int hashCode() {
int hc = 92821 * planHash.hashCode();
hc = 92821 * hc + bindHash;
hc = 92821 * hc + bindValuesKey.hashCode();
return hc;
}
@@ -37,8 +36,7 @@ public class HashQuery {
if (!(obj instanceof HashQuery)) {
return false;
}
HashQuery e = (HashQuery) obj;
return e.bindHash == bindHash && e.planHash.equals(planHash);
return e.bindValuesKey.equals(bindValuesKey) && e.planHash.equals(planHash);
}
}
@@ -92,9 +92,7 @@ public class LoadBeanRequest extends LoadRequest {
* Return the list of Id values for the beans in the lazy load buffer.
*/
public List<Object> getIdList() {
List<Object> idList = new ArrayList<>();
BeanDescriptor<?> desc = loadBuffer.getBeanDescriptor();
for (EntityBeanIntercept ebi : batch) {
idList.add(desc.getId(ebi.getOwner()));
@@ -106,10 +104,8 @@ public class LoadBeanRequest extends LoadRequest {
* Configure the query for lazy loading execution.
*/
public void configureQuery(SpiQuery<?> query, List<Object> idList) {
query.setMode(SpiQuery.Mode.LAZYLOAD_BEAN);
query.setPersistenceContext(loadBuffer.getPersistenceContext());
String mode = isLazy() ? "+lazy" : "+query";
query.setLoadDescription(mode, getDescription());
@@ -117,9 +113,7 @@ public class LoadBeanRequest extends LoadRequest {
// cascade the batch size (if set) for further lazy loading
query.setLazyLoadBatchSize(getBatchSize());
}
loadBuffer.configureQuery(query, lazyLoadProperty);
if (idList.size() == 1) {
query.where().idEq(idList.get(0));
} else {
@@ -131,19 +125,16 @@ public class LoadBeanRequest extends LoadRequest {
* Load the beans into the L2 cache if that is requested and check for load failures due to deletes.
*/
public void postLoad(List<?> list) {
Set<Object> loadedIds = new HashSet<>();
BeanDescriptor<?> desc = loadBuffer.getBeanDescriptor();
// collect Ids and maybe load bean cache
for (Object aList : list) {
EntityBean loadedBean = (EntityBean) aList;
for (Object bean : list) {
EntityBean loadedBean = (EntityBean) bean;
loadedIds.add(desc.getId(loadedBean));
}
if (isLoadCache()) {
desc.cacheBeanPutAll(list);
}
if (lazyLoadProperty != null) {
for (EntityBeanIntercept ebi : batch) {
// check if the underlying row in DB was deleted. Mark the bean as 'failed' if
@@ -54,9 +54,9 @@ public interface SpiExpression extends Expression {
void queryPlanHash(StringBuilder builder);
/**
* Return the hash value for the values that will be bound.
* Build the key for bind values of the query.
*/
int queryBindHash();
void queryBindKey(BindValuesKey key);
/**
* Return true if the expression is the same with respect to bind values.
@@ -629,13 +629,11 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
CQueryPlanKey prepare(SpiOrmQueryRequest<T> request);
/**
* Calculate a hash based on the bind values used in the query.
* Build the key for the bind values used in the query (for l2 query cache).
* <p>
* Combined with queryPlanHash() to return getQueryHash (a unique hash for a
* query).
* </p>
* Combined with queryPlanHash() to return queryHash (a unique key for a query).
*/
int queryBindHash();
void queryBindKey(BindValuesKey key);
/**
* Identifies queries that are exactly the same including bind variables.
@@ -18,7 +18,7 @@ public interface SpiQueryPlan {
String getName();
/**
* The hash for the query plan.
* The hash of the sql.
*/
String getHash();
@@ -23,25 +23,16 @@ import javax.persistence.PersistenceException;
public abstract class AbstractSqlQueryRequest implements CancelableQuery {
protected final SpiSqlBinding query;
protected final SpiEbeanServer server;
protected SpiTransaction transaction;
private boolean createdTransaction;
protected String sql;
protected ResultSet resultSet;
protected String bindLog = "";
protected PreparedStatement pstmt;
protected long startNano;
private final ReentrantLock lock = new ReentrantLock();
/**
* Create the BeanFindRequest.
*/
@@ -161,7 +152,8 @@ public abstract class AbstractSqlQueryRequest implements CancelableQuery {
this.bindLog = binder.bind(bindParams, pstmt, conn);
}
if (isLogSql()) {
transaction.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ")"));
long micros = (System.nanoTime() - startNano) / 1000L;
transaction.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ") --micros(", micros + ")"));
}
} finally {
lock.unlock();
@@ -255,6 +255,9 @@ public class DefaultContainer implements SpiContainer {
}
throw new RuntimeException("DataSource not set?");
}
if (config.skipDataSourceCheck()) {
return true;
}
try (Connection connection = config.getDataSource().getConnection()) {
if (connection.getAutoCommit()) {
logger.warn("DataSource [{}] has autoCommit defaulting to true!", config.getName());
@@ -18,8 +18,8 @@ class DefaultQueryPlanListener implements QueryPlanListener {
String dbName = capture.getDatabase().getName();
for (MetaQueryPlan plan : capture.getPlans()) {
log.info("queryPlan db:{} label:{} queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}",
dbName, plan.getLabel(), plan.getQueryTimeMicros(), plan.getProfileLocation(),
plan.getSql(), plan.getBind(), plan.getPlan());
dbName, plan.label(), plan.queryTimeMicros(), plan.profileLocation(),
plan.sql(), plan.bind(), plan.plan());
}
}
}
@@ -397,6 +397,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
if (dbSchema != null) {
migrationRunner.setDefaultDbSchema(dbSchema);
}
migrationRunner.setPlatform(config.getDatabasePlatform().getPlatform().base().name().toLowerCase());
migrationRunner.loadProperties(config.getProperties());
migrationRunner.run(config.getDataSource());
}
@@ -415,8 +416,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private void collectQueryPlans() {
QueryPlanRequest request = new QueryPlanRequest();
request.setMaxCount(config.getQueryPlanCaptureMaxCount());
request.setMaxTimeMillis(config.getQueryPlanCaptureMaxTimeMillis());
request.maxCount(config.getQueryPlanCaptureMaxCount());
request.maxTimeMillis(config.getQueryPlanCaptureMaxTimeMillis());
// obtains query explain plans ...
List<MetaQueryPlan> plans = metaInfoManager.queryPlanCollectNow(request);
@@ -1283,14 +1284,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
@Override
public <T> boolean exists(Query<?> ormQuery, Transaction transaction) {
Query<?> ormQueryCopy = ormQuery.copy();
ormQueryCopy.setMaxRows(1);
public <T> boolean exists(Query<T> ormQuery, Transaction transaction) {
Query<T> ormQueryCopy = ormQuery.copy().setMaxRows(1);
SpiOrmQueryRequest<?> request = createQueryRequest(Type.ID_LIST, ormQueryCopy, transaction);
try {
request.initTransIfRequired();
List<Object> ids = request.findIds();
return !ids.isEmpty();
return !request.findIds().isEmpty();
} finally {
request.endTransIfRequired();
}
@@ -2056,17 +2055,15 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return transactionManager;
}
public void register(BeanPersistController c) {
List<BeanDescriptor<?>> list = beanDescriptorManager.getBeanDescriptorList();
for (BeanDescriptor<?> aList : list) {
aList.register(c);
public void register(BeanPersistController controller) {
for (BeanDescriptor<?> desc : beanDescriptorManager.getBeanDescriptorList()) {
desc.register(controller);
}
}
public void deregister(BeanPersistController c) {
List<BeanDescriptor<?>> list = beanDescriptorManager.getBeanDescriptorList();
for (BeanDescriptor<?> aList : list) {
aList.deregister(c);
for (BeanDescriptor<?> desc : beanDescriptorManager.getBeanDescriptorList()) {
desc.deregister(c);
}
}
@@ -2329,13 +2326,13 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public void visitMetrics(MetricVisitor visitor) {
visitor.visitStart();
if (visitor.isCollectTransactionMetrics()) {
if (visitor.collectTransactionMetrics()) {
transactionManager.visitMetrics(visitor);
}
if (visitor.isCollectL2Metrics()) {
if (visitor.collectL2Metrics()) {
serverCacheManager.visitMetrics(visitor);
}
if (visitor.isCollectQueryMetrics()) {
if (visitor.collectQueryMetrics()) {
beanDescriptorManager.visitMetrics(visitor);
dtoBeanManager.visitMetrics(visitor);
relationalQueryEngine.visitMetrics(visitor);
@@ -2352,7 +2349,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
List<MetaQueryPlan> queryPlanInit(QueryPlanInit initRequest) {
if (initRequest.isAll()) {
queryPlanManager.setDefaultThreshold(initRequest.getThresholdMicros());
queryPlanManager.setDefaultThreshold(initRequest.thresholdMicros());
}
return beanDescriptorManager.queryPlanInit(initRequest);
}
@@ -28,11 +28,8 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
private static final String ENC_PREFIX_UPPER = EncryptAlias.PREFIX.toUpperCase();
private final SpiDtoQuery<T> query;
private final DtoQueryEngine queryEngine;
private DtoQueryPlan plan;
private DataReader dataReader;
DtoQueryRequest(SpiEbeanServer server, DtoQueryEngine engine, SpiDtoQuery<T> query) {
@@ -78,11 +78,11 @@ class DumpMetrics {
out("-- Dumping metrics for " + server.getName() + " -- ");
ServerMetrics serverMetrics = server.getMetaInfoManager().collectMetrics();
for (MetaTimedMetric metric : serverMetrics.getTimedMetrics()) {
for (MetaTimedMetric metric : serverMetrics.timedMetrics()) {
log(metric);
}
List<MetaCountMetric> countMetrics = serverMetrics.getCountMetrics();
List<MetaCountMetric> countMetrics = serverMetrics.countMetrics();
if (!countMetrics.isEmpty()) {
out("\n-- Counters --");
countMetrics.sort(SortMetric.COUNT_NAME);
@@ -91,7 +91,7 @@ class DumpMetrics {
}
}
List<MetaQueryMetric> queryMetrics = serverMetrics.getQueryMetrics();
List<MetaQueryMetric> queryMetrics = serverMetrics.queryMetrics();
if (!queryMetrics.isEmpty()) {
out("\n-- Queries --");
queryMetrics.sort(sortBy);
@@ -104,8 +104,8 @@ class DumpMetrics {
private void logCount(MetaCountMetric metric) {
StringBuilder sb = new StringBuilder();
sb.append(padNameTimed(metric.getName())).append(" ");
sb.append(" count:").append(pad(metric.getCount()));
sb.append(padNameTimed(metric.name())).append(" ");
sb.append(" count:").append(pad(metric.count()));
out(sb.toString());
}
@@ -120,38 +120,38 @@ class DumpMetrics {
appendQueryName(metric, sb);
appendCounters(metric, sb);
if (dumpHash) {
sb.append("\n hash:").append(metric.getHash());
sb.append("\n hash:").append(metric.hash());
}
appendProfileAndSql(metric, sb);
out(sb.toString());
}
private void appendQueryName(MetaQueryMetric metric, StringBuilder sb) {
sb.append("query:").append(padName(metric.getName())).append(" ");
sb.append("query:").append(padName(metric.name())).append(" ");
}
private void appendProfileAndSql(MetaQueryMetric metric, StringBuilder sb) {
String location = metric.getLocation();
String location = metric.location();
if (dumpLoc && location != null) {
sb.append("\n loc:").append(location);
}
if (dumpSql) {
sb.append(" \n\n sql:").append(metric.getSql()).append("\n\n");
sb.append(" \n\n sql:").append(metric.sql()).append("\n\n");
}
}
private void log(MetaTimedMetric metric) {
StringBuilder sb = new StringBuilder();
sb.append(padNameTimed(metric.getName())).append(" ");
sb.append(padNameTimed(metric.name())).append(" ");
appendCounters(metric, sb);
out(sb.toString());
}
private void appendCounters(MetaTimedMetric timedMetric, StringBuilder sb) {
sb.append(" count:").append(pad(timedMetric.getCount()))
.append(" total:").append(pad(timedMetric.getTotal()))
.append(" mean:").append(pad(timedMetric.getMean()))
.append(" max:").append(pad(timedMetric.getMax()));
sb.append(" count:").append(pad(timedMetric.count()))
.append(" total:").append(pad(timedMetric.total()))
.append(" mean:").append(pad(timedMetric.mean()))
.append(" max:").append(pad(timedMetric.max()));
}
private String padName(String name) {
@@ -31,9 +31,9 @@ class DumpMetricsData {
private void collect(ServerMetrics serverMetrics) {
final List<MetaTimedMetric> timedMetrics = serverMetrics.getTimedMetrics();
final List<MetaCountMetric> countMetrics = serverMetrics.getCountMetrics();
final List<MetaQueryMetric> queryMetrics = serverMetrics.getQueryMetrics();
final List<MetaTimedMetric> timedMetrics = serverMetrics.timedMetrics();
final List<MetaCountMetric> countMetrics = serverMetrics.countMetrics();
final List<MetaQueryMetric> queryMetrics = serverMetrics.queryMetrics();
for (MetaTimedMetric metric : timedMetrics) {
add(metric);
@@ -47,7 +47,7 @@ class DumpMetricsData {
}
private MetricData create(MetaMetric metric) {
MetricData data = new MetricData(metric.getName());
MetricData data = new MetricData(metric.name());
list.add(data);
return data;
}
@@ -55,30 +55,30 @@ class DumpMetricsData {
private void add(MetaTimedMetric metric) {
final MetricData data = create(metric);
appendCounters(data, metric);
data.setLoc(metric.getLocation());
data.setLoc(metric.location());
}
private void addCount(MetaCountMetric metric) {
final MetricData data = create(metric);
data.setCount(metric.getCount());
data.setCount(metric.count());
}
private void addQuery(MetaQueryMetric metric) {
final MetricData data = create(metric);
appendCounters(data, metric);
appendLocationAndSql(data, metric);
data.setHash(metric.getHash());
data.setHash(metric.hash());
}
private void appendLocationAndSql(MetricData data, MetaQueryMetric metric) {
data.setLoc(metric.getLocation());
data.setSql(metric.getSql());
data.setLoc(metric.location());
data.setSql(metric.sql());
}
private void appendCounters(MetricData data, MetaTimedMetric timedMetric) {
data.setCount(timedMetric.getCount());
data.setTotal(timedMetric.getTotal());
data.setMean(timedMetric.getMean());
data.setMax(timedMetric.getMax());
data.setCount(timedMetric.count());
data.setTotal(timedMetric.total());
data.setMean(timedMetric.mean());
data.setMax(timedMetric.max());
}
}
@@ -84,11 +84,11 @@ class DumpMetricsJson implements ServerMetricsAsJson {
private void collect(ServerMetrics serverMetrics) {
try {
start();
for (MetaTimedMetric metric : serverMetrics.getTimedMetrics()) {
for (MetaTimedMetric metric : serverMetrics.timedMetrics()) {
logTimed(metric);
}
List<MetaCountMetric> countMetrics = serverMetrics.getCountMetrics();
List<MetaCountMetric> countMetrics = serverMetrics.countMetrics();
if (!countMetrics.isEmpty()) {
if (sortBy != null) {
countMetrics.sort(SortMetric.COUNT_NAME);
@@ -98,7 +98,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
}
}
List<MetaQueryMetric> queryMetrics = serverMetrics.getQueryMetrics();
List<MetaQueryMetric> queryMetrics = serverMetrics.queryMetrics();
if (!queryMetrics.isEmpty()) {
if (sortBy != null) {
queryMetrics.sort(sortBy);
@@ -170,7 +170,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
}
objStart();
key("name");
val(metric.getName());
val(metric.name());
}
private void metricEnd() throws IOException {
@@ -180,7 +180,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
private void logCount(MetaCountMetric metric) throws IOException {
metricStart(metric);
key("count");
val(metric.getCount());
val(metric.count());
metricEnd();
}
@@ -188,7 +188,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
metricStart(metric);
appendTiming(metric);
if (isIncludeDetail(metric)) {
appendExtra("loc", metric.getLocation());
append("loc", metric.location());
}
metricEnd();
}
@@ -197,11 +197,11 @@ class DumpMetricsJson implements ServerMetricsAsJson {
metricStart(metric);
appendTiming(metric);
if (withHash) {
appendExtra("hash", metric.getHash());
append("hash", metric.hash());
}
if (isIncludeDetail(metric)) {
appendExtra("loc", metric.getLocation());
appendExtra("sql", metric.getSql());
append("loc", metric.location());
append("sql", metric.sql());
}
metricEnd();
}
@@ -210,7 +210,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
return includeExtraAttributes == 2 || includeExtraAttributes == 1 && metric.initialCollection();
}
private void appendExtra(String key, String val) throws IOException {
private void append(String key, String val) throws IOException {
if (val != null) {
key(key);
val(val);
@@ -218,13 +218,14 @@ class DumpMetricsJson implements ServerMetricsAsJson {
}
private void appendTiming(MetaTimedMetric timedMetric) throws IOException {
key("count");
val(timedMetric.getCount());
key("total");
val(timedMetric.getTotal());
key("mean");
val(timedMetric.getMean());
key("max");
val(timedMetric.getMax());
append("count", timedMetric.count());
append("total", timedMetric.total());
append("mean", timedMetric.mean());
append("max", timedMetric.max());
}
private void append(String key, long value) throws IOException {
key(key);
val(value);
}
}
@@ -18,11 +18,8 @@ import java.util.function.Predicate;
public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
private final RelationalQueryEngine queryEngine;
private String[] propertyNames;
private int estimateCapacity;
private int rows;
/**
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.core.bootup;
import io.avaje.classpath.scanner.ClassFilter;
import io.ebean.annotation.DocStore;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.IdGenerator;
@@ -27,27 +26,23 @@ import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
/**
* Interesting classes for a EbeanServer such as Embeddable, Entity,
* ScalarTypes, Finders, Listeners and Controllers.
*/
public class BootupClasses implements ClassFilter {
public class BootupClasses implements Predicate<Class<?>> {
private static final Logger logger = LoggerFactory.getLogger(BootupClasses.class);
private final List<Class<?>> embeddableList = new ArrayList<>();
private final List<Class<?>> entityList = new ArrayList<>();
private final List<Class<? extends ScalarType<?>>> scalarTypeList = new ArrayList<>();
private final List<Class<? extends ScalarTypeConverter<?, ?>>> scalarConverterList = new ArrayList<>();
private final List<Class<? extends AttributeConverter<?, ?>>> attributeConverterList = new ArrayList<>();
// The following objects are instantiated on first request
@@ -55,19 +50,12 @@ public class BootupClasses implements ClassFilter {
// instance list, that holds the instance. Once a class is instantiated
// (or added) it will get removed from the candidate list
private final List<Class<? extends IdGenerator>> idGeneratorCandidates = new ArrayList<>();
private final List<Class<? extends BeanPersistController>> beanPersistControllerCandidates = new ArrayList<>();
private final List<Class<? extends BeanPostLoad>> beanPostLoadCandidates = new ArrayList<>();
private final List<Class<? extends BeanPostConstructListener>> beanPostConstructListenerCandidates = new ArrayList<>();
private final List<Class<? extends BeanFindController>> beanFindControllerCandidates = new ArrayList<>();
private final List<Class<? extends BeanPersistListener>> beanPersistListenerCandidates = new ArrayList<>();
private final List<Class<? extends BeanQueryAdapter>> beanQueryAdapterCandidates = new ArrayList<>();
private final List<Class<? extends ServerConfigStartup>> serverConfigStartupCandidates = new ArrayList<>();
private final List<IdGenerator> idGeneratorInstances = new ArrayList<>();
@@ -98,7 +86,7 @@ public class BootupClasses implements ClassFilter {
public BootupClasses(List<Class<?>> list) {
if (list != null) {
for (Class<?> cls : list) {
isMatch(cls);
test(cls);
}
}
}
@@ -188,13 +176,11 @@ public class BootupClasses implements ClassFilter {
}
public void addChangeLogInstances(DatabaseConfig config) {
readAuditPrepare = config.getReadAuditPrepare();
readAuditLogger = config.getReadAuditLogger();
changeLogPrepare = config.getChangeLogPrepare();
changeLogListener = config.getChangeLogListener();
changeLogRegister = config.getChangeLogRegister();
// if not already set create the implementations found
// via classpath scanning
if (readAuditPrepare == null && readAuditPrepareClass != null) {
@@ -341,18 +327,14 @@ public class BootupClasses implements ClassFilter {
}
@Override
public boolean isMatch(Class<?> cls) {
public boolean test(Class<?> cls) {
if (isEmbeddable(cls)) {
embeddableList.add(cls);
} else if (isEntity(cls)) {
entityList.add(cls);
} else {
return isInterestingInterface(cls);
}
return true;
}
@@ -364,7 +346,6 @@ public class BootupClasses implements ClassFilter {
*/
@SuppressWarnings("unchecked")
private boolean isInterestingInterface(Class<?> cls) {
if (Modifier.isAbstract(cls.getModifiers())) {
// do not include abstract classes as we can
// not instantiate them
@@ -1559,7 +1559,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
void queryPlanInit(QueryPlanInit request, List<MetaQueryPlan> list) {
for (CQueryPlan queryPlan : queryPlanCache.values()) {
if (request.includeHash(queryPlan.getHash())) {
queryPlan.queryPlanInit(request.getThresholdMicros());
queryPlan.queryPlanInit(request.thresholdMicros());
list.add(queryPlan.createMeta(null, null));
}
}
@@ -1572,7 +1572,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
iudMetrics.visit(visitor);
for (CQueryPlan queryPlan : queryPlanCache.values()) {
if (!queryPlan.isEmptyStats()) {
visitor.visitQuery(queryPlan.getSnapshot(visitor.isReset()));
visitor.visitQuery(queryPlan.getSnapshot(visitor.reset()));
}
}
}
@@ -2022,7 +2022,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
public boolean isTableManaged(String tableName) {
return owner.isTableManaged(tableName);
}
/**
* Return the order column property.
*/
@@ -2519,7 +2519,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
if (propName.indexOf('(') > -1) {
return findSqlTreeFormula(propName, path);
}
return _findBeanProperty(propName);
return findProperty(propName);
}
/**
@@ -3198,9 +3198,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
public void checkMutableProperties(EntityBeanIntercept ebi) {
for (BeanProperty beanProperty : propertiesMutable) {
int propertyIndex = beanProperty.getPropertyIndex();
if (!ebi.isDirtyProperty(propertyIndex) && ebi.isLoadedProperty(propertyIndex)) {
if (ebi.isLoadedProperty(propertyIndex)) {
Object value = beanProperty.getValue(ebi.getOwner());
if (value != null && beanProperty.isDirtyValue(value)) {
if (beanProperty.checkMutable(value, ebi.isDirtyProperty(propertyIndex), ebi)) {
// mutable scalar value which is considered dirty so mark
// it as such so that it is included in an update
ebi.markPropertyAsChanged(propertyIndex);
@@ -42,11 +42,7 @@ class BeanEmbeddedMetaFactory {
int dbScale = dbScale(column, sourceProperties[i]);
String colDefn = getDbColumnDefn(column, sourceProperties[i]);
BeanPropertyOverride overrides = new BeanPropertyOverride(dbColumn, dbNullable, dbLength, dbScale, colDefn);
if (sourceProperties[i] instanceof BeanPropertyAssocOne) {
embeddedProperties[i] = new BeanPropertyAssocOne((BeanPropertyAssocOne)sourceProperties[i], overrides);
} else {
embeddedProperties[i] = new BeanProperty(sourceProperties[i], overrides);
}
embeddedProperties[i] = sourceProperties[i].override(overrides);
}
return new BeanEmbeddedMeta(embeddedProperties);
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonToken;
import io.ebean.ValuePair;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.MutableValueInfo;
import io.ebean.bean.PersistenceContext;
import io.ebean.config.EncryptKey;
import io.ebean.config.dbplatform.DbEncryptFunction;
@@ -13,6 +14,7 @@ import io.ebean.core.type.DocPropertyType;
import io.ebean.core.type.ScalarType;
import io.ebean.plugin.Property;
import io.ebean.text.StringParser;
import io.ebean.text.TextException;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.api.SpiQuery;
@@ -31,11 +33,7 @@ import io.ebeaninternal.server.properties.BeanPropertySetter;
import io.ebeaninternal.server.query.STreeProperty;
import io.ebeaninternal.server.query.SqlBeanLoad;
import io.ebeaninternal.server.query.SqlJoinType;
import io.ebeaninternal.server.type.DataBind;
import io.ebeaninternal.server.type.LocalEncryptedType;
import io.ebeaninternal.server.type.ScalarTypeBoolean;
import io.ebeaninternal.server.type.ScalarTypeEnum;
import io.ebeaninternal.server.type.ScalarTypeLogicalType;
import io.ebeaninternal.server.type.*;
import io.ebeaninternal.util.ValueUtil;
import io.ebeanservice.docstore.api.mapping.DocMappingBuilder;
import io.ebeanservice.docstore.api.mapping.DocPropertyMapping;
@@ -52,6 +50,7 @@ import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -366,13 +365,14 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
return InternString.intern(s);
}
public BeanProperty override(BeanPropertyOverride override) {
return new BeanProperty(this, override);
}
/**
* Create a Matching BeanProperty with some attributes overridden.
* <p>
* Primarily for supporting Embedded beans with overridden dbColumn
* mappings.
* Create a Matching BeanProperty with some attributes overridden for Embedded beans.
*/
public BeanProperty(BeanProperty source, BeanPropertyOverride override) {
protected BeanProperty(BeanProperty source, BeanPropertyOverride override) {
this.descriptor = source.descriptor;
this.propertyIndex = source.propertyIndex;
this.name = source.getName();
@@ -642,15 +642,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
}
public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException {
try {
Object value = scalarType.read(ctx.getDataReader());
if (bean != null) {
setValue(bean, value);
}
return value;
} catch (Exception e) {
throw new PersistenceException("Error readSet on " + descriptor + "." + name, e);
}
return readSet(ctx.getDataReader(), bean);
}
@SuppressWarnings("unchecked")
@@ -828,6 +820,13 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
return scalarType.parse(value);
}
/**
* creates a mutableHash for the given JSON value.
*/
public MutableValueInfo createMutableInfo(String json) {
throw new UnsupportedOperationException();
}
/**
* Read the value for this property from L2 cache entry and set it to the bean.
* <p>
@@ -909,7 +908,8 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
/**
* Return the name of the property.
*/
@Override @Nonnull
@Override
@Nonnull
public String getName() {
return name;
}
@@ -1018,8 +1018,8 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
* Return true if the mutable value is considered dirty.
* This is only used for 'mutable' scalar types like hstore etc.
*/
boolean isDirtyValue(Object value) {
return scalarType.isDirty(value);
boolean checkMutable(Object value, boolean alreadyDirty, EntityBeanIntercept ebi) {
return alreadyDirty || value != null && scalarType.isDirty(value);
}
/**
@@ -1281,7 +1281,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
@Override
public Object localEncrypt(Object value) {
return ((LocalEncryptedType)scalarType).localEncrypt(value);
return ((LocalEncryptedType) scalarType).localEncrypt(value);
}
/**
@@ -1394,7 +1394,8 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
/**
* Return the property type.
*/
@Override @Nonnull
@Override
@Nonnull
public Class<?> getPropertyType() {
return propertyType;
}
@@ -94,10 +94,12 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
}
}
/**
* Copy constructor for ManyToOne inside Embeddable.
*/
public BeanPropertyAssocOne(BeanPropertyAssocOne source, BeanPropertyOverride override) {
@Override
public BeanPropertyAssocOne<?> override(BeanPropertyOverride override) {
return new BeanPropertyAssocOne<>(this, override);
}
protected BeanPropertyAssocOne(BeanPropertyAssocOne<T> source, BeanPropertyOverride override) {
super(source, override);
primaryKeyExport = source.primaryKeyExport;
primaryKeyJoin = source.primaryKeyJoin;
@@ -255,12 +257,8 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
}
private SqlUpdate deleteByParentIdList(List<Object> parentIds) {
StringBuilder sb = new StringBuilder(100);
sb.append(deleteByParentIdInSql);
sb.append(targetIdBinder.getIdInValueExpr(false, parentIds.size()));
DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString());
String sql = deleteByParentIdInSql + targetIdBinder.getIdInValueExpr(false, parentIds.size());
DefaultSqlUpdate delete = new DefaultSqlUpdate(sql);
bindParentIds(delete, parentIds);
return delete;
}
@@ -501,7 +499,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
return targetDescriptor.getIdProperty();
}
ScalarType getIdScalarType() {
ScalarType<?> getIdScalarType() {
return targetDescriptor.getIdProperty().scalarType;
}
@@ -0,0 +1,58 @@
package io.ebeaninternal.server.deploy;
import io.ebean.bean.EntityBean;
import io.ebean.core.type.DataReader;
import io.ebean.text.TextException;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import javax.persistence.PersistenceException;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Map;
/**
* A DbJson property that does not use Jackson ObjectMapper.
*/
public class BeanPropertyJsonBasic extends BeanProperty {
public BeanPropertyJsonBasic(BeanDescriptor<?> descriptor, DeployBeanProperty deploy) {
super(descriptor, deploy);
}
protected BeanPropertyJsonBasic(BeanProperty source, BeanPropertyOverride override) {
super(source, override);
}
@Override
public BeanProperty override(BeanPropertyOverride override) {
return new BeanPropertyJsonBasic(this, override);
}
protected Object checkForEmpty(EntityBean bean) {
final Object value = getValue(bean);
if (value instanceof Collection && ((Collection<?>) value).isEmpty()
|| value instanceof Map && ((Map<?, ?>) value).isEmpty()) {
return value;
}
return null;
}
@Override
public Object readSet(DataReader reader, EntityBean bean) throws SQLException {
try {
Object value = scalarType.read(reader);
if (value == null) {
value = checkForEmpty(bean);
}
if (bean != null) {
setValue(bean, value);
}
return value;
} catch (TextException e) {
throw e;
} catch (Exception e) {
throw new PersistenceException("Error readSet on " + descriptor + "." + name, e);
}
}
}
@@ -0,0 +1,208 @@
package io.ebeaninternal.server.deploy;
import io.ebean.annotation.MutationDetection;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.MutableValueInfo;
import io.ebean.bean.MutableValueNext;
import io.ebean.core.type.DataReader;
import io.ebean.core.type.ScalarType;
import io.ebean.text.TextException;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import io.ebeaninternal.server.util.Checksum;
import javax.persistence.PersistenceException;
import java.sql.SQLException;
import java.util.Objects;
/**
* Handle json property with MutationDetection of SOURCE or HASH only.
*/
public class BeanPropertyJsonMapper extends BeanPropertyJsonBasic {
private final boolean sourceDetection;
public BeanPropertyJsonMapper(BeanDescriptor<?> desc, DeployBeanProperty deployProp) {
super(desc, deployProp);
this.sourceDetection = deployProp.getMutationDetection() == MutationDetection.SOURCE;
}
private BeanPropertyJsonMapper(BeanPropertyJsonMapper source, BeanPropertyOverride override) {
super(source, override);
this.sourceDetection = source.sourceDetection;
}
@Override
public BeanProperty override(BeanPropertyOverride override) {
return new BeanPropertyJsonMapper(this, override);
}
@Override
public MutableValueInfo createMutableInfo(String json) {
if (sourceDetection) {
return new SourceMutableValue(scalarType, json);
} else {
return new ChecksumMutableValue(scalarType, json);
}
}
/**
* Next when no prior MutableValueInfo.
*/
private MutableValueNext next(String json) {
if (sourceDetection) {
return new SourceMutableValue(scalarType, json);
} else {
return new NextPair(json, new ChecksumMutableValue(scalarType, json));
}
}
/**
* Return true if the json property is considered dirty.
*/
@Override
boolean checkMutable(Object value, boolean alreadyDirty, EntityBeanIntercept ebi) {
// mutation detection based on json content or checksum of json content
// only perform serialisation to json once
final String json = scalarType.format(value);
final MutableValueInfo oldHash = ebi.mutableInfo(propertyIndex);
if (oldHash == null) {
if (value == null) {
return false; // no change, still null
}
ebi.mutableNext(propertyIndex, next(json));
return true;
}
// only perform compute of checksum/hash once (if checksum based)
final MutableValueNext next = oldHash.nextDirty(json);
if (next != null) {
ebi.mutableNext(propertyIndex, next);
return true;
}
return false;
}
@Override
public Object readSet(DataReader reader, EntityBean bean) throws SQLException {
try {
Object value = scalarType.read(reader);
if (value == null) {
value = checkForEmpty(bean);
}
if (bean != null) {
setValue(bean, value);
String json = reader.popJson();
if (json != null) {
final MutableValueInfo hash = createMutableInfo(json);
bean._ebean_getIntercept().mutableInfo(propertyIndex, hash);
}
}
return value;
} catch (TextException e) {
throw e;
} catch (Exception e) {
throw new PersistenceException("Error readSet on " + descriptor + "." + name, e);
}
}
private static final class NextPair implements MutableValueNext {
private final String json;
private final MutableValueInfo next;
NextPair(String json, MutableValueInfo next) {
this.json = json;
this.next = next;
}
@Override
public String content() {
return json;
}
@Override
public MutableValueInfo info() {
return next;
}
}
/**
* Hold checksum of json source content to use for dirty detection.
* <p>
* Does not support rebuilding 'oldValue' as no original json content.
*/
private static final class ChecksumMutableValue implements MutableValueInfo {
private final ScalarType<?> parent;
private final long checksum;
ChecksumMutableValue(ScalarType<?> parent, String json) {
this.parent = parent;
this.checksum = Checksum.checksum(json);
}
/**
* Create with pre-computed checksum.
*/
ChecksumMutableValue(ScalarType<?> parent, long checksum) {
this.parent = parent;
this.checksum = checksum;
}
@Override
public MutableValueNext nextDirty(String json) {
final long nextChecksum = Checksum.checksum(json);
return nextChecksum == checksum ? null : new NextPair(json, new ChecksumMutableValue(parent, nextChecksum));
}
@Override
public boolean isEqualToObject(Object obj) {
return Checksum.checksum(parent.format(obj)) == checksum;
}
@Override
public Object get() {
return null; // cannot create object from json
}
}
/**
* Hold json source content. This supports rebuilding the 'oldValue'.
*/
private static final class SourceMutableValue implements MutableValueInfo, MutableValueNext {
private final String originalJson;
private final ScalarType<?> parent;
SourceMutableValue(ScalarType<?> parent, String json) {
this.parent = parent;
this.originalJson = json;
}
@Override
public MutableValueNext nextDirty(String json) {
return Objects.equals(originalJson, json) ? null : new SourceMutableValue(parent, json);
}
@Override
public boolean isEqualToObject(Object obj) {
return Objects.equals(originalJson, parent.format(obj));
}
@Override
public Object get() {
// rebuild the 'oldValue' for change log etc
return parent.parse(originalJson);
}
@Override
public String content() {
return originalJson;
}
@Override
public MutableValueInfo info() {
return this;
}
}
}
@@ -28,11 +28,6 @@ class ElementEntityBean implements EntityBean {
return properties[pos];
}
@Override
public String _ebean_getMarker() {
return null;
}
@Override
public Object _ebean_newInstance() {
return new ElementEntityBean(properties);
@@ -702,13 +702,11 @@ public class DeployBeanDescriptor<T> {
}
public void sortProperties() {
ArrayList<DeployBeanProperty> list = new ArrayList<>(propMap.values());
list.sort(PROP_ORDER);
propMap = new LinkedHashMap<>(list.size());
for (DeployBeanProperty aList : list) {
addBeanProperty(aList);
for (DeployBeanProperty property : list) {
addBeanProperty(property);
}
}
@@ -1,18 +1,6 @@
package io.ebeaninternal.server.deploy.meta;
import io.ebean.annotation.CreatedTimestamp;
import io.ebean.annotation.DocCode;
import io.ebean.annotation.DocProperty;
import io.ebean.annotation.DocSortable;
import io.ebean.annotation.Formula;
import io.ebean.annotation.Platform;
import io.ebean.annotation.SoftDelete;
import io.ebean.annotation.UpdatedTimestamp;
import io.ebean.annotation.WhenCreated;
import io.ebean.annotation.WhenModified;
import io.ebean.annotation.Where;
import io.ebean.annotation.WhoCreated;
import io.ebean.annotation.WhoModified;
import io.ebean.annotation.*;
import io.ebean.config.ScalarTypeConverter;
import io.ebean.config.dbplatform.DbDefaultValue;
import io.ebean.config.dbplatform.DbEncrypt;
@@ -39,7 +27,6 @@ import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -109,6 +96,7 @@ public class DeployBeanProperty {
private boolean jsonSerialize = true;
private boolean jsonDeserialize = true;
private MutationDetection mutationDetection;
private boolean dbEncrypted;
private DbEncryptFunction dbEncryptFunction;
@@ -327,6 +315,14 @@ public class DeployBeanProperty {
this.jsonDeserialize = jsonDeserialize;
}
public MutationDetection getMutationDetection() {
return mutationDetection;
}
public void setMutationDetection(MutationDetection dirtyDetection) {
this.mutationDetection = dirtyDetection;
}
/**
* Return the sortOrder for the properties.
*/
@@ -1201,4 +1197,11 @@ public class DeployBeanProperty {
return false;
}
boolean isJsonMapper() {
return scalarType != null && scalarType.isJsonMapper();
}
boolean isJsonType() {
return mutationDetection != null;
}
}
@@ -1,15 +1,7 @@
package io.ebeaninternal.server.deploy.meta;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanDescriptorMap;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.BeanPropertyIdClass;
import io.ebeaninternal.server.deploy.BeanPropertyOrderColumn;
import io.ebeaninternal.server.deploy.BeanPropertySimpleCollection;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.*;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import io.ebeaninternal.server.properties.BeanPropertySetter;
import io.ebeaninternal.server.type.ScalarTypeString;
@@ -30,47 +22,28 @@ public class DeployBeanPropertyLists {
private static final NoopSetter NOOP_SETTER = new NoopSetter();
private BeanProperty versionProperty;
private BeanProperty unmappedJson;
private BeanProperty draft;
private BeanProperty draftDirty;
private BeanProperty tenant;
private final BeanDescriptor<?> desc;
private final LinkedHashMap<String, BeanProperty> propertyMap;
private BeanProperty id;
private final List<BeanProperty> local = new ArrayList<>();
private final List<BeanProperty> mutable = new ArrayList<>();
private final List<BeanPropertyAssocMany<?>> manys = new ArrayList<>();
private final List<BeanProperty> nonManys = new ArrayList<>();
private final List<BeanProperty> aggs = new ArrayList<>();
private final List<BeanPropertyAssocOne<?>> ones = new ArrayList<>();
private final List<BeanPropertyAssocOne<?>> onesImported = new ArrayList<>();
private final List<BeanPropertyAssocOne<?>> embedded = new ArrayList<>();
private final List<BeanProperty> baseScalar = new ArrayList<>();
private final List<BeanProperty> transients = new ArrayList<>();
private final List<BeanProperty> nonTransients = new ArrayList<>();
private final BeanPropertyAssocOne<?> unidirectional;
private final BeanProperty orderColumn;
@SuppressWarnings({"unchecked"})
public DeployBeanPropertyLists(BeanDescriptorMap owner, BeanDescriptor<?> desc, DeployBeanDescriptor<?> deploy) {
this.desc = desc;
@@ -86,7 +59,7 @@ public class DeployBeanPropertyLists {
this.orderColumn = deployOrderColumn != null ? new BeanPropertyOrderColumn(desc, deployOrderColumn) : null;
DeployBeanPropertyAssocOne<?> deployUnidirectional = deploy.getUnidirectional();
this.unidirectional = deployUnidirectional == null ? null : new BeanPropertyAssocOne(owner, desc, deployUnidirectional);
this.unidirectional = deployUnidirectional == null ? null : new BeanPropertyAssocOne<>(owner, desc, deployUnidirectional);
this.propertyMap = new LinkedHashMap<>();
@@ -127,7 +100,7 @@ public class DeployBeanPropertyLists {
}
if (orderColumn != null) {
orderColumn.setDeployOrder(order++);
orderColumn.setDeployOrder(order);
allocateToList(orderColumn);
propertyMap.put(orderColumn.getName(), orderColumn);
}
@@ -154,7 +127,6 @@ public class DeployBeanPropertyLists {
}
private void setImportedPrimaryKeysFor(DeployBeanDescriptor<?> deploy, DeployBeanPropertyAssocOne<?> id) {
for (DeployBeanProperty prop : id.getTargetDeploy().properties()) {
DeployBeanProperty match = findImported(deploy, prop);
if (match != null) {
@@ -164,7 +136,6 @@ public class DeployBeanPropertyLists {
}
private DeployBeanProperty findImported(DeployBeanDescriptor<?> deploy, DeployBeanProperty embeddedScalar) {
// the logical name and db column we are looking for a match on
String name = embeddedScalar.getName();
String dbColumn = embeddedScalar.getDbColumn();
@@ -180,7 +151,6 @@ public class DeployBeanPropertyLists {
return assocOne;
}
}
return null;
}
@@ -368,7 +338,6 @@ public class DeployBeanPropertyLists {
}
public BeanProperty getSoftDeleteProperty() {
for (BeanProperty prop : nonManys) {
if (prop.isSoftDelete()) {
return prop;
@@ -385,7 +354,6 @@ public class DeployBeanPropertyLists {
* Return the properties set via generated values on insert.
*/
public BeanProperty[] getGeneratedInsert() {
List<BeanProperty> list = new ArrayList<>();
for (BeanProperty prop : nonTransients) {
GeneratedProperty gen = prop.getGeneratedProperty();
@@ -400,7 +368,6 @@ public class DeployBeanPropertyLists {
* Return the properties set via generated values on update.
*/
public BeanProperty[] getGeneratedUpdate() {
List<BeanProperty> list = new ArrayList<>();
for (BeanProperty prop : nonTransients) {
GeneratedProperty gen = prop.getGeneratedProperty();
@@ -438,8 +405,7 @@ public class DeployBeanPropertyLists {
}
}
}
return (BeanPropertyAssocOne[]) list.toArray(new BeanPropertyAssocOne[0]);
return list.toArray(new BeanPropertyAssocOne<?>[0]);
}
private BeanPropertyAssocMany<?>[] getMany2Many() {
@@ -449,8 +415,7 @@ public class DeployBeanPropertyLists {
list.add(prop);
}
}
return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[0]);
return list.toArray(new BeanPropertyAssocMany<?>[0]);
}
private BeanPropertyAssocMany<?>[] getMany(Mode mode) {
@@ -471,25 +436,26 @@ public class DeployBeanPropertyLists {
break;
}
}
return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[0]);
return list.toArray(new BeanPropertyAssocMany<?>[0]);
}
@SuppressWarnings({"unchecked", "rawtypes"})
private BeanProperty createBeanProperty(BeanDescriptorMap owner, DeployBeanProperty deployProp) {
if (deployProp instanceof DeployBeanPropertyAssocOne) {
return new BeanPropertyAssocOne(owner, desc, (DeployBeanPropertyAssocOne) deployProp);
}
if (deployProp instanceof DeployBeanPropertySimpleCollection<?>) {
return new BeanPropertySimpleCollection(desc, (DeployBeanPropertySimpleCollection) deployProp);
}
if (deployProp instanceof DeployBeanPropertyAssocMany) {
return new BeanPropertyAssocMany(desc, (DeployBeanPropertyAssocMany) deployProp);
}
if (deployProp.isJsonMapper()) {
return new BeanPropertyJsonMapper(desc, deployProp);
}
if (deployProp.isJsonType()) {
return new BeanPropertyJsonBasic(desc, deployProp);
}
return new BeanProperty(desc, deployProp);
}
@@ -1,10 +1,6 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.annotation.DbArray;
import io.ebean.annotation.DbJson;
import io.ebean.annotation.DbJsonB;
import io.ebean.annotation.DbJsonType;
import io.ebean.annotation.DbMap;
import io.ebean.annotation.*;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.EncryptDeploy;
import io.ebean.config.EncryptDeployManager;
@@ -209,26 +205,22 @@ public class DeployUtil {
}
}
/**
* This property is marked as a Lob object.
*/
void setDbJsonType(DeployBeanProperty prop, DbJson dbJsonType) {
int dbType = getDbJsonStorage(dbJsonType.storage());
setDbJsonType(prop, dbType, dbJsonType.length());
setDbJsonType(prop, dbType, dbJsonType.length(), dbJsonType.mutationDetection());
}
void setDbJsonBType(DeployBeanProperty prop, DbJsonB dbJsonB) {
setDbJsonType(prop, DbPlatformType.JSONB, dbJsonB.length());
setDbJsonType(prop, DbPlatformType.JSONB, dbJsonB.length(), dbJsonB.mutationDetection());
}
private void setDbJsonType(DeployBeanProperty prop, int dbType, int dbLength) {
private void setDbJsonType(DeployBeanProperty prop, int dbType, int dbLength, MutationDetection mutationDetection) {
prop.setDbType(dbType);
prop.setMutationDetection(mutationDetection);
ScalarType<?> scalarType = typeManager.getJsonScalarType(prop, dbType, dbLength);
if (scalarType == null) {
throw new RuntimeException("No ScalarType for JSON property [" + prop + "] [" + dbType + "]");
}
prop.setDbType(dbType);
prop.setScalarType(scalarType);
if (dbType == Types.VARCHAR || dbLength > 0) {
// determine the db column size
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -37,9 +38,9 @@ public abstract class AbstractTextExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
return 0;
}
public void queryBindKey(BindValuesKey key) {
// do nothing, only execute against document store
};
@Override
public boolean isSameByBind(SpiExpression other) {
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -122,14 +123,11 @@ class AllEqualsExpression extends NonPrepareExpression {
}
@Override
public int queryBindHash() {
int hc = 92821;
public void queryBindKey(BindValuesKey key) {
key.add(propMap.size());
for (Object value : propMap.values()) {
hc = hc * 92821 + (value == null ? 0 : value.hashCode());
key.add(value);
}
return hc;
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -49,12 +50,11 @@ public class ArrayContainsExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
int hc = values[0].hashCode();
for (int i = 1; i < values.length; i++) {
hc = hc * 92821 + values[i].hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(values.length);
for (Object value : values) {
key.add(value);
}
return hc;
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -33,8 +34,8 @@ public class ArrayIsEmptyExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
return empty ? 0 : 92821;
public void queryBindKey(BindValuesKey key) {
key.add(empty);
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -49,10 +50,8 @@ class BetweenExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
int hc = low().hashCode();
hc = hc * 92821 + high().hashCode();
return hc;
public void queryBindKey(BindValuesKey key) {
key.add(low()).add(high());
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -95,8 +96,8 @@ class BetweenPropertyExpression extends NonPrepareExpression {
}
@Override
public int queryBindHash() {
return val().hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(val());
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -39,8 +40,8 @@ class BitwiseExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
return Long.hashCode(flags);
public void queryBindKey(BindValuesKey key) {
key.add(flags).add(match);
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
@@ -69,8 +70,8 @@ class CaseInsensitiveEqualExpression extends AbstractValueExpression {
}
@Override
public int queryBindHash() {
return val().hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(val());
}
@Override
@@ -5,6 +5,7 @@ import io.ebean.LikeType;
import io.ebean.bean.EntityBean;
import io.ebean.event.BeanQueryRequest;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -136,10 +137,8 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins) {
list = buildExpressions(desc);
if (list != null) {
for (SpiExpression aList : list) {
aList.containsMany(desc, whereManyJoins);
}
for (SpiExpression expr : list) {
expr.containsMany(desc, whereManyJoins);
}
}
@@ -186,8 +185,8 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
@Override
public void validate(SpiExpressionValidation validation) {
for (SpiExpression aList : list) {
aList.validate(validation);
for (SpiExpression expr : list) {
expr.validate(validation);
}
}
@@ -228,25 +227,20 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
*/
@Override
public void queryPlanHash(StringBuilder builder) {
builder.append("Example[");
for (SpiExpression aList : list) {
aList.queryPlanHash(builder);
for (SpiExpression expr : list) {
expr.queryPlanHash(builder);
builder.append(",");
}
builder.append("]");
}
/**
* Return a hash for the actual bind values used.
*/
@Override
public int queryBindHash() {
int hc = DefaultExampleExpression.class.getName().hashCode();
for (SpiExpression aList : list) {
hc = hc * 92821 + aList.queryBindHash();
public void queryBindKey(BindValuesKey key) {
key.add(list.size());
for (SpiExpression expr : list) {
expr.queryBindKey(key);
}
return hc;
}
@Override
@@ -267,7 +261,6 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
* Build the List of expressions.
*/
private ArrayList<SpiExpression> buildExpressions(BeanDescriptor<?> beanDescriptor) {
ArrayList<SpiExpression> list = new ArrayList<>();
addExpressions(list, beanDescriptor, entity, null);
return list;
@@ -26,6 +26,7 @@ import io.ebean.search.MultiMatch;
import io.ebean.search.TextCommonTerms;
import io.ebean.search.TextQueryString;
import io.ebean.search.TextSimple;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -109,10 +110,8 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
* @return A single SpiExpression that has the nestedPath set
*/
SpiExpression wrap(List<SpiExpression> list, String nestedPath, Junction.Type type) {
DefaultExpressionList<T> wrapper = new DefaultExpressionList<>(query, expr, null, list, false);
wrapper.setAllDocNested(nestedPath);
if (type != null) {
return new JunctionExpression<>(type, wrapper);
} else {
@@ -121,15 +120,15 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
}
void simplifyEntries() {
for (SpiExpression element : list) {
element.simplify();
for (SpiExpression expr : list) {
expr.simplify();
}
}
@Override
public void prefixProperty(String path) {
for (SpiExpression exp : list) {
exp.prefixProperty(path);
for (SpiExpression expr : list) {
expr.prefixProperty(path);
}
}
@@ -174,7 +173,6 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
context.startNested(allDocNestedPath);
}
int size = list.size();
SpiExpression first = list.get(0);
boolean explicitBool = first instanceof SpiJunction<?>;
boolean implicitBool = !explicitBool && size > 1;
@@ -210,7 +208,6 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
@Override
public void writeDocQuery(DocQueryContext context, SpiExpression idEquals) throws IOException {
if (allDocNestedPath != null) {
context.startNested(allDocNestedPath);
}
@@ -227,8 +224,8 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
if (idEquals != null) {
idEquals.writeDocQuery(context);
}
for (SpiExpression aList : list) {
aList.writeDocQuery(context);
for (SpiExpression expr : list) {
expr.writeDocQuery(context);
}
context.endBool();
}
@@ -278,16 +275,15 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
*/
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins) {
for (SpiExpression aList : list) {
aList.containsMany(desc, whereManyJoins);
for (SpiExpression expr : list) {
expr.containsMany(desc, whereManyJoins);
}
}
@Override
public void validate(SpiExpressionValidation validation) {
for (SpiExpression aList : list) {
aList.validate(validation);
for (SpiExpression expr : list) {
expr.validate(validation);
}
}
@@ -630,7 +626,6 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
@Override
public void addSql(SpiExpressionRequest request) {
for (int i = 0, size = list.size(); i < size; i++) {
SpiExpression expression = list.get(i);
if (i > 0) {
@@ -642,15 +637,15 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
@Override
public void addBindValues(SpiExpressionRequest request) {
for (SpiExpression aList : list) {
aList.addBindValues(request);
for (SpiExpression expr : list) {
expr.addBindValues(request);
}
}
@Override
public void prepareExpression(BeanQueryRequest<?> request) {
for (SpiExpression aList : list) {
aList.prepareExpression(request);
for (SpiExpression expr : list) {
expr.prepareExpression(request);
}
}
@@ -667,23 +662,19 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
if (allDocNestedPath != null) {
builder.append("path:").append(allDocNestedPath).append(" ");
}
for (SpiExpression aList : list) {
aList.queryPlanHash(builder);
for (SpiExpression expr : list) {
expr.queryPlanHash(builder);
builder.append(",");
}
builder.append("]");
}
/**
* Calculate a hash based on the expressions.
*/
@Override
public int queryBindHash() {
int hash = DefaultExpressionList.class.getName().hashCode();
for (SpiExpression aList : list) {
hash = hash * 92821 + aList.queryBindHash();
public void queryBindKey(BindValuesKey key) {
key.add(list.size());
for (SpiExpression expr : list) {
expr.queryBindKey(key);
}
return hash;
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiEbeanServer;
@@ -91,8 +92,8 @@ class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpress
}
@Override
public int queryBindHash() {
return subQuery.queryBindHash();
public void queryBindKey(BindValuesKey key) {
subQuery.queryBindKey(key);
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -77,8 +78,8 @@ class IdExpression extends NonPrepareExpression implements SpiExpression {
}
@Override
public int queryBindHash() {
return value.hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(value);
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -133,8 +134,11 @@ public class IdInExpression extends NonPrepareExpression {
}
@Override
public int queryBindHash() {
return idCollection.hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(idCollection.size());
for (Object elem : idCollection) {
key.add(elem);
}
}
@Override
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
import io.ebean.bean.EntityBean;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -176,12 +177,11 @@ class InExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
int hc = 92821;
public void queryBindKey(BindValuesKey key) {
key.add(bindValues.size());
for (Object bindValue : bindValues) {
hc = 92821 * hc + bindValue.hashCode();
key.add(bindValue);
}
return hc;
}
@Override
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.expression;
import io.ebean.Pairs;
import io.ebean.Pairs.Entry;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -124,12 +125,11 @@ class InPairsExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
int hc = 92821;
public void queryBindKey(BindValuesKey key) {
key.add(entries.size());
for (Pairs.Entry entry : entries) {
hc = 92821 * hc + entry.hashCode();
key.add(entry.getA()).add(entry.getB());
}
return hc;
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -72,8 +73,8 @@ class InQueryExpression extends AbstractExpression implements UnsupportedDocStor
}
@Override
public int queryBindHash() {
return subQuery.queryBindHash();
public void queryBindKey(BindValuesKey key) {
subQuery.queryBindKey(key);
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -47,10 +48,8 @@ class InRangeExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
int hc = low().hashCode();
hc = hc * 92821 + high().hashCode();
return hc;
public void queryBindKey(BindValuesKey key) {
key.add(low()).add(high());
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -103,8 +104,8 @@ class IsEmptyExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
return 1;
public void queryBindKey(BindValuesKey key) {
// no bind values
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -83,10 +84,8 @@ class JsonPathExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
int hc = (value == null) ? 0 : value.hashCode();
hc = (upperValue == null) ? hc : hc * 92821 + upperValue.hashCode();
return hc;
public void queryBindKey(BindValuesKey key) {
key.add(value).add(upperValue);
}
@Override
@@ -25,6 +25,7 @@ import io.ebean.search.MultiMatch;
import io.ebean.search.TextCommonTerms;
import io.ebean.search.TextQueryString;
import io.ebean.search.TextSimple;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -113,9 +114,8 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
context.startBool(type);
List<SpiExpression> list = exprList.internalList();
for (SpiExpression aList : list) {
aList.writeDocQuery(context);
for (SpiExpression expr : exprList.internalList()) {
expr.writeDocQuery(context);
}
context.endBool();
}
@@ -123,9 +123,8 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public void writeDocQueryJunction(DocQueryContext context) throws IOException {
context.startBoolGroupList(type);
List<SpiExpression> list = exprList.internalList();
for (SpiExpression aList : list) {
aList.writeDocQuery(context);
for (SpiExpression expr : exprList.internalList()) {
expr.writeDocQuery(context);
}
context.endBoolGroupList();
}
@@ -138,18 +137,15 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
List<SpiExpression> list = exprList.internalList();
// get the current state for 'require outer joins'
boolean parentOuterJoins = manyWhereJoin.isRequireOuterJoins();
if (type == Type.OR) {
// turn on outer joins required for disjunction expressions
manyWhereJoin.setRequireOuterJoins(true);
}
for (SpiExpression aList : list) {
aList.containsMany(desc, manyWhereJoin);
for (SpiExpression expr : list) {
expr.containsMany(desc, manyWhereJoin);
}
if (type == Type.OR && !parentOuterJoins) {
// restore state to not forcing outer joins
@@ -176,18 +172,14 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public void addBindValues(SpiExpressionRequest request) {
List<SpiExpression> list = exprList.internalList();
for (SpiExpression aList : list) {
aList.addBindValues(request);
for (SpiExpression expr : exprList.internalList()) {
expr.addBindValues(request);
}
}
@Override
public void addSql(SpiExpressionRequest request) {
List<SpiExpression> list = exprList.internalList();
if (!list.isEmpty()) {
request.append(type.prefix());
request.append("(");
@@ -204,9 +196,8 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public void prepareExpression(BeanQueryRequest<?> request) {
List<SpiExpression> list = exprList.internalList();
for (SpiExpression aList : list) {
aList.prepareExpression(request);
for (SpiExpression expr : exprList.internalList()) {
expr.prepareExpression(request);
}
}
@@ -216,22 +207,18 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public void queryPlanHash(StringBuilder builder) {
builder.append(type).append("[");
List<SpiExpression> list = exprList.internalList();
for (SpiExpression aList : list) {
aList.queryPlanHash(builder);
for (SpiExpression expr : exprList.internalList()) {
expr.queryPlanHash(builder);
builder.append(",");
}
builder.append("]");
}
@Override
public int queryBindHash() {
int hc = JunctionExpression.class.getName().hashCode();
List<SpiExpression> list = exprList.internalList();
for (SpiExpression aList : list) {
hc = hc * 92821 + aList.queryBindHash();
public void queryBindKey(BindValuesKey key) {
for (SpiExpression expr : exprList.internalList()) {
expr.queryBindKey(key);
}
return hc;
}
@Override
@@ -275,7 +262,6 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
return exprList.textCommonTerms(search, options);
}
@Override
public ExpressionList<T> allEq(Map<String, Object> propertyMap) {
return exprList.allEq(propertyMap);
@@ -1025,7 +1011,6 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public String nestedPath(BeanDescriptor<?> desc) {
PrepareDocNested.prepare(exprList, desc, type);
String nestedPath = exprList.allDocNestedPath;
if (nestedPath != null) {
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.LikeType;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
@@ -70,8 +71,8 @@ class LikeExpression extends AbstractValueExpression {
}
@Override
public int queryBindHash() {
return strValue().hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(strValue());
}
@Override
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.expression;
import io.ebean.Expression;
import io.ebean.Junction;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -168,10 +169,8 @@ abstract class LogicExpression implements SpiExpression {
}
@Override
public int queryBindHash() {
int hc = expOne.queryBindHash();
hc = hc * 92821 + expTwo.queryBindHash();
return hc;
public void queryBindKey(BindValuesKey key) {
key.add(expOne).add(expTwo);
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.LikeType;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
@@ -54,8 +55,8 @@ class NativeILikeExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
return val.hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(val);
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -79,8 +80,8 @@ class NestedPathWrapperExpression implements SpiExpression {
}
@Override
public int queryBindHash() {
return delegate.queryBindHash();
public void queryBindKey(BindValuesKey key) {
delegate.queryBindKey(key);
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -74,9 +75,8 @@ class NoopExpression implements SpiExpression {
}
@Override
public int queryBindHash() {
public void queryBindKey(BindValuesKey key) {
// no bind values
return 0;
}
@Override
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
import io.ebean.Expression;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -99,8 +100,8 @@ final class NotExpression implements SpiExpression {
}
@Override
public int queryBindHash() {
return exp.queryBindHash();
public void queryBindKey(BindValuesKey key) {
exp.queryBindKey(key);
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -94,7 +95,7 @@ class NullExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
return (notNull ? 1 : 0);
public void queryBindKey(BindValuesKey key) {
key.add(notNull);
}
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -73,12 +74,11 @@ class RawExpression extends NonPrepareExpression {
}
@Override
public int queryBindHash() {
int hc = sql.hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(values.length);
for (Object value : values) {
hc = hc * 92821 + value.hashCode();
key.add(value);
}
return hc;
}
@Override
@@ -5,6 +5,7 @@ import io.ebean.plugin.ExpressionPath;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.NaturalKeyQueryData;
import java.io.IOException;
@@ -121,8 +122,8 @@ public class SimpleExpression extends AbstractValueExpression {
}
@Override
public int queryBindHash() {
return value().hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(value());
}
@Override
@@ -1,81 +0,0 @@
package io.ebeaninternal.server.persist;
import java.util.ArrayList;
/**
* Holds a list of bind values for binding to a PreparedStatement.
*/
class BindValues {
private final ArrayList<Value> list = new ArrayList<>();
/**
* Create with a Binder.
*/
public BindValues() {
}
/**
* Add a bind value with its JDBC datatype.
*
* @param value the bind value
* @param dbType the type as per java.sql.Types
*/
public void add(Object value, int dbType, String name) {
list.add(new Value(value, dbType, name));
}
/**
* List of bind values.
*/
public ArrayList<Value> values() {
return list;
}
/**
* A Value has additionally the JDBC data type.
*/
public static class Value {
private final Object value;
private final int dbType;
private final String name;
/**
* Create the value.
*/
Value(Object value, int dbType, String name) {
this.value = value;
this.dbType = dbType;
this.name = name;
}
/**
* Return the type as per java.sql.Types.
*/
public int getDbType() {
return dbType;
}
/**
* Return the value.
*/
public Object getValue() {
return value;
}
/**
* Return the property name.
*/
public String getName() {
return name;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
}
@@ -77,33 +77,6 @@ public class Binder {
return asOfStandardsBased;
}
/**
* Bind the values to the Prepared Statement.
*/
public void bind(BindValues bindValues, DataBind dataBind, StringBuilder bindBuf) throws SQLException {
String logPrefix = "";
ArrayList<BindValues.Value> list = bindValues.values();
for (BindValues.Value bindValue : list) {
Object val = bindValue.getValue();
int dt = bindValue.getDbType();
bindObject(dataBind, val, dt);
if (bindBuf != null) {
bindBuf.append(logPrefix);
if (logPrefix.isEmpty()) {
logPrefix = ", ";
}
bindBuf.append(bindValue.getName());
bindBuf.append("=");
if (isLob(dt)) {
bindBuf.append("[LOB]");
} else {
bindBuf.append(val);
}
}
}
}
/**
* Bind the parameters to the preparedStatement returning the bind log.
*/
@@ -295,7 +295,16 @@ public class SaveManyBeans extends SaveManyBase {
}
transaction.depth(+1);
if (deletions != null && !deletions.isEmpty()) {
for (Object other : deletions) {
EntityBean otherDelete = (EntityBean) other;
// the object from the 'other' side of the ManyToMany
// build a intersection row for 'delete'
IntersectionRow intRow = many.buildManyToManyMapBean(parentBean, otherDelete, publish);
SpiSqlUpdate sqlDelete = intRow.createDelete(server, DeleteMode.HARD);
persister.executeOrQueue(sqlDelete, transaction, queue);
}
}
if (additions != null && !additions.isEmpty()) {
for (Object other : additions) {
EntityBean otherBean = (EntityBean) other;
@@ -318,16 +327,6 @@ public class SaveManyBeans extends SaveManyBase {
}
}
}
if (deletions != null && !deletions.isEmpty()) {
for (Object other : deletions) {
EntityBean otherDelete = (EntityBean) other;
// the object from the 'other' side of the ManyToMany
// build a intersection row for 'delete'
IntersectionRow intRow = many.buildManyToManyMapBean(parentBean, otherDelete, publish);
SpiSqlUpdate sqlDelete = intRow.createDelete(server, DeleteMode.HARD);
persister.executeOrQueue(sqlDelete, transaction, queue);
}
}
// decrease the depth back to what it was
transaction.depth(-1);
}
@@ -63,6 +63,11 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
}
}
@Override
public void pushJson(String json) {
dataBind.pushJson(json);
}
@Override
public long now() {
return now;
@@ -0,0 +1,42 @@
package io.ebeaninternal.server.persist.dmlbind;
import io.ebean.bean.EntityBean;
import io.ebean.bean.MutableValueInfo;
import io.ebeaninternal.server.deploy.BeanProperty;
import java.sql.SQLException;
/**
* For JSON Jackson properties - dirty detection via MD5 of json content.
*/
class BindablePropertyJsonInsert extends BindableProperty {
private final int propertyIndex;
BindablePropertyJsonInsert(BeanProperty prop) {
super(prop);
this.propertyIndex = prop.getPropertyIndex();
}
/**
* Normal binding of a property value from the bean.
*/
@Override
public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
if (bean == null) {
request.bind(null, prop);
} else {
Object value = prop.getValue(bean);
if (value == null) {
request.bind(null, prop);
} else {
// on insert store hash and push json
final String json = prop.format(value);
final MutableValueInfo hash = prop.createMutableInfo(json);
bean._ebean_getIntercept().mutableInfo(propertyIndex, hash);
request.pushJson(json);
request.bind(value, prop);
}
}
}
}
@@ -0,0 +1,33 @@
package io.ebeaninternal.server.persist.dmlbind;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.deploy.BeanProperty;
import java.sql.SQLException;
/**
* For JSON Jackson properties - dirty detection via MD5 of json content.
*/
class BindablePropertyJsonUpdate extends BindableProperty {
private final int propertyIndex;
BindablePropertyJsonUpdate(BeanProperty prop) {
super(prop);
this.propertyIndex = prop.getPropertyIndex();
}
/**
* Normal binding of a property value from the bean.
*/
@Override
public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
if (bean == null) {
request.bind(null, prop);
} else {
// update mutableInfo and push json
request.pushJson(bean._ebean_getIntercept().mutableNext(propertyIndex));
request.bind(prop.getValue(bean), prop);
}
}
}
@@ -57,4 +57,9 @@ public interface BindableRequest {
* Return true if this is an update request.
*/
boolean isUpdate();
/**
* Push json content for scalarType bind().
*/
void pushJson(String json);
}
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.persist.dmlbind;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.BeanPropertyJsonMapper;
import io.ebeaninternal.server.persist.dml.DmlMode;
/**
@@ -23,14 +24,12 @@ class FactoryProperty {
* Create a Bindable for the property given the mode and withLobs flag.
*/
public Bindable create(BeanProperty prop, DmlMode mode, boolean withLobs, boolean allowManyToOne) {
if (DmlMode.INSERT == mode && !prop.isDbInsertable()) {
return null;
}
if (DmlMode.UPDATE == mode && !prop.isDbUpdatable()) {
return null;
}
if (prop.isLob() && !withLobs) {
// Lob exclusion
return null;
@@ -38,11 +37,16 @@ class FactoryProperty {
if (prop.isDbEncrypted()){
return new BindableEncryptedProperty(prop, bindEncryptDataFirst);
}
if (allowManyToOne && prop instanceof BeanPropertyAssocOne) {
return new BindableAssocOne((BeanPropertyAssocOne<?>)prop);
}
if (prop instanceof BeanPropertyJsonMapper) {
if (DmlMode.INSERT == mode) {
return new BindablePropertyJsonInsert(prop);
} else if (DmlMode.UPDATE == mode) {
return new BindablePropertyJsonUpdate(prop);
}
}
return new BindableProperty(prop);
}
}
@@ -13,7 +13,7 @@ final class BasicProfileLocation implements ProfileLocation {
BasicProfileLocation(String fullLocation) {
this.fullLocation = fullLocation;
this.location = shortDesc(fullLocation);
this.location = UtilLocation.loc(fullLocation);
this.label = UtilLocation.label(location);
}
@@ -57,15 +57,4 @@ final class BasicProfileLocation implements ProfileLocation {
// do nothing
}
private String shortDesc(String location) {
int lastPer = location.lastIndexOf('.');
if (lastPer > -1) {
lastPer = location.lastIndexOf('.', lastPer - 1);
if (lastPer > -1) {
return location.substring(lastPer + 1);
}
}
return location;
}
}
@@ -49,7 +49,7 @@ class DCountMetric implements CountMetric {
@Override
public void visit(MetricVisitor visitor) {
long val = visitor.isReset() ? count.sumThenReset() : count.sum();
long val = visitor.reset() ? count.sumThenReset() : count.sum();
if (val > 0) {
visitor.visitCount(new DCountMetricStats(name, val));
}
@@ -66,12 +66,12 @@ class DCountMetric implements CountMetric {
}
@Override
public String getName() {
public String name() {
return name;
}
@Override
public long getCount() {
public long count() {
return count;
}
}
@@ -8,17 +8,13 @@ import io.ebean.ProfileLocation;
class DProfileLocation implements ProfileLocation {
private static final String IO_EBEAN = "io.ebean";
private static final String UNKNOWN = "unknown";
private String fullLocation;
private String location;
private String label;
private final int lineNumber;
private int traceCount;
DProfileLocation() {
@@ -49,10 +45,10 @@ class DProfileLocation implements ProfileLocation {
return false;
}
final String loc = create();
final String shortDesc = shortDesc(loc);
label = UtilLocation.label(shortDesc);
location = shortDesc;
fullLocation = loc;
final String location = UtilLocation.loc(loc);
this.label = UtilLocation.label(location);
this.location = location;
this.fullLocation = loc;
initWith(label);
return true;
}
@@ -113,20 +109,4 @@ class DProfileLocation implements ProfileLocation {
return traceLine.substring(0, traceLine.length() - 1) + ":" + lineNumber + ")";
}
}
private String shortDesc(String location) {
int pos = location.lastIndexOf('(');
if (pos == -1) {
pos = location.length();
}
pos = location.lastIndexOf('.', pos);
if (pos > -1) {
pos = location.lastIndexOf('.', pos - 1);
if (pos > -1) {
return location.substring(pos + 1);
}
}
return location;
}
}
@@ -22,15 +22,8 @@ class DQueryPlanMeta {
name += "_" + label;
}
this.name = name;
this.hash = initHash();
}
private String initHash() {
StringBuilder sb = new StringBuilder(sql).append("|").append(name);
if (profileLocation != null) {
sb.append("|").append(profileLocation.location());
}
return Md5.hash(sb.toString());
String loc = profileLocation == null ? null : profileLocation.location();
this.hash = Md5.hash(sql, name, loc);
}
public Class<?> getType() {
@@ -19,7 +19,7 @@ class DQueryPlanMetric implements QueryPlanMetric {
@Override
public void visit(MetricVisitor visitor) {
TimedMetricStats stats = metric.collect(visitor.isReset());
TimedMetricStats stats = metric.collect(visitor.reset());
if (stats != null) {
visitor.visitQuery(new Stats(meta, stats, collected));
collected = true;
@@ -45,11 +45,11 @@ class DQueryPlanMetric implements QueryPlanMetric {
@Override
public String toString() {
return meta + " " + stats + " sql:" + getSql();
return meta + " " + stats + " sql:" + sql();
}
@Override
public Class<?> getType() {
public Class<?> type() {
return meta.getType();
}
@@ -59,48 +59,48 @@ class DQueryPlanMetric implements QueryPlanMetric {
}
@Override
public String getHash() {
public String hash() {
return meta.getHash();
}
@Override
public String getLabel() {
public String label() {
return meta.getLabel();
}
@Override
public String getSql() {
public String sql() {
return meta.getSql();
}
@Override
public String getName() {
public String name() {
return meta.getName();
}
@Override
public String getLocation() {
public String location() {
return meta.getLocation();
}
@Override
public long getCount() {
return stats.getCount();
public long count() {
return stats.count();
}
@Override
public long getTotal() {
return stats.getTotal();
public long total() {
return stats.total();
}
@Override
public long getMax() {
return stats.getMax();
public long max() {
return stats.max();
}
@Override
public long getMean() {
return stats.getMean();
public long mean() {
return stats.mean();
}
}
}
@@ -7,18 +7,14 @@ import io.ebean.metric.TimedMetricStats;
*/
class DTimeMetricStats implements TimedMetricStats {
private String name;
private final boolean collected;
private String location;
private final long count;
private final long total;
private final long max;
private String name;
private String location;
DTimeMetricStats(String name, boolean collected, long count, long total, long max) {
this.name = name;
this.collected = collected;
@@ -60,12 +56,12 @@ class DTimeMetricStats implements TimedMetricStats {
}
@Override
public String getName() {
public String name() {
return name;
}
@Override
public String getLocation() {
public String location() {
return location;
}
@@ -73,7 +69,7 @@ class DTimeMetricStats implements TimedMetricStats {
* Return the count of values collected.
*/
@Override
public long getCount() {
public long count() {
return count;
}
@@ -81,7 +77,7 @@ class DTimeMetricStats implements TimedMetricStats {
* Return the total of all the values.
*/
@Override
public long getTotal() {
public long total() {
return total;
}
@@ -89,7 +85,7 @@ class DTimeMetricStats implements TimedMetricStats {
* Return the Max value collected.
*/
@Override
public long getMax() {
public long max() {
return max;
}
@@ -97,7 +93,7 @@ class DTimeMetricStats implements TimedMetricStats {
* Return the mean value rounded up.
*/
@Override
public long getMean() {
public long mean() {
return (count < 1) ? 0L : Math.round((double)(total / count));
}
@@ -71,7 +71,7 @@ class DTimedMetric implements TimedMetric {
@Override
public void visit(MetricVisitor visitor) {
DTimeMetricStats metric = collect(visitor.isReset());
DTimeMetricStats metric = collect(visitor.reset());
if (metric != null) {
visitor.visitTimed(metric);
}
@@ -48,7 +48,7 @@ class DTimedProfileLocation extends DProfileLocation implements TimedProfileLoca
@Override
public void visit(MetricVisitor visitor) {
TimedMetricStats collect = timedMetric.collect(visitor.isReset());
TimedMetricStats collect = timedMetric.collect(visitor.reset());
if (collect != null) {
if (overrideMetricName) {
collect.setName(fullName);
@@ -2,15 +2,30 @@ package io.ebeaninternal.server.profile;
final class UtilLocation {
static String label(String shortDescription) {
int pos = shortDescription.indexOf("(");
if (pos == -1) {
return shortDescription;
static String loc(String full) {
final int pos = full.lastIndexOf('(');
if (pos > -1) {
return full.substring(0, pos);
} else {
return trimInit(shortDescription.substring(0, pos));
return full;
}
}
static String label(String location) {
return trimInit(shortDesc(location));
}
private static String shortDesc(String location) {
int pos = location.lastIndexOf('.');
if (pos > -1) {
pos = location.lastIndexOf('.', pos - 1);
if (pos > -1) {
return location.substring(pos + 1);
}
}
return location;
}
/**
* Trim constructor init to be "safe" without greater than or less than chars.
*/
@@ -21,5 +36,4 @@ final class UtilLocation {
}
return desc;
}
}
@@ -541,9 +541,13 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
updateStatistics();
}
long micros() {
return (System.nanoTime() - startNano) / 1000L;
}
private void updateStatistics() {
try {
executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
executionTimeMicros = micros();
if (autoTuneProfiling) {
profilingListener.collectQueryInfo(objectGraphNode, loadedBeanCount, executionTimeMicros);
}
@@ -189,6 +189,12 @@ class CQueryBuilder {
SpiQuery<?> query = request.getQuery();
query.setSingleAttribute();
if (!query.isIncludeSoftDeletes()) {
BeanDescriptor<?> desc = request.getBeanDescriptor();
if (desc.isSoftDelete()) {
query.addSoftDeletePredicate(desc.getSoftDeletePredicate(alias(query.getAlias())));
}
}
CQueryPredicates predicates = new CQueryPredicates(binder, request);
CQueryPlan queryPlan = request.getQueryPlan();
@@ -79,15 +79,10 @@ public class CQueryEngine {
private <T> int executeUpdate(OrmQueryRequest<T> request, CQueryUpdate query) {
try {
int rows = query.execute();
if (request.isLogSql()) {
String logSql = query.getGeneratedSql();
logSql = Str.add(logSql, "; --bind(", query.getBindLog(), ") rows:", String.valueOf(rows));
request.logSql(logSql);
request.logSql(Str.add(query.getGeneratedSql(), "; --bind(", query.getBindLog(), ") --micros(", query.micros() + ") --rows(", rows + ")"));
}
return rows;
} catch (SQLException e) {
throw translate(request, query.getBindLog(), query.getGeneratedSql(), e);
}
@@ -97,7 +92,6 @@ public class CQueryEngine {
* Build and execute the findSingleAttributeList query.
*/
public <A> List<A> findSingleAttributeList(OrmQueryRequest<?> request) {
CQueryFetchSingleAttribute rcQuery = queryBuilder.buildFetchAttributeQuery(request);
request.setCancelableQuery(rcQuery);
return findAttributeList(request, rcQuery);
@@ -108,14 +102,13 @@ public class CQueryEngine {
try {
List<A> list = (List<A>) rcQuery.findList();
if (request.isLogSql()) {
logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog());
logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog(), rcQuery.micros());
}
if (request.isLogSummary()) {
request.getTransaction().logSummary(rcQuery.getSummary());
}
if (request.isQueryCachePut()) {
request.addDependentTables(rcQuery.getDependentTables());
list = Collections.unmodifiableList(list);
request.putToQueryCache(list);
if (Boolean.FALSE.equals(request.getQuery().isReadOnly())) {
@@ -123,7 +116,6 @@ public class CQueryEngine {
}
}
return list;
} catch (SQLException e) {
throw translate(request, rcQuery.getBindLog(), rcQuery.getGeneratedSql(), e);
}
@@ -139,10 +131,8 @@ public class CQueryEngine {
String msg = "ERROR executing query, bindLog[" + bindLog + "] error[" + StringHelper.removeNewLines(e.getMessage()) + "]";
t.logSummary(msg);
}
// ensure 'rollback' is logged if queryOnly transaction
t.getConnection();
// build a decent error message for the exception
String m = "Query threw SQLException:" + e.getMessage() + " Bind values:[" + bindLog + "] Query was:" + sql;
return dbPlatform.translate(m, e);
@@ -152,46 +142,37 @@ public class CQueryEngine {
* Build and execute the find Id's query.
*/
public <A> List<A> findIds(OrmQueryRequest<?> request) {
CQueryFetchSingleAttribute rcQuery = queryBuilder.buildFetchIdsQuery(request);
request.setCancelableQuery(rcQuery);
return findAttributeList(request, rcQuery);
}
private <T> void logGeneratedSql(OrmQueryRequest<T> request, String sql, String bindLog) {
request.logSql(Str.add(sql, "; --bind(", bindLog, ")"));
private <T> void logGeneratedSql(OrmQueryRequest<T> request, String sql, String bindLog, long micros) {
request.logSql(Str.add(sql, "; --bind(", bindLog, ") --micros(", micros + ")"));
}
/**
* Build and execute the row count query.
*/
public <T> int findCount(OrmQueryRequest<T> request) {
CQueryRowCount rcQuery = queryBuilder.buildRowCountQuery(request);
request.setCancelableQuery(rcQuery);
try {
int count = rcQuery.findCount();
if (request.isLogSql()) {
logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog());
logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog(), rcQuery.micros());
}
if (request.isLogSummary()) {
request.getTransaction().logSummary(rcQuery.getSummary());
}
if (request.getQuery().isFutureFetch()) {
request.getTransaction().end();
}
if (request.isQueryCachePut()) {
request.addDependentTables(rcQuery.getDependentTables());
request.putToQueryCache(count);
}
return count;
} catch (SQLException e) {
throw translate(request, rcQuery.getBindLog(), rcQuery.getGeneratedSql(), e);
}
@@ -254,9 +235,7 @@ public class CQueryEngine {
* Execute the find versions query returning version beans.
*/
public <T> List<Version<T>> findVersions(OrmQueryRequest<T> request) {
SpiQuery<T> query = request.getQuery();
String sysPeriodLower = getSysPeriodLower(query);
if (query.isVersionsBetween() && !historySupport.isStandardsBased()) {
query.where().lt(sysPeriodLower, query.getVersionEnd());
@@ -272,26 +251,21 @@ public class CQueryEngine {
if (request.isLogSql()) {
logSql(cquery);
}
List<Version<T>> versions = cquery.readVersions();
// just order in memory rather than use NULLS LAST as that
// is not universally supported, not expect huge list here
versions.sort(OrderVersionDesc.INSTANCE);
deriveVersionDiffs(versions, request);
if (request.isLogSummary()) {
logFindManySummary(cquery);
}
if (request.isAuditReads()) {
cquery.auditFindMany();
}
return versions;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
} finally {
if (cquery != null) {
cquery.close();
@@ -300,9 +274,7 @@ public class CQueryEngine {
}
private <T> void deriveVersionDiffs(List<Version<T>> versions, OrmQueryRequest<T> request) {
BeanDescriptor<T> descriptor = request.getBeanDescriptor();
if (!versions.isEmpty()) {
Version<T> current = versions.get(0);
if (versions.size() > 1) {
@@ -367,10 +339,8 @@ public class CQueryEngine {
* Find a list/map/set of beans.
*/
<T> BeanCollection<T> findMany(OrmQueryRequest<T> request) {
CQuery<T> cquery = queryBuilder.buildQuery(request);
request.setCancelableQuery(cquery);
try {
if (defaultFetchSizeFindList > 0) {
request.setDefaultFetchBuffer(defaultFetchSizeFindList);
@@ -380,30 +350,24 @@ public class CQueryEngine {
logger.trace("Future fetch already cancelled");
return null;
}
if (request.isLogSql()) {
logSql(cquery);
}
BeanCollection<T> beanCollection = cquery.readCollection();
if (request.isLogSummary()) {
logFindManySummary(cquery);
}
if (request.isAuditReads()) {
cquery.auditFindMany();
}
request.executeSecondaryQueries(false);
if (request.isQueryCachePut()) {
request.addDependentTables(cquery.getDependentTables());
}
return beanCollection;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
} finally {
if (cquery != null) {
cquery.close();
@@ -416,38 +380,27 @@ public class CQueryEngine {
*/
@SuppressWarnings("unchecked")
public <T> T find(OrmQueryRequest<T> request) {
EntityBean bean = null;
CQuery<T> cquery = queryBuilder.buildQuery(request);
request.setCancelableQuery(cquery);
try {
cquery.prepareBindExecuteQuery();
if (request.isLogSql()) {
logSql(cquery);
}
if (cquery.readBean()) {
bean = cquery.next();
}
if (request.isLogSummary()) {
logFindBeanSummary(cquery);
}
if (request.isAuditReads()) {
cquery.auditFind(bean);
}
request.executeSecondaryQueries(false);
return (T) bean;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
} finally {
cquery.close();
}
@@ -457,17 +410,13 @@ public class CQueryEngine {
* Log the generated SQL to the transaction log.
*/
private void logSql(CQuery<?> query) {
String sql = query.getGeneratedSql();
sql = Str.add(sql, "; --bind(", query.getBindLog(), ")");
query.getTransaction().logSql(sql);
query.getTransaction().logSql(Str.add(query.getGeneratedSql(), "; --bind(", query.getBindLog(), ") --micros(", query.micros() + ")"));
}
/**
* Log the FindById summary to the transaction log.
*/
private void logFindBeanSummary(CQuery<?> q) {
SpiQuery<?> query = q.getQueryRequest().getQuery();
String loadMode = query.getLoadMode();
String loadDesc = query.getLoadDescription();
@@ -504,7 +453,6 @@ public class CQueryEngine {
msg.append("exeMicros[").append(q.getQueryExecutionTimeMicros());
msg.append("] rows[").append(q.getLoadedRowDetail());
msg.append("] bind[").append(q.getBindLog()).append("]");
q.getTransaction().logSummary(msg.toString());
}
@@ -512,7 +460,6 @@ public class CQueryEngine {
* Log the FindMany to the transaction log.
*/
private void logFindManySummary(CQuery<?> q) {
SpiQuery<?> query = q.getQueryRequest().getQuery();
String loadMode = query.getLoadMode();
String loadDesc = query.getLoadDescription();
@@ -551,7 +498,6 @@ public class CQueryEngine {
msg.append("] rows[").append(q.getLoadedRowDetail());
msg.append("] predicates[").append(q.getLogWhereSql());
msg.append("] bind[").append(q.getBindLog()).append("]");
q.getTransaction().logSummary(msg.toString());
}
}
@@ -29,45 +29,19 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent, Cancelab
private static final Logger logger = LoggerFactory.getLogger(CQueryFetchSingleAttribute.class);
private final CQueryPlan queryPlan;
/**
* The overall find request wrapper object.
*/
private final OrmQueryRequest<?> request;
private final BeanDescriptor<?> desc;
private final SpiQuery<?> query;
/**
* Where clause predicates.
*/
private final CQueryPredicates predicates;
/**
* The final sql that is generated.
*/
private final String sql;
private RsetDataReader dataReader;
/**
* The statement used to create the resultSet.
*/
private PreparedStatement pstmt;
private String bindLog;
private long executionTimeMicros;
private int rowCount;
private final ScalarDataReader<?> reader;
private final boolean containsCounts;
private long profileOffset;
private final ReentrantLock lock = new ReentrantLock();
/**
@@ -95,19 +69,20 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent, Cancelab
.append("] type[").append(desc.getName())
.append("] predicates[").append(predicates.getLogWhereSql())
.append("] bind[").append(bindLog).append("]");
return sb.toString();
}
long micros() {
return executionTimeMicros;
}
/**
* Execute the query returning the row count.
*/
List<Object> findList() throws SQLException {
long startNano = System.nanoTime();
try {
prepareExecute();
List<Object> result = new ArrayList<>();
while (dataReader.next()) {
Object value = reader.read(dataReader);
@@ -117,16 +92,13 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent, Cancelab
result.add(value);
rowCount++;
}
executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
request.slowQueryCheck(executionTimeMicros, rowCount);
if (queryPlan.executionTime(executionTimeMicros)) {
queryPlan.captureBindForQueryPlan(predicates, executionTimeMicros);
}
getTransaction().profileEvent(this);
return result;
} finally {
close();
}
@@ -158,14 +130,12 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent, Cancelab
profileOffset = t.profileOffset();
Connection conn = t.getInternalConnection();
pstmt = conn.prepareStatement(sql);
if (query.getBufferFetchSizeHint() > 0) {
pstmt.setFetchSize(query.getBufferFetchSizeHint());
}
if (query.getTimeout() > 0) {
pstmt.setQueryTimeout(query.getTimeout());
}
bindLog = predicates.bind(pstmt, conn);
} finally {
lock.unlock();
@@ -13,12 +13,12 @@ import io.ebeaninternal.api.SpiQueryBindCapture;
import io.ebeaninternal.api.SpiQueryPlan;
import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.core.timezone.DataTimeZone;
import io.ebeaninternal.server.util.Md5;
import io.ebeaninternal.server.util.Str;
import io.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
import io.ebeaninternal.server.type.DataBind;
import io.ebeaninternal.server.type.DataBindCapture;
import io.ebeaninternal.server.type.RsetDataReader;
import io.ebeaninternal.server.util.Md5;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,46 +54,31 @@ public class CQueryPlan implements SpiQueryPlan {
static final String RESULT_SET_BASED_RAW_SQL = "--ResultSetBasedRawSql";
private final SpiEbeanServer server;
private final ProfileLocation profileLocation;
private final String location;
private final String label;
private final String name;
private final CQueryPlanKey planKey;
private final boolean rawSql;
private final String sql;
private final String hash;
private final String logWhereSql;
private final SqlTree sqlTree;
/**
* Encrypted properties required additional binding.
*/
private final STreeProperty[] encryptedProps;
private final CQueryPlanStats stats;
private final Class<?> beanType;
final DataTimeZone dataTimeZone;
private final int asOfTableCount;
/**
* Key used to identify the query plan in audit logging.
*/
private volatile String auditQueryHash;
private final Set<String> dependentTables;
private final SpiQueryBindCapture bindCapture;
/**
@@ -106,9 +91,9 @@ public class CQueryPlan implements SpiQueryPlan {
this.planKey = request.getQueryPlanKey();
SpiQuery<?> query = request.getQuery();
this.profileLocation = query.getProfileLocation();
this.location = (profileLocation == null) ? null : profileLocation.location();
this.label = query.getPlanLabel();
this.name = deriveName(label, query.getType(), request.getBeanDescriptor().getSimpleName());
this.location = location();
this.asOfTableCount = query.getAsOfTableCount();
this.sql = sqlRes.getSql();
this.sqlTree = sqlTree;
@@ -118,7 +103,7 @@ public class CQueryPlan implements SpiQueryPlan {
this.stats = new CQueryPlanStats(this);
this.dependentTables = sqlTree.dependentTables();
this.bindCapture = initBindCapture(query);
this.hash = md5Hash();
this.hash = Md5.hash(sql, name, location);
}
/**
@@ -130,9 +115,9 @@ public class CQueryPlan implements SpiQueryPlan {
this.beanType = request.getBeanDescriptor().getBeanType();
SpiQuery<?> query = request.getQuery();
this.profileLocation = query.getProfileLocation();
this.location = (profileLocation == null) ? null : profileLocation.location();
this.label = query.getPlanLabel();
this.name = deriveName(label, query.getType(), request.getBeanDescriptor().getSimpleName());
this.location = location();
this.planKey = buildPlanKey(sql, logWhereSql);
this.asOfTableCount = 0;
this.sql = sql;
@@ -143,7 +128,7 @@ public class CQueryPlan implements SpiQueryPlan {
this.stats = new CQueryPlanStats(this);
this.dependentTables = sqlTree.dependentTables();
this.bindCapture = initBindCaptureRaw(sql, query);
this.hash = md5Hash();
this.hash = Md5.hash(sql, name, location);
}
private String deriveName(String label, SpiQuery.Type type, String simpleName) {
@@ -169,10 +154,6 @@ public class CQueryPlan implements SpiQueryPlan {
return sql.equals(RESULT_SET_BASED_RAW_SQL) || query.getType().isUpdate() ? SpiQueryBindCapture.NOOP : server.createQueryBindCapture(this);
}
private String location() {
return (profileLocation == null) ? null : profileLocation.location();
}
private CQueryPlanKey buildPlanKey(String sql, String logWhereSql) {
return new RawSqlQueryPlanKey(sql, false, logWhereSql);
}
@@ -276,21 +257,6 @@ public class CQueryPlan implements SpiQueryPlan {
return rawSql ? planKey.getPartialKey() + "_" + hash : planKey.getPartialKey();
}
/**
* Return the MD5 hash of the sql.
*/
private String md5Hash() {
StringBuilder sb = new StringBuilder(sql)
.append("|").append(name)
.append("|").append(location);
try {
return Md5.hash(sb.toString());
} catch (Exception e) {
logger.error("Failed to MD5 hash the query", e);
return "error";
}
}
SqlTree getSqlTree() {
return sqlTree;
}
@@ -24,10 +24,10 @@ class CQueryPlanRequest {
CQueryPlanRequest(Connection connection, QueryPlanRequest req, Iterator<CQueryBindCapture> iterator) {
this.connection = connection;
this.iterator = iterator;
this.maxCount = req.getMaxCount();
long reqSince = req.getSince();
this.maxCount = req.maxCount();
long reqSince = req.since();
this.since = (reqSince == 0) ? Long.MAX_VALUE: reqSince;
long maxTimeMillis = req.getMaxTimeMillis();
long maxTimeMillis = req.maxTimeMillis();
this.maxTime = maxTimeMillis > 0 ? System.currentTimeMillis() + maxTimeMillis : 0;
}
@@ -83,56 +83,56 @@ public final class CQueryPlanStats {
@Override
public String toString() {
return "label:" + getLabel() + " location:" + getLocation() + " metrics:" + metrics + " sql:" + getSql();
return "label:" + label() + " location:" + location() + " metrics:" + metrics + " sql:" + sql();
}
@Override
public Class<?> getType() {
public Class<?> type() {
return queryPlan.getBeanType();
}
@Override
public String getLabel() {
public String label() {
return queryPlan.getLabel();
}
@Override
public String getName() {
public String name() {
return queryPlan.getName();
}
@Override
public String getLocation() {
public String location() {
return queryPlan.getLocation();
}
@Override
public long getCount() {
return metrics.getCount();
public long count() {
return metrics.count();
}
@Override
public long getTotal() {
return metrics.getTotal();
public long total() {
return metrics.total();
}
@Override
public long getMax() {
return metrics.getMax();
public long max() {
return metrics.max();
}
@Override
public long getMean() {
return metrics.getMean();
public long mean() {
return metrics.mean();
}
@Override
public String getHash() {
public String hash() {
return queryPlan.getHash();
}
@Override
public String getSql() {
public String sql() {
return queryPlan.getSql();
}
@@ -22,44 +22,17 @@ import java.util.concurrent.locks.ReentrantLock;
class CQueryRowCount implements SpiProfileTransactionEvent, CancelableQuery {
private final CQueryPlan queryPlan;
/**
* The overall find request wrapper object.
*/
private final OrmQueryRequest<?> request;
private final BeanDescriptor<?> desc;
private final SpiQuery<?> query;
/**
* Where clause predicates.
*/
private final CQueryPredicates predicates;
/**
* The final sql that is generated.
*/
private final String sql;
/**
* The resultSet that is read and converted to objects.
*/
private ResultSet rset;
/**
* The statement used to create the resultSet.
*/
private PreparedStatement pstmt;
private String bindLog;
private long executionTimeMicros;
private int rowCount;
private long profileOffset;
private final ReentrantLock lock = new ReentrantLock();
/**
@@ -104,11 +77,14 @@ class CQueryRowCount implements SpiProfileTransactionEvent, CancelableQuery {
return sql;
}
long micros() {
return executionTimeMicros;
}
/**
* Execute the query returning the row count.
*/
public int findCount() throws SQLException {
long startNano = System.nanoTime();
try {
SpiTransaction t = getTransaction();
@@ -118,24 +94,19 @@ class CQueryRowCount implements SpiProfileTransactionEvent, CancelableQuery {
try {
query.checkCancelled();
pstmt = conn.prepareStatement(sql);
if (query.getTimeout() > 0) {
pstmt.setQueryTimeout(query.getTimeout());
}
bindLog = predicates.bind(pstmt, conn);
} finally {
lock.unlock();
}
rset = pstmt.executeQuery();
query.checkCancelled();
if (!rset.next()) {
throw new PersistenceException("Expecting 1 row but got none?");
}
rowCount = rset.getInt(1);
executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
request.slowQueryCheck(executionTimeMicros, rowCount);
if (queryPlan.executionTime(executionTimeMicros)) {
@@ -143,7 +114,6 @@ class CQueryRowCount implements SpiProfileTransactionEvent, CancelableQuery {
}
t.profileEvent(this);
return rowCount;
} finally {
close();
}
@@ -19,36 +19,18 @@ import java.util.concurrent.locks.ReentrantLock;
class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery {
private final CQueryPlan queryPlan;
private final OrmQueryRequest<?> request;
private final BeanDescriptor<?> desc;
private final SpiQuery<?> query;
/**
* Where clause predicates.
*/
private final CQueryPredicates predicates;
/**
* The final sql that is generated.
*/
private final String sql;
/**
* The statement used to create the resultSet.
*/
private PreparedStatement pstmt;
private String bindLog;
private int rowCount;
private long profileOffset;
private long executionTimeMicros;
private final ReentrantLock lock = new ReentrantLock();
/**
* Create the Sql select based on the request.
*/
@@ -80,7 +62,6 @@ class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery {
* Execute the update or delete statement returning the row count.
*/
public int execute() throws SQLException {
long startNano = System.nanoTime();
try {
SpiTransaction t = getTransaction();
@@ -90,19 +71,16 @@ class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery {
try {
query.checkCancelled();
pstmt = conn.prepareStatement(sql);
if (query.getTimeout() > 0) {
pstmt.setQueryTimeout(query.getTimeout());
}
bindLog = predicates.bind(pstmt, conn);
} finally {
lock.unlock();
}
rowCount = pstmt.executeUpdate();
query.checkCancelled();
long executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
request.slowQueryCheck(executionTimeMicros, rowCount);
if (queryPlan.executionTime(executionTimeMicros)) {
queryPlan.captureBindForQueryPlan(predicates, executionTimeMicros);
@@ -115,6 +93,10 @@ class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery {
}
}
long micros() {
return executionTimeMicros;
}
private SpiTransaction getTransaction() {
return request.getTransaction();
}
@@ -16,8 +16,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
private final String sql;
private final String bind;
private final String plan;
private String hash;
private final String hash;
private long queryTimeMicros;
private long captureCount;
@@ -32,7 +31,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
}
@Override
public String getHash() {
public String hash() {
return hash;
}
@@ -40,7 +39,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
* Return the associated bean.
*/
@Override
public Class<?> getBeanType() {
public Class<?> beanType() {
return beanType;
}
@@ -48,12 +47,12 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
* Return the query label if set.
*/
@Override
public String getLabel() {
public String label() {
return label;
}
@Override
public ProfileLocation getProfileLocation() {
public ProfileLocation profileLocation() {
return profileLocation;
}
@@ -61,7 +60,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
* Return the sql of query.
*/
@Override
public String getSql() {
public String sql() {
return sql;
}
@@ -69,7 +68,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
* Return a description of the bind values used.
*/
@Override
public String getBind() {
public String bind() {
return bind;
}
@@ -77,7 +76,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
* Return the query plan.
*/
@Override
public String getPlan() {
public String plan() {
return plan;
}
@@ -86,7 +85,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
* to build the query plan.
*/
@Override
public long getQueryTimeMicros() {
public long queryTimeMicros() {
return queryTimeMicros;
}
@@ -94,7 +93,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
* Return the total count of times bind capture has occurred.
*/
@Override
public long getCaptureCount() {
public long captureCount() {
return captureCount;
}
@@ -18,19 +18,15 @@ public class SqlBeanLoad {
private final DbReadContext ctx;
private final EntityBean bean;
private final EntityBeanIntercept ebi;
private final Class<?> type;
private final boolean lazyLoading;
private final boolean refreshLoading;
private final boolean rawSql;
SqlBeanLoad(DbReadContext ctx, Class<?> type, EntityBean bean, Mode queryMode) {
this.ctx = ctx;
this.rawSql = ctx.isRawSql();
this.type = type;
this.lazyLoading = queryMode == Mode.LAZYLOAD_BEAN;
this.refreshLoading = queryMode == Mode.REFRESH_BEAN;
this.bean = bean;
this.ebi = bean == null ? null : bean._ebean_getIntercept();
}
@@ -50,34 +46,21 @@ public class SqlBeanLoad {
}
public Object load(BeanProperty prop) {
if (!rawSql && !prop.isLoadProperty(ctx.isDraftQuery())) {
return null;
}
if ((bean == null)
|| (lazyLoading && ebi.isLoadedProperty(prop.getPropertyIndex()))
|| (type != null && !prop.isAssignableFrom(type))) {
// ignore this property
// ... null: bean already in persistence context
// ... lazyLoading: partial bean that is lazy loading
// ... type: inheritance and not assignable to this instance
prop.loadIgnore(ctx);
return null;
}
try {
Object dbVal = prop.read(ctx);
if (!refreshLoading) {
prop.setValue(bean, dbVal);
} else {
prop.setValueIntercept(bean, dbVal);
}
return dbVal;
return prop.readSet(ctx, bean);
} catch (Exception e) {
bean._ebean_getIntercept().setLoadError(prop.getPropertyIndex(), e);
ctx.handleLoadError(prop.getFullBeanName(), e);
@@ -89,10 +72,6 @@ public class SqlBeanLoad {
* Load the given value into the property.
*/
public void load(BeanProperty target, Object dbVal) {
if (!refreshLoading) {
target.setValue(bean, dbVal);
} else {
target.setValueIntercept(bean, dbVal);
}
target.setValue(bean, dbVal);
}
}
@@ -365,11 +365,11 @@ public final class SqlTreeBuilder {
* This means it can included individual properties of an embedded bean.
* </p>
*/
private void addPropertyToSubQuery(SqlTreeProperties selectProps, STreeType desc, String propName) {
STreeProperty p = desc.findProperty(propName);
private void addPropertyToSubQuery(SqlTreeProperties selectProps, STreeType desc, String propName, String path) {
STreeProperty p = desc.findPropertyWithDynamic(propName, path);
if (p == null) {
logger.error("property [" + propName + "]not found on " + desc + " for query - excluding it.");
return;
} else if (p instanceof STreePropertyAssoc && p.isEmbedded()) {
// if the property is embedded we need to lookup the real column name
int pos = propName.indexOf('.');
@@ -383,7 +383,7 @@ public final class SqlTreeBuilder {
private void addProperty(SqlTreeProperties selectProps, STreeType desc, OrmQueryProperties queryProps, String propName) {
if (subQuery) {
addPropertyToSubQuery(selectProps, desc, propName);
addPropertyToSubQuery(selectProps, desc, propName, queryProps.getPath());
return;
}
@@ -1,30 +1,7 @@
package io.ebeaninternal.server.querydefn;
import io.ebean.CacheMode;
import io.ebean.CountDistinctOrder;
import io.ebean.Database;
import io.ebean.DtoQuery;
import io.ebean.Expression;
import io.ebean.ExpressionFactory;
import io.ebean.ExpressionList;
import io.ebean.FetchConfig;
import io.ebean.FetchGroup;
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.ProfileLocation;
import io.ebean.Query;
import io.ebean.QueryIterator;
import io.ebean.QueryType;
import io.ebean.RawSql;
import io.ebean.Transaction;
import io.ebean.UpdateQuery;
import io.ebean.Version;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.bean.ObjectGraphOrigin;
@@ -32,29 +9,10 @@ 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.CacheIdLookup;
import io.ebeaninternal.api.CacheIdLookupMany;
import io.ebeaninternal.api.CacheIdLookupSingle;
import io.ebeaninternal.api.HashQuery;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiEbeanServer;
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.SpiTransaction;
import io.ebeaninternal.api.*;
import io.ebeaninternal.server.autotune.ProfilingListener;
import io.ebeaninternal.server.core.SpiOrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanNaturalKey;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.*;
import io.ebeaninternal.server.el.ElPropertyDeploy;
import io.ebeaninternal.server.expression.DefaultExpressionList;
import io.ebeaninternal.server.expression.IdInExpression;
@@ -66,14 +24,7 @@ import io.ebeaninternal.server.transaction.ExternalJdbcTransaction;
import javax.persistence.PersistenceException;
import java.sql.Connection;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -91,8 +42,6 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
private static final FetchConfig FETCH_LAZY = FetchConfig.ofLazy();
private final ReentrantLock lock = new ReentrantLock();
private final Class<T> beanType;
private final ExpressionFactory expressionFactory;
@@ -1269,22 +1218,13 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
}
}
/**
* Calculate a hash based on the bind values used in the query.
* <p>
* Used with queryPlanHash() to get a unique hash for a query.
* </p>
*/
@Override
public int queryBindHash() {
int hc = (id == null ? 0 : id.hashCode());
hc = hc * 92821 + (whereExpressions == null ? 0 : whereExpressions.queryBindHash());
hc = hc * 92821 + (havingExpressions == null ? 0 : havingExpressions.queryBindHash());
hc = hc * 92821 + (bindParams == null ? 0 : bindParams.queryBindHash());
hc = hc * 92821 + (asOf == null ? 0 : asOf.hashCode());
hc = hc * 92821 + (versionsStart == null ? 0 : versionsStart.hashCode());
hc = hc * 92821 + (versionsEnd == null ? 0 : versionsEnd.hashCode());
return hc;
public void queryBindKey(BindValuesKey key) {
key.add(id);
if (whereExpressions != null) whereExpressions.queryBindKey(key);
if (havingExpressions != null) havingExpressions.queryBindKey(key);
if (bindParams != null) bindParams.queryBindHash(key);
key.add(asOf).add(versionsStart).add(versionsEnd);
}
/**
@@ -1298,8 +1238,9 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
public HashQuery queryHash() {
// calculateQueryPlanHash is called just after potential AutoTune tuning
// so queryPlanHash is calculated well before this method is called
int hc = queryBindHash();
return new HashQuery(queryPlanKey, hc);
BindValuesKey bindKey = new BindValuesKey();
queryBindKey(bindKey);
return new HashQuery(queryPlanKey, bindKey);
}
@Override
@@ -9,6 +9,7 @@ import io.ebeaninternal.server.query.DefaultSqlRow;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
public class DRawSqlService implements SpiRawSqlService {
@@ -48,7 +49,26 @@ public class DRawSqlService implements SpiRawSqlService {
if (ret.containsKey(name)) {
name = combine(meta.getSchemaName(i), meta.getTableName(i), name);
}
ret.put(name, resultSet.getObject(i));
// convert (C/B)LOBs to java objects.
// A java.sql.Clob depends on an open connection, so storing this object in a map
// that is accessed later, when the connection is closed, will result in a "connection is closed" exception.
// From the java.sql.Clob documentation: "... which means that a Clob object contains a logical pointer to the SQL CLOB
// data rather than the data itself."
switch (meta.getColumnType(i)) {
case Types.CLOB:
case Types.NCLOB:
ret.put(name, resultSet.getString(i));
break;
case Types.BLOB:
ret.put(name, resultSet.getBytes(i));
break;
default:
ret.put(name, resultSet.getObject(i));
break;
}
}
return ret;
}
@@ -36,6 +36,7 @@ public class DataBind implements DataBinder {
private List<InputStream> inputStreams;
protected int pos;
private String json;
public DataBind(DataTimeZone dataTimeZone, PreparedStatement pstmt, Connection connection) {
this.dataTimeZone = dataTimeZone;
@@ -43,6 +44,19 @@ public class DataBind implements DataBinder {
this.connection = connection;
}
@Override
public void pushJson(String json) {
assert this.json == null; // we can only push one value
this.json = json;
}
@Override
public String popJson() {
String ret = json;
json = null;
return ret;
}
@Override
public StringBuilder append(Object entry) {
return bindLog.append(entry);
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.type;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import io.ebean.annotation.*;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.JsonConfig;
@@ -135,7 +134,7 @@ public final class DefaultTypeManager implements TypeManager {
this.postgres = isPostgres(config.getDatabasePlatform());
this.objectMapperPresent = config.getClassLoadConfig().isJacksonObjectMapperPresent();
this.objectMapper = (objectMapperPresent) ? initObjectMapper(config) : null;
this.jsonManager = (objectMapperPresent) ? new TypeJsonManager(postgres, objectMapper, config.isJsonDirtyByDefault()) : null;
this.jsonManager = (objectMapperPresent) ? new TypeJsonManager(postgres, objectMapper, config.getJsonMutationDetection()) : null;
this.extraTypeFactory = new DefaultTypeFactory(config);
this.arrayTypeListFactory = arrayTypeListFactory(config.getDatabasePlatform());
this.arrayTypeSetFactory = arrayTypeSetFactory(config.getDatabasePlatform());
@@ -426,7 +425,7 @@ public final class DefaultTypeManager implements TypeManager {
if (objectMapper == null) {
throw new IllegalArgumentException("Type [" + type + "] unsupported for @DbJson mapping - Jackson ObjectMapper not present");
}
return ScalarTypeJsonObjectMapper.createTypeFor(jsonManager, (AnnotatedField) prop.getJacksonField(), dbType, docType);
return ScalarTypeJsonObjectMapper.createTypeFor(jsonManager, prop, dbType, docType);
}
/**
@@ -557,7 +556,7 @@ public final class DefaultTypeManager implements TypeManager {
// no override or further mapping required
return scalarType;
}
ScalarTypeEnum<?> scalarEnum = (ScalarTypeEnum<?>)scalarType;
ScalarTypeEnum<?> scalarEnum = (ScalarTypeEnum<?>) scalarType;
if (scalarEnum != null && !scalarEnum.isOverrideBy(type)) {
if (type != null && !scalarEnum.isCompatible(type)) {
throw new IllegalStateException("Error mapping Enum type:" + enumType + " It is mapped using 2 different modes when only one is supported (ORDINAL, STRING or an Ebean mapping)");
@@ -674,7 +673,7 @@ public final class DefaultTypeManager implements TypeManager {
private Object initObjectMapper(DatabaseConfig config) {
Object objectMapper = config.getObjectMapper();
if (objectMapper == null) {
objectMapper = new ObjectMapper();
objectMapper = InitObjectMapper.init();
config.setObjectMapper(objectMapper);
}
return objectMapper;
@@ -750,12 +749,15 @@ public final class DefaultTypeManager implements TypeManager {
}
private void initialiseJavaTimeTypes(DatabaseConfig config) {
ZoneId zoneId = getZoneId(config);
typeMap.put(java.nio.file.Path.class, new ScalarTypePath());
addType(java.time.Period.class, new ScalarTypePeriod());
addType(java.time.LocalDate.class, new ScalarTypeLocalDate(jsonDate));
addType(java.time.LocalDateTime.class, new ScalarTypeLocalDateTime(jsonDateTime));
addType(OffsetDateTime.class, new ScalarTypeOffsetDateTime(jsonDateTime));
addType(ZonedDateTime.class, new ScalarTypeZonedDateTime(jsonDateTime));
addType(OffsetDateTime.class, new ScalarTypeOffsetDateTime(jsonDateTime, zoneId));
addType(ZonedDateTime.class, new ScalarTypeZonedDateTime(jsonDateTime, zoneId));
addType(Instant.class, new ScalarTypeInstant(jsonDateTime));
addType(DayOfWeek.class, new ScalarTypeDayOfWeek());
addType(Month.class, new ScalarTypeMonth());
@@ -771,6 +773,11 @@ public final class DefaultTypeManager implements TypeManager {
addType(Duration.class, (durationNanos) ? new ScalarTypeDurationWithNanos() : new ScalarTypeDuration());
}
private ZoneId getZoneId(DatabaseConfig config) {
final String dataTimeZone = config.getDataTimeZone();
return (dataTimeZone == null) ? ZoneOffset.systemDefault() : TimeZone.getTimeZone(dataTimeZone).toZoneId();
}
private void addType(Class<?> clazz, ScalarType<?> scalarType) {
typeMap.put(clazz, scalarType);
logicalMap.putIfAbsent(clazz.getSimpleName(), scalarType);
@@ -0,0 +1,22 @@
package io.ebeaninternal.server.type;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* Initialise the Jackson ObjectMapper.
*/
class InitObjectMapper {
/**
* Create and return the default ObjectMapper.
*/
static Object init() {
SimpleModule module = new SimpleModule();
module.addAbstractTypeMapping(Set.class, LinkedHashSet.class);
return new ObjectMapper().registerModule(module);
}
}
@@ -20,22 +20,29 @@ import java.util.Calendar;
public class RsetDataReader implements DataReader {
private static final int bufferSize = 512;
static final int clobBufferSize = 512;
static final int stringInitialSize = 512;
private final DataTimeZone dataTimeZone;
private final ResultSet rset;
protected int pos;
private String json;
public RsetDataReader(DataTimeZone dataTimeZone, ResultSet rset) {
this.dataTimeZone = dataTimeZone;
this.rset = rset;
}
@Override
public void pushJson(String json) {
this.json = json;
}
@Override
public String popJson() {
return json;
}
@Override
public void close() throws SQLException {
rset.close();
@@ -7,14 +7,13 @@ import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import io.ebean.annotation.MutationDetection;
import io.ebean.core.type.DataBinder;
import io.ebean.core.type.DataReader;
import io.ebean.core.type.DocPropertyType;
import io.ebean.core.type.ScalarType;
import io.ebean.text.TextException;
import io.ebeaninternal.json.ModifyAwareList;
import io.ebeaninternal.json.ModifyAwareMap;
import io.ebeaninternal.json.ModifyAwareSet;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import javax.persistence.PersistenceException;
import java.io.DataInput;
@@ -22,9 +21,6 @@ import java.io.DataOutput;
import java.io.IOException;
import java.sql.SQLException;
import java.sql.Types;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Supports @DbJson properties using Jackson ObjectMapper.
@@ -34,81 +30,85 @@ class ScalarTypeJsonObjectMapper {
/**
* Create and return the appropriate ScalarType.
*/
static ScalarType<?> createTypeFor(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
Class<?> type = field.getRawType();
if (Set.class.equals(type)) {
return new OmSet(jsonManager, field, dbType, docType);
static ScalarType<?> createTypeFor(TypeJsonManager jsonManager, DeployBeanProperty prop, int dbType, DocPropertyType docType) {
AnnotatedField field = (AnnotatedField) prop.getJacksonField();
MutationDetection mode = prop.getMutationDetection();
if (mode == MutationDetection.NONE) {
return new NoMutationDetection(jsonManager, field, dbType, docType);
} else if (mode != MutationDetection.DEFAULT) {
return new GenericObject(jsonManager, field, dbType, docType);
}
if (List.class.equals(type)) {
return new OmList(jsonManager, field, dbType, docType);
}
if (Map.class.equals(type)) {
return new OmMap(jsonManager, field, dbType);
}
return new GenericObject(jsonManager, field, dbType, type);
// using the global default MutationDetection mode (defaults to HASH)
prop.setMutationDetection(jsonManager.mutationDetection());
return new GenericObject(jsonManager, field, dbType, docType);
}
/**
* Maps any type (Object) using Jackson ObjectMapper.
* No mutation detection on this json property.
*/
private static class NoMutationDetection extends Base<Object> {
NoMutationDetection(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
super(Object.class, jsonManager, field, dbType, docType);
}
@Override
public boolean isMutable() {
return false;
}
@Override
public boolean isDirty(Object value) {
return false;
}
}
/**
* Supports HASH and SOURCE dirty detection modes.
*/
private static class GenericObject extends Base<Object> {
GenericObject(TypeJsonManager jsonManager, AnnotatedField field, int dbType, Class<?> rawType) {
super(Object.class, jsonManager, field, dbType, DocPropertyType.OBJECT, rawType);
}
}
/**
* Type for Sets wrapping the ObjectMapper Set as a ModifyAwareSet.
*/
@SuppressWarnings("rawtypes")
private static class OmSet extends Base<Set> {
OmSet(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
super(Set.class, jsonManager, field, dbType, docType);
GenericObject(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
super(Object.class, jsonManager, field, dbType, docType);
}
@Override
@SuppressWarnings("unchecked")
public Set read(DataReader reader) throws SQLException {
Set value = super.read(reader);
return value == null ? null : new ModifyAwareSet(value);
}
}
/**
* Type for Lists wrapping the ObjectMapper List as a ModifyAwareList.
*/
@SuppressWarnings("rawtypes")
private static class OmList extends Base<List> {
OmList(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
super(List.class, jsonManager, field, dbType, docType);
public boolean isJsonMapper() {
return true;
}
@Override
@SuppressWarnings("unchecked")
public List read(DataReader reader) throws SQLException {
List value = super.read(reader);
return value == null ? null : new ModifyAwareList(value);
}
}
/**
* Type for Map wrapping the ObjectMapper Map as a ModifyAwareMap.
*/
@SuppressWarnings("rawtypes")
private static class OmMap extends Base<Map> {
OmMap(TypeJsonManager jsonManager, AnnotatedField field, int dbType) {
super(Map.class, jsonManager, field, dbType, DocPropertyType.OBJECT);
public Object read(DataReader reader) throws SQLException {
String json = reader.getString();
// pushJson such that we MD5 and store on EntityBeanIntercept later
reader.pushJson(json);
if (json == null || json.isEmpty()) {
return null;
}
try {
return objectReader.readValue(json, deserType);
} catch (IOException e) {
throw new TextException("Failed to parse JSON [{}] as " + deserType, json, e);
}
}
@Override
@SuppressWarnings("unchecked")
public Map read(DataReader reader) throws SQLException {
Map value = super.read(reader);
return value == null ? null : new ModifyAwareMap(value);
public void bind(DataBinder binder, Object value) throws SQLException {
// popJson as dirty detection already converted to json string
String rawJson = binder.popJson();
if (rawJson == null && value != null) {
rawJson = formatValue(value); // not expected, need to check?
}
if (pgType != null) {
binder.setObject(PostgresHelper.asObject(pgType, rawJson));
} else {
if (value == null) {
// use varchar, otherwise SqlServer/db2 will fail with 'Invalid JDBC data type 5.001.'
binder.setNull(Types.VARCHAR);
} else {
binder.setString(rawJson);
}
}
}
}
@@ -118,44 +118,27 @@ class ScalarTypeJsonObjectMapper {
*/
private static abstract class Base<T> extends ScalarTypeBase<T> {
private final ObjectWriter objectWriter;
private final ObjectMapper objectReader;
private final JavaType deserType;
private final String pgType;
protected final ObjectWriter objectWriter;
protected final ObjectMapper objectReader;
protected final JavaType deserType;
protected final String pgType;
private final DocPropertyType docType;
private final TypeJsonManager.DirtyHandler dirtyHandler;
Base(Class<T> cls, TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
this(cls, jsonManager, field, dbType, docType, cls);
}
Base(Class<T> cls, TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType, Class<?> rawType) {
super(cls, false, dbType);
this.objectReader = jsonManager.objectMapper();
this.pgType = jsonManager.postgresType(dbType);
this.docType = docType;
this.dirtyHandler = jsonManager.dirtyHandler(cls, rawType);
final JacksonTypeHelper helper = new JacksonTypeHelper(field, objectReader);
this.deserType = helper.type();
this.objectWriter = helper.objectWriter();
}
/**
* Consider as a mutable type. Use the isDirty() method to check for dirty state.
*/
@Override
public boolean isMutable() {
return true;
}
/**
* Return true if the value should be considered dirty (and included in an update).
*/
@Override
public boolean isDirty(Object value) {
return dirtyHandler.isDirty(value);
}
@Override
public T read(DataReader reader) throws SQLException {
String json = reader.getString();
@@ -15,8 +15,11 @@ import static io.ebeaninternal.server.type.IsoJsonDateTimeParser.formatIso;
*/
public class ScalarTypeOffsetDateTime extends ScalarTypeBaseDateTime<OffsetDateTime> {
public ScalarTypeOffsetDateTime(JsonConfig.DateTime mode) {
private final ZoneId zoneId;
public ScalarTypeOffsetDateTime(JsonConfig.DateTime mode, ZoneId zoneId) {
super(mode, OffsetDateTime.class, false, Types.TIMESTAMP);
this.zoneId = zoneId;
}
@Override
@@ -46,7 +49,7 @@ public class ScalarTypeOffsetDateTime extends ScalarTypeBaseDateTime<OffsetDateT
@Override
public OffsetDateTime convertFromInstant(Instant ts) {
return OffsetDateTime.ofInstant(ts, ZoneId.systemDefault());
return OffsetDateTime.ofInstant(ts, zoneId);
}
@Override
@@ -13,8 +13,11 @@ import java.time.ZonedDateTime;
*/
public class ScalarTypeZonedDateTime extends ScalarTypeBaseDateTime<ZonedDateTime> {
public ScalarTypeZonedDateTime(JsonConfig.DateTime mode) {
private final ZoneId zoneId;
public ScalarTypeZonedDateTime(JsonConfig.DateTime mode, ZoneId zoneId) {
super(mode, ZonedDateTime.class, false, Types.TIMESTAMP);
this.zoneId = zoneId;
}
@Override
@@ -44,7 +47,7 @@ public class ScalarTypeZonedDateTime extends ScalarTypeBaseDateTime<ZonedDateTim
@Override
public ZonedDateTime convertFromInstant(Instant ts) {
return ZonedDateTime.ofInstant(ts, ZoneId.systemDefault());
return ZonedDateTime.ofInstant(ts, zoneId);
}
@Override
@@ -2,25 +2,23 @@ package io.ebeaninternal.server.type;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.ebean.ModifyAwareType;
import io.ebean.config.DatabaseConfig;
import io.ebean.annotation.MutationDetection;
import io.ebean.config.dbplatform.DbPlatformType;
class TypeJsonManager {
interface DirtyHandler {
boolean isDirty(Object value);
}
private final boolean postgres;
private final ObjectMapper objectMapper;
private final DirtyHandler defaultHandler;
private final DirtyHandler modifyAwareHandler;
private final MutationDetection mutationDetection;
TypeJsonManager(boolean postgres, Object objectMapper, boolean defaultDirty) {
TypeJsonManager(boolean postgres, Object objectMapper, MutationDetection mutationDetection) {
this.postgres = postgres;
this.objectMapper = (ObjectMapper) objectMapper;
this.defaultHandler = new DefaultHandler(defaultDirty);
this.modifyAwareHandler = new ModifyAwareHandler();
this.mutationDetection = mutationDetection;
}
MutationDetection mutationDetection() {
return mutationDetection;
}
ObjectMapper objectMapper() {
@@ -39,17 +37,6 @@ class TypeJsonManager {
return null;
}
/**
* Return the DirtyHandler to use.
*/
DirtyHandler dirtyHandler(Class<?> cls, Class<?> rawType) {
if (!Object.class.equals(cls) || ModifyAwareType.class.isAssignableFrom(rawType)) {
// Set, List and Map are modify aware
return modifyAwareHandler;
}
return defaultHandler;
}
/**
* Return true if the value should be considered dirty (and included in an update).
*/
@@ -71,28 +58,4 @@ class TypeJsonManager {
}
}
static final class ModifyAwareHandler implements DirtyHandler {
@Override
public boolean isDirty(Object value) {
return checkModifyAware(value);
}
}
/**
* Effectively constant based on {@link DatabaseConfig#isJsonDirtyByDefault()}
*/
static final class DefaultHandler implements DirtyHandler {
private final boolean dirty;
DefaultHandler(boolean dirty) {
this.dirty = dirty;
}
@Override
public boolean isDirty(Object value) {
return dirty;
}
}
}
@@ -0,0 +1,20 @@
package io.ebeaninternal.server.util;
import java.nio.charset.StandardCharsets;
import java.util.zip.CRC32;
/**
* Compute a checksum for String content. Use when we desire cheaper option than MD5.
*/
public final class Checksum {
/**
* Return the checksum for the given String input.
*/
public static long checksum(String input) {
CRC32 checksum = new CRC32();
final byte[] bytes = input.getBytes(StandardCharsets.UTF_8);
checksum.update(bytes, 0, bytes.length);
return checksum.getValue();
}
}
@@ -8,10 +8,15 @@ public final class Md5 {
/**
* Return the MD5 hash of the underlying sql.
*/
public static String hash(String content) {
public static String hash(String... values) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
return digestToHex(md.digest(content.getBytes(StandardCharsets.UTF_8)));
for (String val : values) {
if (val != null) {
md.update(val.getBytes(StandardCharsets.UTF_8));
}
}
return digestToHex(md.digest());
} catch (Exception e) {
throw new RuntimeException("MD5 hashing failed", e);
}
@@ -21,8 +26,7 @@ public final class Md5 {
* Convert the digest into a hex value.
*/
private static String digestToHex(byte[] digest) {
StringBuilder sb = new StringBuilder();
StringBuilder sb = new StringBuilder(32);
for (byte aDigest : digest) {
sb.append(Integer.toString((aDigest & 0xff) + 0x100, 16).substring(1));
}
@@ -90,14 +90,14 @@ public abstract class BaseTestCase {
}
protected List<MetaTimedMetric> visitTimedMetrics() {
return collectMetrics().getTimedMetrics();
return collectMetrics().timedMetrics();
}
protected List<MetaTimedMetric> sqlMetrics() {
List<MetaTimedMetric> timedMetrics = visitTimedMetrics();
return timedMetrics.stream()
.filter((it) -> it.getName().startsWith("sql.") || it.getName().startsWith("orm."))
.filter((it) -> it.name().startsWith("sql.") || it.name().startsWith("orm."))
.collect(Collectors.toList());
}
@@ -191,13 +191,13 @@ public class DtoQuery2Test extends BaseTestCase {
BasicMetricVisitor basic = new BasicMetricVisitor(false, true, true, true);
server().getMetaInfoManager().visitMetrics(basic);
List<MetaQueryMetric> stats = basic.getQueryMetrics();
List<MetaQueryMetric> stats = basic.queryMetrics();
assertThat(stats).hasSize(1);
MetaQueryMetric queryMetric = stats.get(0);
assertThat(queryMetric.getLabel()).isEqualTo("basic");
assertThat(queryMetric.getCount()).isEqualTo(3);
assertThat(queryMetric.getName()).isEqualTo("dto.DCust_basic");
assertThat(queryMetric.label()).isEqualTo("basic");
assertThat(queryMetric.count()).isEqualTo(3);
assertThat(queryMetric.name()).isEqualTo("dto.DCust_basic");
server().findDto(DCust.class, "select c4.id, c4.name from o_customer c4 where lower(c4.name) = :name")
@@ -207,7 +207,7 @@ public class DtoQuery2Test extends BaseTestCase {
BasicMetricVisitor metric2 = server().getMetaInfoManager().visitBasic();
stats = metric2.getQueryMetrics();
stats = metric2.queryMetrics();
assertThat(stats).hasSize(2);
log.info("stats " + stats);
Loaded 100 of 245 files, more files were not shown because too many files have changed in this diff. Show more