Compare commits

..
Author SHA1 Message Date
rob bygrave b746c83ed7 [maven-release-plugin] prepare release ebean-11.38.1 2019-05-01 00:39:44 +12:00
rob bygrave 8e45462c2c Bump ebean-annotation to 4.10 with @DbName 2019-04-30 23:43:52 +12:00
rob bygrave 9fdb375229 #1691 - Refactor io.ebean.Model to use a named database - support for use with non-default named database 2019-04-30 23:42:59 +12:00
Rob BygraveandGitHub 7e6d059ef1 Merge pull request #1690 from ebean-orm/feature/1686
#1686 - Change the default JSON format to ISO8601 for date and datetime types (from epoch millis)
2019-04-30 20:18:20 +12:00
rob bygrave 856a3a6fdd #1689 - ENH: Add query usingTransaction() and usingConnection() 2019-04-30 20:13:12 +12:00
rob bygrave f94c3e81e5 Merge branch 'master' of github.com:ebean-orm/ebean 2019-04-30 16:36:06 +12:00
rob bygrave 4bf9e2b18e Bump ebean-datasource to 4.5.2 2019-04-30 16:34:01 +12:00
Rob BygraveandGitHub 112de29964 Merge pull request #1688 from thibaultmeyer/bugfix/useless-comparison-test
Remove useless comparison test
2019-04-29 09:18:11 +12:00
Thibault Meyer c5d761076a Remove useless comparison test
The result of certain comparison tests can sometimes be inferred
from their context and the results of other comparisons. Inspect
the code to check whether the logic is correct, and consider
simplifying the logical expression.

Signed-off-by: Thibault Meyer <meyer.thibault@gmail.com>
2019-04-28 12:10:56 +02:00
rob bygrave 4bedefa494 Merge branch 'master' of github.com:ebean-orm/ebean 2019-04-28 09:51:08 +12:00
rob bygrave a5e3a70921 #1685 DefaultServerCache expose hit and miss count to assist testing
Expose these counters to assist the testing of duel caches where
 DefaultServerCache is used as the near cache.
2019-04-28 09:50:45 +12:00
Rob BygraveandGitHub f6f4368048 Merge pull request #1687 from thibaultmeyer/bugfix/implicit-conv-from-array-to-str
Fix implicit conversion from array to string
2019-04-28 00:38:23 +12:00
rob bygrave e700f1c83c #1685 Update ServerCacheFactory to support standard metrics collection 2019-04-27 23:22:26 +12:00
rob bygrave dc5ac02952 Bump to 11.38.1-SNAPSHOT 2019-04-27 23:06:09 +12:00
Thibault Meyer b89371acd1 Fix implicit conversion from array to string
When converting an array to a readable string, use Arrays.toString
for one-dimensional arrays. This function iterate over the contents
of the array and produce human-readable output.

