mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-11-12 22:41:35 +00:00
Merge remote-tracking branch 'ebean/master' into pr/enh/softreference_in_cache
This commit is contained in:
commit
5b05510bb9
193 files changed
+2970
-1700
No files matched your search
@@ -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();
|
||||
}
|
||||
@@ -2329,13 +2328,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 +2351,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.
|
||||
*/
|
||||
@@ -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);
|
||||
|
||||
+1
-5
@@ -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);
|
||||
|
||||
+17
-14
@@ -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;
|
||||
}
|
||||
}
|
||||
+12
-46
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
+42
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -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);
|
||||
}
|
||||
+8
-4
@@ -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());
|
||||
}
|
||||
}
|
||||
+4
-34
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
+69
-86
@@ -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();
|
||||
if (json == null || json.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
// pushJson such that we MD5 and store on EntityBeanIntercept later
|
||||
reader.pushJson(json);
|
||||
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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -27,12 +27,12 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
|
||||
@AfterClass
|
||||
public static void reportStats() {
|
||||
ServerMetrics metrics = DB.getDefault().getMetaInfoManager().collectMetrics();
|
||||
for (MetaQueryMetric metric : metrics.getQueryMetrics()) {
|
||||
for (MetaQueryMetric metric : metrics.queryMetrics()) {
|
||||
System.out.println(metric);
|
||||
}
|
||||
|
||||
System.out.println("-- transaction metrics --");
|
||||
for (MetaTimedMetric metric : metrics.getTimedMetrics()) {
|
||||
for (MetaTimedMetric metric : metrics.timedMetrics()) {
|
||||
System.out.println(metric);
|
||||
}
|
||||
}
|
||||
@@ -59,15 +59,15 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
|
||||
|
||||
ServerMetrics metrics = collectMetrics();
|
||||
|
||||
List<MetaQueryMetric> stats = metrics.getQueryMetrics();
|
||||
List<MetaQueryMetric> stats = metrics.queryMetrics();
|
||||
for (MetaQueryMetric stat : stats) {
|
||||
long meanMicros = stat.getMean();
|
||||
long meanMicros = stat.mean();
|
||||
assertThat(meanMicros).isLessThan(900_000);
|
||||
assertThat(stat.getLocation()).isSameAs(loc0.location());
|
||||
assertThat(stat.location()).isSameAs(loc0.location());
|
||||
}
|
||||
|
||||
assertThat(stats).hasSize(1);
|
||||
assertThat(stats.get(0).getCount()).isEqualTo(4);
|
||||
assertThat(stats.get(0).count()).isEqualTo(4);
|
||||
}
|
||||
|
||||
@ForPlatform(Platform.H2)
|
||||
|
||||
@@ -42,14 +42,14 @@ public class DtoQueryTest extends BaseTestCase {
|
||||
|
||||
ServerMetrics metrics = collectMetrics();
|
||||
|
||||
List<MetaQueryMetric> stats = metrics.getQueryMetrics();
|
||||
List<MetaQueryMetric> stats = metrics.queryMetrics();
|
||||
for (MetaQueryMetric stat : stats) {
|
||||
long meanMicros = stat.getMean();
|
||||
long meanMicros = stat.mean();
|
||||
assertThat(meanMicros).isLessThan(900_000);
|
||||
}
|
||||
|
||||
assertThat(stats).hasSize(1);
|
||||
assertThat(stats.get(0).getCount()).isEqualTo(1);
|
||||
assertThat(stats.get(0).count()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -283,13 +283,13 @@ public class DtoQueryTest 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")
|
||||
@@ -299,7 +299,7 @@ public class DtoQueryTest extends BaseTestCase {
|
||||
|
||||
ServerMetrics metric2 = server().getMetaInfoManager().collectMetrics();
|
||||
|
||||
stats = metric2.getQueryMetrics();
|
||||
stats = metric2.queryMetrics();
|
||||
assertThat(stats).hasSize(2);
|
||||
|
||||
log.info("stats " + stats);
|
||||
|
||||
@@ -11,7 +11,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class EbeanServer_refresh {
|
||||
|
||||
@@ -39,8 +39,11 @@ public class EbeanServer_refresh {
|
||||
|
||||
assertEquals(rows, 1);
|
||||
|
||||
basic.setName("modify");
|
||||
assertTrue(DB.getBeanState(basic).isDirty());
|
||||
server.refresh(basic);
|
||||
assertEquals(basic.getStatus(), EBasic.Status.ACTIVE);
|
||||
assertFalse(DB.getBeanState(basic).isDirty());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -39,10 +39,10 @@ public class UpdateQueryTest extends BaseTestCase {
|
||||
assertSql(query).contains("update o_customer set status=?, updtime=? where status = ? and id > ?");
|
||||
|
||||
ServerMetrics metrics = collectMetrics();
|
||||
List<MetaQueryMetric> ormQueryMetrics = metrics.getQueryMetrics();
|
||||
List<MetaQueryMetric> ormQueryMetrics = metrics.queryMetrics();
|
||||
assertThat(ormQueryMetrics).hasSize(1);
|
||||
assertThat(ormQueryMetrics.get(0).getType()).isEqualTo(Customer.class);
|
||||
assertThat(ormQueryMetrics.get(0).getLabel()).isEqualTo("updateActive");
|
||||
assertThat(ormQueryMetrics.get(0).type()).isEqualTo(Customer.class);
|
||||
assertThat(ormQueryMetrics.get(0).label()).isEqualTo("updateActive");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -69,10 +69,10 @@ public class UpdateQueryTest extends BaseTestCase {
|
||||
assertSql(sql.get(0)).contains("update o_customer set status = status");
|
||||
|
||||
ServerMetrics metrics = collectMetrics();
|
||||
List<MetaQueryMetric> ormQueryMetrics = metrics.getQueryMetrics();
|
||||
List<MetaQueryMetric> ormQueryMetrics = metrics.queryMetrics();
|
||||
assertThat(ormQueryMetrics).hasSize(1);
|
||||
assertThat(ormQueryMetrics.get(0).getType()).isEqualTo(Customer.class);
|
||||
assertThat(ormQueryMetrics.get(0).getLabel()).isEqualTo("updateAll");
|
||||
assertThat(ormQueryMetrics.get(0).type()).isEqualTo(Customer.class);
|
||||
assertThat(ormQueryMetrics.get(0).label()).isEqualTo("updateAll");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -4,7 +4,6 @@ import io.ebean.bean.BeanCollection;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
@@ -13,9 +12,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class BeanListTest {
|
||||
|
||||
private Object object1 = new Object();
|
||||
private Object object2 = new Object();
|
||||
private Object object3 = new Object();
|
||||
private final Object object1 = new Object();
|
||||
private final Object object2 = new Object();
|
||||
private final Object object3 = new Object();
|
||||
|
||||
private List<Object> all() {
|
||||
List<Object> all = new ArrayList<>();
|
||||
@@ -33,7 +32,7 @@ public class BeanListTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_setModifyListening_null() throws Exception {
|
||||
public void test_setModifyListening_null() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>();
|
||||
list.setModifyListening(null);
|
||||
@@ -45,7 +44,7 @@ public class BeanListTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_setModifyListening_none() throws Exception {
|
||||
public void test_setModifyListening_none() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>();
|
||||
list.setModifyListening(BeanCollection.ModifyListenMode.NONE);
|
||||
@@ -57,28 +56,28 @@ public class BeanListTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd() throws Exception {
|
||||
public void testAdd() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>();
|
||||
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
list.add(object1);
|
||||
|
||||
assertThat(list.getModifyAdditions()).containsExactly(object1);
|
||||
assertThat(list.getModifyAdditions()).containsOnly(object1);
|
||||
assertThat(list.getModifyRemovals()).isEmpty();
|
||||
|
||||
list.add(object1);
|
||||
assertThat(list.getModifyAdditions()).containsExactly(object1);
|
||||
assertThat(list.getModifyAdditions()).containsOnly(object1);
|
||||
|
||||
list.add(object2);
|
||||
assertThat(list.getModifyAdditions()).containsExactly(object1, object2);
|
||||
assertThat(list.getModifyAdditions()).containsOnly(object1, object2);
|
||||
|
||||
list.remove(object1);
|
||||
assertThat(list.getModifyAdditions()).containsExactly(object2);
|
||||
assertThat(list.getModifyAdditions()).containsOnly(object2);
|
||||
assertThat(list.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddAll_given_emptyStart() throws Exception {
|
||||
public void testAddAll_given_emptyStart() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>();
|
||||
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -86,12 +85,12 @@ public class BeanListTest {
|
||||
// act
|
||||
list.addAll(all());
|
||||
|
||||
assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3);
|
||||
assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3);
|
||||
assertThat(list.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_removals_DeleteThenAddBack_expect_noChange() throws Exception {
|
||||
public void test_removals_DeleteThenAddBack_expect_noChange() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>(some());
|
||||
list.setModifyListening(BeanCollection.ModifyListenMode.REMOVALS);
|
||||
@@ -106,33 +105,33 @@ public class BeanListTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_sort_whenAll_expect_noChange() throws Exception {
|
||||
public void test_sort_whenAll_expect_noChange() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>(all());
|
||||
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
|
||||
// act
|
||||
Collections.sort(list, Comparator.comparingInt(Object::hashCode));
|
||||
list.sort(Comparator.comparingInt(Object::hashCode));
|
||||
|
||||
assertThat(list.getModifyRemovals()).isEmpty();
|
||||
assertThat(list.getModifyAdditions()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_sort_whenRemovals_expect_noChange() throws Exception {
|
||||
public void test_sort_whenRemovals_expect_noChange() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>(all());
|
||||
list.setModifyListening(BeanCollection.ModifyListenMode.REMOVALS);
|
||||
|
||||
// act
|
||||
Collections.sort(list, Comparator.comparingInt(Object::hashCode));
|
||||
list.sort(Comparator.comparingInt(Object::hashCode));
|
||||
|
||||
assertThat(list.getModifyRemovals()).isEmpty();
|
||||
assertThat(list.getModifyAdditions()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd_given_someAlreadyIn() throws Exception {
|
||||
public void testAdd_given_someAlreadyIn() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>(some());
|
||||
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -143,12 +142,12 @@ public class BeanListTest {
|
||||
assertThat(list.contains(object2)).isTrue();
|
||||
list.add(object2); // object2 added as List allows duplicates
|
||||
|
||||
assertThat(list.getModifyAdditions()).containsExactly(object1, object2);
|
||||
assertThat(list.getModifyAdditions()).containsOnly(object1, object2);
|
||||
assertThat(list.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddSome_given_someAlreadyIn() throws Exception {
|
||||
public void testAddSome_given_someAlreadyIn() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>(some());
|
||||
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -156,43 +155,43 @@ public class BeanListTest {
|
||||
// act
|
||||
list.addAll(all());
|
||||
|
||||
assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3);
|
||||
assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3);
|
||||
assertThat(list.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemove_given_beansInAdditions() throws Exception {
|
||||
public void testRemove_given_beansInAdditions() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>();
|
||||
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
list.addAll(all());
|
||||
assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3);
|
||||
assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3);
|
||||
|
||||
// act
|
||||
list.remove(object2);
|
||||
list.remove(object3);
|
||||
|
||||
assertThat(list.getModifyAdditions()).containsExactly(object1);
|
||||
assertThat(list.getModifyAdditions()).containsOnly(object1);
|
||||
assertThat(list.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAll_given_beansInAdditions() throws Exception {
|
||||
public void testRemoveAll_given_beansInAdditions() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>();
|
||||
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
list.addAll(all());
|
||||
assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3);
|
||||
assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3);
|
||||
|
||||
// act
|
||||
list.removeAll(some());
|
||||
|
||||
assertThat(list.getModifyAdditions()).containsExactly(object1);
|
||||
assertThat(list.getModifyAdditions()).containsOnly(object1);
|
||||
assertThat(list.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemove_given_beansNotInAdditions() throws Exception {
|
||||
public void testRemove_given_beansNotInAdditions() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>(all());
|
||||
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -203,11 +202,11 @@ public class BeanListTest {
|
||||
|
||||
// assert
|
||||
assertThat(list.getModifyAdditions()).isEmpty();
|
||||
assertThat(list.getModifyRemovals()).containsExactly(object2, object3);
|
||||
assertThat(list.getModifyRemovals()).containsOnly(object2, object3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAll_given_beansNotInAdditions() throws Exception {
|
||||
public void testRemoveAll_given_beansNotInAdditions() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>(all());
|
||||
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -217,11 +216,11 @@ public class BeanListTest {
|
||||
|
||||
// assert
|
||||
assertThat(list.getModifyAdditions()).isEmpty();
|
||||
assertThat(list.getModifyRemovals()).containsExactly(object2, object3);
|
||||
assertThat(list.getModifyRemovals()).containsOnly(object2, object3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClear() throws Exception {
|
||||
public void testClear() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>(all());
|
||||
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -230,12 +229,12 @@ public class BeanListTest {
|
||||
list.clear();
|
||||
|
||||
//assert
|
||||
assertThat(list.getModifyRemovals()).containsExactly(object1, object2, object3);
|
||||
assertThat(list.getModifyRemovals()).containsOnly(object1, object2, object3);
|
||||
assertThat(list.getModifyAdditions()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClear_given_someBeansInAdditions() throws Exception {
|
||||
public void testClear_given_someBeansInAdditions() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>();
|
||||
list.add(object1);
|
||||
@@ -247,27 +246,27 @@ public class BeanListTest {
|
||||
list.clear();
|
||||
|
||||
//assert
|
||||
assertThat(list.getModifyRemovals()).containsExactly(object1);
|
||||
assertThat(list.getModifyRemovals()).containsOnly(object1);
|
||||
assertThat(list.getModifyAdditions()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetainAll_given_beansInAdditions() throws Exception {
|
||||
public void testRetainAll_given_beansInAdditions() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>();
|
||||
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
list.addAll(all());
|
||||
assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3);
|
||||
assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3);
|
||||
|
||||
// act
|
||||
list.retainAll(some());
|
||||
|
||||
assertThat(list.getModifyAdditions()).containsExactly(object2, object3);
|
||||
assertThat(list.getModifyAdditions()).containsOnly(object2, object3);
|
||||
assertThat(list.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetainAll_given_someBeansInAdditions() throws Exception {
|
||||
public void testRetainAll_given_someBeansInAdditions() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>();
|
||||
list.add(object1);
|
||||
@@ -278,12 +277,12 @@ public class BeanListTest {
|
||||
// act
|
||||
list.retainAll(some());
|
||||
|
||||
assertThat(list.getModifyAdditions()).containsExactly(object3);
|
||||
assertThat(list.getModifyRemovals()).containsExactly(object1);
|
||||
assertThat(list.getModifyAdditions()).containsOnly(object3);
|
||||
assertThat(list.getModifyRemovals()).containsOnly(object1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetainAll_given_noBeansInAdditions() throws Exception {
|
||||
public void testRetainAll_given_noBeansInAdditions() {
|
||||
|
||||
BeanList<Object> list = new BeanList<>(all());
|
||||
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -291,7 +290,7 @@ public class BeanListTest {
|
||||
// act
|
||||
list.retainAll(some());
|
||||
|
||||
assertThat(list.getModifyRemovals()).containsExactly(object1);
|
||||
assertThat(list.getModifyRemovals()).containsOnly(object1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -34,30 +34,30 @@ public class BeanMapTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd() throws Exception {
|
||||
public void testAdd() {
|
||||
|
||||
BeanMap<String, Object> map = new BeanMap<>();
|
||||
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
map.put("1", object1);
|
||||
map.put("4", null);
|
||||
|
||||
assertThat(map.getModifyAdditions()).containsExactly(object1);
|
||||
assertThat(map.getModifyAdditions()).containsOnly(object1);
|
||||
assertThat(map.getModifyRemovals()).isEmpty();
|
||||
|
||||
map.put("1", object1);
|
||||
map.put("4", null);
|
||||
assertThat(map.getModifyAdditions()).containsExactly(object1);
|
||||
assertThat(map.getModifyAdditions()).containsOnly(object1);
|
||||
|
||||
map.put("2", object2);
|
||||
assertThat(map.getModifyAdditions()).containsExactly(object1, object2);
|
||||
assertThat(map.getModifyAdditions()).containsOnly(object1, object2);
|
||||
|
||||
map.remove("1");
|
||||
assertThat(map.getModifyAdditions()).containsExactly(object2);
|
||||
assertThat(map.getModifyAdditions()).containsOnly(object2);
|
||||
assertThat(map.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddAll_given_emptyStart() throws Exception {
|
||||
public void testAddAll_given_emptyStart() {
|
||||
|
||||
BeanMap<String, Object> set = new BeanMap<>();
|
||||
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -65,28 +65,28 @@ public class BeanMapTest {
|
||||
// act
|
||||
set.putAll(all());
|
||||
|
||||
assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3);
|
||||
assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3);
|
||||
assertThat(set.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd_given_someAlreadyIn() throws Exception {
|
||||
public void testAdd_given_someAlreadyIn() {
|
||||
|
||||
BeanMap<String, Object> map = new BeanMap<>(some());
|
||||
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
|
||||
// act
|
||||
assertThat(map.values().contains(object1)).isFalse();
|
||||
assertThat(map.containsValue(object1)).isFalse();
|
||||
map.put("1", object1);
|
||||
assertThat(map.values().contains(object2)).isTrue();
|
||||
assertThat(map.containsValue(object2)).isTrue();
|
||||
map.put("2", object2);
|
||||
|
||||
assertThat(map.getModifyAdditions()).containsExactly(object1);
|
||||
assertThat(map.getModifyAdditions()).containsOnly(object1);
|
||||
assertThat(map.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddSome_given_someAlreadyIn() throws Exception {
|
||||
public void testAddSome_given_someAlreadyIn() {
|
||||
|
||||
BeanMap<String, Object> map = new BeanMap<>(some());
|
||||
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -94,44 +94,44 @@ public class BeanMapTest {
|
||||
// act
|
||||
map.putAll(all());
|
||||
|
||||
assertThat(map.getModifyAdditions()).containsExactly(object1);
|
||||
assertThat(map.getModifyAdditions()).containsOnly(object1);
|
||||
assertThat(map.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemove_given_beansInAdditions() throws Exception {
|
||||
public void testRemove_given_beansInAdditions() {
|
||||
|
||||
BeanMap<String, Object> map = new BeanMap<>();
|
||||
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
map.putAll(all());
|
||||
assertThat(map.getModifyAdditions()).containsExactly(object1, object2, object3);
|
||||
assertThat(map.getModifyAdditions()).containsOnly(object1, object2, object3);
|
||||
|
||||
// act
|
||||
map.remove("2");
|
||||
map.remove("3");
|
||||
|
||||
assertThat(map.getModifyAdditions()).containsExactly(object1);
|
||||
assertThat(map.getModifyAdditions()).containsOnly(object1);
|
||||
assertThat(map.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAll_given_beansInAdditions() throws Exception {
|
||||
public void testRemoveAll_given_beansInAdditions() {
|
||||
|
||||
BeanMap<String, Object> map = new BeanMap<>();
|
||||
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
map.putAll(all());
|
||||
assertThat(map.getModifyAdditions()).containsExactly(object1, object2, object3);
|
||||
assertThat(map.getModifyAdditions()).containsOnly(object1, object2, object3);
|
||||
|
||||
// act
|
||||
map.remove("2");
|
||||
map.remove("3");
|
||||
|
||||
assertThat(map.getModifyAdditions()).containsExactly(object1);
|
||||
assertThat(map.getModifyAdditions()).containsOnly(object1);
|
||||
assertThat(map.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemove_given_beansNotInAdditions() throws Exception {
|
||||
public void testRemove_given_beansNotInAdditions() {
|
||||
|
||||
BeanMap<String, Object> map = new BeanMap<>(all());
|
||||
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -142,11 +142,11 @@ public class BeanMapTest {
|
||||
|
||||
// assert
|
||||
assertThat(map.getModifyAdditions()).isEmpty();
|
||||
assertThat(map.getModifyRemovals()).containsExactly(object2, object3);
|
||||
assertThat(map.getModifyRemovals()).containsOnly(object2, object3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAll_given_beansNotInAdditions() throws Exception {
|
||||
public void testRemoveAll_given_beansNotInAdditions() {
|
||||
|
||||
BeanMap<String, Object> map = new BeanMap<>(all());
|
||||
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -157,11 +157,11 @@ public class BeanMapTest {
|
||||
|
||||
// assert
|
||||
assertThat(map.getModifyAdditions()).isEmpty();
|
||||
assertThat(map.getModifyRemovals()).containsExactly(object2, object3);
|
||||
assertThat(map.getModifyRemovals()).containsOnly(object2, object3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClear() throws Exception {
|
||||
public void testClear() {
|
||||
|
||||
BeanMap<String, Object> map = new BeanMap<>(all());
|
||||
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -170,12 +170,12 @@ public class BeanMapTest {
|
||||
map.clear();
|
||||
|
||||
//assert
|
||||
assertThat(map.getModifyRemovals()).containsExactly(object1, object2, object3);
|
||||
assertThat(map.getModifyRemovals()).containsOnly(object1, object2, object3);
|
||||
assertThat(map.getModifyAdditions()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClear_given_someBeansInAdditions() throws Exception {
|
||||
public void testClear_given_someBeansInAdditions() {
|
||||
|
||||
BeanMap<String, EBasic> map = newModifyListeningMap();
|
||||
map.put("2", object2);
|
||||
@@ -185,7 +185,7 @@ public class BeanMapTest {
|
||||
map.clear();
|
||||
|
||||
//assert
|
||||
assertThat(map.getModifyRemovals()).containsExactly(object1);
|
||||
assertThat(map.getModifyRemovals()).containsOnly(object1);
|
||||
assertThat(map.getModifyAdditions()).isEmpty();
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ public class BeanMapTest {
|
||||
assertThat(map).doesNotContainKeys("1");
|
||||
assertThat(map.get("1")).isNull();
|
||||
|
||||
assertThat(map.getModifyRemovals()).containsExactly(object1);
|
||||
assertThat(map.getModifyRemovals()).containsOnly(object1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -245,7 +245,7 @@ public class BeanMapTest {
|
||||
assertThat(map).isEmpty();
|
||||
assertThat(keySet).isEmpty();
|
||||
|
||||
assertThat(map.getModifyRemovals()).containsExactly(object1, object2);
|
||||
assertThat(map.getModifyRemovals()).containsOnly(object1, object2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -258,20 +258,14 @@ public class BeanMapTest {
|
||||
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
|
||||
final Set<String> keySet = map.keySet();
|
||||
final Iterator<String> iterator = keySet.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final String key = iterator.next();
|
||||
if (key.equals("2")) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
keySet.removeIf(key -> key.equals("2"));
|
||||
|
||||
assertThat(map).hasSize(2);
|
||||
assertThat(keySet).hasSize(2);
|
||||
assertThat(keySet).containsExactly("1", "3");
|
||||
assertThat(map).containsKeys("1", "3");
|
||||
|
||||
assertThat(map.getModifyRemovals()).containsExactly(object2);
|
||||
assertThat(map.getModifyRemovals()).containsOnly(object2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -294,7 +288,7 @@ public class BeanMapTest {
|
||||
assertThat(keySet).containsExactly("1", "4");
|
||||
assertThat(map).containsKeys("1", "4");
|
||||
|
||||
assertThat(map.getModifyRemovals()).containsExactly(object2, object3, object5);
|
||||
assertThat(map.getModifyRemovals()).containsOnly(object2, object3, object5);
|
||||
}
|
||||
|
||||
|
||||
@@ -318,7 +312,7 @@ public class BeanMapTest {
|
||||
assertThat(keySet).containsExactly("2", "3", "5");
|
||||
assertThat(map).containsKeys("2", "3", "5");
|
||||
|
||||
assertThat(map.getModifyRemovals()).containsExactly(object1, object4);
|
||||
assertThat(map.getModifyRemovals()).containsOnly(object1, object4);
|
||||
}
|
||||
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
@@ -348,7 +342,7 @@ public class BeanMapTest {
|
||||
|
||||
assertThat(entries).isEmpty();
|
||||
assertThat(map).isEmpty();
|
||||
assertThat(map.getModifyRemovals()).containsExactly(object1);
|
||||
assertThat(map.getModifyRemovals()).containsOnly(object1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -365,7 +359,7 @@ public class BeanMapTest {
|
||||
assertThat(existed22).isFalse();
|
||||
|
||||
assertThat(map).hasSize(4);
|
||||
assertThat(map.getModifyRemovals()).containsExactly(object1);
|
||||
assertThat(map.getModifyRemovals()).containsOnly(object1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -395,7 +389,7 @@ public class BeanMapTest {
|
||||
}
|
||||
assertThat(map).hasSize(3);
|
||||
assertThat(entries).hasSize(3);
|
||||
assertThat(map.getModifyRemovals()).containsExactly(object2, object5);
|
||||
assertThat(map.getModifyRemovals()).containsOnly(object2, object5);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -406,7 +400,7 @@ public class BeanMapTest {
|
||||
entries.removeAll(asList(new AbstractMap.SimpleEntry<>("1", object1), new AbstractMap.SimpleEntry<>("3", object4), new AbstractMap.SimpleEntry<>("4", object4)));
|
||||
assertThat(map).hasSize(3);
|
||||
assertThat(entries).hasSize(3);
|
||||
assertThat(map.getModifyRemovals()).containsExactly(object1, object4);
|
||||
assertThat(map.getModifyRemovals()).containsOnly(object1, object4);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -417,7 +411,7 @@ public class BeanMapTest {
|
||||
entries.retainAll(asList(new AbstractMap.SimpleEntry<>("1", object1), new AbstractMap.SimpleEntry<>("3", object4), new AbstractMap.SimpleEntry<>("4", object4)));
|
||||
assertThat(map).hasSize(2);
|
||||
assertThat(entries).hasSize(2);
|
||||
assertThat(map.getModifyRemovals()).containsExactly(object2, object3, object5);
|
||||
assertThat(map.getModifyRemovals()).containsOnly(object2, object3, object5);
|
||||
}
|
||||
|
||||
private BeanMap<String, EBasic> newModifyListeningMap() {
|
||||
|
||||
@@ -31,28 +31,28 @@ public class BeanSetTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd() throws Exception {
|
||||
public void testAdd() {
|
||||
|
||||
BeanSet<Object> set = new BeanSet<>();
|
||||
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
set.add(object1);
|
||||
|
||||
assertThat(set.getModifyAdditions()).containsExactly(object1);
|
||||
assertThat(set.getModifyAdditions()).containsOnly(object1);
|
||||
assertThat(set.getModifyRemovals()).isEmpty();
|
||||
|
||||
set.add(object1);
|
||||
assertThat(set.getModifyAdditions()).containsExactly(object1);
|
||||
assertThat(set.getModifyAdditions()).containsOnly(object1);
|
||||
|
||||
set.add(object2);
|
||||
assertThat(set.getModifyAdditions()).containsExactly(object1, object2);
|
||||
assertThat(set.getModifyAdditions()).containsOnly(object1, object2);
|
||||
|
||||
set.remove(object1);
|
||||
assertThat(set.getModifyAdditions()).containsExactly(object2);
|
||||
assertThat(set.getModifyAdditions()).containsOnly(object2);
|
||||
assertThat(set.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddAll_given_emptyStart() throws Exception {
|
||||
public void testAddAll_given_emptyStart() {
|
||||
|
||||
BeanSet<Object> set = new BeanSet<>();
|
||||
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -60,12 +60,12 @@ public class BeanSetTest {
|
||||
// act
|
||||
set.addAll(all());
|
||||
|
||||
assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3);
|
||||
assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3);
|
||||
assertThat(set.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd_given_someAlreadyIn() throws Exception {
|
||||
public void testAdd_given_someAlreadyIn() {
|
||||
|
||||
BeanSet<Object> set = new BeanSet<>(some());
|
||||
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -76,12 +76,12 @@ public class BeanSetTest {
|
||||
assertThat(set.contains(object2)).isTrue();
|
||||
set.add(object2);
|
||||
|
||||
assertThat(set.getModifyAdditions()).containsExactly(object1);
|
||||
assertThat(set.getModifyAdditions()).containsOnly(object1);
|
||||
assertThat(set.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddSome_given_someAlreadyIn() throws Exception {
|
||||
public void testAddSome_given_someAlreadyIn() {
|
||||
|
||||
BeanSet<Object> set = new BeanSet<>(some());
|
||||
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -89,43 +89,43 @@ public class BeanSetTest {
|
||||
// act
|
||||
set.addAll(all());
|
||||
|
||||
assertThat(set.getModifyAdditions()).containsExactly(object1);
|
||||
assertThat(set.getModifyAdditions()).containsOnly(object1);
|
||||
assertThat(set.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemove_given_beansInAdditions() throws Exception {
|
||||
public void testRemove_given_beansInAdditions() {
|
||||
|
||||
BeanSet<Object> set = new BeanSet<>();
|
||||
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
set.addAll(all());
|
||||
assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3);
|
||||
assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3);
|
||||
|
||||
// act
|
||||
set.remove(object2);
|
||||
set.remove(object3);
|
||||
|
||||
assertThat(set.getModifyAdditions()).containsExactly(object1);
|
||||
assertThat(set.getModifyAdditions()).containsOnly(object1);
|
||||
assertThat(set.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAll_given_beansInAdditions() throws Exception {
|
||||
public void testRemoveAll_given_beansInAdditions() {
|
||||
|
||||
BeanSet<Object> set = new BeanSet<>();
|
||||
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
set.addAll(all());
|
||||
assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3);
|
||||
assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3);
|
||||
|
||||
// act
|
||||
set.removeAll(some());
|
||||
|
||||
assertThat(set.getModifyAdditions()).containsExactly(object1);
|
||||
assertThat(set.getModifyAdditions()).containsOnly(object1);
|
||||
assertThat(set.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemove_given_beansNotInAdditions() throws Exception {
|
||||
public void testRemove_given_beansNotInAdditions() {
|
||||
|
||||
BeanSet<Object> set = new BeanSet<>(all());
|
||||
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -136,11 +136,11 @@ public class BeanSetTest {
|
||||
|
||||
// assert
|
||||
assertThat(set.getModifyAdditions()).isEmpty();
|
||||
assertThat(set.getModifyRemovals()).containsExactly(object2, object3);
|
||||
assertThat(set.getModifyRemovals()).containsOnly(object2, object3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAll_given_beansNotInAdditions() throws Exception {
|
||||
public void testRemoveAll_given_beansNotInAdditions() {
|
||||
|
||||
BeanSet<Object> set = new BeanSet<>(all());
|
||||
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -150,11 +150,11 @@ public class BeanSetTest {
|
||||
|
||||
// assert
|
||||
assertThat(set.getModifyAdditions()).isEmpty();
|
||||
assertThat(set.getModifyRemovals()).containsExactly(object2, object3);
|
||||
assertThat(set.getModifyRemovals()).containsOnly(object2, object3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClear() throws Exception {
|
||||
public void testClear() {
|
||||
|
||||
BeanSet<Object> set = new BeanSet<>(all());
|
||||
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -163,12 +163,12 @@ public class BeanSetTest {
|
||||
set.clear();
|
||||
|
||||
//assert
|
||||
assertThat(set.getModifyRemovals()).containsExactly(object1, object2, object3);
|
||||
assertThat(set.getModifyRemovals()).containsOnly(object1, object2, object3);
|
||||
assertThat(set.getModifyAdditions()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClear_given_someBeansInAdditions() throws Exception {
|
||||
public void testClear_given_someBeansInAdditions() {
|
||||
|
||||
BeanSet<Object> set = new BeanSet<>();
|
||||
set.add(object1);
|
||||
@@ -180,27 +180,27 @@ public class BeanSetTest {
|
||||
set.clear();
|
||||
|
||||
//assert
|
||||
assertThat(set.getModifyRemovals()).containsExactly(object1);
|
||||
assertThat(set.getModifyRemovals()).containsOnly(object1);
|
||||
assertThat(set.getModifyAdditions()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetainAll_given_beansInAdditions() throws Exception {
|
||||
public void testRetainAll_given_beansInAdditions() {
|
||||
|
||||
BeanSet<Object> set = new BeanSet<>();
|
||||
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
set.addAll(all());
|
||||
assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3);
|
||||
assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3);
|
||||
|
||||
// act
|
||||
set.retainAll(some());
|
||||
|
||||
assertThat(set.getModifyAdditions()).containsExactly(object2, object3);
|
||||
assertThat(set.getModifyAdditions()).containsOnly(object2, object3);
|
||||
assertThat(set.getModifyRemovals()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetainAll_given_someBeansInAdditions() throws Exception {
|
||||
public void testRetainAll_given_someBeansInAdditions() {
|
||||
|
||||
BeanSet<Object> set = new BeanSet<>();
|
||||
set.add(object1);
|
||||
@@ -211,12 +211,12 @@ public class BeanSetTest {
|
||||
// act
|
||||
set.retainAll(some());
|
||||
|
||||
assertThat(set.getModifyAdditions()).containsExactly(object3);
|
||||
assertThat(set.getModifyRemovals()).containsExactly(object1);
|
||||
assertThat(set.getModifyAdditions()).containsOnly(object3);
|
||||
assertThat(set.getModifyRemovals()).containsOnly(object1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetainAll_given_noBeansInAdditions() throws Exception {
|
||||
public void testRetainAll_given_noBeansInAdditions() {
|
||||
|
||||
BeanSet<Object> set = new BeanSet<>(all());
|
||||
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
@@ -224,7 +224,7 @@ public class BeanSetTest {
|
||||
// act
|
||||
set.retainAll(some());
|
||||
|
||||
assertThat(set.getModifyRemovals()).containsExactly(object1);
|
||||
assertThat(set.getModifyRemovals()).containsOnly(object1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package io.ebean.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.ebean.annotation.MutationDetection;
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
import io.ebean.config.dbplatform.IdType;
|
||||
import io.ebean.datasource.DataSourceConfig;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class DatabaseConfigTest {
|
||||
|
||||
@Test
|
||||
public void testLoadFromEbeanProperties() {
|
||||
|
||||
DatabaseConfig config = new DatabaseConfig();
|
||||
config.loadFromProperties();
|
||||
|
||||
assertEquals(PersistBatch.NONE, config.getPersistBatch());
|
||||
assertNotNull(config.getProperties());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void evalPropertiesInput() {
|
||||
|
||||
String home = System.getenv("HOME");
|
||||
|
||||
Properties props = new Properties();
|
||||
props.setProperty("ddl.initSql", "${HOME}/initSql");
|
||||
|
||||
DatabaseConfig config = new DatabaseConfig();
|
||||
config.loadFromProperties(props);
|
||||
|
||||
String ddlInitSql = config.getDdlInitSql();
|
||||
assertThat(ddlInitSql).isEqualTo(home+"/initSql");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWithProperties() {
|
||||
|
||||
DatabaseConfig config = new DatabaseConfig();
|
||||
config.setPersistBatch(PersistBatch.NONE);
|
||||
config.setPersistBatchOnCascade(PersistBatch.NONE);
|
||||
config.setAutoReadOnlyDataSource(false);
|
||||
config.setReadOnlyDataSource(null);
|
||||
config.setReadOnlyDataSourceConfig(new DataSourceConfig());
|
||||
|
||||
Properties props = new Properties();
|
||||
props.setProperty("persistBatch", "ALL");
|
||||
props.setProperty("persistBatchOnCascade", "ALL");
|
||||
props.setProperty("dbuuid", "binary");
|
||||
props.setProperty("jdbcFetchSizeFindEach", "42");
|
||||
props.setProperty("jdbcFetchSizeFindList", "43");
|
||||
props.setProperty("backgroundExecutorShutdownSecs", "98");
|
||||
props.setProperty("backgroundExecutorSchedulePoolSize", "4");
|
||||
props.setProperty("dbOffline", "true");
|
||||
props.setProperty("jsonDateTime", "MILLIS");
|
||||
props.setProperty("jsonDate", "MILLIS");
|
||||
props.setProperty("jsonMutationDetection", "NONE");
|
||||
props.setProperty("autoReadOnlyDataSource", "true");
|
||||
props.setProperty("disableL2Cache", "true");
|
||||
props.setProperty("notifyL2CacheInForeground", "true");
|
||||
props.setProperty("idType", "SEQUENCE");
|
||||
props.setProperty("mappingLocations", "classpath:/foo;bar");
|
||||
props.setProperty("namingConvention", "io.ebean.config.MatchingNamingConvention");
|
||||
props.setProperty("idGeneratorAutomatic", "true");
|
||||
props.setProperty("enabledL2Regions", "r0,users,orgs");
|
||||
props.setProperty("caseSensitiveCollation", "false");
|
||||
props.setProperty("loadModuleInfo", "true");
|
||||
props.setProperty("forUpdateNoKey", "true");
|
||||
props.setProperty("defaultServer", "false");
|
||||
props.setProperty("skipDataSourceCheck", "true");
|
||||
|
||||
props.setProperty("queryPlan.enable", "true");
|
||||
props.setProperty("queryPlan.thresholdMicros", "10000");
|
||||
props.setProperty("queryPlan.capture", "true");
|
||||
props.setProperty("queryPlan.capturePeriodSecs", "42");
|
||||
props.setProperty("queryPlan.captureMaxTimeMillis", "560");
|
||||
props.setProperty("queryPlan.captureMaxCount", "7");
|
||||
|
||||
config.loadFromProperties(props);
|
||||
|
||||
assertFalse(config.isDefaultServer());
|
||||
assertTrue(config.isDisableL2Cache());
|
||||
assertTrue(config.isNotifyL2CacheInForeground());
|
||||
assertTrue(config.isDbOffline());
|
||||
assertTrue(config.isAutoReadOnlyDataSource());
|
||||
assertTrue(config.isAutoLoadModuleInfo());
|
||||
assertTrue(config.skipDataSourceCheck());
|
||||
|
||||
assertTrue(config.isIdGeneratorAutomatic());
|
||||
assertFalse(config.getPlatformConfig().isCaseSensitiveCollation());
|
||||
assertTrue(config.getPlatformConfig().isForUpdateNoKey());
|
||||
|
||||
assertThat(config.getNamingConvention()).isInstanceOf(MatchingNamingConvention.class);
|
||||
|
||||
assertEquals(MutationDetection.NONE, config.getJsonMutationDetection());
|
||||
config.setJsonMutationDetection(MutationDetection.SOURCE);
|
||||
assertEquals(MutationDetection.SOURCE, config.getJsonMutationDetection());
|
||||
assertEquals(IdType.SEQUENCE, config.getIdType());
|
||||
assertEquals(PersistBatch.ALL, config.getPersistBatch());
|
||||
assertEquals(PersistBatch.ALL, config.getPersistBatchOnCascade());
|
||||
assertEquals(PlatformConfig.DbUuid.BINARY, config.getPlatformConfig().getDbUuid());
|
||||
assertEquals(JsonConfig.DateTime.MILLIS, config.getJsonDateTime());
|
||||
assertEquals(JsonConfig.Date.MILLIS, config.getJsonDate());
|
||||
|
||||
assertEquals("r0,users,orgs", config.getEnabledL2Regions());
|
||||
|
||||
assertEquals(42, config.getJdbcFetchSizeFindEach());
|
||||
assertEquals(43, config.getJdbcFetchSizeFindList());
|
||||
assertEquals(4, config.getBackgroundExecutorSchedulePoolSize());
|
||||
assertEquals(98, config.getBackgroundExecutorShutdownSecs());
|
||||
|
||||
assertTrue(config.isQueryPlanEnable());
|
||||
assertEquals(10000, config.getQueryPlanThresholdMicros());
|
||||
assertTrue(config.isQueryPlanCapture());
|
||||
assertEquals(42, config.getQueryPlanCapturePeriodSecs());
|
||||
assertEquals(560, config.getQueryPlanCaptureMaxTimeMillis());
|
||||
assertEquals(7, config.getQueryPlanCaptureMaxCount());
|
||||
|
||||
assertThat(config.getMappingLocations()).containsExactly("classpath:/foo","bar");
|
||||
|
||||
config.setPersistBatch(PersistBatch.NONE);
|
||||
config.setPersistBatchOnCascade(PersistBatch.NONE);
|
||||
|
||||
Properties props1 = new Properties();
|
||||
props1.setProperty("ebean.persistBatch", "ALL");
|
||||
props1.setProperty("ebean.persistBatchOnCascade", "ALL");
|
||||
|
||||
config.setNotifyL2CacheInForeground(true);
|
||||
config.setDisableL2Cache(true);
|
||||
props1.setProperty("ebean.disableL2Cache", "false");
|
||||
props1.setProperty("ebean.notifyL2CacheInForeground", "false");
|
||||
|
||||
config.loadFromProperties(props1);
|
||||
assertFalse(config.isDisableL2Cache());
|
||||
assertFalse(config.isNotifyL2CacheInForeground());
|
||||
|
||||
assertEquals(PersistBatch.ALL, config.getPersistBatch());
|
||||
assertEquals(PersistBatch.ALL, config.getPersistBatchOnCascade());
|
||||
|
||||
config.setEnabledL2Regions("r0,orgs");
|
||||
assertEquals("r0,orgs", config.getEnabledL2Regions());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_defaults() {
|
||||
|
||||
DatabaseConfig config = new DatabaseConfig();
|
||||
assertTrue(config.isIdGeneratorAutomatic());
|
||||
assertTrue(config.isDefaultServer());
|
||||
assertFalse(config.isAutoPersistUpdates());
|
||||
assertFalse(config.skipDataSourceCheck());
|
||||
|
||||
config.setIdGeneratorAutomatic(false);
|
||||
assertFalse(config.isIdGeneratorAutomatic());
|
||||
assertEquals(JsonConfig.DateTime.ISO8601, config.getJsonDateTime());
|
||||
assertEquals(JsonConfig.Date.ISO8601, config.getJsonDate());
|
||||
assertEquals(MutationDetection.HASH, config.getJsonMutationDetection());
|
||||
assertTrue(config.getPlatformConfig().isCaseSensitiveCollation());
|
||||
assertTrue(config.isAutoLoadModuleInfo());
|
||||
|
||||
assertFalse(config.isQueryPlanEnable());
|
||||
assertEquals(Long.MAX_VALUE, config.getQueryPlanThresholdMicros());
|
||||
assertFalse(config.isQueryPlanCapture());
|
||||
assertEquals(600, config.getQueryPlanCapturePeriodSecs());
|
||||
assertEquals(10000L, config.getQueryPlanCaptureMaxTimeMillis());
|
||||
assertEquals(10, config.getQueryPlanCaptureMaxCount());
|
||||
|
||||
config.setLoadModuleInfo(false);
|
||||
assertFalse(config.isAutoLoadModuleInfo());
|
||||
config.setAutoPersistUpdates(true);
|
||||
assertTrue(config.isAutoPersistUpdates());
|
||||
config.setSkipDataSourceCheck(true);
|
||||
assertTrue(config.skipDataSourceCheck());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_putServiceObject() {
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
DatabaseConfig config = new DatabaseConfig();
|
||||
config.putServiceObject(objectMapper);
|
||||
|
||||
ObjectMapper mapper0 = config.getServiceObject(ObjectMapper.class);
|
||||
ObjectMapper mapper1 = (ObjectMapper)config.getServiceObject("objectMapper");
|
||||
|
||||
assertThat(objectMapper).isSameAs(mapper0);
|
||||
assertThat(objectMapper).isSameAs(mapper1);
|
||||
}
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
package io.ebean.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
import io.ebean.config.dbplatform.IdType;
|
||||
import io.ebean.datasource.DataSourceConfig;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ServerConfigTest {
|
||||
|
||||
@Test
|
||||
public void testLoadFromEbeanProperties() {
|
||||
|
||||
ServerConfig serverConfig = new ServerConfig();
|
||||
serverConfig.loadFromProperties();
|
||||
|
||||
assertEquals(PersistBatch.NONE, serverConfig.getPersistBatch());
|
||||
assertNotNull(serverConfig.getProperties());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void evalPropertiesInput() {
|
||||
|
||||
String home = System.getenv("HOME");
|
||||
|
||||
Properties props = new Properties();
|
||||
props.setProperty("ddl.initSql", "${HOME}/initSql");
|
||||
|
||||
ServerConfig serverConfig = new ServerConfig();
|
||||
serverConfig.loadFromProperties(props);
|
||||
|
||||
String ddlInitSql = serverConfig.getDdlInitSql();
|
||||
assertThat(ddlInitSql).isEqualTo(home+"/initSql");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWithProperties() {
|
||||
|
||||
ServerConfig serverConfig = new ServerConfig();
|
||||
serverConfig.setPersistBatch(PersistBatch.NONE);
|
||||
serverConfig.setPersistBatchOnCascade(PersistBatch.NONE);
|
||||
serverConfig.setAutoReadOnlyDataSource(false);
|
||||
serverConfig.setReadOnlyDataSource(null);
|
||||
serverConfig.setReadOnlyDataSourceConfig(new DataSourceConfig());
|
||||
|
||||
Properties props = new Properties();
|
||||
props.setProperty("persistBatch", "ALL");
|
||||
props.setProperty("persistBatchOnCascade", "ALL");
|
||||
props.setProperty("dbuuid", "binary");
|
||||
props.setProperty("jdbcFetchSizeFindEach", "42");
|
||||
props.setProperty("jdbcFetchSizeFindList", "43");
|
||||
props.setProperty("backgroundExecutorShutdownSecs", "98");
|
||||
props.setProperty("backgroundExecutorSchedulePoolSize", "4");
|
||||
props.setProperty("dbOffline", "true");
|
||||
props.setProperty("jsonDateTime", "MILLIS");
|
||||
props.setProperty("jsonDate", "MILLIS");
|
||||
props.setProperty("jsonDirtyByDefault", "false");
|
||||
props.setProperty("autoReadOnlyDataSource", "true");
|
||||
props.setProperty("disableL2Cache", "true");
|
||||
props.setProperty("notifyL2CacheInForeground", "true");
|
||||
props.setProperty("idType", "SEQUENCE");
|
||||
props.setProperty("mappingLocations", "classpath:/foo;bar");
|
||||
props.setProperty("namingConvention", "io.ebean.config.MatchingNamingConvention");
|
||||
props.setProperty("idGeneratorAutomatic", "true");
|
||||
props.setProperty("enabledL2Regions", "r0,users,orgs");
|
||||
props.setProperty("caseSensitiveCollation", "false");
|
||||
props.setProperty("loadModuleInfo", "true");
|
||||
props.setProperty("forUpdateNoKey", "true");
|
||||
props.setProperty("defaultServer", "false");
|
||||
|
||||
props.setProperty("queryPlan.enable", "true");
|
||||
props.setProperty("queryPlan.thresholdMicros", "10000");
|
||||
props.setProperty("queryPlan.capture", "true");
|
||||
props.setProperty("queryPlan.capturePeriodSecs", "42");
|
||||
props.setProperty("queryPlan.captureMaxTimeMillis", "560");
|
||||
props.setProperty("queryPlan.captureMaxCount", "7");
|
||||
|
||||
serverConfig.loadFromProperties(props);
|
||||
|
||||
assertFalse(serverConfig.isDefaultServer());
|
||||
assertTrue(serverConfig.isDisableL2Cache());
|
||||
assertTrue(serverConfig.isNotifyL2CacheInForeground());
|
||||
assertTrue(serverConfig.isDbOffline());
|
||||
assertTrue(serverConfig.isAutoReadOnlyDataSource());
|
||||
assertTrue(serverConfig.isAutoLoadModuleInfo());
|
||||
|
||||
assertTrue(serverConfig.isIdGeneratorAutomatic());
|
||||
assertFalse(serverConfig.getPlatformConfig().isCaseSensitiveCollation());
|
||||
assertTrue(serverConfig.getPlatformConfig().isForUpdateNoKey());
|
||||
|
||||
assertThat(serverConfig.getNamingConvention()).isInstanceOf(MatchingNamingConvention.class);
|
||||
|
||||
assertEquals(IdType.SEQUENCE, serverConfig.getIdType());
|
||||
assertEquals(PersistBatch.ALL, serverConfig.getPersistBatch());
|
||||
assertEquals(PersistBatch.ALL, serverConfig.getPersistBatchOnCascade());
|
||||
assertEquals(PlatformConfig.DbUuid.BINARY, serverConfig.getPlatformConfig().getDbUuid());
|
||||
assertEquals(JsonConfig.DateTime.MILLIS, serverConfig.getJsonDateTime());
|
||||
assertEquals(JsonConfig.Date.MILLIS, serverConfig.getJsonDate());
|
||||
assertFalse(serverConfig.isJsonDirtyByDefault());
|
||||
serverConfig.setJsonDirtyByDefault(true);
|
||||
assertTrue(serverConfig.isJsonDirtyByDefault());
|
||||
|
||||
assertEquals("r0,users,orgs", serverConfig.getEnabledL2Regions());
|
||||
|
||||
assertEquals(42, serverConfig.getJdbcFetchSizeFindEach());
|
||||
assertEquals(43, serverConfig.getJdbcFetchSizeFindList());
|
||||
assertEquals(4, serverConfig.getBackgroundExecutorSchedulePoolSize());
|
||||
assertEquals(98, serverConfig.getBackgroundExecutorShutdownSecs());
|
||||
|
||||
assertTrue(serverConfig.isQueryPlanEnable());
|
||||
assertEquals(10000, serverConfig.getQueryPlanThresholdMicros());
|
||||
assertTrue(serverConfig.isQueryPlanCapture());
|
||||
assertEquals(42, serverConfig.getQueryPlanCapturePeriodSecs());
|
||||
assertEquals(560, serverConfig.getQueryPlanCaptureMaxTimeMillis());
|
||||
assertEquals(7, serverConfig.getQueryPlanCaptureMaxCount());
|
||||
|
||||
assertThat(serverConfig.getMappingLocations()).containsExactly("classpath:/foo","bar");
|
||||
|
||||
serverConfig.setPersistBatch(PersistBatch.NONE);
|
||||
serverConfig.setPersistBatchOnCascade(PersistBatch.NONE);
|
||||
|
||||
Properties props1 = new Properties();
|
||||
props1.setProperty("ebean.persistBatch", "ALL");
|
||||
props1.setProperty("ebean.persistBatchOnCascade", "ALL");
|
||||
|
||||
serverConfig.setNotifyL2CacheInForeground(true);
|
||||
serverConfig.setDisableL2Cache(true);
|
||||
props1.setProperty("ebean.disableL2Cache", "false");
|
||||
props1.setProperty("ebean.notifyL2CacheInForeground", "false");
|
||||
|
||||
serverConfig.loadFromProperties(props1);
|
||||
assertFalse(serverConfig.isDisableL2Cache());
|
||||
assertFalse(serverConfig.isNotifyL2CacheInForeground());
|
||||
|
||||
assertEquals(PersistBatch.ALL, serverConfig.getPersistBatch());
|
||||
assertEquals(PersistBatch.ALL, serverConfig.getPersistBatchOnCascade());
|
||||
|
||||
serverConfig.setEnabledL2Regions("r0,orgs");
|
||||
assertEquals("r0,orgs", serverConfig.getEnabledL2Regions());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_defaults() {
|
||||
|
||||
ServerConfig serverConfig = new ServerConfig();
|
||||
assertTrue(serverConfig.isIdGeneratorAutomatic());
|
||||
assertTrue(serverConfig.isDefaultServer());
|
||||
assertFalse(serverConfig.isAutoPersistUpdates());
|
||||
|
||||
serverConfig.setIdGeneratorAutomatic(false);
|
||||
assertFalse(serverConfig.isIdGeneratorAutomatic());
|
||||
assertEquals(JsonConfig.DateTime.ISO8601, serverConfig.getJsonDateTime());
|
||||
assertEquals(JsonConfig.Date.ISO8601, serverConfig.getJsonDate());
|
||||
assertTrue(serverConfig.isJsonDirtyByDefault());
|
||||
assertTrue(serverConfig.getPlatformConfig().isCaseSensitiveCollation());
|
||||
assertTrue(serverConfig.isAutoLoadModuleInfo());
|
||||
|
||||
assertFalse(serverConfig.isQueryPlanEnable());
|
||||
assertEquals(Long.MAX_VALUE, serverConfig.getQueryPlanThresholdMicros());
|
||||
assertFalse(serverConfig.isQueryPlanCapture());
|
||||
assertEquals(600, serverConfig.getQueryPlanCapturePeriodSecs());
|
||||
assertEquals(10000L, serverConfig.getQueryPlanCaptureMaxTimeMillis());
|
||||
assertEquals(10, serverConfig.getQueryPlanCaptureMaxCount());
|
||||
|
||||
serverConfig.setLoadModuleInfo(false);
|
||||
assertFalse(serverConfig.isAutoLoadModuleInfo());
|
||||
serverConfig.setAutoPersistUpdates(true);
|
||||
assertTrue(serverConfig.isAutoPersistUpdates());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_putServiceObject() {
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
ServerConfig config = new ServerConfig();
|
||||
config.putServiceObject(objectMapper);
|
||||
|
||||
ObjectMapper mapper0 = config.getServiceObject(ObjectMapper.class);
|
||||
ObjectMapper mapper1 = (ObjectMapper)config.getServiceObject("objectMapper");
|
||||
|
||||
assertThat(objectMapper).isSameAs(mapper0);
|
||||
assertThat(objectMapper).isSameAs(mapper1);
|
||||
}
|
||||
}
|
||||
@@ -650,7 +650,7 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean exists(Query<?> ormQuery, Transaction transaction) {
|
||||
public <T> boolean exists(Query<T> ormQuery, Transaction transaction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,11 +23,11 @@ public class BeanIudMetricsTest {
|
||||
BasicMetricVisitor basic = new BasicMetricVisitor();
|
||||
iudMetrics.visit(basic);
|
||||
|
||||
List<MetaTimedMetric> timed = basic.getTimedMetrics();
|
||||
List<MetaTimedMetric> timed = basic.timedMetrics();
|
||||
assertThat(timed).hasSize(1);
|
||||
|
||||
assertThat(timed.get(0).getCount()).isEqualTo(4);
|
||||
assertThat(timed.get(0).getName()).isEqualTo("iud.one.insertBatch");
|
||||
assertThat(timed.get(0).count()).isEqualTo(4);
|
||||
assertThat(timed.get(0).name()).isEqualTo("iud.one.insertBatch");
|
||||
|
||||
iudMetrics.addBatch(PersistRequest.Type.UPDATE, startNanos, 1);
|
||||
iudMetrics.addBatch(PersistRequest.Type.DELETE_SOFT, startNanos, 2);
|
||||
@@ -37,15 +37,15 @@ public class BeanIudMetricsTest {
|
||||
|
||||
basic = new BasicMetricVisitor();
|
||||
iudMetrics.visit(basic);
|
||||
timed = basic.getTimedMetrics();
|
||||
timed = basic.timedMetrics();
|
||||
assertThat(timed).hasSize(3);
|
||||
|
||||
assertThat(timed.get(0).getCount()).isEqualTo(16);
|
||||
assertThat(timed.get(0).getName()).isEqualTo("iud.one.insertBatch");
|
||||
assertThat(timed.get(1).getCount()).isEqualTo(3);
|
||||
assertThat(timed.get(1).getName()).isEqualTo("iud.one.updateBatch");
|
||||
assertThat(timed.get(2).getCount()).isEqualTo(12);
|
||||
assertThat(timed.get(2).getName()).isEqualTo("iud.one.deleteBatch");
|
||||
assertThat(timed.get(0).count()).isEqualTo(16);
|
||||
assertThat(timed.get(0).name()).isEqualTo("iud.one.insertBatch");
|
||||
assertThat(timed.get(1).count()).isEqualTo(3);
|
||||
assertThat(timed.get(1).name()).isEqualTo("iud.one.updateBatch");
|
||||
assertThat(timed.get(2).count()).isEqualTo(12);
|
||||
assertThat(timed.get(2).name()).isEqualTo("iud.one.deleteBatch");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -63,15 +63,15 @@ public class BeanIudMetricsTest {
|
||||
BasicMetricVisitor basic = new BasicMetricVisitor();
|
||||
iudMetrics.visit(basic);
|
||||
|
||||
List<MetaTimedMetric> timed = basic.getTimedMetrics();
|
||||
List<MetaTimedMetric> timed = basic.timedMetrics();
|
||||
assertThat(timed).hasSize(3);
|
||||
|
||||
assertThat(timed.get(0).getCount()).isEqualTo(1);
|
||||
assertThat(timed.get(0).getName()).isEqualTo("iud.one.insert");
|
||||
assertThat(timed.get(1).getCount()).isEqualTo(2);
|
||||
assertThat(timed.get(1).getName()).isEqualTo("iud.one.update");
|
||||
assertThat(timed.get(2).getCount()).isEqualTo(2);
|
||||
assertThat(timed.get(2).getName()).isEqualTo("iud.one.delete");
|
||||
assertThat(timed.get(0).count()).isEqualTo(1);
|
||||
assertThat(timed.get(0).name()).isEqualTo("iud.one.insert");
|
||||
assertThat(timed.get(1).count()).isEqualTo(2);
|
||||
assertThat(timed.get(1).name()).isEqualTo("iud.one.update");
|
||||
assertThat(timed.get(2).count()).isEqualTo(2);
|
||||
assertThat(timed.get(2).name()).isEqualTo("iud.one.delete");
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -33,7 +33,7 @@ public class TestNotEnhancedMappedSuper extends BaseTestCase {
|
||||
|
||||
NotEnhancedMappedSuper mappedSuper = new NotEnhancedMappedSuper();
|
||||
boolean enhanced = (mappedSuper instanceof EntityBean);
|
||||
Assert.assertFalse(enhanced);
|
||||
Assert.assertTrue(enhanced);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -14,7 +14,7 @@ public class BasicProfileLocationTest {
|
||||
|
||||
assertThat(loc.obtain()).isTrue();
|
||||
assertThat(loc.fullLocation()).endsWith(":12)");
|
||||
assertThat(loc.location()).isEqualTo("NativeMethodAccessorImpl.invoke0(Native Method:12)");
|
||||
assertThat(loc.location()).isEqualTo("java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0");
|
||||
assertThat(loc.label()).isEqualTo("NativeMethodAccessorImpl.invoke0");
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public class BasicProfileLocationTest {
|
||||
BasicProfileLocation loc = new BasicProfileLocation("com.foo.Bar.all");
|
||||
assertThat(loc.obtain()).isFalse();
|
||||
assertThat(loc.fullLocation()).isEqualTo("com.foo.Bar.all");
|
||||
assertThat(loc.location()).isEqualTo("Bar.all");
|
||||
assertThat(loc.location()).isEqualTo("com.foo.Bar.all");
|
||||
assertThat(loc.label()).isEqualTo("Bar.all");
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public class BasicProfileLocationTest {
|
||||
BasicProfileLocation loc = new BasicProfileLocation("foo.Bar.all");
|
||||
assertThat(loc.obtain()).isFalse();
|
||||
assertThat(loc.fullLocation()).isEqualTo("foo.Bar.all");
|
||||
assertThat(loc.location()).isEqualTo("Bar.all");
|
||||
assertThat(loc.location()).isEqualTo("foo.Bar.all");
|
||||
assertThat(loc.label()).isEqualTo("Bar.all");
|
||||
}
|
||||
}
|
||||
@@ -21,17 +21,17 @@ public class DTimedMetricMapTest {
|
||||
BasicMetricVisitor visitor = new BasicMetricVisitor();
|
||||
metricMap.visit(visitor);
|
||||
|
||||
MetaTimedMetric timedMetric = visitor.getTimedMetrics().get(0);
|
||||
assertThat(timedMetric.getCount()).isEqualTo(1);
|
||||
assertThat(timedMetric.getTotal()).isGreaterThan(10);
|
||||
MetaTimedMetric timedMetric = visitor.timedMetrics().get(0);
|
||||
assertThat(timedMetric.count()).isEqualTo(1);
|
||||
assertThat(timedMetric.total()).isGreaterThan(10);
|
||||
|
||||
metricMap.addSinceNanos("some", nanos);
|
||||
|
||||
visitor = new BasicMetricVisitor();
|
||||
metricMap.visit(visitor);
|
||||
|
||||
timedMetric = visitor.getTimedMetrics().get(0);
|
||||
assertThat(timedMetric.getCount()).isEqualTo(1);
|
||||
assertThat(timedMetric.getTotal()).isGreaterThan(10);
|
||||
timedMetric = visitor.timedMetrics().get(0);
|
||||
assertThat(timedMetric.count()).isEqualTo(1);
|
||||
assertThat(timedMetric.total()).isGreaterThan(10);
|
||||
}
|
||||
}
|
||||
@@ -17,16 +17,16 @@ public class DTimedMetricTest {
|
||||
metric.addSinceNanos(start);
|
||||
|
||||
DTimeMetricStats stats = metric.collect(true);
|
||||
assertThat(stats.getCount()).isEqualTo(1);
|
||||
assertThat(stats.getTotal()).isGreaterThan(10);
|
||||
assertThat(stats.getMax()).isEqualTo(stats.getTotal());
|
||||
assertThat(stats.count()).isEqualTo(1);
|
||||
assertThat(stats.total()).isGreaterThan(10);
|
||||
assertThat(stats.max()).isEqualTo(stats.total());
|
||||
|
||||
metric.addSinceNanos(start);
|
||||
|
||||
stats = metric.collect(true);
|
||||
assertThat(stats.getCount()).isEqualTo(1);
|
||||
assertThat(stats.getTotal()).isGreaterThan(10);
|
||||
assertThat(stats.getMax()).isEqualTo(stats.getTotal());
|
||||
assertThat(stats.count()).isEqualTo(1);
|
||||
assertThat(stats.total()).isGreaterThan(10);
|
||||
assertThat(stats.max()).isEqualTo(stats.total());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -40,16 +40,16 @@ public class DTimedMetricTest {
|
||||
metric.addBatchSince(start, 5);
|
||||
|
||||
DTimeMetricStats stats = metric.collect(true);
|
||||
assertThat(stats.getCount()).isEqualTo(5);
|
||||
assertThat(stats.getTotal()).isGreaterThan(10000);
|
||||
assertThat(stats.getMax()).isEqualTo(stats.getTotal() / 5);
|
||||
assertThat(stats.getMax()).isGreaterThan(10000 / 5);
|
||||
assertThat(stats.count()).isEqualTo(5);
|
||||
assertThat(stats.total()).isGreaterThan(10000);
|
||||
assertThat(stats.max()).isEqualTo(stats.total() / 5);
|
||||
assertThat(stats.max()).isGreaterThan(10000 / 5);
|
||||
|
||||
metric.addBatchSince(start, 2);
|
||||
|
||||
stats = metric.collect(true);
|
||||
assertThat(stats.getCount()).isEqualTo(2);
|
||||
assertThat(stats.getTotal()).isGreaterThan(10000);
|
||||
assertThat(stats.getMax()).isEqualTo(stats.getTotal() / 2);
|
||||
assertThat(stats.count()).isEqualTo(2);
|
||||
assertThat(stats.total()).isGreaterThan(10000);
|
||||
assertThat(stats.max()).isEqualTo(stats.total() / 2);
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ public class SortMetricTest {
|
||||
list.add(create("a"));
|
||||
list.sort(sortMetric);
|
||||
|
||||
String names = list.stream().map(DTimeMetricStats::getName).collect(Collectors.joining());
|
||||
String names = list.stream().map(DTimeMetricStats::name).collect(Collectors.joining());
|
||||
|
||||
assertEquals("nullabcd", names);
|
||||
}
|
||||
|
||||
@@ -8,8 +8,13 @@ public class UtilLocationTest {
|
||||
|
||||
@Test
|
||||
public void label() {
|
||||
|
||||
assertThat(UtilLocation.label("foo")).isEqualTo("foo");
|
||||
assertThat(UtilLocation.label("ProfileLocationTest$Other.<init>(ProfileLocationTest.java:47)")).isEqualTo("ProfileLocationTest$Other.init");
|
||||
assertThat(UtilLocation.label("ProfileLocationTest$Other.<init>")).isEqualTo("ProfileLocationTest$Other.init");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loc() {
|
||||
assertThat(UtilLocation.loc("org.foo.MyFoo.doIt(MyFoo.java:12)")).isEqualTo("org.foo.MyFoo.doIt");
|
||||
assertThat(UtilLocation.label("org.foo.MyFoo.doIt")).isEqualTo("MyFoo.doIt");
|
||||
}
|
||||
}
|
||||
@@ -9,17 +9,25 @@ import io.ebean.RawSqlBuilder;
|
||||
import io.ebean.SqlRow;
|
||||
import io.ebean.annotation.ForPlatform;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.datasource.DataSourceConfig;
|
||||
import io.ebeaninternal.server.core.DefaultServer;
|
||||
import io.ebeaninternal.server.rawsql.SpiRawSql.Sql;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Customer;
|
||||
import org.tests.model.basic.EBasicClob;
|
||||
import org.tests.model.basic.PersistentFileContent;
|
||||
import org.tests.model.basic.ResetBasicData;
|
||||
import org.tests.model.rawsql.ERawSqlAggBean;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -283,4 +291,58 @@ public class TestRawSqlBuilder extends BaseTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCLobClosedConnection() throws Exception {
|
||||
final EBasicClob eBasicClob = new EBasicClob();
|
||||
eBasicClob.setName("eBasicClob");
|
||||
final String description = "This is the CLob description";
|
||||
eBasicClob.setDescription(description);
|
||||
DB.save(eBasicClob);
|
||||
|
||||
final String sql = "select description from ebasic_clob where id = ?";
|
||||
|
||||
List<SqlRow> rows = new ArrayList<>();
|
||||
final DataSourceConfig config = ((DefaultServer) DB.getDefault()).getServerConfig().getDataSourceConfig();
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(config.getUrl(), config.getUsername(), config.getPassword());
|
||||
PreparedStatement stmt = connection.prepareStatement(sql)) {
|
||||
stmt.setLong(1, eBasicClob.getId());
|
||||
|
||||
try (ResultSet resultSet = stmt.executeQuery()) {
|
||||
while (resultSet.next()) {
|
||||
rows.add(RawSqlBuilder.sqlRow(resultSet, "true", false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(rows).hasSize(1);
|
||||
assertThat(rows.get(0).getString("description")).isEqualTo(description);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBLobClosedConnection() throws Exception {
|
||||
final PersistentFileContent pfc = new PersistentFileContent();
|
||||
final byte[] bytes = "This is the blob as String".getBytes(StandardCharsets.UTF_8);
|
||||
pfc.setContent(bytes);
|
||||
DB.save(pfc);
|
||||
|
||||
List<SqlRow> rows = new ArrayList<>();
|
||||
final DataSourceConfig config = ((DefaultServer) DB.getDefault()).getServerConfig().getDataSourceConfig();
|
||||
|
||||
final String sql = "select content from persistent_file_content where id = ?";
|
||||
try (Connection connection = DriverManager.getConnection(config.getUrl(), config.getUsername(), config.getPassword());
|
||||
PreparedStatement stmt = connection.prepareStatement(sql)) {
|
||||
stmt.setLong(1, pfc.getId());
|
||||
|
||||
try (ResultSet resultSet = stmt.executeQuery()) {
|
||||
while (resultSet.next()) {
|
||||
rows.add(RawSqlBuilder.sqlRow(resultSet, "true", false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(rows).hasSize(1);
|
||||
assertThat(rows.get(0).get("content")).isEqualTo(bytes);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package io.ebeaninternal.server.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class ChecksumTest {
|
||||
|
||||
@Test
|
||||
public void checksum() {
|
||||
final long val = Checksum.checksum("Hello world");
|
||||
assertThat(val).isEqualTo(2346098258L);
|
||||
assertThat(Checksum.checksum("Hello world")).isEqualTo(val);
|
||||
assertThat(Checksum.checksum("hello world")).isNotEqualTo(val);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checksum_shortString() {
|
||||
final long val0 = Checksum.checksum("2012-01-11");
|
||||
final long val1 = Checksum.checksum("2012-10-02");
|
||||
assertThat(val0).isNotEqualTo(val1);
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,51 @@ package io.ebeaninternal.server.util;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
|
||||
public class Md5Test {
|
||||
|
||||
@Test
|
||||
public void hash() throws Exception {
|
||||
|
||||
String content = "some random content we wish to hash";
|
||||
String hash1 = Md5.hash(content);
|
||||
String hash2 = Md5.hash(content);
|
||||
assertEquals(hash1, hash2);
|
||||
assertEquals(hash1, "62c20bf679ff56cb746452ab5c88e3ed");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashDifferent() throws Exception {
|
||||
String hash1 = Md5.hash("one");
|
||||
String hash2 = Md5.hash("two");
|
||||
String hash3 = Md5.hash("onetwo");
|
||||
|
||||
assertNotEquals(hash1, hash2);
|
||||
assertNotEquals(hash2, hash3);
|
||||
assertEquals(hash1, "f97c5d29941bfb1b2fdab0874906ab82");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashMulti() {
|
||||
String hash1 = Md5.hash("one", "two");
|
||||
String hash2 = Md5.hash("onetwo");
|
||||
|
||||
assertEquals(hash1, hash2);
|
||||
assertEquals(hash1, "5b9164ad6f496d9dee12ec7634ce253f");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashMulti_when_null() {
|
||||
String hash1 = Md5.hash("one", null);
|
||||
String hash2 = Md5.hash("one");
|
||||
|
||||
assertEquals(hash1, hash2);
|
||||
assertEquals(hash1, "f97c5d29941bfb1b2fdab0874906ab82");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_null() {
|
||||
String hash1 = Md5.hash(null, null);
|
||||
assertEquals(hash1, "d41d8cd98f00b204e9800998ecf8427e");
|
||||
}
|
||||
}
|
||||
@@ -80,15 +80,15 @@ public class TestBatchInsertFlush extends BaseTestCase {
|
||||
}
|
||||
|
||||
ServerMetrics metrics = collectMetrics();
|
||||
List<MetaTimedMetric> txnStats = metrics.getTimedMetrics();
|
||||
List<MetaTimedMetric> txnStats = metrics.timedMetrics();
|
||||
for (MetaTimedMetric txnMetric : txnStats) {
|
||||
System.out.println(txnMetric);
|
||||
}
|
||||
assertThat(txnStats).hasSize(4);
|
||||
assertThat(txnStats.get(0).getName()).isEqualTo("txn.main");
|
||||
assertThat(txnStats.get(1).getName()).isEqualTo("txn.named.TestBatchInsertFlush.no_cascade");
|
||||
assertThat(txnStats.get(2).getName()).isEqualTo("iud.TSDetail.insertBatch");
|
||||
assertThat(txnStats.get(3).getName()).isEqualTo("iud.TSMaster.insertBatch");
|
||||
assertThat(txnStats.get(0).name()).isEqualTo("txn.main");
|
||||
assertThat(txnStats.get(1).name()).isEqualTo("txn.named.TestBatchInsertFlush.no_cascade");
|
||||
assertThat(txnStats.get(2).name()).isEqualTo("iud.TSDetail.insertBatch");
|
||||
assertThat(txnStats.get(3).name()).isEqualTo("iud.TSMaster.insertBatch");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package org.tests.json;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.BeanState;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.ValuePair;
|
||||
import io.ebean.event.BeanPersistAdapter;
|
||||
import io.ebean.event.BeanPersistRequest;
|
||||
import io.ebeantest.LoggedSql;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.json.EBasicJsonJackson3;
|
||||
@@ -11,11 +15,33 @@ import org.tests.model.json.PlainBeanDirtyAware;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestDbJson_Jackson3 extends BaseTestCase {
|
||||
|
||||
public static class EBasicJsonListPersistController extends BeanPersistAdapter {
|
||||
|
||||
private static Map<String, ValuePair> updatedValues;
|
||||
|
||||
@Override
|
||||
public boolean isRegisterFor(Class<?> cls) {
|
||||
return EBasicJsonList.class.isAssignableFrom(cls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preInsert(BeanPersistRequest<?> request) {
|
||||
updatedValues = request.getUpdatedValues();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preUpdate(BeanPersistRequest<?> request) {
|
||||
updatedValues = request.getUpdatedValues();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@Test
|
||||
public void updateIncludesJsonColumn_when_explicit_isMarkedDirty() {
|
||||
|
||||
@@ -24,6 +50,7 @@ public class TestDbJson_Jackson3 extends BaseTestCase {
|
||||
EBasicJsonJackson3 bean = new EBasicJsonJackson3();
|
||||
bean.setName("b1");
|
||||
bean.setPlainValue(contentBean);
|
||||
bean.setPlainValue2(contentBean);
|
||||
|
||||
bean.save();
|
||||
|
||||
@@ -32,20 +59,15 @@ public class TestDbJson_Jackson3 extends BaseTestCase {
|
||||
|
||||
LoggedSql.start();
|
||||
found.save();
|
||||
|
||||
List<String> sql = LoggedSql.collect();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("update ebasic_json_jackson3 set name=?, version=? where id=? and version=?");
|
||||
expectedSql(0, "update ebasic_json_jackson3 set name=?, version=? where id=? and version=?");
|
||||
|
||||
found.setName("b1-mod2");
|
||||
found.getPlainValue().setName("b");
|
||||
found.getPlainValue().setMarkedDirty(true);
|
||||
// found.getPlainValue().setMarkedDirty(true); // Irrelevant for SOURCE or HASH based mutation detection
|
||||
|
||||
found.save();
|
||||
|
||||
sql = LoggedSql.stop();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("update ebasic_json_jackson3 set name=?, plain_value=?, version=? where id=? and version=?");
|
||||
expectedSql(0, "update ebasic_json_jackson3 set name=?, plain_value=?, version=? where id=? and version=?");
|
||||
LoggedSql.stop();
|
||||
|
||||
final EBasicJsonJackson3 found2 = DB.find(EBasicJsonJackson3.class, bean.getId());
|
||||
|
||||
@@ -69,11 +91,138 @@ public class TestDbJson_Jackson3 extends BaseTestCase {
|
||||
found.setName("p1-mod");
|
||||
found.setBeanList(null);
|
||||
|
||||
BeanState state = DB.getBeanState(found);
|
||||
assertThat(state.getChangedProps()).containsExactlyInAnyOrder("name", "beanList");
|
||||
|
||||
ValuePair pair = state.getDirtyValues().get("name");
|
||||
assertThat(pair.getNewValue()).isEqualTo("p1-mod");
|
||||
assertThat(pair.getOldValue()).isEqualTo("p1");
|
||||
|
||||
pair = state.getDirtyValues().get("beanList");
|
||||
assertThat(pair.getNewValue()).isEqualTo(null);
|
||||
assertThat((List<PlainBean>)pair.getOldValue()).hasSize(1)
|
||||
.extracting(PlainBean::getName).containsExactly("a");
|
||||
|
||||
|
||||
LoggedSql.start();
|
||||
DB.save(found);
|
||||
|
||||
final List<String> sql = LoggedSql.stop();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("update ebasic_json_list set name=?, bean_list=?, plain_bean=?, version=? where id=?");
|
||||
// plain_bean=?, no longer included with MD5 dirty detection
|
||||
expectedSql(0, "update ebasic_json_list set name=?, bean_list=?, version=? where id=?");
|
||||
|
||||
assertThat(EBasicJsonListPersistController.updatedValues.entrySet())
|
||||
.extracting(Map.Entry::toString)
|
||||
.containsExactlyInAnyOrder("beanList=null,[name:a]","name=p1-mod,p1","version=2,1");
|
||||
|
||||
assertThat(DB.getBeanState(found).isDirty()).isFalse();
|
||||
|
||||
found.getPlainBean().setName("b");
|
||||
assertThat(DB.getBeanState(found).isDirty()).isTrue();
|
||||
|
||||
state = DB.getBeanState(found);
|
||||
assertThat(state.getChangedProps()).containsExactlyInAnyOrder("plainBean");
|
||||
pair = state.getDirtyValues().get("plainBean");
|
||||
assertThat(pair.getNewValue()).hasToString("name:b");
|
||||
assertThat(pair.getOldValue()).hasToString("name:a");
|
||||
|
||||
|
||||
LoggedSql.start();
|
||||
DB.save(found);
|
||||
|
||||
// plain_bean=?, no longer included with MD5 dirty detection
|
||||
expectedSql(0, "update ebasic_json_list set plain_bean=?, version=? where id=?");
|
||||
|
||||
assertThat(EBasicJsonListPersistController.updatedValues.entrySet())
|
||||
.extracting(Map.Entry::toString)
|
||||
.containsExactlyInAnyOrder("plainBean=name:b,name:a", "version=3,2");
|
||||
|
||||
LoggedSql.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateIncludesJsonColumn_when_list_loadedAndNotDirtyAware() {
|
||||
|
||||
PlainBean contentBean = new PlainBean("a", 42);
|
||||
EBasicJsonList bean = new EBasicJsonList();
|
||||
bean.setName("p1");
|
||||
bean.setPlainBean(contentBean);
|
||||
bean.setBeanList(Arrays.asList(contentBean));
|
||||
|
||||
DB.save(bean);
|
||||
final EBasicJsonList found = DB.find(EBasicJsonList.class, bean.getId());
|
||||
found.getBeanList().get(0).setName("p1-mod");
|
||||
|
||||
BeanState state = DB.getBeanState(found);
|
||||
assertThat(state.getChangedProps()).containsExactlyInAnyOrder("beanList");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void update_with_differentDbJsonSettings() {
|
||||
PlainBeanDirtyAware contentBean1 = new PlainBeanDirtyAware("x", 42);
|
||||
PlainBeanDirtyAware contentBean2 = new PlainBeanDirtyAware("y", 43);
|
||||
PlainBeanDirtyAware contentBean3 = new PlainBeanDirtyAware("z", 44);
|
||||
|
||||
EBasicJsonJackson3 bean = new EBasicJsonJackson3();
|
||||
bean.setName("b1");
|
||||
bean.setPlainValue(contentBean1);
|
||||
bean.setPlainValue2(contentBean2);
|
||||
bean.setPlainValue3(contentBean3);
|
||||
|
||||
BeanState state = DB.getBeanState(bean);
|
||||
// a new bean is not considered as dirty (thus have no changed props)
|
||||
assertThat(state.isDirty()).isFalse();
|
||||
assertThat(state.isNewOrDirty()).isTrue();
|
||||
assertThat(state.getChangedProps()).isEmpty();
|
||||
|
||||
bean.save();
|
||||
|
||||
bean = DB.find(EBasicJsonJackson3.class, bean.getId());
|
||||
state = DB.getBeanState(bean);
|
||||
// a fresh loaded bean is also not considered as dirty
|
||||
assertThat(state.isDirty()).isFalse();
|
||||
assertThat(state.isNewOrDirty()).isFalse();
|
||||
assertThat(state.getChangedProps()).isEmpty();
|
||||
|
||||
bean.getPlainValue().setName("a"); // has SOURCE
|
||||
|
||||
assertThat(state.isDirty()).isTrue();
|
||||
assertThat(state.getChangedProps()).containsExactly("plainValue");
|
||||
|
||||
bean.getPlainValue2().setName("b");
|
||||
assertThat(state.getChangedProps()).containsExactlyInAnyOrder("plainValue", "plainValue2");
|
||||
|
||||
bean.getPlainValue3().setName("c"); // has mutationDetection = NONE
|
||||
|
||||
Map<String, ValuePair> dirtyValues = state.getDirtyValues();
|
||||
assertThat(dirtyValues).hasSize(2).containsKeys("plainValue", "plainValue2");
|
||||
|
||||
assertThat(dirtyValues.get("plainValue")).hasToString("name:a,name:x"); // SOURCE -> origValue present
|
||||
assertThat(dirtyValues.get("plainValue2")).hasToString("name:b,null"); // without SOURCE no origValue present
|
||||
|
||||
LoggedSql.start();
|
||||
bean.save();
|
||||
expectedSql(0, "update ebasic_json_jackson3 set plain_value=?, plain_value2=?, version=? where id=?");
|
||||
|
||||
bean = DB.find(EBasicJsonJackson3.class, bean.getId());
|
||||
LoggedSql.collect(); // ignore the select
|
||||
assertThat(bean.getPlainValue().getName()).isEqualTo("a");
|
||||
assertThat(bean.getPlainValue2().getName()).isEqualTo("b");
|
||||
assertThat(bean.getPlainValue3().getName()).isEqualTo("z"); // value is not updated
|
||||
|
||||
bean.getPlainValue3().setName("c");
|
||||
bean.getPlainValue3().setMarkedDirty(true); // This is ignored because it is MutationDetection.NONE
|
||||
bean.save();
|
||||
// no update as plainValue3 has MutationDetection.NONE (ModifyAwareType = NONE isn't an expected combination to me)
|
||||
assertThat(LoggedSql.collect()).isEmpty();
|
||||
|
||||
bean.getPlainValue2().setName("b2"); // effectively HASH mode mutation detection
|
||||
bean.save();
|
||||
expectedSql(0, "update ebasic_json_jackson3 set plain_value2=?, version=? where id=? and version=?");
|
||||
|
||||
LoggedSql.stop();
|
||||
}
|
||||
|
||||
private void expectedSql(int i, String s) {
|
||||
assertThat(LoggedSql.collect().get(i)).contains(s);
|
||||
}
|
||||
}
|
||||
@@ -76,10 +76,11 @@ public class TestDbJson_List extends BaseTestCase {
|
||||
update_when_dirty();
|
||||
update_when_dirty_flags();
|
||||
update_when_dirty_SetListMap();
|
||||
|
||||
DB.delete(found);
|
||||
}
|
||||
|
||||
//@Test//(dependsOnMethods = "insert")
|
||||
public void json_parse_format() {
|
||||
private void json_parse_format() {
|
||||
|
||||
String asJson = DB.json().toJson(found);
|
||||
assertThat(asJson).contains("\"tags\":[\"one\",\"two\"]");
|
||||
@@ -104,8 +105,7 @@ public class TestDbJson_List extends BaseTestCase {
|
||||
assertThat(fromJson.getBeanMap()).hasSize(2);
|
||||
}
|
||||
|
||||
//@Test//(dependsOnMethods = "insert")
|
||||
public void update_when_notDirty() {
|
||||
private void update_when_notDirty() {
|
||||
|
||||
found.setName("mod");
|
||||
LoggedSqlCollector.start();
|
||||
@@ -113,10 +113,11 @@ public class TestDbJson_List extends BaseTestCase {
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
|
||||
// we don't update the phone numbers (as they are not dirty)
|
||||
assertSql(sql.get(0)).contains("update ebasic_json_list set name=?, plain_bean=?, version=? where");
|
||||
// plain_bean=?, no longer included with MD5 dirty detection
|
||||
assertSql(sql.get(0)).contains("update ebasic_json_list set name=?, version=? where");
|
||||
}
|
||||
|
||||
public void update_when_dirty() {
|
||||
private void update_when_dirty() {
|
||||
|
||||
//found.setName("modAgain");
|
||||
found.getTags().add("three");
|
||||
@@ -126,10 +127,11 @@ public class TestDbJson_List extends BaseTestCase {
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
|
||||
// we don't update the phone numbers (as they are not dirty)
|
||||
assertSql(sql.get(0)).contains("update ebasic_json_list set plain_bean=?, tags=?, version=? where id=? and version=?");
|
||||
// plain_bean=? not included using MD5 dirty detection
|
||||
assertSql(sql.get(0)).contains("update ebasic_json_list set tags=?, version=? where id=? and version=?");
|
||||
}
|
||||
|
||||
public void update_when_dirty_flags() {
|
||||
private void update_when_dirty_flags() {
|
||||
|
||||
//found.setName("modAgain");
|
||||
found.getFlags().remove(42L);
|
||||
@@ -139,10 +141,11 @@ public class TestDbJson_List extends BaseTestCase {
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
|
||||
// we don't update the phone numbers (as they are not dirty)
|
||||
assertSql(sql.get(0)).contains("update ebasic_json_list set plain_bean=?, flags=?, version=? where id=? and version=?;");
|
||||
// plain_bean=? not included with MD5 dirty detection
|
||||
assertSql(sql.get(0)).contains("update ebasic_json_list set flags=?, version=? where id=? and version=?;");
|
||||
}
|
||||
|
||||
public void update_when_dirty_SetListMap() {
|
||||
private void update_when_dirty_SetListMap() {
|
||||
|
||||
//found.setName("modAgain");
|
||||
found.getBeanSet().clear();
|
||||
@@ -154,7 +157,8 @@ public class TestDbJson_List extends BaseTestCase {
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
|
||||
// we don't update the phone numbers (as they are not dirty)
|
||||
assertSql(sql.get(0)).contains("update ebasic_json_list set beans=?, bean_list=?, bean_map=?, plain_bean=?, version=? where id=? and version=?");
|
||||
// plain_bean=? not included with MD5 dirty detection
|
||||
assertSql(sql.get(0)).contains("update ebasic_json_list set beans=?, bean_list=?, bean_map=?, version=? where id=? and version=?");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -221,4 +225,19 @@ public class TestDbJson_List extends BaseTestCase {
|
||||
|
||||
DB.delete(bean);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullToEmpty() {
|
||||
EBasicJsonList bean = new EBasicJsonList();
|
||||
bean.setFlags(null);
|
||||
bean.setTags(null);
|
||||
bean.setBeanMap(null);
|
||||
DB.save(bean);
|
||||
|
||||
bean = DB.find(EBasicJsonList.class).setId(bean.getId()).findOne();
|
||||
|
||||
assertThat(bean.getFlags()).isEmpty();
|
||||
assertThat(bean.getTags()).isEmpty();
|
||||
assertThat(bean.getBeanMap()).isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,15 @@
|
||||
package org.tests.m2m;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.DB;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.MRole;
|
||||
import org.tests.model.basic.MUser;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestM2MModifyTest extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
@@ -19,8 +20,8 @@ public class TestM2MModifyTest extends BaseTestCase {
|
||||
MRole r1 = new MRole("r1");
|
||||
|
||||
// Save r1 and r2
|
||||
Ebean.save(r0);
|
||||
Ebean.save(r1);
|
||||
DB.save(r0);
|
||||
DB.save(r1);
|
||||
|
||||
// Create a new user
|
||||
MUser u0 = new MUser("usr0");
|
||||
@@ -28,32 +29,27 @@ public class TestM2MModifyTest extends BaseTestCase {
|
||||
u0.addRole(r1);
|
||||
|
||||
// Save the user
|
||||
Ebean.save(u0);
|
||||
DB.save(u0);
|
||||
|
||||
List<MRole> roles = u0.getRoles();
|
||||
|
||||
Assert.assertTrue(roles.size() == 2);
|
||||
assertThat(roles).hasSize(2);
|
||||
|
||||
u0 = Ebean.find(MUser.class, u0.getUserid());
|
||||
u0 = DB.find(MUser.class, u0.getUserid());
|
||||
|
||||
roles = u0.getRoles();
|
||||
int nrRoles = roles.size();
|
||||
|
||||
Assert.assertTrue(nrRoles == 2);
|
||||
assertThat(roles).hasSize(2);
|
||||
|
||||
roles.clear();
|
||||
roles.add(r0);
|
||||
roles.add(r1);
|
||||
roles.remove(r1);
|
||||
|
||||
Ebean.save(u0);
|
||||
DB.save(u0);
|
||||
|
||||
u0 = Ebean.find(MUser.class, u0.getUserid());
|
||||
u0 = DB.find(MUser.class, u0.getUserid());
|
||||
|
||||
roles = u0.getRoles();
|
||||
|
||||
nrRoles = roles.size();
|
||||
|
||||
Assert.assertTrue(nrRoles == 1);
|
||||
assertThat(roles).hasSize(1);
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ public class TestM2mDeleteObject extends BaseTestCase {
|
||||
|
||||
List<MetaTimedMetric> sqlMetrics = sqlMetrics();
|
||||
assertThat(sqlMetrics).hasSize(1);
|
||||
assertThat(sqlMetrics.get(0).getName()).isEqualTo("orm.update.deleteAllPermissions");
|
||||
assertThat(sqlMetrics.get(0).name()).isEqualTo("orm.update.deleteAllPermissions");
|
||||
|
||||
Tenant t = new Tenant("tenant");
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ public class CustomerFinder extends Finder<Integer, Customer> {
|
||||
}
|
||||
|
||||
public List<Customer> byNameStatus(String nameStartsWith, Customer.Status status) {
|
||||
|
||||
return query("where status = :status and name istartsWith :name order by name")
|
||||
.setParameter("status", status)
|
||||
.setParameter("name", nameStartsWith)
|
||||
@@ -45,7 +44,6 @@ public class CustomerFinder extends Finder<Integer, Customer> {
|
||||
}
|
||||
|
||||
public List<String> namesStartingWith(String name) {
|
||||
|
||||
return nativeSql("select name from o_customer where name like ? order by name")
|
||||
.setParameter(name + "%")
|
||||
.findSingleAttributeList();
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package org.tests.model.embedded;
|
||||
|
||||
import io.ebean.annotation.DbJson;
|
||||
import org.tests.model.json.PlainBean;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import java.util.Map;
|
||||
|
||||
@Embeddable
|
||||
public class EAddress {
|
||||
@@ -18,6 +22,12 @@ public class EAddress {
|
||||
@Enumerated(EnumType.STRING)
|
||||
EAddressStatus status;
|
||||
|
||||
@DbJson
|
||||
PlainBean jbean;
|
||||
|
||||
@DbJson
|
||||
Map<String, Object> jraw;
|
||||
|
||||
public String getStreet() {
|
||||
return street;
|
||||
}
|
||||
@@ -42,6 +52,22 @@ public class EAddress {
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
public PlainBean getJbean() {
|
||||
return jbean;
|
||||
}
|
||||
|
||||
public void setJbean(PlainBean jbean) {
|
||||
this.jbean = jbean;
|
||||
}
|
||||
|
||||
public Map<String, Object> getJraw() {
|
||||
return jraw;
|
||||
}
|
||||
|
||||
public void setJraw(Map<String, Object> jraw) {
|
||||
this.jraw = jraw;
|
||||
}
|
||||
|
||||
public EAddressStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ public class EPerson {
|
||||
@Embedded
|
||||
@AttributeOverrides({
|
||||
@AttributeOverride(name = "city", column = @Column(name = "addr_city")),
|
||||
@AttributeOverride(name = "status", column = @Column(name = "addr_status"))
|
||||
@AttributeOverride(name = "status", column = @Column(name = "addr_status")),
|
||||
@AttributeOverride(name = "jbean", column = @Column(name = "addr_jbean"))
|
||||
})
|
||||
EAddress address;
|
||||
|
||||
|
||||
@@ -2,11 +2,15 @@ package org.tests.model.json;
|
||||
|
||||
import io.ebean.Model;
|
||||
import io.ebean.annotation.DbJson;
|
||||
import io.ebean.annotation.MutationDetection;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
|
||||
import static io.ebean.annotation.MutationDetection.NONE;
|
||||
import static io.ebean.annotation.MutationDetection.SOURCE;
|
||||
|
||||
@Entity
|
||||
public class EBasicJsonJackson3 extends Model {
|
||||
|
||||
@@ -15,9 +19,15 @@ public class EBasicJsonJackson3 extends Model {
|
||||
|
||||
String name;
|
||||
|
||||
@DbJson(length = 500)
|
||||
@DbJson(length = 500, mutationDetection = SOURCE)
|
||||
PlainBeanDirtyAware plainValue;
|
||||
|
||||
@DbJson(length = 500)
|
||||
PlainBeanDirtyAware plainValue2;
|
||||
|
||||
@DbJson(length = 500, mutationDetection = NONE)
|
||||
PlainBeanDirtyAware plainValue3;
|
||||
|
||||
@Version
|
||||
long version;
|
||||
|
||||
@@ -45,6 +55,22 @@ public class EBasicJsonJackson3 extends Model {
|
||||
this.plainValue = plainValue;
|
||||
}
|
||||
|
||||
public PlainBeanDirtyAware getPlainValue2() {
|
||||
return plainValue2;
|
||||
}
|
||||
|
||||
public void setPlainValue2(PlainBeanDirtyAware plainValue2) {
|
||||
this.plainValue2 = plainValue2;
|
||||
}
|
||||
|
||||
public PlainBeanDirtyAware getPlainValue3() {
|
||||
return plainValue3;
|
||||
}
|
||||
|
||||
public void setPlainValue3(PlainBeanDirtyAware plainValue3) {
|
||||
this.plainValue3 = plainValue3;
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@@ -7,12 +7,10 @@ import io.ebean.annotation.DbJsonType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import static io.ebean.annotation.MutationDetection.HASH;
|
||||
import static io.ebean.annotation.MutationDetection.SOURCE;
|
||||
|
||||
@Entity
|
||||
public class EBasicJsonList {
|
||||
@@ -22,16 +20,17 @@ public class EBasicJsonList {
|
||||
|
||||
String name;
|
||||
|
||||
// @JsonDeserialize(as=LinkedHashSet.class)
|
||||
@DbJson(length = 700, name = "beans")
|
||||
Set<PlainBean> beanSet;
|
||||
|
||||
@DbJsonB
|
||||
@DbJsonB(mutationDetection = HASH)
|
||||
List<PlainBean> beanList;
|
||||
|
||||
@DbJson(length = 700)
|
||||
Map<String, PlainBean> beanMap = new LinkedHashMap<>();
|
||||
|
||||
@DbJson(length = 500)
|
||||
@DbJson(length = 500, mutationDetection = SOURCE) // such that we can rebuild old values
|
||||
PlainBean plainBean;
|
||||
|
||||
@DbJson(length = 50)
|
||||
|
||||
@@ -20,7 +20,7 @@ public class EBasicJsonMapVarchar {
|
||||
String name;
|
||||
|
||||
@DbJson(storage = DbJsonType.VARCHAR)//, length = 2200)
|
||||
Map<String, Object> content;
|
||||
Map<String, Object> content;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.tests.model.json;
|
||||
|
||||
import io.ebean.annotation.DbJson;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
|
||||
import static io.ebean.annotation.MutationDetection.NONE;
|
||||
|
||||
@Entity
|
||||
public class EBasicPlain {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
String attr;
|
||||
|
||||
@DbJson(length = 500)
|
||||
PlainBean plainBean;
|
||||
|
||||
@DbJson(length = 500, mutationDetection = NONE) // only update when property set
|
||||
PlainBean plainBean2;
|
||||
|
||||
@Version
|
||||
long version;
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getAttr() {
|
||||
return attr;
|
||||
}
|
||||
|
||||
public void setAttr(String attr) {
|
||||
this.attr = attr;
|
||||
}
|
||||
|
||||
public PlainBean getPlainBean() {
|
||||
return plainBean;
|
||||
}
|
||||
|
||||
public void setPlainBean(PlainBean plainBean) {
|
||||
this.plainBean = plainBean;
|
||||
}
|
||||
|
||||
public PlainBean getPlainBean2() {
|
||||
return plainBean2;
|
||||
}
|
||||
|
||||
public void setPlainBean2(PlainBean plainBean2) {
|
||||
this.plainBean2 = plainBean2;
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package org.tests.model.json;
|
||||
|
||||
import io.ebean.DB;
|
||||
import io.ebeantest.LoggedSql;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestJacksonPlainBean {
|
||||
|
||||
@Test
|
||||
public void insertNullStayNull() {
|
||||
|
||||
// insert with jackson beans as null
|
||||
EBasicPlain bean = new EBasicPlain();
|
||||
bean.setAttr("n0");
|
||||
DB.save(bean);
|
||||
|
||||
LoggedSql.start();
|
||||
bean.setAttr("n1");
|
||||
DB.save(bean);
|
||||
expectedSql(0, "update ebasic_plain set attr=?, version=? where id=? and version=?");
|
||||
|
||||
bean.setPlainBean(new PlainBean("x", 1));
|
||||
DB.save(bean);
|
||||
expectedSql(0, "update ebasic_plain set plain_bean=?, version=? where id=? and version=?");
|
||||
|
||||
final EBasicPlain found = DB.find(EBasicPlain.class, bean.getId());
|
||||
found.setAttr("n2");
|
||||
DB.save(found);
|
||||
expectedSql(1, "update ebasic_plain set attr=?, version=? where id=? and version=?");
|
||||
|
||||
LoggedSql.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertUpdate() {
|
||||
|
||||
DB.getDefault();
|
||||
LoggedSql.start();
|
||||
|
||||
PlainBean content = new PlainBean("foo", 42);
|
||||
EBasicPlain bean = new EBasicPlain();
|
||||
bean.setAttr("attr0");
|
||||
bean.setPlainBean(content);
|
||||
bean.setPlainBean2(new PlainBean("bar", 27));
|
||||
|
||||
DB.save(bean);
|
||||
expectedSql(0, "insert into ebasic_plain (attr, plain_bean, plain_bean2, version) values (?,?,?,?)");
|
||||
|
||||
// inserted plainBean has not been mutated
|
||||
bean.setAttr("attr1");
|
||||
DB.save(bean);
|
||||
expectedSql(0, "update ebasic_plain set attr=?, version=? where id=? and version=?");
|
||||
|
||||
// inserted plainBean has now been mutated
|
||||
content.setName("notFoo");
|
||||
bean.setAttr("attr2");
|
||||
DB.save(bean);
|
||||
expectedSql(0, "update ebasic_plain set attr=?, plain_bean=?, version=? where id=? and version=?");
|
||||
|
||||
|
||||
final EBasicPlain found = DB.find(EBasicPlain.class, bean.getId());
|
||||
|
||||
// update mutating PlainBean only
|
||||
final PlainBean plainBean = found.getPlainBean();
|
||||
plainBean.setName("mod1");
|
||||
DB.save(found);
|
||||
expectedSql(1, "update ebasic_plain set plain_bean=?, version=? where id=? and version=?");
|
||||
|
||||
// dirtyDetection = false, so not included in update
|
||||
found.getPlainBean2().setName("Modification Ignored");
|
||||
// dirtyDetection = true, mutation detected
|
||||
plainBean.setName("mod2");
|
||||
DB.save(found);
|
||||
expectedSql(0, "update ebasic_plain set plain_bean=?, version=? where id=? and version=?");
|
||||
|
||||
// update bean, not mutating PlainBean
|
||||
found.setAttr("attr3");
|
||||
DB.save(found);
|
||||
expectedSql(0, "update ebasic_plain set attr=?, version=? where id=? and version=?");
|
||||
|
||||
// dirtyDetection = false, set a new plainBean2 instance, included in update
|
||||
found.setPlainBean2(new PlainBean("bar", 27));
|
||||
DB.save(found);
|
||||
expectedSql(0, "update ebasic_plain set plain_bean2=?, version=? where id=? and version=?");
|
||||
|
||||
LoggedSql.stop();
|
||||
}
|
||||
|
||||
private void expectedSql(int i, String s) {
|
||||
assertThat(LoggedSql.collect().get(i)).contains(s);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.tests.model.orphanremoval;
|
||||
|
||||
import io.ebean.Model;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Version;
|
||||
|
||||
@Entity
|
||||
public class OmBeanListChild extends Model {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private final String name;
|
||||
|
||||
@ManyToOne
|
||||
private OmBeanListParent parent;
|
||||
|
||||
@Version
|
||||
private long version;
|
||||
|
||||
public OmBeanListChild(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.tests.model.orphanremoval;
|
||||
|
||||
import io.ebean.Model;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Version;
|
||||
import java.util.List;
|
||||
|
||||
import static javax.persistence.CascadeType.ALL;
|
||||
|
||||
@Entity
|
||||
public class OmBeanListParent extends Model {
|
||||
|
||||
@Id
|
||||
private long id;
|
||||
|
||||
@Version
|
||||
private long version;
|
||||
|
||||
@OneToMany(cascade = ALL, mappedBy = "parent", orphanRemoval = true)
|
||||
private List<OmBeanListChild> children;
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public List<OmBeanListChild> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(List<OmBeanListChild> children) {
|
||||
// So a BeanList is used and overwriting children will replace the table entries
|
||||
this.children.clear();
|
||||
this.children.addAll(children);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package org.tests.model.orphanremoval;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class TestOrphanRemovalOverwrite {
|
||||
|
||||
@Test
|
||||
public void testOverwritingMapping() {
|
||||
OmBeanListParent parent = new OmBeanListParent();
|
||||
parent.save();
|
||||
|
||||
// Refreshing/querying sets the modifyListening flag on the association's BeanList
|
||||
parent.refresh();
|
||||
|
||||
List<OmBeanListChild> childList = singletonList(new OmBeanListChild("child1"));
|
||||
// Adding the children to the BeanList causes _ebean_getIdentity to be invoked before the children have been persisted and
|
||||
// have Ids.
|
||||
parent.setChildren(childList);
|
||||
|
||||
// Give the children Ids
|
||||
parent.save();
|
||||
|
||||
// Refreshing here generates new objects for the associated children that are referred to by the parent.
|
||||
parent.refresh();
|
||||
|
||||
assertNotNull("The children should now have Ids as they are persisted to the db.", childList.get(0).getId());
|
||||
assertEquals("The children should have the same Id as the ones on the parent.",
|
||||
childList.get(0).getId(), parent.getChildren().get(0).getId());
|
||||
assertEquals("The children should therefore equal the children on the parent.", childList, parent.getChildren());
|
||||
}
|
||||
}
|
||||
@@ -7,20 +7,26 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class ProfileLocationTest {
|
||||
|
||||
private static ProfileLocation loc = ProfileLocation.create(12, "foo");
|
||||
|
||||
private static ProfileLocation loc2 = ProfileLocation.create();
|
||||
private static final ProfileLocation loc = ProfileLocation.create(12, "foo");
|
||||
private static final ProfileLocation locB = ProfileLocation.create();
|
||||
private static final ProfileLocation loc2 = ProfileLocation.create();
|
||||
|
||||
private boolean doIt() {
|
||||
locB.obtain(); // simulate a location moving by line number only
|
||||
return loc.obtain();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_obtain() {
|
||||
assertThat(doIt()).isTrue();
|
||||
assertThat(loc.fullLocation()).isEqualTo("org.tests.profile.ProfileLocationTest.doIt(ProfileLocationTest.java:15)");
|
||||
assertThat(loc.location()).isEqualTo("ProfileLocationTest.doIt(ProfileLocationTest.java:15)");
|
||||
assertThat(loc.fullLocation()).isEqualTo("org.tests.profile.ProfileLocationTest.doIt(ProfileLocationTest.java:16)");
|
||||
assertThat(loc.location()).isEqualTo("org.tests.profile.ProfileLocationTest.doIt");
|
||||
assertThat(loc.label()).isEqualTo("ProfileLocationTest.doIt");
|
||||
|
||||
// same hash even when the line number has changed
|
||||
assertThat(locB.fullLocation()).isEqualTo("org.tests.profile.ProfileLocationTest.doIt(ProfileLocationTest.java:15)");
|
||||
assertThat(locB.location()).isEqualTo("org.tests.profile.ProfileLocationTest.doIt");
|
||||
assertThat(locB.label()).isEqualTo("ProfileLocationTest.doIt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -35,7 +41,7 @@ public class ProfileLocationTest {
|
||||
other.hashCode();
|
||||
|
||||
assertThat(loc2.label()).isEqualTo("ProfileLocationTest$Other.init");
|
||||
assertThat(loc2.location()).isEqualTo("ProfileLocationTest$Other.<init>(ProfileLocationTest.java:44)");
|
||||
assertThat(loc2.location()).isEqualTo("org.tests.profile.ProfileLocationTest$Other.<init>");
|
||||
}
|
||||
|
||||
static class Other {
|
||||
|
||||
@@ -30,18 +30,18 @@ import io.ebean.annotation.Platform;
|
||||
* Tests, if all kind of queries are cancelable. There are two ways how to
|
||||
* cancel a query: <br/>
|
||||
* <b>At begin:</b>
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* query = DB.find(...)
|
||||
* query.cancel();
|
||||
* query.findList();
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* The query was caneled before executing. In this case we do hit the DB driver
|
||||
* <br/>
|
||||
* <br/>
|
||||
* <b>During run:</b>
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* // Thread 1: Thread 2
|
||||
* query = DB.find(...)
|
||||
@@ -50,26 +50,26 @@ import io.ebean.annotation.Platform;
|
||||
* ...finding query.cancel();
|
||||
* ...JDBC-Exception
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* The test tries to simulate a slow query by installing the
|
||||
* {@link SlowDownEBasic} 'SELECT' trigger. The trigger can be configured to
|
||||
* wait 3 * <code>timing</code> ms and a second thread will cancel the query in
|
||||
* <code>timing</code> ms.
|
||||
*
|
||||
*
|
||||
* in this case, we expect a JDBC exception from the driver. <br/>
|
||||
* <br/>
|
||||
* NOTE:<br/>
|
||||
* H2 checks the cancel flag in org.h2.command.Prepared::setCurrentRowNumber
|
||||
* only every 128th row. So we need at least 128 models and we cannot check
|
||||
* queries like findCount or findOne, because they only return one row.
|
||||
*
|
||||
*
|
||||
* @author Roland Praml, FOCONIS AG
|
||||
*
|
||||
*/
|
||||
public class SqlQueryCancelTest extends BaseTestCase {
|
||||
|
||||
private int timing = 10;
|
||||
|
||||
private final int timing = 20;
|
||||
|
||||
@BeforeClass
|
||||
public static void setupTestData() throws SQLException {
|
||||
for (int i = 0; i < 128; i++) {
|
||||
@@ -98,10 +98,10 @@ public class SqlQueryCancelTest extends BaseTestCase {
|
||||
doCancelSqlDuringRun(q -> q.findEachWhile(e -> true));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void cancelOrmQueryAtBegin() throws SQLException {
|
||||
doCancelOrmAtBegin(Query::findCount);
|
||||
doCancelOrmAtBegin(Query::findCount);
|
||||
doCancelOrmAtBegin(Query::findFutureCount);
|
||||
// We cannot test 'findCount' due H2 restrictions
|
||||
doCancelOrmAtBegin(Query::findFutureIds);
|
||||
@@ -206,7 +206,7 @@ public class SqlQueryCancelTest extends BaseTestCase {
|
||||
.isInstanceOf(PersistenceException.class)
|
||||
.hasMessageContaining("Query was cancelled");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void cancelSqlDtoQueryAtBegin() throws SQLException {
|
||||
|
||||
@@ -290,7 +290,7 @@ public class SqlQueryCancelTest extends BaseTestCase {
|
||||
private void doCancelOrmFutureDuringRun(Function<Query<EBasic>, Future<?>> test) throws SQLException, InterruptedException, ExecutionException {
|
||||
Query<EBasic> warmup = DB.find(EBasic.class);
|
||||
test.apply(warmup).get();
|
||||
|
||||
|
||||
Query<EBasic> query = DB.find(EBasic.class);
|
||||
executeDelayed(query::cancel);
|
||||
assertThatThrownBy(() -> {
|
||||
@@ -311,18 +311,18 @@ public class SqlQueryCancelTest extends BaseTestCase {
|
||||
.isInstanceOf(PersistenceException.class)
|
||||
.hasMessageContaining("Query was cancelled");
|
||||
}
|
||||
|
||||
|
||||
private void doCancelOrmDtoDuringRun(Consumer<DtoQuery<EBasicDto>> test) throws SQLException {
|
||||
DtoQuery<EBasicDto> warmup = DB.find(EBasic.class).select("id,status").asDto(EBasicDto.class);
|
||||
test.accept(warmup);
|
||||
|
||||
|
||||
DtoQuery<EBasicDto> query = DB.find(EBasic.class).select("id,status").asDto(EBasicDto.class);
|
||||
executeDelayed(query::cancel);
|
||||
assertThatThrownBy(() -> test.accept(query))
|
||||
.isInstanceOf(PersistenceException.class)
|
||||
.hasCauseInstanceOf(org.h2.jdbc.JdbcSQLTimeoutException.class);
|
||||
}
|
||||
|
||||
|
||||
private void doCancelSqlDtoAtBegin(Consumer<DtoQuery<EBasicDto>> test) throws SQLException {
|
||||
DtoQuery<EBasicDto> query = DB.findDto(EBasicDto.class, "select id, status from e_basic");
|
||||
query.cancel();
|
||||
@@ -334,14 +334,14 @@ public class SqlQueryCancelTest extends BaseTestCase {
|
||||
private void doCancelSqlDtoDuringRun(Consumer<DtoQuery<EBasicDto>> test) throws SQLException {
|
||||
DtoQuery<EBasicDto> warmup = DB.findDto(EBasicDto.class, "select id, status from e_basic");
|
||||
test.accept(warmup);
|
||||
|
||||
|
||||
DtoQuery<EBasicDto> query = DB.findDto(EBasicDto.class, "select id, status from e_basic");
|
||||
executeDelayed(query::cancel);
|
||||
assertThatThrownBy(() -> test.accept(query))
|
||||
.isInstanceOf(PersistenceException.class)
|
||||
.hasCauseInstanceOf(org.h2.jdbc.JdbcSQLTimeoutException.class);
|
||||
}
|
||||
|
||||
|
||||
private void executeDelayed(Runnable r) throws SQLException {
|
||||
// We modify the DB here. Otherwise we may hit an internal H2 cache, if the
|
||||
// same query is performed. Queries from the cache cannot be canceled.
|
||||
|
||||
@@ -176,7 +176,7 @@ public class TestCustomerFinder extends BaseTestCase {
|
||||
// change default collect query plan threshold to 200 micros
|
||||
QueryPlanInit init0 = new QueryPlanInit();
|
||||
init0.setAll(true);
|
||||
init0.setThresholdMicros(2);
|
||||
init0.thresholdMicros(2);
|
||||
final List<MetaQueryPlan> plans = server().getMetaInfoManager().queryPlanInit(init0);
|
||||
assertThat(plans.size()).isGreaterThan(1);
|
||||
|
||||
@@ -186,7 +186,7 @@ public class TestCustomerFinder extends BaseTestCase {
|
||||
// change query plan threshold to 100 micros
|
||||
QueryPlanInit init = new QueryPlanInit();
|
||||
init.setAll(true);
|
||||
init.setThresholdMicros(1);
|
||||
init.thresholdMicros(1);
|
||||
final List<MetaQueryPlan> appliedToPlans = server().getMetaInfoManager().queryPlanInit(init);
|
||||
assertThat(appliedToPlans.size()).isGreaterThan(4);
|
||||
|
||||
@@ -195,30 +195,30 @@ public class TestCustomerFinder extends BaseTestCase {
|
||||
|
||||
ServerMetrics metrics = DB.getDefault().getMetaInfoManager().collectMetrics();
|
||||
|
||||
List<MetaQueryMetric> planStats = metrics.getQueryMetrics();
|
||||
List<MetaQueryMetric> planStats = metrics.queryMetrics();
|
||||
assertThat(planStats.size()).isGreaterThan(4);
|
||||
|
||||
for (MetaQueryMetric planStat : planStats) {
|
||||
System.out.println(planStat);
|
||||
}
|
||||
|
||||
for (MetaTimedMetric txnTimed : metrics.getTimedMetrics()) {
|
||||
for (MetaTimedMetric txnTimed : metrics.timedMetrics()) {
|
||||
System.out.println(txnTimed);
|
||||
}
|
||||
|
||||
// obtains db query plans ...
|
||||
QueryPlanRequest request = new QueryPlanRequest();
|
||||
// collect max 1000 plans (use something more like 10)
|
||||
request.setMaxCount(1_000);
|
||||
request.maxCount(1_000);
|
||||
// don't collect any more plans if used 10 secs
|
||||
request.setMaxTimeMillis(10_000);
|
||||
request.maxTimeMillis(10_000);
|
||||
List<MetaQueryPlan> plans0 = server().getMetaInfoManager().queryPlanCollectNow(request);
|
||||
assertThat(plans0).isNotEmpty();
|
||||
|
||||
for (MetaQueryPlan plan : plans) {
|
||||
logger.info("queryplan label:{}, queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}",
|
||||
plan.getLabel(), plan.getQueryTimeMicros(), plan.getProfileLocation(),
|
||||
plan.getSql(), plan.getBind(), plan.getPlan());
|
||||
logger.info("queryPlan label:{}, queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}",
|
||||
plan.label(), plan.queryTimeMicros(), plan.profileLocation(),
|
||||
plan.sql(), plan.bind(), plan.plan());
|
||||
System.out.println(plan);
|
||||
}
|
||||
|
||||
@@ -242,9 +242,9 @@ public class TestCustomerFinder extends BaseTestCase {
|
||||
|
||||
assertThat(metricsJson).contains("\"name\":\"txn.main\"");
|
||||
assertThat(metricsJson).contains("\"name\":\"orm.Customer.findList\"");
|
||||
assertThat(metricsJson).contains("\"loc\":\"CustomerFinder.byNameStatus(CustomerFinder.java:44)\"");
|
||||
assertThat(metricsJson).contains("\"loc\":\"org.tests.model.basic.finder.CustomerFinder.byNameStatus\"");
|
||||
if (isH2() || isPostgres()) {
|
||||
assertThat(metricsJson).contains("\"hash\":\"cc20eb930403cfd418db2d0475c6e26a\"");
|
||||
assertThat(metricsJson).contains("\"hash\":\"de3affa5b4bff07e19c1c012590dcde6\"");
|
||||
assertThat(metricsJson).contains("\"sql\":\"select t0.id, t0.status,");
|
||||
}
|
||||
}
|
||||
@@ -267,7 +267,7 @@ public class TestCustomerFinder extends BaseTestCase {
|
||||
assertThat(metricsJson).contains("\"name\":\"txn.main\"");
|
||||
assertThat(metricsJson).contains("\"name\":\"orm.Customer.findList\"");
|
||||
assertThat(metricsJson).doesNotContain("\"loc\":");
|
||||
assertThat(metricsJson).doesNotContain("\"hash\":");
|
||||
assertThat(metricsJson).doesNotContain("\"sqlHash\":");
|
||||
assertThat(metricsJson).doesNotContain("\"sql\":");
|
||||
}
|
||||
|
||||
|
||||
@@ -407,7 +407,7 @@ public class SqlQueryTests extends BaseTestCase {
|
||||
|
||||
List<MetaTimedMetric> sqlMetrics = sqlMetrics();
|
||||
assertThat(sqlMetrics).hasSize(1);
|
||||
assertThat(sqlMetrics.get(0).getName()).isEqualTo("sql.query.findEach-Max10Rows");
|
||||
assertThat(sqlMetrics.get(0).name()).isEqualTo("sql.query.findEach-Max10Rows");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package org.tests.rawsql.nativesql;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.Query;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.embedded.EAddress;
|
||||
import org.tests.model.embedded.EPerson;
|
||||
import org.tests.model.json.PlainBean;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -17,25 +20,31 @@ public class TestNativeWithEmbedded extends BaseTestCase {
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
Map<String, Object> rawMap = new LinkedHashMap<>();
|
||||
rawMap.put("a","1");
|
||||
EPerson person = new EPerson();
|
||||
person.setName("Frank");
|
||||
EAddress address = new EAddress();
|
||||
address.setStreet("1 foo st");
|
||||
address.setCity("barv");
|
||||
address.setJbean(new PlainBean("hi", 3));
|
||||
address.setJraw(rawMap);
|
||||
person.setAddress(address);
|
||||
|
||||
Ebean.save(person);
|
||||
DB.save(person);
|
||||
|
||||
String sql = "select id, name, street, suburb, addr_city, addr_status from eperson where id = ?";
|
||||
String sql = "select id, name, street, suburb, addr_city, addr_status, addr_jbean, jraw from eperson where id = ?";
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Query<EPerson> query = Ebean.findNative(EPerson.class, sql);
|
||||
Query<EPerson> query = DB.findNative(EPerson.class, sql);
|
||||
query.setParameter(person.getId());
|
||||
EPerson one = query.findOne();
|
||||
|
||||
assertThat(one.getName()).isEqualTo("Frank");
|
||||
assertThat(one.getAddress().getStreet()).isEqualTo("1 foo st");
|
||||
assertThat(one.getAddress().getJbean().getName()).isEqualTo("hi");
|
||||
assertThat(one.getAddress().getJraw().get("a")).isEqualTo("1");
|
||||
|
||||
List<String> loggedSql = LoggedSqlCollector.stop();
|
||||
assertThat(loggedSql).hasSize(1);
|
||||
|
||||
@@ -58,6 +58,52 @@ public class TestSoftDeleteBasic extends BaseTestCase {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findSingleAttribute() {
|
||||
|
||||
EBasicSoftDelete bean = new EBasicSoftDelete();
|
||||
bean.setName("findSingleAttribute");
|
||||
DB.save(bean);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
final String name0 = DB.find(EBasicSoftDelete.class)
|
||||
.select("name")
|
||||
.where().eq("name", "findSingleAttribute")
|
||||
.findSingleAttribute();
|
||||
|
||||
List<String> sql0 = LoggedSqlCollector.current();
|
||||
assertThat(sql0.get(0)).contains("where t0.name = ? and t0.deleted =");
|
||||
assertThat(name0).isEqualTo("findSingleAttribute");
|
||||
|
||||
// now soft delete the bean
|
||||
DB.delete(bean);
|
||||
List<String> sqlUpdate = LoggedSqlCollector.current();
|
||||
assertThat(sqlUpdate.get(0)).contains("update ebasic_sdchild set");
|
||||
|
||||
// use setIncludeSoftDeletes
|
||||
final String name1 = DB.find(EBasicSoftDelete.class)
|
||||
.select("name")
|
||||
.where().eq("name", "findSingleAttribute")
|
||||
.setIncludeSoftDeletes()
|
||||
.findSingleAttribute();
|
||||
|
||||
List<String> sql1 = LoggedSqlCollector.current();
|
||||
assertThat(sql1.get(0)).doesNotContain(" and t0.deleted =");
|
||||
assertThat(name1).isEqualTo("findSingleAttribute");
|
||||
|
||||
|
||||
// not using setIncludeSoftDeletes, so don't find it
|
||||
final String name2 = DB.find(EBasicSoftDelete.class)
|
||||
.select("name")
|
||||
.where().eq("name", "findSingleAttribute")
|
||||
.findSingleAttribute();
|
||||
|
||||
List<String> sql2 = LoggedSqlCollector.stop();
|
||||
assertThat(sql2.get(0)).contains(" and t0.deleted =");
|
||||
assertThat(name2).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindIdsWhenIncludeSoftDeletedChlld() {
|
||||
|
||||
|
||||
Loaded 100 of 193 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user