mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
32
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b746c83ed7 | ||
|
|
8e45462c2c | ||
|
|
9fdb375229 | ||
|
|
7e6d059ef1 | ||
|
|
856a3a6fdd | ||
|
|
f94c3e81e5 | ||
|
|
4bf9e2b18e | ||
|
|
112de29964 | ||
|
|
c5d761076a | ||
|
|
4bedefa494 | ||
|
|
a5e3a70921 | ||
|
|
f6f4368048 | ||
|
|
e700f1c83c | ||
|
|
dc5ac02952 | ||
|
|
b89371acd1 | ||
|
|
6c88325dad | ||
|
|
ecec966a85 | ||
|
|
c46ac04c0a | ||
|
|
25e6d05542 | ||
|
|
d3ead5d2f9 | ||
|
|
28a70fa550 | ||
|
|
cf31830cbc | ||
|
|
b4105ab5d0 | ||
|
|
1bb2b1318e | ||
|
|
65fa1c056b | ||
|
|
242945aa18 | ||
|
|
5e77be6e6d | ||
|
|
8c0d6b1a72 | ||
|
|
86436ac3ac | ||
|
|
ae228d6b0e | ||
|
|
410d1a9b37 | ||
|
|
f667a73dc1 |
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+73
-11
@@ -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>
|
||||
* <code>
|
||||
* 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();
|
||||
* }
|
||||
* </code>
|
||||
* </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);
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
+1
-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
|
||||
|
||||
+13
-8
@@ -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
|
||||
|
||||
+1
-1
@@ -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
Reference in New Issue
Block a user