Signed-off-by: Thibault Meyer <meyer.thibault@gmail.com>
2019-04-27 13:06:02 +02:00
rob bygrave 6c88325dad #1685 Update ServerCacheFactory to support standard metrics collection 2019-04-27 22:57:30 +12:00
rob bygrave ecec966a85 #1686 - Change the default JSON format to ISO8601 for date and datetime types (from epoch millis) 2019-04-27 11:20:21 +12:00
rob bygrave c46ac04c0a #1684 - Fix JSON marshalling on LocalDate, sql.Date etc - explicit UTC
Make explicit use of UTC
2019-04-27 10:33:17 +12:00
rob bygrave 25e6d05542 #1685 - Add support for MetaCounterMetrics and refactor DefaultServerCache to use them 2019-04-27 00:34:45 +12:00
rob bygrave d3ead5d2f9 #1684 - ENH: Support ISO date format for JSON marshalling on LocalDate, sql.Date etc 2019-04-26 19:01:16 +12:00
rob bygrave 28a70fa550 #1684 - ENH: Support ISO date format for JSON marshalling on LocalDate, sql.Date etc 2019-04-26 19:00:49 +12:00
rob bygrave cf31830cbc #1677 - No effective change - Add test but not failing etc 2019-04-26 13:21:19 +12:00
rob bygrave b4105ab5d0 #1683 - Refactor MetaQueryPlan and MetaOrmQueryMetric - rename getQueryPlanHash() to getSqlHash() 2019-04-26 12:55:40 +12:00
Rob BygraveandGitHub 1bb2b1318e Merge pull request #1682 from ebean-orm/feature/1679-region
Feature/1679 region
2019-04-26 12:03:11 +12:00
rob bygrave 65fa1c056b #1681 - Support hitting L2 bean cache via ... findList() with idsIn() expression 2019-04-26 12:01:16 +12:00
rob bygrave 242945aa18 #1681 - Update tests for TestCacheViaComplexNaturalKey 2019-04-26 11:54:52 +12:00
Rob BygraveandGitHub 5e77be6e6d Update sponsor image link for PayinTech 2019-04-26 09:16:40 +12:00
rob bygrave 8c0d6b1a72 #1679 - ENH: Support enabling/disabling L2 cache by "region" at runtime 2019-04-24 15:41:53 +12:00
rob bygrave 86436ac3ac #1679 - ENH: Support enabling/disabling L2 cache by "region" at runtime 2019-04-24 15:41:07 +12:00
rob bygrave ae228d6b0e #1679 - ENH: Support enabling/disabling L2 cache by "region" at runtime 2019-04-24 14:00:22 +12:00
rob bygrave 410d1a9b37 update sponsor logo for premium-minds 2019-04-12 20:53:35 +12:00
rob bygrave f667a73dc1 [maven-release-plugin] prepare for next development iteration 2019-04-12 13:38:53 +12:00
110 changed files with 2034 additions and 740 deletions
+2 -2
View File
@@ -12,12 +12,12 @@
</td>
<td align="center" valign="middle">
<a href="https://www.payintech.com/" target="_blank">
<img width="222px" src="https://www.payintech.com/wp-content/uploads/2015/10/PayinTech-logo-noir.png">
<img width="222px" src="https://ebean.io/images/sponsor_PayinTech-logo-noir.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.premium-minds.com" target="_blank">
<img width="222px" src="https://ebean.io/images/logo-premium-minds.png">
<img width="222px" src="https://ebean.io/images/logo-med-principal.png">
</a>
</td>
</tr>
+8 -8
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.37.1</version>
<version>11.38.1</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -22,7 +22,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-11.37.1</tag>
<tag>ebean-11.38.1</tag>
</scm>
<profiles>
@@ -117,7 +117,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>4.7</version>
<version>4.10</version>
</dependency>
<dependency>
@@ -129,7 +129,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-datasource</artifactId>
<version>4.5.1</version>
<version>4.5.2</version>
</dependency>
<dependency>
@@ -220,10 +220,10 @@
<!-- Test scope -->
<!--<dependency>-->
<!--<groupId>oracle</groupId>-->
<!--<artifactId>oracle-jdbc</artifactId>-->
<!--<version>7.0</version>-->
<!--<scope>test</scope>-->
<!--<groupId>oracle</groupId>-->
<!--<artifactId>oracle-jdbc</artifactId>-->
<!--<version>7.0</version>-->
<!--<scope>test</scope>-->
<!--</dependency>-->
<dependency>
@@ -9,6 +9,7 @@ import io.ebean.search.TextSimple;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.NonUniqueResultException;
import java.sql.Connection;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.List;
@@ -186,6 +187,16 @@ public interface ExpressionList<T> {
*/
Query<T> setIncludeSoftDeletes();
/**
* Execute the query using the given transaction.
*/
Query<T> usingTransaction(Transaction transaction);
/**
* Execute the query using the given connection.
*/
Query<T> usingConnection(Connection connection);
/**
* Execute as a delete query deleting the 'root level' beans that match the predicates
* in the query.
+21 -32
View File
@@ -75,6 +75,25 @@ import io.ebean.bean.EntityBean;
*/
public abstract class Model {
/**
* The name of the database this entity will use, null for the default database.
*/
private final String _$dbName;
/**
* Create using the default database.
*/
public Model() {
this._$dbName = null;
}
/**
* Create with a named database (typically not the default database).
*/
public Model(String dbName) {
this._$dbName = dbName;
}
/**
* Return the underlying 'default' Database.
* <p>
@@ -107,17 +126,8 @@ public abstract class Model {
*
* }</pre>
*/
public static Database db() {
return DB.getDefault();
}
/**
* Return a named Database that is typically different to the default database.
*
* @param server The name of the Database. If this is null then the default Database is returned.
*/
public static Database db(String server) {
return DB.byName(server);
public Database db() {
return DB.byName(_$dbName);
}
/**
@@ -245,27 +255,6 @@ public abstract class Model {
return db().deletePermanent(this);
}
/**
* Perform an update using this entity against the specified server.
*/
public void update(String server) {
db(server).update(this);
}
/**
* Perform an insert using this entity against the specified server.
*/
public void insert(String server) {
db(server).insert(this);
}
/**
* Perform a delete using this entity against the specified server.
*/
public boolean delete(String server) {
return db(server).delete(this);
}
/**
* Refreshes this entity from the database.
*
+11
View File
@@ -3,6 +3,7 @@ package io.ebean;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.NonUniqueResultException;
import java.sql.Connection;
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
@@ -631,6 +632,16 @@ public interface Query<T> {
*/
Query<T> apply(FetchPath fetchPath);
/**
* Execute the query using the given transaction.
*/
Query<T> usingTransaction(Transaction transaction);
/**
* Execute the query using the given connection.
*/
Query<T> usingConnection(Connection connection);
/**
* Execute the query returning the list of Id's.
* <p>
+9
View File
@@ -1,5 +1,7 @@
package io.ebean.cache;
import io.ebean.meta.MetricVisitor;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
@@ -79,4 +81,11 @@ public interface ServerCache {
* @param reset if true the statistics are reset.
*/
ServerCacheStatistics getStatistics(boolean reset);
/**
* Visit the metrics for the cache.
*/
default void visit(MetricVisitor visitor) {
// do nothing by default
}
}
+10 -1
View File
@@ -9,13 +9,15 @@ public class ServerCacheConfig {
private final ServerCacheType type;
private final String cacheKey;
private final String shortName;
private final ServerCacheOptions cacheOptions;
private final CurrentTenantProvider tenantProvider;
private final QueryCacheEntryValidate queryCacheEntryValidate;
public ServerCacheConfig(ServerCacheType type, String cacheKey, ServerCacheOptions cacheOptions, CurrentTenantProvider tenantProvider, QueryCacheEntryValidate queryCacheEntryValidate) {
public ServerCacheConfig(ServerCacheType type, String cacheKey, String shortName, ServerCacheOptions cacheOptions, CurrentTenantProvider tenantProvider, QueryCacheEntryValidate queryCacheEntryValidate) {
this.type = type;
this.cacheKey = cacheKey;
this.shortName = shortName;
this.cacheOptions = cacheOptions;
this.tenantProvider = tenantProvider;
this.queryCacheEntryValidate = queryCacheEntryValidate;
@@ -35,6 +37,13 @@ public class ServerCacheConfig {
return cacheKey;
}
/**
* Return the short name for the cache.
*/
public String getShortName() {
return shortName;
}
/**
* Return the tuning options.
*/
+9
View File
@@ -1,10 +1,19 @@
package io.ebean.cache;
import io.ebean.meta.MetricVisitor;
/**
* Defines method for constructing caches for beans and queries.
*/
public interface ServerCacheFactory {
/**
* Visit the metrics for the cache.
*/
default void visit(MetricVisitor visitor) {
// do nothing by default
}
/**
* Create the cache for the given type with options.
*/
+36 -2
View File
@@ -1,10 +1,19 @@
package io.ebean.cache;
import io.ebean.meta.MetricVisitor;
import java.util.List;
/**
* The cache service for server side caching of beans and query results.
*/
public interface ServerCacheManager {
/**
* Visit the metrics for all the server caches.
*/
void visitMetrics(MetricVisitor visitor);
/**
* Return true if the L2 caching is local.
* <p>
@@ -14,6 +23,31 @@ public interface ServerCacheManager {
*/
boolean isLocalL2Caching();
/**
* Return all the cache regions.
*/
List<ServerCacheRegion> allRegions();
/**
* Set the regions that are enabled.
* <p>
* Typically this is set on startup and at runtime (via dynamic configuration).
* </p>
*
* @param regions A region name or comma delimited list of region names.
*/
void setEnabledRegions(String regions);
/**
* Enable or disable all the cache regions.
*/
void setAllRegionsEnabled(boolean enabled);
/**
* Return the cache region by name. Typically to enable or disable the region.
*/
ServerCacheRegion getRegion(String name);
/**
* Return the cache for mapping natural keys to id values.
*/
@@ -46,7 +80,7 @@ public interface ServerCacheManager {
/**
* Clear all the local caches.
*
* <p>
* This is used when the L2 Cache is based on clustered near-caches (Like Ebean-K8s-L2Cache).
* It is not used when the L2 cache is a distributed cache such as HazelCast or Ignite etc.
*/
@@ -54,7 +88,7 @@ public interface ServerCacheManager {
/**
* Clear the local caches for this bean type.
*
* <p>
* This is used when the L2 Cache is based on clustered near-caches (Like Ebean-K8s-L2Cache).
* It is not used when the L2 cache is a distributed cache such as HazelCast or Ignite etc.
*/
+23
View File
@@ -0,0 +1,23 @@
package io.ebean.cache;
/**
* Cache region can be enabled independently.
*/
public interface ServerCacheRegion {
/**
* Return the region name.
*/
String getName();
/**
* Return true if the cache region is enabled.
*/
boolean isEnabled();
/**
* Set to true to enable the cache region.
*/
void setEnabled(boolean enabled);
}
+12 -96
View File
@@ -21,23 +21,13 @@ public class ServerCacheStatistics {
protected long missCount;
protected long insertCount;
protected long updateCount;
protected long putCount;
protected long removeCount;
protected long clearCount;
protected long evictionRunCount;
protected long evictionRunMicros;
protected long evictByIdle;
protected long evictByTTL;
protected long evictByLRU;
protected long evictCount;
@Override
public String toString() {
@@ -49,15 +39,10 @@ public class ServerCacheStatistics {
sb.append(" hitRatio:").append(getHitRatio());
sb.append(" hit:").append(hitCount);
sb.append(" miss:").append(missCount);
sb.append(" insert:").append(insertCount);
sb.append(" update:").append(updateCount);
sb.append(" put:").append(putCount);
sb.append(" remove:").append(removeCount);
sb.append(" clear:").append(clearCount);
sb.append(" evictByIdle:").append(evictByIdle);
sb.append(" evictByTTL:").append(evictByTTL);
sb.append(" evictByLRU:").append(evictByLRU);
sb.append(" evictionRunCount:").append(evictionRunCount);
sb.append(" evictionRunMicros:").append(evictionRunMicros);
sb.append(" evict:").append(evictCount);
return sb.toString();
}
@@ -153,29 +138,15 @@ public class ServerCacheStatistics {
/**
* Set the put insert count.
*/
public void setInsertCount(long insertCount) {
this.insertCount = insertCount;
public void setPutCount(long putCount) {
this.putCount = putCount;
}
/**
* Return the put insert count.
*/
public long getInsertCount() {
return insertCount;
}
/**
* Set the put update count.
*/
public void setUpdateCount(long updateCount) {
this.updateCount = updateCount;
}
/**
* Return the put update count.
*/
public long getUpdateCount() {
return updateCount;
public long getPutCount() {
return putCount;
}
/**
@@ -206,73 +177,18 @@ public class ServerCacheStatistics {
return clearCount;
}
/**
* Set the eviction run count.
*/
public void setEvictionRunCount(long evictCount) {
this.evictionRunCount = evictCount;
}
/**
* Return the eviction run count.
*/
public long getEvictionRunCount() {
return evictionRunCount;
}
/**
* Set the eviction run time in micros.
*/
public void setEvictionRunMicros(long evictionRunMicros) {
this.evictionRunMicros = evictionRunMicros;
}
/**
* Return the eviction run time in micros.
*/
public long getEvictionRunMicros() {
return evictionRunMicros;
}
/**
* Set the count of entries evicted due to idle time.
*/
public void setEvictByIdle(long evictByIdle) {
this.evictByIdle = evictByIdle;
public void setEvictCount(long evictCount) {
this.evictCount = evictCount;
}
/**
* Return the count of entries evicted due to idle time.
*/
public long getEvictByIdle() {
return evictByIdle;
public long getEvictCount() {
return evictCount;
}
/**
* Set the count of entries evicted due to time to live.
*/
public void setEvictByTTL(long evictByTTL) {
this.evictByTTL = evictByTTL;
}
/**
* Return the count of entries evicted due to time to live.
*/
public long getEvictByTTL() {
return evictByTTL;
}
/**
* Set the count of entries evicted due to time least recently used.
*/
public void setEvictByLRU(long evictByLRU) {
this.evictByLRU = evictByLRU;
}
/**
* Return the count of entries evicted due to time least recently used.
*/
public long getEvictByLRU() {
return evictByLRU;
}
}
@@ -5,6 +5,22 @@ package io.ebean.config;
*/
public abstract class JsonConfig {
/**
* Defined the format used for Date types.
*/
public enum Date {
/**
* Format as epoch millis.
*/
MILLIS,
/**
* Format as ISO-8601 date format.
*/
ISO8601
}
/**
* Defined the format used for DateTime types.
*/
@@ -169,7 +169,12 @@ public class ServerConfig {
/**
* The JSON format used for DateTime types. Default to millis.
*/
private JsonConfig.DateTime jsonDateTime = JsonConfig.DateTime.MILLIS;
private JsonConfig.DateTime jsonDateTime = JsonConfig.DateTime.ISO8601;
/**
* The JSON format used for Date types. Default to millis.
*/
private JsonConfig.Date jsonDate = JsonConfig.Date.ISO8601;
/**
* For writing JSON specify if null values or empty collections should be exluded.
@@ -479,6 +484,7 @@ public class ServerConfig {
*/
private boolean disableL2Cache;
private String enabledL2Regions;
/**
* Should the javax.validation.constraints.NotNull enforce a notNull column in DB.
@@ -682,6 +688,20 @@ public class ServerConfig {
this.jsonDateTime = jsonDateTime;
}
/**
* Return the JSON format used for Date types.
*/
public JsonConfig.Date getJsonDate() {
return jsonDate;
}
/**
* Set the JSON format to use for Date types.
*/
public void setJsonDate(JsonConfig.Date jsonDate) {
this.jsonDate = jsonDate;
}
/**
* Return the JSON include mode used when writing JSON.
*/
@@ -2914,6 +2934,7 @@ public class ServerConfig {
collectQueryPlans = p.getBoolean("collectQueryPlans", collectQueryPlans);
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
enabledL2Regions = p.get("enabledL2Regions", enabledL2Regions);
notifyL2CacheInForeground = p.getBoolean("notifyL2CacheInForeground", notifyL2CacheInForeground);
explicitTransactionBeginMode = p.getBoolean("explicitTransactionBeginMode", explicitTransactionBeginMode);
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
@@ -2986,12 +3007,8 @@ public class ServerConfig {
queryBatchSize = p.getInt("queryBatchSize", queryBatchSize);
jsonInclude = p.getEnum(JsonConfig.Include.class, "jsonInclude", jsonInclude);
String jsonDateTimeFormat = p.get("jsonDateTime", null);
if (jsonDateTimeFormat != null) {
jsonDateTime = JsonConfig.DateTime.valueOf(jsonDateTimeFormat);
} else {
jsonDateTime = JsonConfig.DateTime.MILLIS;
}
jsonDateTime = p.getEnum(JsonConfig.DateTime.class, "jsonDateTime", jsonDateTime);
jsonDate = p.getEnum(JsonConfig.Date.class, "jsonDate", jsonDate);
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
ddlRun = p.getBoolean("ddl.run", ddlRun);
@@ -3136,6 +3153,20 @@ public class ServerConfig {
this.expressionNativeIlike = expressionNativeIlike;
}
/**
* Return the enabled L2 cache regions.
*/
public String getEnabledL2Regions() {
return enabledL2Regions;
}
/**
* Set the enabled L2 cache regions (comma delimited).
*/
public void setEnabledL2Regions(String enabledL2Regions) {
this.enabledL2Regions = enabledL2Regions;
}
/**
* Return true if L2 cache is disabled.
*/
@@ -65,13 +65,9 @@ public class SqlServer2005SqlLimiter implements SqlLimiter {
sb.append(NEW_LINE).append(") ");
sb.append(rowNumberWindowAlias);
sb.append(" where ");
if (firstRow > 0) {
sb.append(" rn > ").append(firstRow);
if (lastRow > 0) {
sb.append(" and ");
}
}
sb.append(" rn > ").append(firstRow);
if (lastRow > 0) {
sb.append(" and ");
sb.append(" rn <= ").append(lastRow);
}
@@ -38,10 +38,8 @@ public class SqlServerSqlLimiter implements SqlLimiter {
}
sb.append(dbSql);
if (firstRow > 0) {
sb.append(" ").append("offset");
sb.append(" ").append(firstRow).append(" rows");
}
sb.append(" ").append("offset");
sb.append(" ").append(firstRow).append(" rows");
if (maxRows > 0) {
sb.append(" fetch next ").append(maxRows).append(" rows only");
}
@@ -8,11 +8,13 @@ public abstract class AbstractMetricVisitor implements MetricVisitor {
private final boolean reset;
private final boolean collectTransactionMetrics;
private final boolean collectQueryMetrics;
private final boolean collectL2Metrics;
public AbstractMetricVisitor(boolean reset, boolean collectTransactionMetrics, boolean collectQueryMetrics) {
public AbstractMetricVisitor(boolean reset, boolean collectTransactionMetrics, boolean collectQueryMetrics, boolean collectL2Metrics) {
this.reset = reset;
this.collectTransactionMetrics = collectTransactionMetrics;
this.collectQueryMetrics = collectQueryMetrics;
this.collectL2Metrics = collectL2Metrics;
}
@Override
@@ -30,6 +32,11 @@ public abstract class AbstractMetricVisitor implements MetricVisitor {
return collectQueryMetrics;
}
@Override
public boolean isCollectL2Metrics() {
return collectL2Metrics;
}
@Override
public void visitStart() {
// do nothing by default
@@ -11,19 +11,20 @@ public class BasicMetricVisitor extends AbstractMetricVisitor implements ServerM
private final List<MetaTimedMetric> timed = new ArrayList<>();
private final List<MetaQueryMetric> dtoQuery = new ArrayList<>();
private final List<MetaOrmQueryMetric> ormQuery = new ArrayList<>();
private final List<MetaCountMetric> countMetrics = new ArrayList<>();
/**
* Construct to reset and collect everything.
*/
public BasicMetricVisitor() {
super(true, true, true);
super(true, true, true, true);
}
/**
* Construct specifying reset and what to collect.
*/
public BasicMetricVisitor(boolean reset, boolean collectTransactionMetrics, boolean collectQueryMetrics) {
super(reset, collectTransactionMetrics, collectQueryMetrics);
public BasicMetricVisitor(boolean reset, boolean collectTransactionMetrics, boolean collectQueryMetrics, boolean collectL2Metrics) {
super(reset, collectTransactionMetrics, collectQueryMetrics, collectL2Metrics);
}
/**
@@ -50,6 +51,11 @@ public class BasicMetricVisitor extends AbstractMetricVisitor implements ServerM
return ormQuery;
}
@Override
public List<MetaCountMetric> getCountMetrics() {
return countMetrics;
}
@Override
public void visitTimed(MetaTimedMetric metric) {
timed.add(metric);
@@ -64,4 +70,9 @@ public class BasicMetricVisitor extends AbstractMetricVisitor implements ServerM
public void visitOrmQuery(MetaOrmQueryMetric metric) {
ormQuery.add(metric);
}
@Override
public void visitCount(MetaCountMetric metric) {
countMetrics.add(metric);
}
}
@@ -0,0 +1,23 @@
package io.ebean.meta;
/**
* Count metrics.
*/
public interface MetaCountMetric {
/**
* Return the metric type.
*/
MetricType getMetricType();
/**
* Return the metric name.
*/
String getName();
/**
* Return the total count.
*/
long getCount();
}
@@ -13,9 +13,9 @@ public interface MetaOrmQueryMetric extends MetaQueryMetric {
boolean isAutoTuned();
/**
* Return a string representation of the query plan hash.
* Return the hash of the sql.
*/
String getQueryPlanHash();
String getSqlHash();
/**
* Return the time of the last query executed using this plan.
+31 -3
View File
@@ -1,23 +1,51 @@
package io.ebean.meta;
import io.ebean.ProfileLocation;
public interface MetaQueryPlan {
/**
* Return the bean type for the query.
*/
Class<?> getBeanType();
/**
* Return a string representation of the query plan hash.
* Return the label of the query.
*/
String getQueryPlanHash();
String getLabel();
/**
* Return the profile location for the query.
*/
ProfileLocation getProfileLocation();
/**
* Return the sql of the query.
*/
String getSql();
/**
* Return the hash of the sql.
*/
String getSqlHash();
/**
* Return a description of the bind values.
*/
String getBind();
/**
* Return the raw plan.
*/
String getPlan();
/**
* Return the query execution time associated with the bind values capture.
*/
long getQueryTimeMicros();
/**
* Return the total count of times bind capture has occurred.
*/
long getCaptureCount();
}
@@ -20,6 +20,11 @@ public interface MetricVisitor {
*/
boolean isCollectQueryMetrics();
/**
* Return true if we should visit the L2 cache metrics.
*/
boolean isCollectL2Metrics();
/**
* Visit has started.
*/
@@ -40,6 +45,11 @@ public interface MetricVisitor {
*/
void visitOrmQuery(MetaOrmQueryMetric metric);
/**
* Visit a Counter metric.
*/
void visitCount(MetaCountMetric metric);
/**
* Visit has completed.
*/
@@ -21,4 +21,10 @@ public interface ServerMetrics {
* Return the ORM query metrics.
*/
List<MetaOrmQueryMetric> getOrmQueryMetrics();
/**
* Return the Counter metrics.
*/
List<MetaCountMetric> getCountMetrics();
}
+24 -10
View File
@@ -7,12 +7,35 @@ import java.util.Comparator;
*/
public class SortMetric {
public static final Comparator<MetaCountMetric> COUNT_NAME = new CountName();
public static final Comparator<MetaTimedMetric> NAME = new Name();
public static final Comparator<MetaTimedMetric> COUNT = new Count();
public static final Comparator<MetaTimedMetric> TOTAL = new Total();
public static final Comparator<MetaTimedMetric> MEAN = new Mean();
public static final Comparator<MetaTimedMetric> MAX = new Max();
private static int stringCompare(String name, String name2) {
if (name == null) {
return name2 == null ? 0 : -1;
}
if (name2 == null) {
return 1;
}
return name.compareTo(name2);
}
/**
* Sort MetaCountMetric's by name.
*/
public static class CountName implements Comparator<MetaCountMetric> {
@Override
public int compare(MetaCountMetric o1, MetaCountMetric o2) {
return stringCompare(o1.getName(), o2.getName());
}
}
/**
* Sort by name.
*/
@@ -20,16 +43,7 @@ public class SortMetric {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
String name = o1.getName();
String name2 = o2.getName();
if (name == null) {
return name2 == null ? 0 : -1;
}
if (name2 == null) {
return 1;
}
int i = name.compareTo(name2);
int i = stringCompare(o1.getName(), o2.getName());
return i != 0 ? i : Long.compare(o1.getCount(), o2.getCount());
}
}
@@ -0,0 +1,39 @@
package io.ebean.metric;
import io.ebean.meta.MetricVisitor;
/**
* Metric for timed events like transaction execution times.
*/
public interface CountMetric {
/**
* Add to the counter.
*/
void add(long micros);
/**
* Increment the counter by 1.
*/
void increment();
/**
* Return the count value.
*/
long get(boolean reset);
/**
* Return true if there are no metrics collected since the last collection.
*/
boolean isEmpty();
/**
* Reset the statistics.
*/
void reset();
/**
* Visit non empty metrics.
*/
void visit(MetricVisitor visitor);
}
@@ -0,0 +1,6 @@
package io.ebean.metric;
import io.ebean.meta.MetaCountMetric;
public interface CountMetricStats extends MetaCountMetric {
}
@@ -25,6 +25,11 @@ public interface MetricFactory {
*/
TimedMetric createTimedMetric(MetricType metricType, String name);
/**
* Create a counter metric.
*/
CountMetric createCountMetric(MetricType metricType, String name);
/**
* Create a Timed metric.
*/
@@ -0,0 +1,52 @@
package io.ebeaninternal.api;
import io.ebeaninternal.server.expression.IdInExpression;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Used for bean cache lookup with where ids in expression.
*/
public class CacheIdLookup<T> {
private final IdInExpression idInExpression;
private int remaining;
public CacheIdLookup(IdInExpression idInExpression) {
this.idInExpression = idInExpression;
}
/**
* Return the Id values for the in expression.
*/
public Collection<?> idValues() {
return idInExpression.idValues();
}
/**
* Process the hits returning the beans fetched from cache and
* adjusting the in expression (to not fetch the hits).
*/
public List<T> removeHits(BeanCacheResult<T> cacheResult) {
Set<Object> hitIds = new HashSet<>();
List<T> beans = new ArrayList<>(hitIds.size());
for (BeanCacheResult.Entry<T> hit : cacheResult.hits()) {
hitIds.add(hit.getKey());
beans.add(hit.getBean());
}
this.remaining = idInExpression.removeIds(hitIds);
return beans;
}
public boolean allHits() {
return remaining == 0;
}
}
@@ -69,18 +69,18 @@ public class NaturalKeyQueryData<T> {
/**
* Match for IN expression. We only allow one IN clause.
*/
public List<Object> matchIn(String propName, List<Object> sourceValues) {
public boolean matchIn(String propName, List<Object> inValues) {
if (hasIn) {
// only 1 IN allowed (to project naturalIds)
return null;
return false;
}
if (matchProperty(propName)) {
this.hasIn = true;
this.inProperty = propName;
this.inValues = new ArrayList<>(sourceValues);
return this.inValues;
this.inValues = inValues;
return true;
}
return null;
return false;
}
/**
@@ -198,21 +198,19 @@ public class NaturalKeyQueryData<T> {
this.hitCount = hits.size();
List<T> beans = new ArrayList<>(hitCount);
for (BeanCacheResult.Entry<T> hit : hits) {
if (inValues != null) {
Object naturalKey = hit.getKey();
Object inValue = set.getInValue(naturalKey);
inValues.remove(inValue);
} else if (inPairs != null) {
Object naturalKey = hit.getKey();
Pairs.Entry inValue = (Pairs.Entry)set.getInValue(naturalKey);
inPairs.remove(inValue);
}
removeKey(set.getInValue(hit.getKey()));
beans.add(hit.getBean());
}
return beans;
}
private void removeKey(Object inValue) {
if (inValues != null) {
inValues.remove(inValue);
} else if (inPairs != null) {
//noinspection SuspiciousMethodCalls
inPairs.remove(inValue);
}
}
}
@@ -0,0 +1,25 @@
package io.ebeaninternal.api;
public interface SpiCacheControl {
/**
* Return true if bean caching or query caching.
*/
boolean isCaching();
/**
* Return true if bean caching.
*/
boolean isBeanCaching();
/**
* Return true if natural key caching.
*/
boolean isNaturalKeyCaching();
/**
* Return true if query caching.
*/
boolean isQueryCaching();
}
@@ -0,0 +1,10 @@
package io.ebeaninternal.api;
import io.ebean.cache.ServerCacheRegion;
/**
* Cache region can be enabled independently.
*/
public interface SpiCacheRegion extends ServerCacheRegion {
}
@@ -429,6 +429,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
*/
void resetBeanCacheAutoMode(boolean findOne);
/**
* Bean cache lookup for find by ids.
*/
CacheIdLookup<T> cacheIdLookup();
/**
* Collect natural key data for this query or null if the query does not match
* the requirements of natural key lookup.
@@ -2,6 +2,10 @@ package io.ebeaninternal.server.cache;
import io.ebean.cache.ServerCache;
import io.ebean.cache.ServerCacheManager;
import io.ebean.cache.ServerCacheRegion;
import io.ebean.meta.MetricVisitor;
import java.util.List;
/**
* Adapts SpiCacheManager to ServerCacheManager.
@@ -17,11 +21,36 @@ public class DefaultCacheAdapter implements ServerCacheManager {
this.cacheManager = cacheManager;
}
@Override
public void visitMetrics(MetricVisitor visitor) {
cacheManager.visitMetrics(visitor);
}
@Override
public boolean isLocalL2Caching() {
return cacheManager.isLocalL2Caching();
}
@Override
public List<ServerCacheRegion> allRegions() {
return cacheManager.allRegions();
}
@Override
public void setEnabledRegions(String regions) {
cacheManager.setEnabledRegions(regions);
}
@Override
public ServerCacheRegion getRegion(String region) {
return cacheManager.getRegion(region);
}
@Override
public void setAllRegionsEnabled(boolean enabled) {
cacheManager.setAllRegionsEnabled(enabled);
}
@Override
public ServerCache getNaturalKeyCache(Class<?> beanType) {
return cacheManager.getNaturalKeyCache(beanType);
@@ -10,6 +10,7 @@ import io.ebean.cache.ServerCacheFactory;
import io.ebean.cache.ServerCacheOptions;
import io.ebean.cache.ServerCacheType;
import io.ebean.config.CurrentTenantProvider;
import io.ebean.meta.MetricVisitor;
import io.ebean.util.AnnotationUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,32 +47,51 @@ class DefaultCacheHolder {
this.queryCacheEntryValidate = builder.getQueryCacheEntryValidate();
}
ServerCache getCache(Class<?> beanType, String cacheKey, ServerCacheType type) {
return getCacheInternal(beanType, cacheKey, type);
void visitMetrics(MetricVisitor visitor) {
cacheFactory.visit(visitor);
for (ServerCache serverCache : allCaches.values()) {
serverCache.visit(visitor);
}
}
private String key(String cacheKey, ServerCacheType type) {
return cacheKey + type.code();
ServerCache getCache(Class<?> beanType, ServerCacheType type) {
return getCacheInternal(beanType, type, null);
}
ServerCache getCache(Class<?> beanType, String collectionProperty) {
return getCacheInternal(beanType, ServerCacheType.COLLECTION_IDS, collectionProperty);
}
private String key(String beanName, ServerCacheType type) {
return beanName + type.code();
}
private String key(String beanName, String collectionProperty, ServerCacheType type) {
if (collectionProperty != null) {
return beanName + "." + collectionProperty + type.code();
} else {
return beanName + type.code();
}
}
/**
* Return the cache for a given bean type.
*/
private ServerCache getCacheInternal(Class<?> beanType, String cacheKey, ServerCacheType type) {
private ServerCache getCacheInternal(Class<?> beanType, ServerCacheType type, String collectionProperty) {
String fullKey = key(cacheKey, type);
return allCaches.computeIfAbsent(fullKey, s -> createCache(beanType, type, fullKey));
String shortName = key(beanType.getSimpleName(), collectionProperty, type);
String fullKey = key(beanType.getName(), collectionProperty, type);
return allCaches.computeIfAbsent(fullKey, s -> createCache(beanType, type, fullKey, shortName));
}
private ServerCache createCache(Class<?> beanType, ServerCacheType type, String key) {
private ServerCache createCache(Class<?> beanType, ServerCacheType type, String key, String shortName) {
ServerCacheOptions options = getCacheOptions(beanType, type);
if (type == ServerCacheType.COLLECTION_IDS) {
synchronized (this) {
collectIdCaches.computeIfAbsent(beanType.getName(), s -> new ConcurrentSkipListSet<>()).add(key);
}
}
return cacheFactory.createCache(new ServerCacheConfig(type, key, options, tenantProvider, queryCacheEntryValidate));
return cacheFactory.createCache(new ServerCacheConfig(type, key, shortName, options, tenantProvider, queryCacheEntryValidate));
}
void clearAll() {
@@ -107,12 +127,10 @@ class DefaultCacheHolder {
* Return the cache options for a given bean type.
*/
ServerCacheOptions getCacheOptions(Class<?> beanType, ServerCacheType type) {
switch (type) {
case QUERY:
return getQueryOptions(beanType);
default:
return getBeanOptions(beanType);
if (type == ServerCacheType.QUERY) {
return getQueryOptions(beanType);
}
return getBeanOptions(beanType);
}
private ServerCacheOptions getQueryOptions(Class<?> cls) {
@@ -4,6 +4,9 @@ import io.ebean.BackgroundExecutor;
import io.ebean.cache.ServerCache;
import io.ebean.cache.ServerCacheStatistics;
import io.ebean.cache.TenantAwareKey;
import io.ebean.meta.MetricVisitor;
import io.ebean.metric.CountMetric;
import io.ebean.metric.MetricFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -14,7 +17,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;
import static io.ebean.meta.MetricType.L2;
/**
* The default cache implementation.
@@ -37,39 +41,45 @@ public class DefaultServerCache implements ServerCache {
*/
protected final Map<Object, CacheEntry> map;
protected final LongAdder missCount = new LongAdder();
protected final LongAdder hitCount = new LongAdder();
protected final LongAdder insertCount = new LongAdder();
protected final LongAdder updateCount = new LongAdder();
protected final LongAdder removeCount = new LongAdder();
protected final LongAdder clearCount = new LongAdder();
protected final LongAdder evictByIdle = new LongAdder();
protected final LongAdder evictByTTL = new LongAdder();
protected final LongAdder evictByLRU = new LongAdder();
protected final LongAdder evictCount = new LongAdder();
protected final LongAdder evictMicros = new LongAdder();
protected final CountMetric hitCount;
protected final CountMetric missCount;
protected final CountMetric putCount;
protected final CountMetric removeCount;
protected final CountMetric clearCount;
protected final CountMetric evictCount;
protected final String name;
protected final String shortName;
protected int maxSize;
private int maxSize;
protected final int trimFrequency;
private final int trimFrequency;
protected int maxIdleSecs;
private int maxIdleSecs;
protected int maxSecsToLive;
private int maxSecsToLive;
protected TenantAwareKey tenantAwareKey;
private TenantAwareKey tenantAwareKey;
public DefaultServerCache(DefaultServerCacheConfig config) {
this.name = config.getName();
this.shortName = config.getShortName();
this.map = config.getMap();
this.maxSize = config.getMaxSize();
this.tenantAwareKey = new TenantAwareKey(config.getTenantProvider());
this.maxIdleSecs = config.getMaxIdleSecs();
this.maxSecsToLive = config.getMaxSecsToLive();
this.trimFrequency = config.determineTrimFrequency();
MetricFactory factory = MetricFactory.get();
String prefix = "l2n.";
this.hitCount = factory.createCountMetric(L2, prefix + shortName + ".hit");
this.missCount = factory.createCountMetric(L2, prefix + shortName + ".miss");
this.putCount = factory.createCountMetric(L2, prefix + shortName + ".put");
this.removeCount = factory.createCountMetric(L2, prefix + shortName + ".remove");
this.clearCount = factory.createCountMetric(L2, prefix + shortName + ".clear");
this.evictCount = factory.createCountMetric(L2, prefix + shortName + ".evict");
}
public void periodicTrim(BackgroundExecutor executor) {
@@ -81,6 +91,16 @@ public class DefaultServerCache implements ServerCache {
executor.executePeriodically(trim, trimFreqSecs, TimeUnit.SECONDS);
}
@Override
public void visit(MetricVisitor visitor) {
hitCount.visit(visitor);
missCount.visit(visitor);
putCount.visit(visitor);
removeCount.visit(visitor);
clearCount.visit(visitor);
evictCount.visit(visitor);
}
@Override
public ServerCacheStatistics getStatistics(boolean reset) {
@@ -88,47 +108,36 @@ public class DefaultServerCache implements ServerCache {
cacheStats.setCacheName(name);
cacheStats.setMaxSize(maxSize);
// these counters won't necessarily be consistent with
// respect to each other as activity can occur while
// they are being calculated here but they should be good enough
// and we don't want to reduce concurrent use to make them consistent
long clear = reset ? clearCount.sumThenReset() : clearCount.sum();
long remove = reset ? removeCount.sumThenReset() : removeCount.sum();
long update = reset ? updateCount.sumThenReset() : updateCount.sum();
long insert = reset ? insertCount.sumThenReset() : insertCount.sum();
long miss = reset ? missCount.sumThenReset() : missCount.sum();
long hit = reset ? hitCount.sumThenReset() : hitCount.sum();
long evict = reset ? evictCount.sumThenReset() : evictCount.sum();
long evictTime = reset ? evictMicros.sumThenReset() : evictMicros.sum();
long evictIdle = reset ? evictByIdle.sumThenReset() : evictByIdle.sum();
long evictTTL = reset ? evictByTTL.sumThenReset() : evictByTTL.sum();
long evictLRU = reset ? evictByLRU.sumThenReset() : evictByLRU.sum();
int size = size();
cacheStats.setSize(size);
cacheStats.setHitCount(hit);
cacheStats.setMissCount(miss);
cacheStats.setInsertCount(insert);
cacheStats.setUpdateCount(update);
cacheStats.setRemoveCount(remove);
cacheStats.setClearCount(clear);
cacheStats.setEvictionRunCount(evict);
cacheStats.setEvictionRunMicros(evictTime);
cacheStats.setEvictByIdle(evictIdle);
cacheStats.setEvictByTTL(evictTTL);
cacheStats.setEvictByLRU(evictLRU);
cacheStats.setSize(size());
cacheStats.setHitCount(hitCount.get(reset));
cacheStats.setMissCount(missCount.get(reset));
cacheStats.setPutCount(putCount.get(reset));
cacheStats.setRemoveCount(removeCount.get(reset));
cacheStats.setClearCount(clearCount.get(reset));
cacheStats.setEvictCount(evictCount.get(reset));
return cacheStats;
}
/**
* Return the count of get hits.
*/
public long getHitCount() {
return hitCount.get(false);
}
/**
* Return the count of get misses.
*/
public long getMissCount() {
return missCount.get(false);
}
@Override
public int getHitRatio() {
long mc = missCount.sum();
long hc = hitCount.sum();
long mc = missCount.get(false);
long hc = hitCount.get(false);
long totalCount = hc + mc;
if (totalCount == 0) {
@@ -145,6 +154,10 @@ public class DefaultServerCache implements ServerCache {
return name;
}
public String getShortName() {
return shortName;
}
/**
* Clear the cache.
*/
@@ -171,10 +184,7 @@ public class DefaultServerCache implements ServerCache {
if (entry == null) {
missCount.increment();
return null;
} else {
// Important that hitCount.increment() MUST be low latency under concurrent
// use hence must use LongAdder or better here
hitCount.increment();
return unwrapEntry(entry);
}
@@ -204,14 +214,9 @@ public class DefaultServerCache implements ServerCache {
*/
@Override
public void put(Object id, Object value) {
Object key = key(id);
CacheEntry entry = map.put(key, new CacheEntry(key, value));
if (entry == null) {
insertCount.increment();
} else {
updateCount.increment();
}
map.put(key, new CacheEntry(key, value));
putCount.increment();
}
/**
@@ -219,7 +224,6 @@ public class DefaultServerCache implements ServerCache {
*/
@Override
public void remove(Object id) {
CacheEntry entry = map.remove(key(id));
if (entry != null) {
removeCount.increment();
@@ -269,7 +273,7 @@ public class DefaultServerCache implements ServerCache {
List<CacheEntry> activeList = new ArrayList<>(map.size());
long idleExpireNano = startNanos - TimeUnit.SECONDS.toNanos(maxIdleSecs);
long idleExpireNano = startNanos - TimeUnit.SECONDS.toNanos(maxIdleSecs);
long ttlExpireNano = startNanos - TimeUnit.SECONDS.toNanos(maxSecsToLive);
Iterator<CacheEntry> it = map.values().iterator();
@@ -301,17 +305,12 @@ public class DefaultServerCache implements ServerCache {
}
}
long exeNanos = System.nanoTime() - startNanos;
long exeMicros = TimeUnit.MICROSECONDS.convert(exeNanos, TimeUnit.NANOSECONDS);
// increment the eviction statistics
evictMicros.add(exeMicros);
evictCount.increment();
evictByIdle.add(trimmedByIdle);
evictByTTL.add(trimmedByTTL);
evictByLRU.add(trimmedByLRU);
evictCount.add(trimmedByIdle);
evictCount.add(trimmedByTTL);
evictCount.add(trimmedByLRU);
if (logger.isTraceEnabled()) {
long exeMicros = TimeUnit.MICROSECONDS.convert(System.nanoTime() - startNanos, TimeUnit.NANOSECONDS);
logger.trace("Executed trim of cache {} in [{}]millis idle[{}] timeToLive[{}] accessTime[{}]"
, name, exeMicros, trimmedByIdle, trimmedByTTL, trimmedByLRU);
}
@@ -46,6 +46,10 @@ public class DefaultServerCacheConfig {
return config.getCacheKey();
}
public String getShortName() {
return config.getShortName();
}
public Map<Object, DefaultServerCache.CacheEntry> getMap() {
return map;
}
@@ -1,14 +1,30 @@
package io.ebeaninternal.server.cache;
import io.ebean.cache.ServerCache;
import io.ebean.cache.ServerCacheRegion;
import io.ebean.cache.ServerCacheType;
import io.ebean.meta.MetricVisitor;
import io.ebeaninternal.api.SpiCacheRegion;
import io.ebeaninternal.server.cluster.ClusterManager;
import io.ebeaninternal.server.deploy.DCacheRegion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Manages the bean and query caches.
*/
public class DefaultServerCacheManager implements SpiCacheManager {
private static final Logger log = LoggerFactory.getLogger("io.ebean.cache.REGION");
private final Map<String, SpiCacheRegion> regionMap = new HashMap<>();
private final ClusterManager clusterManager;
private final DefaultCacheHolder cacheHolder;
@@ -39,6 +55,56 @@ public class DefaultServerCacheManager implements SpiCacheManager {
return localL2Caching;
}
@Override
public List<ServerCacheRegion> allRegions() {
return new ArrayList<>(regionMap.values());
}
@Override
public void setEnabledRegions(String regions) {
if (regions != null) {
List<String> enabledRegionNames = Arrays.asList(regions.split(","));
List<String> disabled = new ArrayList<>();
List<String> enabled = new ArrayList<>();
for (SpiCacheRegion region : regionMap.values()) {
if (enabledRegionNames.contains(region.getName())) {
enabled.add(region.getName());
if (!region.isEnabled()) {
region.setEnabled(true);
log.debug("Cache region[{}] enabled", region.getName());
}
} else {
disabled.add(region.getName());
if (region.isEnabled()) {
region.setEnabled(false);
log.debug("Cache region[{}] disabled", region.getName());
}
}
}
log.info("Cache regions enabled:{} disabled:{}", enabled, disabled);
}
}
@Override
public void setAllRegionsEnabled(boolean enabled) {
log.debug("All cache regions enabled[{}]", enabled);
for (SpiCacheRegion region : regionMap.values()) {
region.setEnabled(enabled);
}
}
@Override
public SpiCacheRegion getRegion(String region) {
return regionMap.computeIfAbsent(region, DCacheRegion::new);
}
@Override
public void visitMetrics(MetricVisitor visitor) {
cacheHolder.visitMetrics(visitor);
}
/**
* Clear all caches.
*/
@@ -57,7 +123,7 @@ public class DefaultServerCacheManager implements SpiCacheManager {
@Override
public void clear(Class<?> beanType) {
cacheHolder.clear(name(beanType));
cacheHolder.clear(beanType.getName());
if (clusterManager != null) {
clusterManager.cacheClear(serverName, beanType);
}
@@ -65,17 +131,17 @@ public class DefaultServerCacheManager implements SpiCacheManager {
@Override
public void clearLocal(Class<?> beanType) {
cacheHolder.clear(name(beanType));
cacheHolder.clear(beanType.getName());
}
@Override
public ServerCache getCollectionIdsCache(Class<?> beanType, String propertyName) {
return cacheHolder.getCache(beanType, name(beanType) + "." + propertyName, ServerCacheType.COLLECTION_IDS);
public ServerCache getCollectionIdsCache(Class<?> beanType, String collectionProperty) {
return cacheHolder.getCache(beanType, collectionProperty);
}
@Override
public ServerCache getNaturalKeyCache(Class<?> beanType) {
return cacheHolder.getCache(beanType, name(beanType), ServerCacheType.NATURAL_KEY);
return cacheHolder.getCache(beanType, ServerCacheType.NATURAL_KEY);
}
/**
@@ -83,7 +149,7 @@ public class DefaultServerCacheManager implements SpiCacheManager {
*/
@Override
public ServerCache getQueryCache(Class<?> beanType) {
return cacheHolder.getCache(beanType, name(beanType), ServerCacheType.QUERY);
return cacheHolder.getCache(beanType, ServerCacheType.QUERY);
}
/**
@@ -91,11 +157,7 @@ public class DefaultServerCacheManager implements SpiCacheManager {
*/
@Override
public ServerCache getBeanCache(Class<?> beanType) {
return cacheHolder.getCache(beanType, name(beanType), ServerCacheType.BEAN);
}
private String name(Class<?> beanType) {
return beanType.getName();
return cacheHolder.getCache(beanType, ServerCacheType.BEAN);
}
}
@@ -1,12 +1,22 @@
package io.ebeaninternal.server.cache;
import io.ebean.cache.ServerCache;
import io.ebean.cache.ServerCacheRegion;
import io.ebean.meta.MetricVisitor;
import io.ebeaninternal.api.SpiCacheRegion;
import java.util.List;
/**
* The cache service for server side caching of beans and query results.
*/
public interface SpiCacheManager {
/**
* Visit and collect the metrics.
*/
void visitMetrics(MetricVisitor visitor);
/**
* Return true if the L2 caching is local.
* <p>
@@ -16,6 +26,28 @@ public interface SpiCacheManager {
*/
boolean isLocalL2Caching();
/**
* Return all the regions.
*/
List<ServerCacheRegion> allRegions();
/**
* Set the regions that are enabled.
*
* @param regions A single region name or a comma delimited list of region names.
*/
void setEnabledRegions(String regions);
/**
* Enable or disable all cache regions.
*/
void setAllRegionsEnabled(boolean enabled);
/**
* Return the cache region. We can enable L2 caching by region.
*/
SpiCacheRegion getRegion(String region);
/**
* Return the cache for mapping natural keys to id values.
*/
@@ -12,45 +12,70 @@ public class CacheOptions {
*/
public static final CacheOptions NO_CACHING = new CacheOptions();
public static final CacheOptions INVALIDATE_QUERY_CACHE = new CacheOptions(true);
private static final String R0 = "r0";
private static final CacheOptions INVALIDATE_QUERY_CACHE_R0 = new CacheOptions(true);
private final boolean invalidateQueryCache;
private final boolean enableBeanCache;
private final boolean enableQueryCache;
private final boolean readOnly;
private final String[] naturalKey;
private final String region;
public static CacheOptions invalidateQueryCache(String region) {
if (R0.equals(region)) {
return INVALIDATE_QUERY_CACHE_R0;
} else {
return new CacheOptions(true, region);
}
}
/**
* Construct for no caching.
*/
private CacheOptions() {
invalidateQueryCache = false;
enableBeanCache = false;
enableQueryCache = false;
readOnly = false;
naturalKey = null;
this.invalidateQueryCache = false;
this.enableBeanCache = false;
this.enableQueryCache = false;
this.readOnly = false;
this.naturalKey = null;
this.region = null;
}
/**
* Construct for invalidateQueryCache.
*/
private CacheOptions(boolean invalidateQueryCache) {
this(invalidateQueryCache, R0);
}
private CacheOptions(boolean invalidateQueryCache, String region) {
this.invalidateQueryCache = invalidateQueryCache;
enableBeanCache = false;
enableQueryCache = false;
readOnly = false;
naturalKey = null;
this.enableBeanCache = false;
this.enableQueryCache = false;
this.readOnly = false;
this.naturalKey = null;
this.region = region;
}
/**
* Construct with cache annotation.
*/
public CacheOptions(Cache cache, String[] naturalKey) {
invalidateQueryCache = false;
enableBeanCache = cache.enableBeanCache();
enableQueryCache = cache.enableQueryCache();
readOnly = cache.readOnly();
this.invalidateQueryCache = false;
this.enableBeanCache = cache.enableBeanCache();
this.enableQueryCache = cache.enableQueryCache();
this.readOnly = cache.readOnly();
this.naturalKey = naturalKey;
this.region = cache.region();
}
/**
* Return the cache region name.
*/
public String getRegion() {
return region;
}
/**
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.core;
import io.ebean.meta.AbstractMetricVisitor;
import io.ebean.meta.BasicMetricVisitor;
import io.ebean.meta.MetaCountMetric;
import io.ebean.meta.MetaInfoManager;
import io.ebean.meta.MetaOrmQueryMetric;
import io.ebean.meta.MetaOrmQueryNode;
@@ -71,7 +72,7 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
private static class ResetVisitor extends AbstractMetricVisitor {
ResetVisitor() {
super(true, true, true);
super(true, true, true, true);
}
@Override
@@ -88,6 +89,11 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
public void visitOrmQuery(MetaOrmQueryMetric metric) {
// ignore
}
@Override
public void visitCount(MetaCountMetric metric) {
// ignore
}
}
}
@@ -446,6 +446,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
if (encryptKeyManager != null) {
encryptKeyManager.initialise();
}
serverCacheManager.setEnabledRegions(serverConfig.getEnabledL2Regions());
}
/**
@@ -484,7 +485,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
*/
private void shutdownInternal(boolean shutdownDataSource, boolean deregisterDriver) {
logger.debug("Shutting down EbeanServer {}", serverName);
logger.debug("Shutting down instance:{}", serverName);
if (shutdown) {
// already shutdown
return;
@@ -790,23 +791,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
transactionManager.exitScopedTransaction(returnOrThrowable, opCode);
}
/**
* Returns the current transaction (or null) from the scope.
*/
@Override
public SpiTransaction currentServerTransaction() {
return transactionManager.getActive();
}
/**
* Start a transaction with 'REQUIRED' semantics.
* <p>
* If a transaction already exists that transaction will be used.
* </p>
* <p>
* Note that the transaction is stored in a ThreadLocal variable.
* </p>
*/
@Override
public Transaction beginTransaction() {
return beginTransaction(TxScope.required());
@@ -817,12 +806,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return transactionManager.beginScopedTransaction(txScope);
}
/**
* Start a transaction with a specific Isolation Level.
* <p>
* Note that the transaction is stored in a ThreadLocal variable.
* </p>
*/
@Override
public Transaction beginTransaction(TxIsolation isolation) {
// start an explicit transaction
@@ -837,10 +820,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return t;
}
/**
* Return the current transaction or null if there is not one currently in
* scope.
*/
@Override
public Transaction currentTransaction() {
return transactionManager.getActive();
@@ -851,50 +830,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
currentTransaction().flush();
}
/**
* Commit the current transaction.
*/
@Override
public void commitTransaction() {
currentTransaction().commit();
}
/**
* Rollback the current transaction.
*/
@Override
public void rollbackTransaction() {
currentTransaction().rollback();
}
/**
* If the current transaction has already been committed do nothing otherwise
* rollback the transaction.
* <p>
* Useful to put in a finally block to ensure the transaction is ended, rather
* than a rollbackTransaction() in each catch block.
* </p>
* <p>
* Code example:<br />
* <p>
* <pre>
* &lt;code&gt;
* Ebean.startTransaction();
* try {
* // do some fetching and or persisting
*
* // commit at the end
* Ebean.commitTransaction();
*
* } finally {
* // if commit didn't occur then rollback the transaction
* Ebean.endTransaction();
* }
* &lt;/code&gt;
* </pre>
* <p>
* </p>
*/
@Override
public void endTransaction() {
Transaction transaction = transactionManager.getInScope();
@@ -903,13 +848,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
/**
* return the next unique identity value.
* <p>
* Uses the BeanDescriptor deployment information to determine the sequence to
* use.
* </p>
*/
@Override
public Object nextId(Class<?> beanType) {
BeanDescriptor<?> desc = getBeanDescriptor(beanType);
@@ -1133,14 +1071,21 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
<T> SpiOrmQueryRequest<T> createQueryRequest(Type type, Query<T> query, Transaction t) {
SpiOrmQueryRequest<T> request = buildQueryRequest(type, query, t);
request.prepareQuery();
return request;
}
<T> SpiOrmQueryRequest<T> buildQueryRequest(Type type, Query<T> query, Transaction t) {
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
spiQuery.setType(type);
spiQuery.checkNamedParameters();
return createQueryRequest(spiQuery, t);
return buildQueryRequest(spiQuery, t);
}
private <T> SpiOrmQueryRequest<T> createQueryRequest(SpiQuery<T> query, Transaction t) {
private <T> SpiOrmQueryRequest<T> buildQueryRequest(SpiQuery<T> query, Transaction t) {
if (t == null) {
t = currentServerTransaction();
@@ -1163,10 +1108,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
query.setOrigin(createCallStack());
}
OrmQueryRequest<T> request = new OrmQueryRequest<>(this, queryEngine, query, (SpiTransaction) t);
request.prepareQuery();
return request;
return new OrmQueryRequest<>(this, queryEngine, query, (SpiTransaction) t);
}
/**
@@ -1237,7 +1179,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
SpiOrmQueryRequest<T> request = createQueryRequest(spiQuery, t);
SpiOrmQueryRequest<T> request = buildQueryRequest(spiQuery, t);
request.prepareQuery();
if (request.isUseDocStore()) {
return docStore().find(request);
}
@@ -1590,15 +1533,18 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@SuppressWarnings("unchecked")
private <T> List<T> findList(Query<T> query, Transaction t, boolean findOne) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, t);
SpiOrmQueryRequest<T> request = buildQueryRequest(Type.LIST, query, t);
request.resetBeanCacheAutoMode(findOne);
if ((t == null || !t.isSkipCache()) && request.getFromBeanCache()) {
// hit bean cache and got all results from cache
return request.getBeanCacheHits();
}
request.prepareQuery();
Object result = request.getFromQueryCache();
if (result != null) {
return (List<T>) result;
}
if ((t == null || !t.isSkipCache()) && request.getFromBeanCache()) {
return request.getBeanCacheHits();
}
if (request.isUseDocStore()) {
return docStore().findList(request);
}
@@ -2443,6 +2389,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
if (visitor.isCollectTransactionMetrics()) {
transactionManager.visitMetrics(visitor);
}
if (visitor.isCollectL2Metrics()) {
serverCacheManager.visitMetrics(visitor);
}
if (visitor.isCollectQueryMetrics()) {
beanDescriptorManager.visitMetrics(visitor);
dtoBeanManager.visitMetrics(visitor);
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.core;
import io.ebean.ProfileLocation;
import io.ebean.meta.MetaCountMetric;
import io.ebean.meta.MetaOrmQueryMetric;
import io.ebean.meta.MetaQueryMetric;
import io.ebean.meta.MetaTimedMetric;
@@ -83,6 +84,15 @@ class DumpMetrics {
log(metric);
}
List<MetaCountMetric> countMetrics = serverMetrics.getCountMetrics();
if (!countMetrics.isEmpty()) {
out("\n-- Counters --");
countMetrics.sort(SortMetric.COUNT_NAME);
for (MetaCountMetric metric : countMetrics) {
logCount(metric);
}
}
List<MetaOrmQueryMetric> ormQueryMetrics = serverMetrics.getOrmQueryMetrics();
if (!ormQueryMetrics.isEmpty()) {
out("\n-- ORM queries --");
@@ -102,6 +112,14 @@ class DumpMetrics {
}
}
private void logCount(MetaCountMetric metric) {
StringBuilder sb = new StringBuilder();
sb.append(padNameTimed(metric.getName())).append(" ");
sb.append(" count:").append(pad(metric.getCount()));
out(sb.toString());
}
private void out(String sb) {
System.out.println(sb);
}
@@ -113,7 +131,7 @@ class DumpMetrics {
appendQueryName(metric, sb);
appendCounters(metric, sb);
if (dumpHash) {
sb.append("\n hash:").append(metric.getQueryPlanHash());
sb.append("\n hash:").append(metric.getSqlHash());
}
appendProfileAndSql(metric, sb);
out(sb.toString());
@@ -16,6 +16,7 @@ import io.ebean.event.BeanQueryAdapter;
import io.ebean.text.json.JsonReadOptions;
import io.ebeaninternal.api.BeanCacheResult;
import io.ebeaninternal.api.CQueryPlanKey;
import io.ebeaninternal.api.CacheIdLookup;
import io.ebeaninternal.api.HashQuery;
import io.ebeaninternal.api.LoadContext;
import io.ebeaninternal.api.NaturalKeyQueryData;
@@ -208,7 +209,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
/**
* Prepare the query and calculate the query plan key.
*/
void prepareQuery() {
@Override
public void prepareQuery() {
beanDescriptor.prepareQuery(query);
adapterPreQuery();
this.secondaryQueries = query.convertJoins();
@@ -598,6 +600,14 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
// - merge the 2 results and return
//
CacheIdLookup<T> idLookup = query.cacheIdLookup();
if (idLookup != null) {
BeanCacheResult<T> cacheResult = beanDescriptor.cacheIdLookup(persistenceContext, idLookup.idValues());
// adjust the query (IN clause) based on the cache hits
this.cacheBeans = idLookup.removeHits(cacheResult);
return idLookup.allHits();
}
if (!beanDescriptor.isNaturalKeyCaching()) {
return false;
}
@@ -29,6 +29,11 @@ public interface SpiOrmQueryRequest<T> extends BeanQueryRequest<T>, DocQueryRequ
*/
BeanDescriptor<T> getBeanDescriptor();
/**
* Prepare the query for execution.
*/
void prepareQuery();
/**
* This will create a local (readOnly) transaction if no current transaction
* exists.
@@ -1490,6 +1490,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return cacheHelp.beanCacheLoad(bean, ebi, cacheKey(id), context);
}
public BeanCacheResult<T> cacheIdLookup(PersistenceContext context, Collection<?> ids) {
return cacheHelp.cacheIdLookup(context, ids);
}
/**
* Use natural key lookup to hit the bean cache.
*/
@@ -7,6 +7,8 @@ import io.ebean.bean.PersistenceContext;
import io.ebean.cache.QueryCacheEntry;
import io.ebean.cache.ServerCache;
import io.ebeaninternal.api.BeanCacheResult;
import io.ebeaninternal.api.SpiCacheControl;
import io.ebeaninternal.api.SpiCacheRegion;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.api.TransactionEventTable.TableIUD;
import io.ebeaninternal.server.cache.CacheChangeSet;
@@ -72,6 +74,9 @@ final class BeanDescriptorCacheHelp<T> {
private final boolean noCaching;
private final SpiCacheControl cacheControl;
private final SpiCacheRegion cacheRegion;
/**
* Set to true if all persist changes need to notify the cache.
*/
@@ -113,6 +118,13 @@ final class BeanDescriptorCacheHelp<T> {
this.naturalKeyCache = null;
}
this.noCaching = (beanCache == null && queryCache == null);
if (noCaching) {
this.cacheControl = DCacheControlNone.INSTANCE;
this.cacheRegion = (invalidateQueryCache) ? cacheManager.getRegion(cacheOptions.getRegion()) : DCacheRegionNone.INSTANCE;
} else {
this.cacheRegion = cacheManager.getRegion(cacheOptions.getRegion());
this.cacheControl = new DCacheControl(cacheRegion, (beanCache != null), (naturalKeyCache != null), (queryCache != null));
}
}
/**
@@ -123,8 +135,8 @@ final class BeanDescriptorCacheHelp<T> {
cacheNotifyOnDelete = !cacheNotifyOnAll && isNotifyOnDeletes();
if (logger.isDebugEnabled()) {
if (isBeanCaching() || isQueryCaching() || cacheNotifyOnAll || cacheNotifyOnDelete) {
String notifyMode = cacheNotifyOnAll ? "All" : (cacheNotifyOnDelete ? "Delete" : "None");
if (cacheNotifyOnAll || cacheNotifyOnDelete) {
String notifyMode = cacheNotifyOnAll ? "All" : "Delete";
logger.debug("l2 caching on {} - beanCaching:{} queryCaching:{} notifyMode:{} ",
desc.getFullName(), isBeanCaching(), isQueryCaching(), notifyMode);
}
@@ -148,36 +160,36 @@ final class BeanDescriptorCacheHelp<T> {
* Return true if the persist request needs to notify the cache.
*/
boolean isCacheNotify(PersistRequest.Type type) {
return cacheNotifyOnAll
|| cacheNotifyOnDelete && (type == PersistRequest.Type.DELETE || type == PersistRequest.Type.DELETE_PERMANENT);
return cacheRegion.isEnabled()
&& (cacheNotifyOnAll || cacheNotifyOnDelete && (type == PersistRequest.Type.DELETE || type == PersistRequest.Type.DELETE_PERMANENT));
}
/**
* Return true if there is currently query caching for this type of bean.
*/
boolean isQueryCaching() {
return queryCache != null;
return cacheControl.isQueryCaching();
}
/**
* Return true if there is currently bean caching for this type of bean.
*/
boolean isBeanCaching() {
return beanCache != null;
return cacheControl.isBeanCaching();
}
/**
* Return true if there is natural key caching for this type of bean.
*/
boolean isNaturalKeyCaching() {
return naturalKeyCache != null;
return cacheControl.isNaturalKeyCaching();
}
/**
* Return true if there is bean or query caching on this type.
*/
boolean isCaching() {
return beanCache != null || queryCache != null;
return cacheControl.isCaching();
}
/**
@@ -206,7 +218,7 @@ final class BeanDescriptorCacheHelp<T> {
/**
* Add query cache clear to the changeSet.
*/
void queryCacheClear(CacheChangeSet changeSet) {
private void queryCacheClear(CacheChangeSet changeSet) {
if (queryCache != null) {
changeSet.addClearQuery(desc);
}
@@ -292,8 +304,8 @@ final class BeanDescriptorCacheHelp<T> {
return false;
}
Object ownerBean = bc.getOwnerBean();
EntityBeanIntercept ebi = ((EntityBean) ownerBean)._ebean_getIntercept();
EntityBean ownerBean = bc.getOwnerBean();
EntityBeanIntercept ebi = ownerBean._ebean_getIntercept();
PersistenceContext persistenceContext = ebi.getPersistenceContext();
BeanDescriptor<?> targetDescriptor = many.getTargetDescriptor();
@@ -362,6 +374,31 @@ final class BeanDescriptorCacheHelp<T> {
return new CachedManyIds(idList);
}
/**
* Hit the bean cache with the given ids returning the hits.
*/
BeanCacheResult<T> cacheIdLookup(PersistenceContext context, Collection<?> ids) {
Set<Object> keys = new HashSet<>(ids.size());
for (Object id : ids) {
keys.add(desc.cacheKey(id));
}
Map<Object, Object> beanDataMap = beanCache.getAll(keys);
if (beanLog.isTraceEnabled()) {
beanLog.trace(" GET MANY {}({}) - hits:{}", cacheName, ids, beanDataMap.keySet());
}
BeanCacheResult<T> result = new BeanCacheResult<>();
for (Map.Entry<Object, Object> entry : beanDataMap.entrySet()) {
CachedBeanData cachedBeanData = (CachedBeanData) entry.getValue();
T bean = convertToBean(entry.getKey(), false, context, cachedBeanData);
result.add(bean, desc.getBeanId(bean));
}
return result;
}
/**
* Use natural keys to hit the bean cache and return resulting hits.
*/
@@ -0,0 +1,42 @@
package io.ebeaninternal.server.deploy;
import io.ebeaninternal.api.SpiCacheControl;
import io.ebeaninternal.api.SpiCacheRegion;
/**
* Default implementation of SpiCacheControl.
*/
class DCacheControl implements SpiCacheControl {
private final SpiCacheRegion region;
private final boolean bean;
private final boolean naturalKey;
private final boolean query;
DCacheControl(SpiCacheRegion region, boolean bean, boolean naturalKey, boolean query) {
this.region = region;
this.bean = bean;
this.naturalKey = naturalKey;
this.query = query;
}
@Override
public boolean isCaching() {
return (bean || query) && region.isEnabled();
}
@Override
public boolean isBeanCaching() {
return bean && region.isEnabled();
}
@Override
public boolean isNaturalKeyCaching() {
return naturalKey && region.isEnabled();
}
@Override
public boolean isQueryCaching() {
return query && region.isEnabled();
}
}
@@ -0,0 +1,31 @@
package io.ebeaninternal.server.deploy;
import io.ebeaninternal.api.SpiCacheControl;
/**
* Cache control used when no caching is on the bean type.
*/
class DCacheControlNone implements SpiCacheControl {
static final SpiCacheControl INSTANCE = new DCacheControlNone();
@Override
public boolean isCaching() {
return false;
}
@Override
public boolean isBeanCaching() {
return false;
}
@Override
public boolean isNaturalKeyCaching() {
return false;
}
@Override
public boolean isQueryCaching() {
return false;
}
}
@@ -0,0 +1,29 @@
package io.ebeaninternal.server.deploy;
import io.ebeaninternal.api.SpiCacheRegion;
public class DCacheRegion implements SpiCacheRegion {
private boolean enabled = true;
private final String name;
public DCacheRegion(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
@@ -0,0 +1,23 @@
package io.ebeaninternal.server.deploy;
import io.ebeaninternal.api.SpiCacheRegion;
class DCacheRegionNone implements SpiCacheRegion {
static SpiCacheRegion INSTANCE = new DCacheRegionNone();
@Override
public String getName() {
return "<none>";
}
@Override
public boolean isEnabled() {
return false;
}
@Override
public void setEnabled(boolean enabled) {
throw new IllegalStateException("Not expected");
}
}
@@ -473,8 +473,8 @@ public class DeployBeanDescriptor<T> {
/**
* Set that this type invalidates query caches.
*/
public void setInvalidateQueryCache() {
this.cacheOptions = CacheOptions.INVALIDATE_QUERY_CACHE;
public void setInvalidateQueryCache(String region) {
this.cacheOptions = CacheOptions.invalidateQueryCache(region);
}
/**
@@ -202,13 +202,12 @@ public class AnnotationClass extends AnnotationParser {
} else {
InvalidateQueryCache invalidateQueryCache = AnnotationUtil.findAnnotationRecursive(cls, InvalidateQueryCache.class);
if (invalidateQueryCache != null) {
descriptor.setInvalidateQueryCache();
descriptor.setInvalidateQueryCache(invalidateQueryCache.region());
}
}
}
Set<NamedQuery> namedQueries = AnnotationUtil.findAnnotationsRecursive(cls, NamedQuery.class);
for (NamedQuery namedQuery : namedQueries) {
for (NamedQuery namedQuery : AnnotationUtil.findAnnotationsRecursive(cls, NamedQuery.class)) {
descriptor.addNamedQuery(namedQuery.name(), namedQuery.query());
}
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.Model;
import io.ebean.annotation.DbArray;
import io.ebean.annotation.DbHstore;
import io.ebean.annotation.DbJson;
@@ -64,17 +65,18 @@ public class DeployCreateProperties {
* </p>
*/
private boolean ignoreFieldByName(String fieldName) {
if (fieldName.startsWith("_ebean_") || fieldName.equals("_$targetDatabase")) {
if (fieldName.startsWith("_ebean_")) {
// ignore Ebean internal fields
return true;
}
if (fieldName.startsWith("ajc$instance$")) {
// ignore AspectJ internal fields
return true;
}
// ignore AspectJ internal fields
return fieldName.startsWith("ajc$instance$");
}
// we are interested in this field
return false;
private boolean ignoreField(Field field) {
return Modifier.isStatic(field.getModifiers())
|| Modifier.isTransient(field.getModifiers())
|| ignoreFieldByName(field.getName());
}
/**
@@ -83,6 +85,10 @@ public class DeployCreateProperties {
*/
private void createProperties(DeployBeanDescriptor<?> desc, Class<?> beanType, int level) {
if (beanType.equals(Model.class)) {
// ignore all fields on model (_$dbName)
return;
}
boolean scalaObject = desc.isScalaObject();
try {
@@ -92,16 +98,7 @@ public class DeployCreateProperties {
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (Modifier.isStatic(field.getModifiers())) {
// not interested in static fields
logger.trace("Skipping static field {} in {}", field.getName(), beanType.getName());
} else if (Modifier.isTransient(field.getModifiers())) {
// not interested in transient fields
logger.trace("Skipping transient field {} in {}", field.getName(), beanType.getName());
} else if (!ignoreFieldByName(field.getName())) {
if (!ignoreField(field)) {
String fieldName = getFieldName(field, beanType);
String initFieldName = initCap(fieldName);
@@ -36,6 +36,7 @@ import io.ebeaninternal.api.SpiJunction;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import java.io.IOException;
import java.sql.Connection;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
@@ -358,6 +359,16 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return query.apply(fetchPath);
}
@Override
public Query<T> usingTransaction(Transaction transaction) {
return query.usingTransaction(transaction);
}
@Override
public Query<T> usingConnection(Connection connection) {
return query.usingConnection(connection);
}
@Override
public int delete() {
return query.delete();
@@ -9,20 +9,38 @@ import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.id.IdBinder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* In a collection of Id values.
*/
public class IdInExpression extends NonPrepareExpression {
private final Collection<?> idCollection;
private final List<Object> idCollection;
private boolean multiValueIdSupported;
public IdInExpression(Collection<?> idCollection) {
this.idCollection = idCollection;
this.idCollection = new ArrayList<>(idCollection);
}
/**
* Return the ids this expression is looking to fetch.
*/
public Collection<Object> idValues() {
return idCollection;
}
/**
* Remove Ids that where obtained from l2 cache. Don't fetch these from DB.
*/
public int removeIds(Set<Object> hitIds) {
idCollection.removeAll(hitIds);
return idCollection.size();
}
@Override
@@ -63,23 +63,25 @@ class InExpression extends AbstractExpression {
return vals;
}
private List<Object> initBindValues() {
if (bindValues == null) {
bindValues = values();
}
return bindValues;
}
@Override
public boolean naturalKey(NaturalKeyQueryData<?> data) {
// can't use naturalKey cache for NOT IN or when "empty"
if (not || empty) {
return false;
}
List<Object> copy = data.matchIn(propName, bindValues);
if (copy == null) {
return false;
}
bindValues = copy;
return true;
return data.matchIn(propName, initBindValues());
}
@Override
public void prepareExpression(BeanQueryRequest<?> request) {
bindValues = values();
initBindValues();
if (bindValues.size() > 0) {
multiValueSupported = request.isMultiValueSupported((bindValues.get(0)).getClass());
}
@@ -146,9 +148,7 @@ class InExpression extends AbstractExpression {
if (prop != null) {
request.append(prop.getAssocIdInExpr(propName));
String inClause = prop.getAssocIdInValueExpr(not, bindValues.size());
request.append(inClause);
request.append(prop.getAssocIdInValueExpr(not, bindValues.size()));
} else {
request.append(propName);
request.appendInExpression(not, bindValues);
@@ -54,7 +54,6 @@ class InPairsExpression extends AbstractExpression {
@Override
public void prepareExpression(BeanQueryRequest<?> request) {
// at this stage translating pairs into varchar via DB concat
multiValueSupported = request.isMultiValueSupported(String.class);
}
@@ -34,6 +34,7 @@ import io.ebeaninternal.api.SpiJunction;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import java.io.IOException;
import java.sql.Connection;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.List;
@@ -330,6 +331,16 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
throw new IllegalStateException("filterMany not allowed on Junction expression list");
}
@Override
public Query<T> usingTransaction(Transaction transaction) {
return exprList.usingTransaction(transaction);
}
@Override
public Query<T> usingConnection(Connection connection) {
return exprList.usingConnection(connection);
}
@Override
public int delete() {
return exprList.delete();
@@ -0,0 +1,90 @@
package io.ebeaninternal.server.profile;
import io.ebean.meta.MetricType;
import io.ebean.meta.MetricVisitor;
import io.ebean.metric.CountMetric;
import io.ebean.metric.CountMetricStats;
import java.util.concurrent.atomic.LongAdder;
/**
* Used to collect counter metrics.
*/
class DCountMetric implements CountMetric {
private final MetricType metricType;
private final String name;
private final LongAdder count = new LongAdder();
DCountMetric(MetricType metricType, String name) {
this.metricType = metricType;
this.name = name;
}
/**
* Add a value. Usually the value is Time or Bytes etc.
*/
@Override
public void add(long value) {
count.add(value);
}
public void increment() {
count.increment();
}
@Override
public boolean isEmpty() {
return count.sum() == 0;
}
@Override
public void reset() {
count.reset();
}
@Override
public long get(boolean reset) {
return reset ? count.sumThenReset() : count.sum();
}
@Override
public void visit(MetricVisitor visitor) {
long val = visitor.isReset() ? count.sumThenReset() : count.sum();
if (val > 0) {
visitor.visitCount(new DCountMetricStats(metricType, name, val));
}
}
private static class DCountMetricStats implements CountMetricStats {
private final MetricType metricType;
private final String name;
private final long count;
private DCountMetricStats(MetricType metricType, String name, long count) {
this.metricType = metricType;
this.name = name;
this.count = count;
}
@Override
public MetricType getMetricType() {
return metricType;
}
@Override
public String getName() {
return name;
}
@Override
public long getCount() {
return count;
}
}
}
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.profile;
import io.ebean.ProfileLocation;
import io.ebean.meta.MetricType;
import io.ebean.metric.CountMetric;
import io.ebean.metric.MetricFactory;
import io.ebean.metric.QueryPlanMetric;
import io.ebean.metric.TimedMetric;
@@ -22,6 +23,11 @@ public class DMetricFactory implements MetricFactory {
return new DTimedMetric(metricType, name);
}
@Override
public CountMetric createCountMetric(MetricType metricType, String name) {
return new DCountMetric(metricType, name);
}
@Override
public QueryPlanMetric createQueryPlanMetric(MetricType metricType, Class<?> type, String label, ProfileLocation profileLocation, String sql) {
return new DQueryPlanMetric(new DQueryPlanMeta(type, label, profileLocation, sql), new DTimedMetric(metricType, label));
@@ -60,7 +60,7 @@ class CQueryBindCapture {
DQueryPlanOutput queryPlan = planLogger.logQueryPlan(request.getConnection(), cQueryPlan, last);
if (queryPlan != null) {
queryPlan.with(queryTimeMicros, captureCount, cQueryPlan.getPlanKey().toString());
queryPlan.with(queryTimeMicros, captureCount, cQueryPlan.getSqlHash());
request.process(queryPlan);
}
}
@@ -48,10 +48,8 @@ class CQueryPlanRawSql extends CQueryPlan {
// an associated bean is in the raw SQL but is mapped columnIgnore
for (int i = 0; i < indexPositions.length; i++) {
if (indexPositions[i] == 0) {
if (i < indexPositions.length) {
// expect discriminator column to immediately proceed id column
indexPositions[i] = indexPositions[i + 1] - 1;
}
// expect discriminator column to immediately proceed id column
indexPositions[i] = indexPositions[i + 1] - 1;
}
}
@@ -238,7 +238,7 @@ public final class CQueryPlanStats {
}
@Override
public String getQueryPlanHash() {
public String getSqlHash() {
return queryPlan.getSqlHash();
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.query;
import io.ebean.ProfileLocation;
import io.ebean.meta.MetaQueryPlan;
/**
@@ -9,29 +10,28 @@ class DQueryPlanOutput implements MetaQueryPlan {
private final Class<?> beanType;
private final String label;
private final ProfileLocation profileLocation;
private final String sql;
private final String bind;
private final String plan;
private String planHash;
private String sqlHash;
private long queryTimeMicros;
private long captureCount;
DQueryPlanOutput(Class<?> beanType, String label, String sql, String bind, String plan) {
DQueryPlanOutput(Class<?> beanType, String label, String sql, String bind, String plan, ProfileLocation profileLocation) {
this.beanType = beanType;
this.label = label;
this.sql = sql;
this.bind = bind;
this.plan = plan;
this.profileLocation = profileLocation;
}
@Override
public String getQueryPlanHash() {
return planHash;
public String getSqlHash() {
return sqlHash;
}
/**
@@ -50,6 +50,11 @@ class DQueryPlanOutput implements MetaQueryPlan {
return label;
}
@Override
public ProfileLocation getProfileLocation() {
return profileLocation;
}
/**
* Return the sql of query.
*/
@@ -84,8 +89,7 @@ class DQueryPlanOutput implements MetaQueryPlan {
}
/**
* Return the total count of times bind capture has occurred. We don't want this to be
* massive as that implies a high overhead.
* Return the total count of times bind capture has occurred.
*/
@Override
public long getCaptureCount() {
@@ -94,15 +98,15 @@ class DQueryPlanOutput implements MetaQueryPlan {
@Override
public String toString() {
return " BeanType:" + ((beanType == null) ? "" : beanType.getSimpleName()) + " planHash:" + planHash + " label:" + label + " queryTimeMicros:" + queryTimeMicros + " captureCount:" + captureCount + "\n SQL:" + sql + "\nBIND:" + bind + "\nPLAN:" + plan;
return " BeanType:" + ((beanType == null) ? "" : beanType.getSimpleName()) + " planHash:" + sqlHash + " label:" + label + " queryTimeMicros:" + queryTimeMicros + " captureCount:" + captureCount + "\n SQL:" + sql + "\nBIND:" + bind + "\nPLAN:" + plan;
}
/**
* Additionally set the query execution time and the number of bind captures.
*/
void with(long queryTimeMicros, long captureCount, String planHash) {
void with(long queryTimeMicros, long captureCount, String sqlHash) {
this.queryTimeMicros = queryTimeMicros;
this.captureCount = captureCount;
this.planHash = planHash;
this.sqlHash = sqlHash;
}
}
@@ -26,7 +26,7 @@ public abstract class QueryPlanLogger {
}
protected DQueryPlanOutput createPlan(CQueryPlan plan, String bind, String planString) {
return new DQueryPlanOutput(plan.getBeanType(), plan.getName(), plan.getSql(), bind, planString);
return new DQueryPlanOutput(plan.getBeanType(), plan.getName(), plan.getSql(), bind, planString, plan.getProfileLocation());
}
DQueryPlanOutput readQueryPlanBasic(CQueryPlan plan, BindCapture bind, ResultSet rset) throws SQLException {
@@ -33,6 +33,7 @@ import io.ebean.event.readaudit.ReadEvent;
import io.ebean.plugin.BeanType;
import io.ebeaninternal.api.BindParams;
import io.ebeaninternal.api.CQueryPlanKey;
import io.ebeaninternal.api.CacheIdLookup;
import io.ebeaninternal.api.HashQuery;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
@@ -43,6 +44,7 @@ import io.ebeaninternal.api.SpiExpressionValidation;
import io.ebeaninternal.api.SpiNamedParam;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.SpiQuerySecondary;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.server.autotune.ProfilingListener;
import io.ebeaninternal.server.core.SpiOrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
@@ -50,15 +52,17 @@ import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.expression.DefaultExpressionList;
import io.ebeaninternal.server.expression.IdInExpression;
import io.ebeaninternal.server.expression.SimpleExpression;
import io.ebeaninternal.server.query.CancelableQuery;
import io.ebeaninternal.server.query.NativeSqlQueryPlanKey;
import io.ebeaninternal.server.rawsql.SpiRawSql;
import io.ebeaninternal.server.transaction.ExternalJdbcTransaction;
import javax.persistence.PersistenceException;
import java.sql.Connection;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -89,6 +93,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
private final ExpressionFactory expressionFactory;
private SpiTransaction transaction;
/**
* For lazy loading of ManyToMany we need to add a join to the intersection table. This is that
* join to the intersection table.
@@ -733,6 +739,21 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return !manualId && !distinct && !singleAttribute;
}
@Override
public CacheIdLookup<T> cacheIdLookup() {
if (whereExpressions == null) {
return null;
}
List<SpiExpression> underlyingList = whereExpressions.getUnderlyingList();
if (underlyingList.size() == 1) {
SpiExpression singleExpression = underlyingList.get(0);
if (singleExpression instanceof IdInExpression) {
return new CacheIdLookup<>((IdInExpression)singleExpression);
}
}
return null;
}
@Override
public NaturalKeyQueryData<T> naturalKey() {
@@ -795,6 +816,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
@Override
public DefaultOrmQuery<T> copy(SpiEbeanServer server) {
DefaultOrmQuery<T> copy = new DefaultOrmQuery<>(beanDescriptor, server, expressionFactory);
copy.transaction = transaction;
copy.m2mIncludeJoin = m2mIncludeJoin;
copy.profilingListener = profilingListener;
copy.profileLocation = profileLocation;
@@ -1414,9 +1436,21 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return this;
}
@Override
public Query<T> usingTransaction(Transaction transaction) {
this.transaction = (SpiTransaction)transaction;
return this;
}
@Override
public Query<T> usingConnection(Connection connection) {
this.transaction = new ExternalJdbcTransaction(connection);
return this;
}
@Override
public int delete() {
return server.delete(this, null);
return server.delete(this, transaction);
}
@Override
@@ -1426,7 +1460,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
@Override
public int update() {
return server.update(this, null);
return server.update(this, transaction);
}
@Override
@@ -1439,12 +1473,12 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
// a copy of this query is made in the server
// as the query needs to modified (so we modify
// the copy rather than this query instance)
return server.findIds(this, null);
return server.findIds(this, transaction);
}
@Override
public boolean exists() {
return server.exists(this, null);
return server.exists(this, transaction);
}
@Override
@@ -1452,28 +1486,28 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
// a copy of this query is made in the server
// as the query needs to modified (so we modify
// the copy rather than this query instance)
return server.findCount(this, null);
return server.findCount(this, transaction);
}
@Override
public void findEachWhile(Predicate<T> consumer) {
server.findEachWhile(this, consumer, null);
server.findEachWhile(this, consumer, transaction);
}
@Override
public void findEach(Consumer<T> consumer) {
server.findEach(this, consumer, null);
server.findEach(this, consumer, transaction);
}
@Override
public QueryIterator<T> findIterate() {
return server.findIterate(this, null);
return server.findIterate(this, transaction);
}
@Override
public List<Version<T>> findVersions() {
this.temporalMode = TemporalMode.VERSIONS;
return server.findVersions(this, null);
return server.findVersions(this, transaction);
}
@Override
@@ -1484,28 +1518,28 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
this.temporalMode = TemporalMode.VERSIONS;
this.versionsStart = start;
this.versionsEnd = end;
return server.findVersions(this, null);
return server.findVersions(this, transaction);
}
@Override
public List<T> findList() {
return server.findList(this, null);
return server.findList(this, transaction);
}
@Override
public Set<T> findSet() {
return server.findSet(this, null);
return server.findSet(this, transaction);
}
@Override
public <K> Map<K, T> findMap() {
return server.findMap(this, null);
return server.findMap(this, transaction);
}
@Override
@SuppressWarnings("unchecked")
public <A> List<A> findSingleAttributeList() {
return (List<A>) server.findSingleAttributeList(this, null);
return (List<A>) server.findSingleAttributeList(this, transaction);
}
@Override
@@ -1516,32 +1550,32 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
@Override
public T findOne() {
return server.findOne(this, null);
return server.findOne(this, transaction);
}
@Override
public Optional<T> findOneOrEmpty() {
return server.findOneOrEmpty(this, null);
return server.findOneOrEmpty(this, transaction);
}
@Override
public FutureIds<T> findFutureIds() {
return server.findFutureIds(this, null);
return server.findFutureIds(this, transaction);
}
@Override
public FutureList<T> findFutureList() {
return server.findFutureList(this, null);
return server.findFutureList(this, transaction);
}
@Override
public FutureRowCount<T> findFutureCount() {
return server.findFutureCount(this, null);
return server.findFutureCount(this, transaction);
}
@Override
public PagedList<T> findPagedList() {
return server.findPagedList(this, null);
return server.findPagedList(this, transaction);
}
/**
@@ -1848,8 +1882,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
@Override
public void checkNamedParameters() {
if (namedParams != null) {
Collection<ONamedParam> values = namedParams.values();
for (ONamedParam value : values) {
for (ONamedParam value : namedParams.values()) {
value.checkValueSet();
}
}
@@ -79,27 +79,24 @@ public class DefaultTypeFactory {
/**
* Create the default ScalarType for java.util.Date.
*/
public ScalarType<java.util.Date> createUtilDate(JsonConfig.DateTime mode) {
public ScalarType<java.util.Date> createUtilDate(JsonConfig.DateTime mode, JsonConfig.Date jsonDate) {
// by default map anonymous java.util.Date to java.sql.Timestamp.
// String mapType =
// properties.getProperty("type.mapping.java.util.Date","timestamp");
int utilDateType = getTemporalMapType("timestamp");
return createUtilDate(mode, utilDateType);
return createUtilDate(mode, jsonDate, utilDateType);
}
/**
* Create a ScalarType for java.util.Date explicitly specifying the type to
* map to.
*/
public ScalarType<java.util.Date> createUtilDate(JsonConfig.DateTime mode, int utilDateType) {
public ScalarType<java.util.Date> createUtilDate(JsonConfig.DateTime jsonDateTime, JsonConfig.Date jsonDate, int utilDateType) {
switch (utilDateType) {
case Types.DATE:
return new ScalarTypeUtilDate.DateType();
return new ScalarTypeUtilDate.DateType(jsonDate);
case Types.TIMESTAMP:
return new ScalarTypeUtilDate.TimestampType(mode);
return new ScalarTypeUtilDate.TimestampType(jsonDateTime);
default:
throw new RuntimeException("Invalid type " + utilDateType);
@@ -130,8 +130,6 @@ public final class DefaultTypeManager implements TypeManager {
private final ScalarType<?> timeType = new ScalarTypeTime();
private final ScalarType<?> dateType = new ScalarTypeDate();
private final ScalarType<?> urlType = new ScalarTypeURL();
private final ScalarType<?> uriType = new ScalarTypeURI();
private final ScalarType<?> localeType = new ScalarTypeLocale();
@@ -143,6 +141,7 @@ public final class DefaultTypeManager implements TypeManager {
private final ScalarType<?> classType = new ScalarTypeClass();
private final JsonConfig.DateTime jsonDateTime;
private final JsonConfig.Date jsonDate;
private final Object objectMapper;
@@ -189,6 +188,7 @@ public final class DefaultTypeManager implements TypeManager {
this.java7Present = config.getClassLoadConfig().isJava7Present();
this.jsonDateTime = config.getJsonDateTime();
this.jsonDate = config.getJsonDate();
this.typeMap = new ConcurrentHashMap<>();
this.nativeMap = new ConcurrentHashMap<>();
this.logicalMap = new ConcurrentHashMap<>();
@@ -205,9 +205,9 @@ public final class DefaultTypeManager implements TypeManager {
this.defaultEnumType = config.getDefaultEnumType();
initialiseStandard(jsonDateTime, config);
initialiseJavaTimeTypes(jsonDateTime, config);
initialiseJodaTypes(jsonDateTime, config);
initialiseStandard(config);
initialiseJavaTimeTypes(config);
initialiseJodaTypes(config);
initialiseJacksonTypes(config);
loadTypesFromProviders(config, objectMapper);
@@ -522,7 +522,7 @@ public final class DefaultTypeManager implements TypeManager {
}
// a util Date with jdbcType not matching server wide settings
if (type.equals(java.util.Date.class)) {
return extraTypeFactory.createUtilDate(jsonDateTime, jdbcType);
return extraTypeFactory.createUtilDate(jsonDateTime, jsonDate, jdbcType);
}
// a Calendar with jdbcType not matching server wide settings
if (type.equals(java.util.Calendar.class)) {
@@ -874,7 +874,7 @@ public final class DefaultTypeManager implements TypeManager {
}
}
private void initialiseJavaTimeTypes(JsonConfig.DateTime mode, ServerConfig config) {
private void initialiseJavaTimeTypes(ServerConfig config) {
if (java7Present) {
typeMap.put(java.nio.file.Path.class, new ScalarTypePath());
@@ -883,16 +883,16 @@ public final class DefaultTypeManager implements TypeManager {
if (config.getClassLoadConfig().isJavaTimePresent()) {
logger.debug("Registering java.time data types");
addType(java.time.Period.class, new ScalarTypePeriod());
addType(java.time.LocalDate.class, new ScalarTypeLocalDate());
addType(java.time.LocalDateTime.class, new ScalarTypeLocalDateTime(mode));
addType(OffsetDateTime.class, new ScalarTypeOffsetDateTime(mode));
addType(ZonedDateTime.class, new ScalarTypeZonedDateTime(mode));
addType(Instant.class, new ScalarTypeInstant(mode));
addType(java.time.LocalDate.class, new ScalarTypeLocalDate(jsonDate));
addType(java.time.LocalDateTime.class, new ScalarTypeLocalDateTime(jsonDateTime));
addType(OffsetDateTime.class, new ScalarTypeOffsetDateTime(jsonDateTime));
addType(ZonedDateTime.class, new ScalarTypeZonedDateTime(jsonDateTime));
addType(Instant.class, new ScalarTypeInstant(jsonDateTime));
addType(DayOfWeek.class, new ScalarTypeDayOfWeek());
addType(Month.class, new ScalarTypeMonth());
addType(Year.class, new ScalarTypeYear());
addType(YearMonth.class, new ScalarTypeYearMonthDate());
addType(YearMonth.class, new ScalarTypeYearMonthDate(jsonDate));
addType(MonthDay.class, new ScalarTypeMonthDay());
addType(OffsetTime.class, new ScalarTypeOffsetTime());
addType(ZoneId.class, new ScalarTypeZoneId());
@@ -915,16 +915,16 @@ public final class DefaultTypeManager implements TypeManager {
* Detect if Joda classes are in the classpath and if so register the Joda data types.
*/
@SuppressWarnings("deprecation")
private void initialiseJodaTypes(JsonConfig.DateTime mode, ServerConfig config) {
private void initialiseJodaTypes(ServerConfig config) {
// detect if Joda classes are in the classpath
if (config.getClassLoadConfig().isJodaTimePresent()) {
// Joda classes are in the classpath so register the types
logger.debug("Registering Joda data types");
addType(LocalDateTime.class, new ScalarTypeJodaLocalDateTime(mode));
addType(DateTime.class, new ScalarTypeJodaDateTime(mode));
addType(LocalDate.class, new ScalarTypeJodaLocalDate());
addType(org.joda.time.DateMidnight.class, new ScalarTypeJodaDateMidnight());
addType(LocalDateTime.class, new ScalarTypeJodaLocalDateTime(jsonDateTime));
addType(DateTime.class, new ScalarTypeJodaDateTime(jsonDateTime));
addType(LocalDate.class, new ScalarTypeJodaLocalDate(jsonDate));
addType(org.joda.time.DateMidnight.class, new ScalarTypeJodaDateMidnight(jsonDate));
addType(org.joda.time.Period.class, new ScalarTypeJodaPeriod());
String jodaLocalTimeMode = config.getJodaLocalTimeMode();
@@ -944,7 +944,7 @@ public final class DefaultTypeManager implements TypeManager {
* Register all the standard types supported. This is the standard JDBC types
* plus some other common types such as java.util.Date and java.util.Calendar.
*/
private void initialiseStandard(JsonConfig.DateTime mode, ServerConfig config) {
private void initialiseStandard(ServerConfig config) {
DatabasePlatform databasePlatform = config.getDatabasePlatform();
int platformClobType = databasePlatform.getClobDbType();
@@ -952,10 +952,10 @@ public final class DefaultTypeManager implements TypeManager {
nativeMap.put(DbPlatformType.HSTORE, hstoreType);
ScalarType<?> utilDateType = extraTypeFactory.createUtilDate(mode);
ScalarType<?> utilDateType = extraTypeFactory.createUtilDate(jsonDateTime, jsonDate);
addType(java.util.Date.class, utilDateType);
ScalarType<?> calType = extraTypeFactory.createCalendar(mode);
ScalarType<?> calType = extraTypeFactory.createCalendar(jsonDateTime);
addType(Calendar.class, calType);
ScalarType<?> mathBigIntType = extraTypeFactory.createMathBigInteger();
@@ -1079,10 +1079,12 @@ public final class DefaultTypeManager implements TypeManager {
// Temporal types
addType(Time.class, timeType);
nativeMap.put(Types.TIME, timeType);
ScalarTypeDate dateType = new ScalarTypeDate(jsonDate);
addType(Date.class, dateType);
nativeMap.put(Types.DATE, dateType);
ScalarType<?> timestampType = new ScalarTypeTimestamp(mode);
ScalarType<?> timestampType = new ScalarTypeTimestamp(jsonDateTime);
addType(Timestamp.class, timestampType);
nativeMap.put(Types.TIMESTAMP, timestampType);
}
@@ -1,9 +1,10 @@
package io.ebeaninternal.server.type;
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import io.ebean.config.JsonConfig;
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
import java.io.DataInput;
import java.io.DataOutput;
@@ -17,8 +18,11 @@ import java.sql.Types;
*/
public abstract class ScalarTypeBaseDate<T> extends ScalarTypeBase<T> {
public ScalarTypeBaseDate(Class<T> type, boolean jdbcNative, int jdbcType) {
protected final JsonConfig.Date mode;
public ScalarTypeBaseDate(JsonConfig.Date mode, Class<T> type, boolean jdbcNative, int jdbcType) {
super(type, jdbcNative, jdbcType);
this.mode = mode;
}
/**
@@ -93,9 +97,18 @@ public abstract class ScalarTypeBaseDate<T> extends ScalarTypeBase<T> {
@Override
public void jsonWrite(JsonGenerator writer, T value) throws IOException {
writer.writeNumber(convertToMillis(value));
if (mode == JsonConfig.Date.ISO8601) {
writer.writeString(toIsoFormat(value));
} else {
writer.writeNumber(convertToMillis(value));
}
}
/**
* Convert the value to ISO8601 format.
*/
protected abstract String toIsoFormat(T value);
@Override
public DocPropertyType getDocType() {
return DocPropertyType.DATE;
@@ -19,7 +19,7 @@ import java.sql.Types;
public abstract class ScalarTypeBaseDateTime<T> extends ScalarTypeBase<T> {
protected final DateTimeJsonParser dateTimeParser = new DateTimeJsonParser();
protected final UtilDateTimeParser dateTimeParser = new UtilDateTimeParser();
protected final JsonConfig.DateTime mode;
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.type;
import io.ebean.config.JsonConfig;
import io.ebeaninternal.server.core.BasicTypeConverter;
import java.sql.Date;
@@ -11,8 +12,13 @@ import java.sql.Types;
*/
public class ScalarTypeDate extends ScalarTypeBaseDate<java.sql.Date> {
public ScalarTypeDate() {
super(Date.class, true, Types.DATE);
public ScalarTypeDate(JsonConfig.Date mode) {
super(mode, Date.class, true, Types.DATE);
}
@Override
protected String toIsoFormat(Date value) {
return value.toLocalDate().toString();
}
@Override
@@ -1,6 +1,8 @@
package io.ebeaninternal.server.type;
import io.ebean.config.JsonConfig;
import io.ebeaninternal.server.core.BasicTypeConverter;
import org.joda.time.DateMidnight;
import java.sql.Date;
import java.sql.Types;
@@ -14,8 +16,13 @@ public class ScalarTypeJodaDateMidnight extends ScalarTypeBaseDate<org.joda.time
/**
* Instantiates a new scalar type joda date midnight.
*/
public ScalarTypeJodaDateMidnight() {
super(org.joda.time.DateMidnight.class, false, Types.DATE);
public ScalarTypeJodaDateMidnight(JsonConfig.Date mode) {
super(mode, org.joda.time.DateMidnight.class, false, Types.DATE);
}
@Override
protected String toIsoFormat(DateMidnight value) {
return value.toString();
}
@Override
@@ -1,6 +1,8 @@
package io.ebeaninternal.server.type;
import io.ebean.config.JsonConfig;
import io.ebeaninternal.server.core.BasicTypeConverter;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import java.sql.Date;
@@ -11,8 +13,13 @@ import java.sql.Types;
*/
public class ScalarTypeJodaLocalDate extends ScalarTypeBaseDate<LocalDate> {
public ScalarTypeJodaLocalDate() {
super(LocalDate.class, false, Types.DATE);
public ScalarTypeJodaLocalDate(JsonConfig.Date mode) {
super(mode, LocalDate.class, false, Types.DATE);
}
@Override
protected String toIsoFormat(LocalDate value) {
return value.toString();
}
@Override
@@ -22,7 +29,7 @@ public class ScalarTypeJodaLocalDate extends ScalarTypeBaseDate<LocalDate> {
@Override
public long convertToMillis(LocalDate value) {
return value.toDateTimeAtStartOfDay().getMillis();
return value.toDateTimeAtStartOfDay(DateTimeZone.UTC).getMillis();
}
@Override
@@ -1,12 +1,13 @@
package io.ebeaninternal.server.type;
import io.ebean.config.JsonConfig;
import io.ebeaninternal.server.core.BasicTypeConverter;
import java.sql.Date;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
/**
@@ -14,13 +15,22 @@ import java.time.ZonedDateTime;
*/
public class ScalarTypeLocalDate extends ScalarTypeBaseDate<LocalDate> {
public ScalarTypeLocalDate() {
super(LocalDate.class, false, Types.DATE);
public ScalarTypeLocalDate(JsonConfig.Date mode) {
super(mode, LocalDate.class, false, Types.DATE);
}
protected String toIsoFormat(LocalDate value) {
return value.toString();
}
@Override
public LocalDate convertFromMillis(long systemTimeMillis) {
return new Timestamp(systemTimeMillis).toLocalDateTime().toLocalDate();
}
@Override
public long convertToMillis(LocalDate value) {
ZonedDateTime zonedDateTime = value.atStartOfDay(ZoneId.systemDefault());
ZonedDateTime zonedDateTime = value.atStartOfDay(ZoneOffset.UTC);
return zonedDateTime.toInstant().toEpochMilli();
}
@@ -50,10 +60,4 @@ public class ScalarTypeLocalDate extends ScalarTypeBaseDate<LocalDate> {
return (LocalDate) value;
}
@Override
public LocalDate convertFromMillis(long systemTimeMillis) {
return new Timestamp(systemTimeMillis).toLocalDateTime().toLocalDate();
}
}
@@ -82,10 +82,13 @@ public class ScalarTypeUtilDate {
public static class DateType extends ScalarTypeBaseDate<java.util.Date> {
public DateType() {
super(Date.class, false, Types.DATE);
public DateType(JsonConfig.Date mode) {
super(mode, Date.class, false, Types.DATE);
}
protected String toIsoFormat(java.util.Date value) {
return UtilDateParser.format(value);
}
@Override
public long convertToMillis(java.util.Date value) {
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.type;
import io.ebean.config.JsonConfig;
import io.ebeaninternal.server.core.BasicTypeConverter;
import java.sql.Date;
@@ -7,7 +8,7 @@ import java.sql.Timestamp;
import java.sql.Types;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
/**
@@ -15,12 +16,16 @@ import java.time.ZonedDateTime;
*/
public class ScalarTypeYearMonthDate extends ScalarTypeBaseDate<YearMonth> {
public ScalarTypeYearMonthDate() {
super(YearMonth.class, false, Types.DATE);
public ScalarTypeYearMonthDate(JsonConfig.Date mode) {
super(mode, YearMonth.class, false, Types.DATE);
}
protected String toIsoFormat(YearMonth value) {
return value.atDay(1).toString();
}
protected LocalDate toLocalDate(YearMonth yearMonth) {
return LocalDate.of(yearMonth.getYear(), yearMonth.getMonth(), 1);
return yearMonth.atDay(1);
}
protected YearMonth fromLocalDate(LocalDate localDate) {
@@ -34,8 +39,7 @@ public class ScalarTypeYearMonthDate extends ScalarTypeBaseDate<YearMonth> {
@Override
public long convertToMillis(YearMonth value) {
LocalDate localDate = toLocalDate(value);
ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());
ZonedDateTime zonedDateTime = value.atDay(1).atStartOfDay(ZoneOffset.UTC);
return zonedDateTime.toInstant().toEpochMilli();
}
@@ -0,0 +1,26 @@
package io.ebeaninternal.server.type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
class UtilDateParser {
private static final SimpleDateFormat dateTimeProto = new SimpleDateFormat("yyyy-MM-dd");
private static SimpleDateFormat formatter() {
return (SimpleDateFormat) dateTimeProto.clone();
}
static Date parse(String jsonDateTime) {
try {
return formatter().parse(jsonDateTime);
} catch (ParseException e) {
throw new RuntimeException("Error parsing Date[" + jsonDateTime + "]", e);
}
}
static String format(Date value) {
return formatter().format(value);
}
}
@@ -6,15 +6,14 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class DateTimeJsonParser {
class UtilDateTimeParser {
private final SimpleDateFormat dateTimeProto20;
private final SimpleDateFormat dateTimeProto22;
private final SimpleDateFormat dateTimeProto23;
private final SimpleDateFormat dateTimeProto24;
public DateTimeJsonParser() {
UtilDateTimeParser() {
dateTimeProto20 = init("yyyy-MM-dd'T'HH:mm:ss'Z'");
dateTimeProto22 = init("yyyy-MM-dd'T'HH:mm:ss.S'Z'");
dateTimeProto23 = init("yyyy-MM-dd'T'HH:mm:ss.SS'Z'");
@@ -95,7 +95,7 @@ class BindCaptureTypes {
@Override
public String toString() {
return String.valueOf(x);
return Arrays.toString(x);
}
}
+1 -1
View File
@@ -48,7 +48,7 @@ public abstract class BaseTestCase {
DB_CLOCK_DELTA = 100;
}
logger.debug("... preStart");
if (!AgentLoader.loadAgentFromClasspath("ebean-agent", "debug=1")) {
if (!AgentLoader.loadAgentByMainClass("io.ebean.enhance.Transformer", "debug=1")) {
logger.info("avaje-ebeanorm-agent not found in classpath - not dynamically loaded");
}
try {
+1 -1
View File
@@ -192,7 +192,7 @@ public class DtoQueryTest extends BaseTestCase {
}
// collect without reset
BasicMetricVisitor basic = new BasicMetricVisitor(false, true, true);
BasicMetricVisitor basic = new BasicMetricVisitor(false, true, true, true);
server().getMetaInfoManager().visitMetrics(basic);
List<MetaQueryMetric> stats = basic.getDtoQueryMetrics();
@@ -118,7 +118,7 @@ public class NamedDtoQueryTest extends BaseTestCase {
}
// collect without reset
BasicMetricVisitor basic = new BasicMetricVisitor(false, true, true);
BasicMetricVisitor basic = new BasicMetricVisitor(false, true, true, true);
server().getMetaInfoManager().visitMetrics(basic);
List<MetaQueryMetric> stats = basic.getDtoQueryMetrics();
@@ -0,0 +1,48 @@
package io.ebean.cache;
import io.ebean.DB;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class ServerCacheManagerTest {
@Test
public void test() {
ServerCacheManager cacheManager = DB.getDefault().getServerCacheManager();
for (ServerCacheRegion region : cacheManager.allRegions()) {
assertTrue(region.isEnabled());
}
cacheManager.setEnabledRegions("r0,doesNotExist");
assertThat(cacheManager.getRegion("email").isEnabled()).isFalse();
assertThat(cacheManager.getRegion("r0").isEnabled()).isTrue();
cacheManager.setEnabledRegions("r0");
assertThat(cacheManager.getRegion("email").isEnabled()).isFalse();
assertThat(cacheManager.getRegion("r0").isEnabled()).isTrue();
cacheManager.setEnabledRegions(null);
assertThat(cacheManager.getRegion("email").isEnabled()).isFalse();
assertThat(cacheManager.getRegion("r0").isEnabled()).isTrue();
cacheManager.setEnabledRegions("email");
assertThat(cacheManager.getRegion("email").isEnabled()).isTrue();
assertThat(cacheManager.getRegion("r0").isEnabled()).isFalse();
cacheManager.setAllRegionsEnabled(false);
for (ServerCacheRegion region : cacheManager.allRegions()) {
assertFalse(region.isEnabled());
}
cacheManager.setAllRegionsEnabled(true);
for (ServerCacheRegion region : cacheManager.allRegions()) {
assertTrue(region.isEnabled());
}
}
}
@@ -60,7 +60,8 @@ public class ServerConfigTest {
props.setProperty("backgroundExecutorShutdownSecs", "98");
props.setProperty("backgroundExecutorSchedulePoolSize", "4");
props.setProperty("dbOffline", "true");
props.setProperty("jsonDateTime", "ISO8601");
props.setProperty("jsonDateTime", "MILLIS");
props.setProperty("jsonDate", "MILLIS");
props.setProperty("autoReadOnlyDataSource", "true");
props.setProperty("disableL2Cache", "true");
props.setProperty("notifyL2CacheInForeground", "true");
@@ -68,6 +69,7 @@ public class ServerConfigTest {
props.setProperty("mappingLocations", "classpath:/foo;bar");
props.setProperty("namingConvention", "io.ebean.config.MatchingNamingConvention");
props.setProperty("idGeneratorAutomatic", "true");
props.setProperty("enabledL2Regions", "r0,users,orgs");
serverConfig.loadFromProperties(props);
@@ -84,7 +86,10 @@ public class ServerConfigTest {
assertEquals(PersistBatch.ALL, serverConfig.getPersistBatch());
assertEquals(PersistBatch.ALL, serverConfig.getPersistBatchOnCascade());
assertEquals(PlatformConfig.DbUuid.BINARY, serverConfig.getPlatformConfig().getDbUuid());
assertEquals(JsonConfig.DateTime.ISO8601, serverConfig.getJsonDateTime());
assertEquals(JsonConfig.DateTime.MILLIS, serverConfig.getJsonDateTime());
assertEquals(JsonConfig.Date.MILLIS, serverConfig.getJsonDate());
assertEquals("r0,users,orgs", serverConfig.getEnabledL2Regions());
assertEquals(42, serverConfig.getJdbcFetchSizeFindEach());
assertEquals(43, serverConfig.getJdbcFetchSizeFindList());
@@ -111,6 +116,9 @@ public class ServerConfigTest {
assertEquals(PersistBatch.ALL, serverConfig.getPersistBatch());
assertEquals(PersistBatch.ALL, serverConfig.getPersistBatchOnCascade());
serverConfig.setEnabledL2Regions("r0,orgs");
assertEquals("r0,orgs", serverConfig.getEnabledL2Regions());
}
@Test
@@ -121,6 +129,8 @@ public class ServerConfigTest {
serverConfig.setIdGeneratorAutomatic(false);
assertFalse(serverConfig.isIdGeneratorAutomatic());
assertEquals(JsonConfig.DateTime.ISO8601, serverConfig.getJsonDateTime());
assertEquals(JsonConfig.Date.ISO8601, serverConfig.getJsonDate());
}
@Test
@@ -34,19 +34,22 @@ public class DefaultCacheHolderTest {
DefaultCacheHolder holder = new DefaultCacheHolder(options());
DefaultServerCache cache = cache(holder, Customer.class, "customer");
assertThat(cache.getName()).isEqualTo("customer_B");
DefaultServerCache cache = cache(holder, Customer.class);
assertThat(cache.getName()).isEqualTo("org.tests.model.basic.Customer_B");
assertThat(cache.getShortName()).isEqualTo("Customer_B");
DefaultServerCache cache1 = cache(holder, Customer.class, "customer");
DefaultServerCache cache1 = cache(holder, Customer.class);
assertThat(cache1).isSameAs(cache);
DefaultServerCache cache2 = cache(holder, Contact.class, "contact");
DefaultServerCache cache2 = cache(holder, Contact.class);
assertThat(cache1).isNotSameAs(cache2);
assertThat(cache2.getName()).isEqualTo("contact_B");
assertThat(cache2.getName()).isEqualTo("org.tests.model.basic.Contact_B");
assertThat(cache2.getShortName()).isEqualTo("Contact_B");
}
private DefaultServerCache cache(DefaultCacheHolder holder, Class<?> type, String name) {
return (DefaultServerCache) holder.getCache(type, name, ServerCacheType.BEAN);
private DefaultServerCache cache(DefaultCacheHolder holder, Class<?> type) {
return (DefaultServerCache) holder.getCache(type, ServerCacheType.BEAN);
}
@Test
@@ -57,8 +60,9 @@ public class DefaultCacheHolderTest {
DefaultCacheHolder holder = new DefaultCacheHolder(builder);
tenantId.set("ten_1");
DefaultServerCache cache = cache(holder, Customer.class, "customer");
assertThat(cache.getName()).isEqualTo("customer_B");
DefaultServerCache cache = cache(holder, Customer.class);
assertThat(cache.getName()).isEqualTo("org.tests.model.basic.Customer_B");
assertThat(cache.getShortName()).isEqualTo("Customer_B");
cache.put("1", "value-for-tenant1");
cache.put("2", "an other value-for-tenant1");
@@ -114,7 +118,7 @@ public class DefaultCacheHolderTest {
public void clearAll() {
DefaultCacheHolder holder = new DefaultCacheHolder(options());
DefaultServerCache cache = cache(holder, Customer.class, "customer");
DefaultServerCache cache = cache(holder, Customer.class);
cache.put("foo", "foo");
assertThat(cache.size()).isEqualTo(1);
holder.clearAll();
@@ -128,7 +132,7 @@ public class DefaultCacheHolderTest {
CacheManagerOptions options = options().with(tenantId::get);
DefaultCacheHolder holder = new DefaultCacheHolder(options);
DefaultServerCache cache = cache(holder, Customer.class, "customer");
DefaultServerCache cache = cache(holder, Customer.class);
cache.put("foo", "foo");
assertThat(cache.size()).isEqualTo(1);
@@ -16,7 +16,7 @@ public class DefaultServerCacheConfigTest {
options.setMaxSecsToLive(maxSecsToLive);
options.setTrimFrequency(trimFreq);
return new DefaultServerCacheConfig(new ServerCacheConfig(null, null, options, null, null));
return new DefaultServerCacheConfig(new ServerCacheConfig(null, null, null, options, null, null));
}
@Test
@@ -43,6 +43,7 @@ public class DefaultServerCacheManagerTest {
DefaultServerCache cache = cache(manager, Customer.class);
assertThat(cache.getName()).isEqualTo("org.tests.model.basic.Customer_B");
assertThat(cache.getShortName()).isEqualTo("Customer_B");
DefaultServerCache cache1 = cache(manager, Customer.class);
assertThat(cache1).isSameAs(cache);
@@ -50,28 +51,32 @@ public class DefaultServerCacheManagerTest {
DefaultServerCache cache2 = cache(manager, Contact.class);
assertThat(cache1).isNotSameAs(cache2);
assertThat(cache2.getName()).isEqualTo("org.tests.model.basic.Contact_B");
assertThat(cache2.getShortName()).isEqualTo("Contact_B");
DefaultServerCache natKeyCache = (DefaultServerCache) manager.getNaturalKeyCache(Customer.class);
assertThat(natKeyCache.getName()).isEqualTo("org.tests.model.basic.Customer_N");
assertThat(natKeyCache.getShortName()).isEqualTo("Customer_N");
DefaultServerCache queryCache = (DefaultServerCache) manager.getQueryCache(Customer.class);
assertThat(queryCache.getName()).isEqualTo("org.tests.model.basic.Customer_Q");
assertThat(queryCache.getShortName()).isEqualTo("Customer_Q");
DefaultServerCache collCache = (DefaultServerCache) manager.getCollectionIdsCache(Customer.class, "contacts");
assertThat(collCache.getName()).isEqualTo("org.tests.model.basic.Customer.contacts_C");
assertThat(collCache.getShortName()).isEqualTo("Customer.contacts_C");
cache.clearCount.sumThenReset();
collCache.clearCount.sumThenReset();
queryCache.clearCount.sumThenReset();
natKeyCache.clearCount.sumThenReset();
cache.clearCount.reset();
collCache.clearCount.reset();
queryCache.clearCount.reset();
natKeyCache.clearCount.reset();
manager.clear(Customer.class);
assertThat(cache.clearCount.sumThenReset()).isEqualTo(1);
assertThat(natKeyCache.clearCount.sumThenReset()).isEqualTo(1);
assertThat(queryCache.clearCount.sumThenReset()).isEqualTo(1);
assertThat(collCache.clearCount.sumThenReset()).isEqualTo(1);
assertThat(cache.clearCount.get(true)).isEqualTo(1);
assertThat(natKeyCache.clearCount.get(true)).isEqualTo(1);
assertThat(queryCache.clearCount.get(true)).isEqualTo(1);
assertThat(collCache.clearCount.get(true)).isEqualTo(1);
}
private DefaultServerCache cache(DefaultServerCacheManager manager, Class<?> beanType) {
@@ -17,7 +17,7 @@ public class DefaultServerCacheTest {
cacheOptions.setMaxSecsToLive(600);
cacheOptions.setTrimFrequency(60);
ServerCacheConfig con = new ServerCacheConfig(ServerCacheType.BEAN, "foo", cacheOptions, null, null);
ServerCacheConfig con = new ServerCacheConfig(ServerCacheType.BEAN, "foo", null, cacheOptions, null, null);
DefaultServerCacheConfig config = new DefaultServerCacheConfig(con);
return new DefaultServerCache(config);
}
@@ -27,14 +27,21 @@ public class DefaultServerCacheTest {
DefaultServerCache cache = createCache();
assertEquals(0, cache.getHitRatio());
assertEquals(0, cache.getHitCount());
assertEquals(0, cache.getMissCount());
cache.put("A", "A");
cache.get("A");
assertEquals(100, cache.getHitRatio());
assertEquals(1, cache.getHitCount());
assertEquals(0, cache.getMissCount());
cache.get("B");
assertEquals(50, cache.getHitRatio());
assertEquals(1, cache.getMissCount());
cache.get("B");
cache.get("B");
assertEquals(25, cache.getHitRatio());
assertEquals(3, cache.getMissCount());
assertEquals(1, cache.getHitCount());
}
@Test
@@ -21,7 +21,7 @@ public class DefaultServerCache_RunEvictionTest {
cacheOptions.setMaxSecsToLive(2);
cacheOptions.setTrimFrequency(1);
ServerCacheConfig con = new ServerCacheConfig(ServerCacheType.BEAN, "foo", cacheOptions, null, null);
ServerCacheConfig con = new ServerCacheConfig(ServerCacheType.BEAN, "foo", "foo", cacheOptions, null, null);
return new DefaultServerCache(new DefaultServerCacheConfig(con));
}
@@ -61,7 +61,7 @@ public class TestDiffHelpInsertSimple extends BaseTestCase {
String asJson = buffer.toString();
assertThat(asJson).startsWith("{\"status\":\"NEW\",\"orderDate\":9466");
assertThat(asJson).startsWith("{\"status\":\"NEW\",\"orderDate\":\"2000-01-01\"");
assertThat(asJson).endsWith(",\"customer\":{\"id\":1234}}");
}
@@ -1,71 +0,0 @@
package io.ebeaninternal.server.type;
import org.junit.Test;
import java.sql.Timestamp;
import static org.assertj.core.api.StrictAssertions.assertThat;
public class DateTimeJsonParserTest {
@Test
public void parseFormat_when_hasMillis() throws Exception {
DateTimeJsonParser parser = new DateTimeJsonParser();
String input = "2016-02-28T20:39:00.123Z";
String formatted = parseAndFormat(parser, input);
assertThat(formatted).isEqualTo("2016-02-28T20:39:00.123Z");
}
@Test
public void parseFormat_when_noMillis() throws Exception {
DateTimeJsonParser parser = new DateTimeJsonParser();
String input = "2016-02-28T20:39:00Z";
String formatted = parseAndFormat(parser, input);
assertThat(formatted).isEqualTo("2016-02-28T20:39:00.000Z");
}
@Test
public void parseFormat_when_millis_1dp() throws Exception {
DateTimeJsonParser parser = new DateTimeJsonParser();
String input = "2016-02-28T20:39:00.0Z";
String formatted = parseAndFormat(parser, input);
assertThat(formatted).isEqualTo("2016-02-28T20:39:00.000Z");
}
@Test
public void parseFormat_when_millis_2dp() throws Exception {
DateTimeJsonParser parser = new DateTimeJsonParser();
String input = "2016-02-28T20:39:00.00Z";
String formatted = parseAndFormat(parser, input);
assertThat(formatted).isEqualTo("2016-02-28T20:39:00.000Z");
}
@Test
public void parseFormat_when_millis_3dp() throws Exception {
DateTimeJsonParser parser = new DateTimeJsonParser();
String input = "2016-02-28T20:39:00.000Z";
String formatted = parseAndFormat(parser, input);
assertThat(formatted).isEqualTo("2016-02-28T20:39:00.000Z");
}
private String parseAndFormat(DateTimeJsonParser parser, String input) {
Timestamp timestamp = parser.parse(input);
return parser.format(timestamp);
}
}
@@ -1,11 +1,11 @@
package io.ebeaninternal.server.type;
import io.ebean.config.JsonConfig;
import io.ebeaninternal.server.text.json.WriteJson;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import io.ebean.config.JsonConfig;
import io.ebeaninternal.server.text.json.WriteJson;
import java.io.IOException;
import java.io.StringWriter;
@@ -25,8 +25,7 @@ public class JsonTester<T> {
this.type = type;
}
public void test(T value) throws IOException {
public String test(T value) throws IOException {
StringWriter writer = new StringWriter();
JsonGenerator generator = factory.createGenerator(writer);
@@ -48,5 +47,6 @@ public class JsonTester<T> {
T val1 = type.jsonRead(parser);
assertEquals(value, val1);
return writer.toString();
}
}
@@ -1,14 +1,17 @@
package io.ebeaninternal.server.type;
import io.ebean.config.JsonConfig;
import org.junit.Test;
import java.io.IOException;
import java.sql.Date;
import static org.assertj.core.api.StrictAssertions.assertThat;
import static org.junit.Assert.assertEquals;
public class ScalarTypeDateTest {
ScalarTypeDate type = new ScalarTypeDate();
private ScalarTypeDate type = new ScalarTypeDate(JsonConfig.Date.MILLIS);
@Test
public void formatParse_PG_DATE_POSITIVE_INFINITY() {
@@ -19,4 +22,18 @@ public class ScalarTypeDateTest {
Date parsed = type.parse(format);
assertEquals(parsed, postgresInfinityDate);
}
@Test
public void json() throws IOException {
Date val = new Date(1557360000000L);
JsonTester<Date> jsonMillis = new JsonTester<>(type);
assertThat(jsonMillis.test(val)).isEqualTo("{\"key\":1557360000000}");
JsonTester<Date> jsonIso = new JsonTester<>(new ScalarTypeDate(JsonConfig.Date.ISO8601));
Date val1 = jsonIso.type.parse("2019-05-09");
assertThat(jsonIso.test(val1)).isEqualTo("{\"key\":\"2019-05-09\"}");
}
}
@@ -1,8 +1,10 @@
package io.ebeaninternal.server.type;
import io.ebean.config.JsonConfig;
import org.joda.time.LocalDate;
import org.junit.Test;
import java.io.IOException;
import java.sql.Date;
import static org.assertj.core.api.Assertions.assertThat;
@@ -10,7 +12,7 @@ import static org.assertj.core.api.Assertions.assertThat;
public class ScalarTypeJodaLocalDateTest {
private ScalarTypeJodaLocalDate type = new ScalarTypeJodaLocalDate();
private ScalarTypeJodaLocalDate type = new ScalarTypeJodaLocalDate(JsonConfig.Date.MILLIS);
@Test
public void convertToMillis_convertFromMillis() {
@@ -58,4 +60,15 @@ public class ScalarTypeJodaLocalDateTest {
assertThat(beanType).isEqualTo(localDate);
}
@Test
public void json() throws IOException {
LocalDate val = new LocalDate(2019, 5, 9);
JsonTester<LocalDate> jsonMillis = new JsonTester<>(type);
assertThat(jsonMillis.test(val)).isEqualTo("{\"key\":1557360000000}");
JsonTester<LocalDate> jsonIso = new JsonTester<>(new ScalarTypeJodaLocalDate(JsonConfig.Date.ISO8601) );
assertThat(jsonIso.test(val)).isEqualTo("{\"key\":\"2019-05-09\"}");
}
}
@@ -1,19 +1,22 @@
package io.ebeaninternal.server.type;
import io.ebean.config.JsonConfig;
import org.junit.Test;
import java.io.IOException;
import java.sql.Date;
import java.time.LocalDate;
import static org.assertj.core.api.StrictAssertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class ScalarTypeLocalDateTest {
ScalarTypeLocalDate type = new ScalarTypeLocalDate();
ScalarTypeLocalDate type = new ScalarTypeLocalDate(JsonConfig.Date.ISO8601);
@Test
public void testConvertToMillis() throws Exception {
public void testConvertToMillis() {
LocalDate date = LocalDate.of(2014, 5, 20);
long millis = type.convertToMillis(date);
@@ -23,7 +26,7 @@ public class ScalarTypeLocalDateTest {
}
@Test
public void testConvertFromDate() throws Exception {
public void testConvertFromDate() {
LocalDate localDate = LocalDate.now();
Date date = Date.valueOf(localDate);
@@ -37,7 +40,7 @@ public class ScalarTypeLocalDateTest {
@Test
public void testToJdbcType() throws Exception {
public void testToJdbcType() {
LocalDate localDate = LocalDate.now();
Object o = type.toJdbcType(localDate);
@@ -45,7 +48,7 @@ public class ScalarTypeLocalDateTest {
}
@Test
public void testToBeanType() throws Exception {
public void testToBeanType() {
LocalDate localDate = LocalDate.now();
Date date = Date.valueOf(localDate);
@@ -54,4 +57,15 @@ public class ScalarTypeLocalDateTest {
assertEquals(localDate, localDate1);
}
@Test
public void json() throws IOException {
LocalDate val = LocalDate.of(2019, 5, 9);
JsonTester<LocalDate> jsonMillis = new JsonTester<>(new ScalarTypeLocalDate(JsonConfig.Date.MILLIS));
assertThat(jsonMillis.test(val)).isEqualTo("{\"key\":1557360000000}");
JsonTester<LocalDate> jsonIso = new JsonTester<>(new ScalarTypeLocalDate(JsonConfig.Date.ISO8601) );
assertThat(jsonIso.test(val)).isEqualTo("{\"key\":\"2019-05-09\"}");
}
}
@@ -0,0 +1,27 @@
package io.ebeaninternal.server.type;
import io.ebean.config.JsonConfig;
import org.junit.Test;
import java.io.IOException;
import java.util.Date;
import static org.assertj.core.api.StrictAssertions.assertThat;
public class ScalarTypeUtilDateTest {
private ScalarTypeUtilDate.DateType dateType = new ScalarTypeUtilDate.DateType(JsonConfig.Date.MILLIS);
@Test
public void json() throws IOException {
Date val = new Date(1557316800000L);
JsonTester<Date> jsonMillis = new JsonTester<>(dateType);
assertThat(jsonMillis.test(val)).isEqualTo("{\"key\":1557316800000}");
JsonTester<Date> jsonIso = new JsonTester<>(new ScalarTypeUtilDate.DateType(JsonConfig.Date.ISO8601) );
Date val1 = jsonIso.type.parse("2019-05-09");
assertThat(jsonIso.test(val1)).isEqualTo("{\"key\":\"2019-05-09\"}");
}
}
@@ -1,24 +1,30 @@
package io.ebeaninternal.server.type;
import io.ebean.config.JsonConfig;
import org.junit.Test;
import java.io.IOException;
import java.sql.Date;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
public class ScalarTypeYearMonthDateTest {
ScalarTypeYearMonthDate type = new ScalarTypeYearMonthDate();
private ScalarTypeYearMonthDate type = new ScalarTypeYearMonthDate(JsonConfig.Date.MILLIS);
@Test
public void testConvertFromMillis() throws Exception {
public void testConvertFromMillis() {
LocalDate today = LocalDate.now();
LocalDate firstMonthDay = today.withDayOfMonth(1);
Date date = Date.valueOf(firstMonthDay);
long epochMilli = date.getTime();
ZonedDateTime zonedDateTime = firstMonthDay.atStartOfDay(ZoneOffset.UTC);
long epochMilli = zonedDateTime.toInstant().toEpochMilli();
YearMonth yearMonth = type.convertFromMillis(epochMilli);
long val1 = type.convertToMillis(yearMonth);
@@ -27,7 +33,7 @@ public class ScalarTypeYearMonthDateTest {
}
@Test
public void testConvertDate() throws Exception {
public void testConvertDate() {
LocalDate today = LocalDate.now();
LocalDate firstMonthDay = today.withDayOfMonth(1);
@@ -40,7 +46,7 @@ public class ScalarTypeYearMonthDateTest {
@Test
public void testToJdbcType() throws Exception {
public void testToJdbcType() {
LocalDate today = LocalDate.now();
LocalDate firstMonthDay = today.withDayOfMonth(1);
@@ -51,4 +57,16 @@ public class ScalarTypeYearMonthDateTest {
assertEquals(date, val1);
}
@Test
public void json() throws IOException {
YearMonth val = YearMonth.of(2019, 5);
JsonTester<YearMonth> jsonMillis = new JsonTester<>(type);
assertThat(jsonMillis.test(val)).isEqualTo("{\"key\":1556668800000}");
JsonTester<YearMonth> jsonIso = new JsonTester<>(new ScalarTypeYearMonthDate(JsonConfig.Date.ISO8601) );
assertThat(jsonIso.test(val)).isEqualTo("{\"key\":\"2019-05-01\"}");
}
}
@@ -0,0 +1,18 @@
package io.ebeaninternal.server.type;
import org.junit.Test;
import java.util.Date;
import static org.assertj.core.api.Assertions.assertThat;
public class UtilDateParserTest {
@Test
public void parse() {
Date val = UtilDateParser.parse("2019-05-09");
String format = UtilDateParser.format(val);
assertThat(format).isEqualTo("2019-05-09");
}
}
@@ -0,0 +1,44 @@
package io.ebeaninternal.server.type;
import org.junit.Test;
import java.sql.Timestamp;
import static org.assertj.core.api.StrictAssertions.assertThat;
public class UtilDateTimeParserTest {
private UtilDateTimeParser parser = new UtilDateTimeParser();
@Test
public void parseFormat_when_hasMillis() {
parseAndFormat("2016-02-28T20:39:00.123Z", "2016-02-28T20:39:00.123Z");
}
@Test
public void parseFormat_when_noMillis() {
parseAndFormat("2016-02-28T20:39:00Z", "2016-02-28T20:39:00.000Z");
}
@Test
public void parseFormat_when_millis_1dp() {
parseAndFormat("2016-02-28T20:39:00.0Z", "2016-02-28T20:39:00.000Z");
}
@Test
public void parseFormat_when_millis_2dp() {
parseAndFormat("2016-02-28T20:39:00.00Z", "2016-02-28T20:39:00.000Z");
}
@Test
public void parseFormat_when_millis_3dp() {
parseAndFormat("2016-02-28T20:39:00.000Z", "2016-02-28T20:39:00.000Z");
}
private void parseAndFormat(String input, String expected) {
Timestamp timestamp = parser.parse(input);
String format = parser.format(timestamp);
assertThat(format).isEqualTo(expected);
}
}
@@ -2,8 +2,8 @@ package org.tests.basic;
import io.ebean.AcquireLockException;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.EbeanServer;
import io.ebean.DB;
import io.ebean.Database;
import io.ebean.Query;
import io.ebean.Transaction;
import io.ebean.annotation.ForPlatform;
@@ -30,7 +30,7 @@ public class TestQueryForUpdate extends BaseTestCase {
ResetBasicData.reset();
Query<Customer> query = Ebean.find(Customer.class)
Query<Customer> query = DB.find(Customer.class)
.forUpdate()
.order().desc("id");
@@ -49,16 +49,16 @@ public class TestQueryForUpdate extends BaseTestCase {
public void testForUpdate_when_alreadyInPC() {
EBasic basic = new EBasic("test PC cache");
Ebean.save(basic);
DB.save(basic);
try (Transaction transaction = Ebean.beginTransaction()) {
try (Transaction transaction = DB.beginTransaction()) {
LoggedSqlCollector.start();
EBasic basic0 = Ebean.find(EBasic.class, basic.getId());
EBasic basic0 = DB.find(EBasic.class, basic.getId());
assertThat(basic0).isNotNull();
EBasic basic1 = Ebean.find(EBasic.class)
EBasic basic1 = DB.find(EBasic.class)
.setId( basic.getId())
.forUpdate()
.findOne();
@@ -86,7 +86,7 @@ public class TestQueryForUpdate extends BaseTestCase {
ResetBasicData.reset();
Query<Customer> query = Ebean.find(Customer.class)
Query<Customer> query = DB.find(Customer.class)
.forUpdateNoWait()
.order().desc("id");
@@ -110,10 +110,10 @@ public class TestQueryForUpdate extends BaseTestCase {
ResetBasicData.reset();
EbeanServer server = Ebean.getDefaultServer();
Database server = DB.getDefault();
try (Transaction txn = Ebean.beginTransaction()) {
Query<Customer> query = Ebean.find(Customer.class)
try (Transaction txn = DB.beginTransaction()) {
Query<Customer> query = DB.find(Customer.class)
.forUpdateNoWait()
.setMaxRows(1)
.order().desc("id");
@@ -131,12 +131,12 @@ public class TestQueryForUpdate extends BaseTestCase {
// row is locked and we can't acquire it
try (Transaction txn2 = server.createTransaction()) {
logger.info("... attempt another acquire using 2nd transaction");
Query<Customer> query2 =
server.find(Customer.class)
.where().idEq(first.getId())
.forUpdateNoWait();
server.find(Customer.class)
.where().idEq(first.getId())
.forUpdateNoWait()
.usingTransaction(txn2)
.findOne();
server.extended().findOne(query2, txn2);
assertTrue(false); // never get here
} catch (AcquireLockException e) {
logger.info("... got AcquireLockException " + e);
@@ -0,0 +1,63 @@
package org.tests.basic;
import io.ebean.BaseTestCase;
import io.ebean.DB;
import io.ebean.Transaction;
import org.junit.Test;
import org.tests.model.basic.Country;
import org.tests.model.basic.ResetBasicData;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import static org.assertj.core.api.Assertions.assertThat;
public class TestQueryUsingConnection extends BaseTestCase {
@Test
public void usingConnection() throws SQLException {
ResetBasicData.reset();
DataSource dataSource = DB.getDefault().getPluginApi().getDataSource();
try (Connection connection = dataSource.getConnection()) {
int count = DB.find(Country.class)
.usingConnection(connection)
.findCount();
assertThat(count).isGreaterThan(0);
}
}
@Test
public void usingTransaction() {
ResetBasicData.reset();
try (Transaction transaction = DB.getDefault().createTransaction()) {
Transaction current = Transaction.current();
assertThat(current).isNull();
Country x = new Country();
x.setCode("xx");
x.setName("WillRollThisBack");
DB.getDefault().insert(x, transaction);
final int count = DB.find(Country.class)
.usingTransaction(transaction)
.findCount();
final int otherCount = DB.find(Country.class).findCount();
transaction.rollback();
assertThat(count).isEqualTo(otherCount + 1);
}
}
}

Some files were not shown because too many files have changed in this diff Show More