mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
123
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4e22a4ca7 | ||
|
|
9b8b4414c4 | ||
|
|
b8fd2c7a02 | ||
|
|
a94f5d74ca | ||
|
|
3829613c37 | ||
|
|
0762eaf0ab | ||
|
|
7fbdab6220 | ||
|
|
cf4d45befb | ||
|
|
dcd81a857d | ||
|
|
0cac723eac | ||
|
|
c6ba4d8155 | ||
|
|
201958f38c | ||
|
|
9326c9c79e | ||
|
|
1042fdb280 | ||
|
|
197056dbc5 | ||
|
|
895b5ee4ff | ||
|
|
c839fe6520 | ||
|
|
2678f85506 | ||
|
|
2b1b4af7a2 | ||
|
|
4218b8a2e8 | ||
|
|
fe7d73a4a4 | ||
|
|
81e86b07e3 | ||
|
|
910c04e373 | ||
|
|
205f1f5e89 | ||
|
|
a1a6d2b725 | ||
|
|
30da589ea0 | ||
|
|
c66e9547e1 | ||
|
|
73990e3fb7 | ||
|
|
f7920de3c2 | ||
|
|
3dd5069f5a | ||
|
|
1d363776ec | ||
|
|
c512eb564d | ||
|
|
7f332e91ff | ||
|
|
8e9d644bc6 | ||
|
|
fa5ce9cbba | ||
|
|
b7905daeb5 | ||
|
|
9da7c9468e | ||
|
|
9eee224e03 | ||
|
|
906d5f04ad | ||
|
|
654c3f7f67 | ||
|
|
c61e6b04a2 | ||
|
|
f893653090 | ||
|
|
579c1efcf6 | ||
|
|
662f9cdeb3 | ||
|
|
b4782425f8 | ||
|
|
2648423930 | ||
|
|
b62cea5073 | ||
|
|
c10ad11b8b | ||
|
|
4aa8a0dc63 | ||
|
|
e4569f9362 | ||
|
|
ed83bababc | ||
|
|
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 | ||
|
|
4509c6b1ed | ||
|
|
d32559baf4 | ||
|
|
2639e799c5 | ||
|
|
46c66b7169 | ||
|
|
30ae435f33 | ||
|
|
4f4743807c | ||
|
|
7b7a492241 | ||
|
|
4af8e53315 | ||
|
|
1c6fa575f2 | ||
|
|
2430404e1f | ||
|
|
d433e0ea46 | ||
|
|
42591a5a87 | ||
|
|
65a62bf683 | ||
|
|
3e3f3c35e7 | ||
|
|
f4db7ca0bb | ||
|
|
b0e487cfbd | ||
|
|
23fd29ddf1 | ||
|
|
f21cac5d4f | ||
|
|
c2e5619f70 | ||
|
|
18e0ff7067 | ||
|
|
4119c02f0d | ||
|
|
8985d37aed | ||
|
|
23e035894f | ||
|
|
32b8fd0e26 | ||
|
|
4971db54c8 | ||
|
|
e417b3cde2 | ||
|
|
d23ca31e42 | ||
|
|
c9b71ae373 | ||
|
|
f06f21cf5e | ||
|
|
f8b97ff397 | ||
|
|
fc032c632b | ||
|
|
54534ea7f3 | ||
|
|
812dcbb4bb | ||
|
|
a3d4b57b94 | ||
|
|
61479d365a | ||
|
|
3ba9a52b0e | ||
|
|
518ba37fb2 | ||
|
|
fd6a7f64eb | ||
|
|
01f2881940 | ||
|
|
aef512a851 |
@@ -1,6 +1,34 @@
|
||||
[](https://travis-ci.org/ebean-orm/ebean)
|
||||
[](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean)
|
||||
|
||||
# Sponsors
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.foconis.de/" target="_blank">
|
||||
<img width="222px" src="https://www.foconis.de/templates/yootheme/cache/foconis_logo_322-709da1de.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.payintech.com/" target="_blank">
|
||||
<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-med-principal.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://timerbe.de" target="_blank">
|
||||
<img width="222px" src="https://ebean.io/images/logo-timerbee.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
# Need help?
|
||||
Post questions or issues to the Ebean google group - https://groups.google.com/forum/#!forum/ebean
|
||||
|
||||
|
||||
@@ -9,20 +9,20 @@
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>11.36.2</version>
|
||||
<version>11.39.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ebean</name>
|
||||
<url>http://ebean-orm.github.io/</url>
|
||||
|
||||
<properties>
|
||||
<jackson-core.version>2.9.5</jackson-core.version>
|
||||
<jackson-core.version>2.9.8</jackson-core.version>
|
||||
<snakeyaml.version>1.21</snakeyaml.version>
|
||||
</properties>
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-11.36.2</tag>
|
||||
<tag>ebean-11.39.1</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -111,13 +111,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>persistence-api</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<version>2.2.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-annotation</artifactId>
|
||||
<version>4.6</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>
|
||||
@@ -141,7 +141,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-migration</artifactId>
|
||||
<version>11.14.1</version>
|
||||
<version>11.16.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -220,16 +220,16 @@
|
||||
<!-- 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>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-docker-run</artifactId>
|
||||
<version>1.5.1</version>
|
||||
<groupId>io.ebean.test</groupId>
|
||||
<artifactId>ebean-test-docker</artifactId>
|
||||
<version>2.5.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -250,7 +250,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-agent</artifactId>
|
||||
<version>11.27.1</version>
|
||||
<version>11.39.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -280,7 +280,7 @@
|
||||
<dependency>
|
||||
<groupId>com.microsoft.sqlserver</groupId>
|
||||
<artifactId>mssql-jdbc</artifactId>
|
||||
<version>7.2.0.jre8</version>
|
||||
<version>7.2.2.jre8</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -330,7 +330,7 @@
|
||||
<plugin>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-maven-plugin</artifactId>
|
||||
<version>11.27.1</version>
|
||||
<version>11.39.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
|
||||
@@ -109,6 +109,12 @@ public interface DtoQuery<T> {
|
||||
*/
|
||||
DtoQuery<T> setLabel(String label);
|
||||
|
||||
/**
|
||||
* Set the profile location of this query. This is used to relate query execution metrics
|
||||
* back to a location like a specific line of code.
|
||||
*/
|
||||
DtoQuery<T> setProfileLocation(ProfileLocation profileLocation);
|
||||
|
||||
/**
|
||||
* Set a timeout on this query.
|
||||
* <p>
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -40,6 +40,11 @@ public interface ProfileLocation {
|
||||
*/
|
||||
String shortDescription();
|
||||
|
||||
/**
|
||||
* Return the short label.
|
||||
*/
|
||||
String label();
|
||||
|
||||
/**
|
||||
* Add execution time.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -10,22 +10,15 @@ import java.util.Set;
|
||||
*/
|
||||
public class ServerCacheNotification {
|
||||
|
||||
private final long modifyTimestamp;
|
||||
|
||||
private final Set<String> dependentTables;
|
||||
|
||||
public ServerCacheNotification(long modifyTimestamp, Set<String> dependentTables) {
|
||||
this.modifyTimestamp = modifyTimestamp;
|
||||
public ServerCacheNotification(Set<String> dependentTables) {
|
||||
this.dependentTables = dependentTables;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ts:" + modifyTimestamp + " tables:" + dependentTables;
|
||||
}
|
||||
|
||||
public long getModifyTimestamp() {
|
||||
return modifyTimestamp;
|
||||
return "tables:" + dependentTables;
|
||||
}
|
||||
|
||||
public Set<String> getDependentTables() {
|
||||
|
||||
@@ -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,7 +5,7 @@ package io.ebean.config;
|
||||
*/
|
||||
public class AutoTuneConfig {
|
||||
|
||||
private AutoTuneMode mode = AutoTuneMode.DEFAULT_ON;
|
||||
private AutoTuneMode mode = AutoTuneMode.DEFAULT_OFF;
|
||||
|
||||
private String queryTuningFile = "ebean-autotune.xml";
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -52,6 +52,8 @@ public class PlatformConfig {
|
||||
*/
|
||||
private boolean databaseInetAddressVarchar;
|
||||
|
||||
private boolean caseSensitiveCollation = true;
|
||||
|
||||
/**
|
||||
* Modify the default mapping of standard types such as default precision for DECIMAL etc.
|
||||
*/
|
||||
@@ -74,6 +76,7 @@ public class PlatformConfig {
|
||||
this.idType = platformConfig.idType;
|
||||
this.geometrySRID = platformConfig.geometrySRID;
|
||||
this.dbUuid = platformConfig.dbUuid;
|
||||
this.caseSensitiveCollation = platformConfig.caseSensitiveCollation;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,6 +93,20 @@ public class PlatformConfig {
|
||||
this.allQuotedIdentifiers = allQuotedIdentifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the collation is case sensitive.
|
||||
*/
|
||||
public boolean isCaseSensitiveCollation() {
|
||||
return caseSensitiveCollation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to false to indicate that the collation is case insensitive.
|
||||
*/
|
||||
public void setCaseSensitiveCollation(boolean caseSensitiveCollation) {
|
||||
this.caseSensitiveCollation = caseSensitiveCollation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a value used to represent TRUE in the database.
|
||||
* <p>
|
||||
@@ -255,6 +272,7 @@ public class PlatformConfig {
|
||||
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
|
||||
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
|
||||
databaseInetAddressVarchar = p.getBoolean("databaseInetAddressVarchar", databaseInetAddressVarchar);
|
||||
caseSensitiveCollation = p.getBoolean("caseSensitiveCollation", caseSensitiveCollation);
|
||||
|
||||
DbUuid dbUuid = p.getEnum(DbUuid.class, "dbuuid", null);
|
||||
if (dbUuid != null) {
|
||||
|
||||
@@ -32,6 +32,7 @@ import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.migration.MigrationRunner;
|
||||
import io.ebean.util.StringHelper;
|
||||
|
||||
import javax.persistence.EnumType;
|
||||
import javax.sql.DataSource;
|
||||
import java.time.Clock;
|
||||
import java.util.ArrayList;
|
||||
@@ -168,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.
|
||||
@@ -229,6 +235,8 @@ public class ServerConfig {
|
||||
|
||||
private int persistBatchSize = 20;
|
||||
|
||||
private EnumType defaultEnumType = EnumType.ORDINAL;
|
||||
|
||||
private boolean disableLazyLoading;
|
||||
|
||||
/**
|
||||
@@ -476,6 +484,7 @@ public class ServerConfig {
|
||||
*/
|
||||
private boolean disableL2Cache;
|
||||
|
||||
private String enabledL2Regions;
|
||||
|
||||
/**
|
||||
* Should the javax.validation.constraints.NotNull enforce a notNull column in DB.
|
||||
@@ -509,7 +518,7 @@ public class ServerConfig {
|
||||
/**
|
||||
* Controls the default order by id setting of queries. See {@link Query#orderById(boolean)}
|
||||
*/
|
||||
private boolean defaultOrderById = false;
|
||||
private boolean defaultOrderById;
|
||||
|
||||
/**
|
||||
* The mappingLocations for searching xml mapping.
|
||||
@@ -521,6 +530,10 @@ public class ServerConfig {
|
||||
*/
|
||||
private boolean idGeneratorAutomatic = true;
|
||||
|
||||
private boolean dumpMetricsOnShutdown;
|
||||
|
||||
private String dumpMetricsOptions;
|
||||
|
||||
/**
|
||||
* Construct a Database Configuration for programmatically creating an Database.
|
||||
*/
|
||||
@@ -572,8 +585,9 @@ public class ServerConfig {
|
||||
|
||||
|
||||
/**
|
||||
* Sets the default orderById setting for queries.
|
||||
* Deprecated - look to have explicit order by. Sets the default orderById setting for queries.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setDefaultOrderById(boolean defaultOrderById) {
|
||||
this.defaultOrderById = defaultOrderById;
|
||||
}
|
||||
@@ -675,6 +689,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.
|
||||
*/
|
||||
@@ -978,6 +1006,14 @@ public class ServerConfig {
|
||||
this.queryBatchSize = queryBatchSize;
|
||||
}
|
||||
|
||||
public EnumType getDefaultEnumType() {
|
||||
return defaultEnumType;
|
||||
}
|
||||
|
||||
public void setDefaultEnumType(EnumType defaultEnumType) {
|
||||
this.defaultEnumType = defaultEnumType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if lazy loading is disabled on queries by default.
|
||||
*/
|
||||
@@ -2892,11 +2928,14 @@ public class ServerConfig {
|
||||
}
|
||||
loadDocStoreSettings(p);
|
||||
|
||||
dumpMetricsOnShutdown = p.getBoolean("dumpMetricsOnShutdown", dumpMetricsOnShutdown);
|
||||
dumpMetricsOptions = p.get("dumpMetricsOptions", dumpMetricsOptions);
|
||||
queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds);
|
||||
slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis);
|
||||
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);
|
||||
@@ -2963,17 +3002,14 @@ public class ServerConfig {
|
||||
localTimeWithNanos = p.getBoolean("localTimeWithNanos", localTimeWithNanos);
|
||||
jodaLocalTimeMode = p.get("jodaLocalTimeMode", jodaLocalTimeMode);
|
||||
|
||||
defaultEnumType = p.getEnum(EnumType.class, "defaultEnumType", defaultEnumType);
|
||||
disableLazyLoading = p.getBoolean("disableLazyLoading", disableLazyLoading);
|
||||
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", lazyLoadBatchSize);
|
||||
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);
|
||||
@@ -3118,6 +3154,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.
|
||||
*/
|
||||
@@ -3275,6 +3325,36 @@ public class ServerConfig {
|
||||
this.collectQueryPlans = collectQueryPlans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if metrics should be dumped when the server is shutdown.
|
||||
*/
|
||||
public boolean isDumpMetricsOnShutdown() {
|
||||
return dumpMetricsOnShutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if metrics should be dumped when the server is shutdown.
|
||||
*/
|
||||
public void setDumpMetricsOnShutdown(boolean dumpMetricsOnShutdown) {
|
||||
this.dumpMetricsOnShutdown = dumpMetricsOnShutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the options for dumping metrics.
|
||||
*/
|
||||
public String getDumpMetricsOptions() {
|
||||
return dumpMetricsOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include 'sql' or 'hash' in options such that they are included in the output.
|
||||
*
|
||||
* @param dumpMetricsOptions Example "sql,hash", "sql"
|
||||
*/
|
||||
public void setDumpMetricsOptions(String dumpMetricsOptions) {
|
||||
this.dumpMetricsOptions = dumpMetricsOptions;
|
||||
}
|
||||
|
||||
public enum UuidVersion {
|
||||
VERSION4,
|
||||
VERSION1,
|
||||
|
||||
@@ -49,6 +49,8 @@ public class DatabasePlatform {
|
||||
*/
|
||||
protected boolean useExtraTransactionOnIterateSecondaryQueries;
|
||||
|
||||
protected boolean supportsDeleteTableAlias;
|
||||
|
||||
/**
|
||||
* The behaviour used when ending a read only transaction at read committed isolation level.
|
||||
*/
|
||||
@@ -69,6 +71,8 @@ public class DatabasePlatform {
|
||||
*/
|
||||
protected boolean allQuotedIdentifiers;
|
||||
|
||||
protected boolean caseSensitiveCollation = true;
|
||||
|
||||
/**
|
||||
* For limit/offset, row_number etc limiting of SQL queries.
|
||||
*/
|
||||
@@ -176,10 +180,10 @@ public class DatabasePlatform {
|
||||
* findIterate() and findVisit().
|
||||
*/
|
||||
protected boolean forwardOnlyHintOnFindIterate;
|
||||
|
||||
|
||||
/**
|
||||
* If set then use the CONCUR_UPDATABLE hint when creating ResultSets.
|
||||
*
|
||||
*
|
||||
* This is {@code false} for HANA
|
||||
*/
|
||||
protected boolean supportsResultSetConcurrencyModeUpdatable = true;
|
||||
@@ -224,6 +228,7 @@ public class DatabasePlatform {
|
||||
*/
|
||||
public void configure(PlatformConfig config) {
|
||||
this.sequenceBatchSize = config.getDatabaseSequenceBatchSize();
|
||||
this.caseSensitiveCollation = config.isCaseSensitiveCollation();
|
||||
configureIdType(config.getIdType());
|
||||
configure(config, config.isAllQuotedIdentifiers());
|
||||
}
|
||||
@@ -306,6 +311,23 @@ public class DatabasePlatform {
|
||||
return supportsNativeIlike;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the platform supports delete statements with table alias.
|
||||
*/
|
||||
public boolean isSupportsDeleteTableAlias() {
|
||||
return supportsDeleteTableAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the collation is case sensitive.
|
||||
* <p>
|
||||
* This is expected to be used for testing only.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isCaseSensitiveCollation() {
|
||||
return caseSensitiveCollation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum table name length.
|
||||
* <p>
|
||||
@@ -524,7 +546,7 @@ public class DatabasePlatform {
|
||||
public void setForwardOnlyHintOnFindIterate(boolean forwardOnlyHintOnFindIterate) {
|
||||
this.forwardOnlyHintOnFindIterate = forwardOnlyHintOnFindIterate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the ResultSet CONCUR_UPDATABLE Hint should be used on
|
||||
* createNativeSqlTree() PreparedStatements.
|
||||
@@ -535,7 +557,7 @@ public class DatabasePlatform {
|
||||
public boolean isSupportsResultSetConcurrencyModeUpdatable() {
|
||||
return supportsResultSetConcurrencyModeUpdatable;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to true if the ResultSet CONCUR_UPDATABLE Hint should be used by default on createNativeSqlTree() PreparedStatements.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package io.ebean.config.dbplatform.clickhouse;
|
||||
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.config.dbplatform.DbType;
|
||||
import io.ebean.config.dbplatform.IdType;
|
||||
|
||||
import java.sql.Types;
|
||||
|
||||
public class ClickHousePlatform extends DatabasePlatform {
|
||||
|
||||
public ClickHousePlatform() {
|
||||
super();
|
||||
this.platform = Platform.CLICKHOUSE;
|
||||
//this.dbEncrypt =
|
||||
//this.historySupport =
|
||||
//this.exceptionTranslator =
|
||||
|
||||
this.nativeUuidType = true;
|
||||
this.dbDefaultValue.setNow("now()");
|
||||
this.columnAliasPrefix = null;
|
||||
|
||||
this.dbIdentity.setIdType(IdType.IDENTITY);
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(false);
|
||||
this.dbIdentity.setSupportsSequence(false);
|
||||
this.dbIdentity.setSupportsIdentity(true);
|
||||
|
||||
this.booleanDbType = Types.INTEGER;
|
||||
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("UInt8"));
|
||||
|
||||
// using unsigned as default types ...
|
||||
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("UInt8", false));
|
||||
dbTypeMap.put(DbType.SMALLINT, new DbPlatformType("UInt16", false));
|
||||
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("UInt32", false));
|
||||
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("UInt64", false));
|
||||
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("Decimal", 16,4));
|
||||
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("Float64", false));
|
||||
|
||||
dbTypeMap.put(DbType.DATE, new DbPlatformType("Date", false));
|
||||
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("DateTime", false));
|
||||
|
||||
dbTypeMap.put(DbType.VARCHAR, new DbPlatformType("String", false));
|
||||
dbTypeMap.put(DbType.LONGVARCHAR, new DbPlatformType("String", false));
|
||||
dbTypeMap.put(DbType.CLOB, new DbPlatformType("String", false));
|
||||
dbTypeMap.put(DbType.JSONVARCHAR, new DbPlatformType("String", false));
|
||||
|
||||
dbTypeMap.put(DbType.UUID, new DbPlatformType("UUID", false));
|
||||
dbTypeMap.put(DbType.INET, new DbPlatformType("String", false));
|
||||
dbTypeMap.put(DbType.CDIR, new DbPlatformType("String", false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNativeArrayType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,7 @@ public class H2Platform extends DatabasePlatform {
|
||||
this.dbEncrypt = new H2DbEncrypt();
|
||||
this.historySupport = new H2HistorySupport();
|
||||
this.nativeUuidType = true;
|
||||
this.supportsDeleteTableAlias = true;
|
||||
this.dbDefaultValue.setNow("now()");
|
||||
this.columnAliasPrefix = null;
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ public class MySqlPlatform extends DatabasePlatform {
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsIdentity(true);
|
||||
this.dbIdentity.setSupportsSequence(false);
|
||||
|
||||
|
||||
this.dbDefaultValue.setNow("now(6)"); // must have same precision as TIMESTAMP
|
||||
this.dbDefaultValue.setFalse("0");
|
||||
this.dbDefaultValue.setTrue("1");
|
||||
@@ -51,8 +51,8 @@ public class MySqlPlatform extends DatabasePlatform {
|
||||
this.openQuote = "`";
|
||||
this.closeQuote = "`";
|
||||
// use pipe for escaping as it depends if mysql runs in no_backslash_escapes or not.
|
||||
this.likeClauseRaw = "like binary ? escape ''";
|
||||
this.likeClauseEscaped = "like binary ? escape '|'";
|
||||
this.likeClauseRaw = "like ? escape ''";
|
||||
this.likeClauseEscaped = "like ? escape '|'";
|
||||
|
||||
this.forwardOnlyHintOnFindIterate = true;
|
||||
this.booleanDbType = Types.BIT;
|
||||
|
||||
@@ -23,6 +23,7 @@ public class OraclePlatform extends DatabasePlatform {
|
||||
public OraclePlatform() {
|
||||
super();
|
||||
this.platform = Platform.ORACLE;
|
||||
this.supportsDeleteTableAlias = true;
|
||||
this.maxTableNameLength = 30;
|
||||
this.maxConstraintNameLength = 30;
|
||||
this.dbEncrypt = new OracleDbEncrypt();
|
||||
|
||||
@@ -30,6 +30,7 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
super();
|
||||
this.platform = Platform.POSTGRES;
|
||||
this.supportsNativeIlike = true;
|
||||
this.supportsDeleteTableAlias = true;
|
||||
this.selectCountWithAlias = true;
|
||||
this.blobDbType = Types.LONGVARBINARY;
|
||||
this.clobDbType = Types.VARCHAR;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,8 +47,8 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
|
||||
this.openQuote = "[";
|
||||
this.closeQuote = "]";
|
||||
this.likeSpecialCharacters = new char[]{'%', '_', '['};
|
||||
this.likeClauseRaw = "like ? collate Latin1_General_BIN";
|
||||
this.likeClauseEscaped = "like ? collate Latin1_General_BIN";
|
||||
this.likeClauseRaw = "like ?";
|
||||
this.likeClauseEscaped = "like ?";
|
||||
|
||||
booleanDbType = Types.INTEGER;
|
||||
this.dbDefaultValue.setFalse("0");
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -14,189 +14,192 @@ import javax.naming.NamingException;
|
||||
*/
|
||||
final class PropertyEval {
|
||||
|
||||
/**
|
||||
* Prefix for looking up JNDI Environment variable.
|
||||
*/
|
||||
private static final String JAVA_COMP_ENV = "java:comp/env/";
|
||||
/**
|
||||
* Prefix for looking up JNDI Environment variable.
|
||||
*/
|
||||
private static final String JAVA_COMP_ENV = "java:comp/env/";
|
||||
|
||||
/**
|
||||
* Used to detect the start of an expression.
|
||||
*/
|
||||
private static final String START = "${";
|
||||
/**
|
||||
* Used to detect the start of an expression.
|
||||
*/
|
||||
private static final String START = "${";
|
||||
|
||||
/**
|
||||
* Used to detect the end of an expression.
|
||||
*/
|
||||
private static final String END = "}";
|
||||
/**
|
||||
* Used to detect the end of an expression.
|
||||
*/
|
||||
private static final String END = "}";
|
||||
|
||||
/**
|
||||
* Specify the PropertyHolder.
|
||||
*/
|
||||
private PropertyEval() {
|
||||
}
|
||||
/**
|
||||
* Specify the PropertyHolder.
|
||||
*/
|
||||
private PropertyEval() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property value evaluating and replacing any expressions such as
|
||||
* ${CATALINA_HOME}.
|
||||
*/
|
||||
static String eval(String val) {
|
||||
if (val == null) {
|
||||
return null;
|
||||
}
|
||||
int sp = val.indexOf(START);
|
||||
if (sp > -1) {
|
||||
int ep = val.indexOf(END, sp + 1);
|
||||
if (ep > -1) {
|
||||
return eval(val, sp, ep);
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
/**
|
||||
* Return the property value evaluating and replacing any expressions such as
|
||||
* ${CATALINA_HOME}.
|
||||
*/
|
||||
static String eval(String val) {
|
||||
if (val == null) {
|
||||
return null;
|
||||
}
|
||||
int sp = val.indexOf(START);
|
||||
if (sp > -1) {
|
||||
int ep = val.indexOf(END, sp + 1);
|
||||
if (ep > -1) {
|
||||
return eval(val, sp, ep);
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the expression using JNDI, Environment variables, System Properties
|
||||
* or existing an property in SystemProperties itself.
|
||||
*/
|
||||
private static String evaluateExpression(String exp) {
|
||||
/**
|
||||
* Convert the expression using JNDI, Environment variables, System Properties
|
||||
* or existing an property in SystemProperties itself.
|
||||
*/
|
||||
private static String evaluateExpression(String exp) {
|
||||
|
||||
if (isJndiExpression(exp)) {
|
||||
// JNDI property lookup...
|
||||
String val = getJndiProperty(exp);
|
||||
if (val != null) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
String val = System.getProperty(exp);
|
||||
if (val == null) {
|
||||
val = System.getenv(exp);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
if (exp.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (isJndiExpression(exp)) {
|
||||
// JNDI property lookup...
|
||||
String val = getJndiProperty(exp);
|
||||
if (val != null) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
String val = System.getProperty(exp);
|
||||
if (val == null) {
|
||||
val = System.getenv(exp);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
private static String eval(String val, int sp, int ep) {
|
||||
private static String eval(String val, int sp, int ep) {
|
||||
|
||||
return new EvalBuffer(val, sp, ep).process();
|
||||
}
|
||||
return new EvalBuffer(val, sp, ep).process();
|
||||
}
|
||||
|
||||
|
||||
private static boolean isJndiExpression(String exp) {
|
||||
return exp.startsWith("JNDI:") || exp.startsWith("jndi:");
|
||||
}
|
||||
private static boolean isJndiExpression(String exp) {
|
||||
return exp.startsWith("JNDI:") || exp.startsWith("jndi:");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null if JNDI is not setup or if the property is not found.
|
||||
*
|
||||
* @param key the key of the JNDI Environment property including a JNDI: prefix.
|
||||
*/
|
||||
private static String getJndiProperty(String key) {
|
||||
/**
|
||||
* Returns null if JNDI is not setup or if the property is not found.
|
||||
*
|
||||
* @param key the key of the JNDI Environment property including a JNDI: prefix.
|
||||
*/
|
||||
private static String getJndiProperty(String key) {
|
||||
|
||||
try {
|
||||
// remove the JNDI: prefix
|
||||
key = key.substring(5);
|
||||
return (String) getJndiObject(key);
|
||||
} catch (NamingException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
try {
|
||||
// remove the JNDI: prefix
|
||||
key = key.substring(5);
|
||||
return (String) getJndiObject(key);
|
||||
} catch (NamingException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to getProperty but throws NamingException if JNDI is not setup or
|
||||
* if the property is not found.
|
||||
*/
|
||||
private static Object getJndiObject(String key) throws NamingException {
|
||||
/**
|
||||
* Similar to getProperty but throws NamingException if JNDI is not setup or
|
||||
* if the property is not found.
|
||||
*/
|
||||
private static Object getJndiObject(String key) throws NamingException {
|
||||
|
||||
InitialContext ctx = new InitialContext();
|
||||
return ctx.lookup(JAVA_COMP_ENV + key);
|
||||
}
|
||||
InitialContext ctx = new InitialContext();
|
||||
return ctx.lookup(JAVA_COMP_ENV + key);
|
||||
}
|
||||
|
||||
|
||||
private static class EvalBuffer {
|
||||
private static class EvalBuffer {
|
||||
|
||||
private final StringBuilder buf = new StringBuilder();
|
||||
private final String original;
|
||||
private int position;
|
||||
private int start;
|
||||
private int end;
|
||||
private final StringBuilder buf = new StringBuilder();
|
||||
private final String original;
|
||||
private int position;
|
||||
private int start;
|
||||
private int end;
|
||||
|
||||
private String expression;
|
||||
private String defaultValue;
|
||||
private String expression;
|
||||
private String defaultValue;
|
||||
|
||||
EvalBuffer(String val, int start, int ep) {
|
||||
this.original = val;
|
||||
this.start = start;
|
||||
this.end = ep;
|
||||
this.position = 0;
|
||||
moveToStart();
|
||||
}
|
||||
EvalBuffer(String val, int start, int ep) {
|
||||
this.original = val;
|
||||
this.start = start;
|
||||
this.end = ep;
|
||||
this.position = 0;
|
||||
moveToStart();
|
||||
}
|
||||
|
||||
void moveToStart() {
|
||||
if (start > position) {
|
||||
buf.append(original.substring(position, start));
|
||||
position = start;
|
||||
}
|
||||
}
|
||||
void moveToStart() {
|
||||
if (start > position) {
|
||||
buf.append(original.substring(position, start));
|
||||
position = start;
|
||||
}
|
||||
}
|
||||
|
||||
void parseForDefault() {
|
||||
int colonPos = original.lastIndexOf(':', end);
|
||||
if (colonPos > start) {
|
||||
expression = original.substring(start + START.length(), colonPos);
|
||||
defaultValue = original.substring(colonPos + 1, end);
|
||||
} else {
|
||||
expression = original.substring(start + START.length(), end);
|
||||
}
|
||||
}
|
||||
void parseForDefault() {
|
||||
int colonPos = original.lastIndexOf(':', end);
|
||||
if (colonPos > start) {
|
||||
expression = original.substring(start + START.length(), colonPos);
|
||||
defaultValue = original.substring(colonPos + 1, end);
|
||||
} else {
|
||||
expression = original.substring(start + START.length(), end);
|
||||
}
|
||||
}
|
||||
|
||||
void evaluate() {
|
||||
String eval = evaluateExpression(expression);
|
||||
if (eval != null) {
|
||||
buf.append(eval);
|
||||
} else {
|
||||
if (defaultValue != null) {
|
||||
buf.append(defaultValue);
|
||||
} else {
|
||||
buf.append(START).append(expression).append(END);
|
||||
}
|
||||
}
|
||||
}
|
||||
void evaluate() {
|
||||
String eval = evaluateExpression(expression);
|
||||
if (eval != null) {
|
||||
buf.append(eval);
|
||||
} else {
|
||||
if (defaultValue != null) {
|
||||
buf.append(defaultValue);
|
||||
} else {
|
||||
buf.append(START).append(expression).append(END);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String end() {
|
||||
if (end < original.length() - 1) {
|
||||
buf.append(original.substring(end + 1));
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
String end() {
|
||||
if (end < original.length() - 1) {
|
||||
buf.append(original.substring(end + 1));
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
boolean next() {
|
||||
if (end < original.length()) {
|
||||
int startPos = original.indexOf(START, end + 1);
|
||||
if (startPos > -1) {
|
||||
int endPos = original.indexOf(END, startPos + 1);
|
||||
if (endPos > -1) {
|
||||
if (startPos > end + 1) {
|
||||
buf.append(original.substring(end + 1, startPos));
|
||||
}
|
||||
this.start = startPos;
|
||||
this.end = endPos;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
boolean next() {
|
||||
if (end < original.length()) {
|
||||
int startPos = original.indexOf(START, end + 1);
|
||||
if (startPos > -1) {
|
||||
int endPos = original.indexOf(END, startPos + 1);
|
||||
if (endPos > -1) {
|
||||
if (startPos > end + 1) {
|
||||
buf.append(original.substring(end + 1, startPos));
|
||||
}
|
||||
this.start = startPos;
|
||||
this.end = endPos;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void evalNext() {
|
||||
parseForDefault();
|
||||
evaluate();
|
||||
}
|
||||
private void evalNext() {
|
||||
parseForDefault();
|
||||
evaluate();
|
||||
}
|
||||
|
||||
String process() {
|
||||
evalNext();
|
||||
while (next()) {
|
||||
evalNext();
|
||||
}
|
||||
return end();
|
||||
}
|
||||
String process() {
|
||||
evalNext();
|
||||
while (next()) {
|
||||
evalNext();
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,29 +6,31 @@ import java.util.List;
|
||||
/**
|
||||
* A simple MetricVisitor that can collect the desired metrics into lists.
|
||||
*/
|
||||
public class BasicMetricVisitor extends AbstractMetricVisitor {
|
||||
public class BasicMetricVisitor extends AbstractMetricVisitor implements ServerMetrics {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return timed metrics for Transactions, labelled SqlQuery, labelled SqlUpdate.
|
||||
*/
|
||||
@Override
|
||||
public List<MetaTimedMetric> getTimedMetrics() {
|
||||
return timed;
|
||||
}
|
||||
@@ -36,6 +38,7 @@ public class BasicMetricVisitor extends AbstractMetricVisitor {
|
||||
/**
|
||||
* Return the DTO query metrics.
|
||||
*/
|
||||
@Override
|
||||
public List<MetaQueryMetric> getDtoQueryMetrics() {
|
||||
return dtoQuery;
|
||||
}
|
||||
@@ -43,10 +46,16 @@ public class BasicMetricVisitor extends AbstractMetricVisitor {
|
||||
/**
|
||||
* Return the ORM query metrics.
|
||||
*/
|
||||
@Override
|
||||
public List<MetaOrmQueryMetric> getOrmQueryMetrics() {
|
||||
return ormQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaCountMetric> getCountMetrics() {
|
||||
return countMetrics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTimed(MetaTimedMetric metric) {
|
||||
timed.add(metric);
|
||||
@@ -61,4 +70,9 @@ public class BasicMetricVisitor extends AbstractMetricVisitor {
|
||||
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();
|
||||
|
||||
}
|
||||
@@ -7,6 +7,15 @@ import java.util.List;
|
||||
*/
|
||||
public interface MetaInfoManager {
|
||||
|
||||
/**
|
||||
* Return the metrics for the database instance.
|
||||
* <p>
|
||||
* This will reset the metrics (reset counters back to zero etc) and
|
||||
* will only return the non-empty metrics.
|
||||
* </p>
|
||||
*/
|
||||
ServerMetrics collectMetrics();
|
||||
|
||||
/**
|
||||
* Collect query plans.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -9,20 +7,15 @@ import java.util.List;
|
||||
*/
|
||||
public interface MetaOrmQueryMetric extends MetaQueryMetric {
|
||||
|
||||
/**
|
||||
* Return the profile location.
|
||||
*/
|
||||
ProfileLocation getProfileLocation();
|
||||
|
||||
/**
|
||||
* Return true if this query plan was tuned by AutoTune.
|
||||
*/
|
||||
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,5 +1,7 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
|
||||
/**
|
||||
* Query execution metrics.
|
||||
*/
|
||||
@@ -15,6 +17,11 @@ public interface MetaQueryMetric extends MetaTimedMetric {
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* Return the profile location.
|
||||
*/
|
||||
ProfileLocation getProfileLocation();
|
||||
|
||||
/**
|
||||
* The actual SQL of the query.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Metrics of the Database instance.
|
||||
*/
|
||||
public interface ServerMetrics {
|
||||
|
||||
/**
|
||||
* Return timed metrics for Transactions, labelled SqlQuery, labelled SqlUpdate.
|
||||
*/
|
||||
List<MetaTimedMetric> getTimedMetrics();
|
||||
|
||||
/**
|
||||
* Return the DTO query metrics.
|
||||
*/
|
||||
List<MetaQueryMetric> getDtoQueryMetrics();
|
||||
|
||||
/**
|
||||
* Return the ORM query metrics.
|
||||
*/
|
||||
List<MetaOrmQueryMetric> getOrmQueryMetrics();
|
||||
|
||||
/**
|
||||
* Return the Counter metrics.
|
||||
*/
|
||||
List<MetaCountMetric> getCountMetrics();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Comparator for timed metrics sorted by name and then count.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public static class Name implements Comparator<MetaTimedMetric> {
|
||||
|
||||
@Override
|
||||
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
|
||||
int i = stringCompare(o1.getName(), o2.getName());
|
||||
return i != 0 ? i : Long.compare(o1.getCount(), o2.getCount());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by count desc.
|
||||
*/
|
||||
public static class Count implements Comparator<MetaTimedMetric> {
|
||||
|
||||
@Override
|
||||
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
|
||||
return Long.compare(o2.getCount(), o1.getCount());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by total time desc.
|
||||
*/
|
||||
public static class Total implements Comparator<MetaTimedMetric> {
|
||||
|
||||
@Override
|
||||
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
|
||||
return Long.compare(o2.getTotal(), o1.getTotal());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by mean desc.
|
||||
*/
|
||||
public static class Mean implements Comparator<MetaTimedMetric> {
|
||||
|
||||
@Override
|
||||
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
|
||||
return Long.compare(o2.getMean(), o1.getMean());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by max desc.
|
||||
*/
|
||||
public static class Max implements Comparator<MetaTimedMetric> {
|
||||
|
||||
@Override
|
||||
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
|
||||
return Long.compare(o2.getMax(), o1.getMax());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebean.metric;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.meta.MetricType;
|
||||
|
||||
/**
|
||||
@@ -24,9 +25,14 @@ public interface MetricFactory {
|
||||
*/
|
||||
TimedMetric createTimedMetric(MetricType metricType, String name);
|
||||
|
||||
/**
|
||||
* Create a counter metric.
|
||||
*/
|
||||
CountMetric createCountMetric(MetricType metricType, String name);
|
||||
|
||||
/**
|
||||
* Create a Timed metric.
|
||||
*/
|
||||
QueryPlanMetric createQueryPlanMetric(MetricType metricType, Class<?> type, String label, String sql);
|
||||
QueryPlanMetric createQueryPlanMetric(MetricType metricType, Class<?> type, String label, ProfileLocation profileLocation, String sql);
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public class ScopedTransaction extends SpiTransactionProxy {
|
||||
|
||||
private void clearScopeOnce() {
|
||||
if (!scopeCleared) {
|
||||
manager.set(null);
|
||||
manager.clear();
|
||||
scopeCleared = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.DtoQuery;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebeaninternal.server.dto.DtoMappingRequest;
|
||||
import io.ebeaninternal.server.dto.DtoQueryPlan;
|
||||
|
||||
@@ -37,9 +38,23 @@ public interface SpiDtoQuery<T> extends DtoQuery<T>, SpiSqlBinding {
|
||||
/**
|
||||
* Return the label for the query.
|
||||
*/
|
||||
@Override
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* Return the label with fallback to profile location label.
|
||||
*/
|
||||
String getPlanLabel();
|
||||
|
||||
/**
|
||||
* Obtain the location if necessary.
|
||||
*/
|
||||
void obtainLocation();
|
||||
|
||||
/**
|
||||
* Return the profile location.
|
||||
*/
|
||||
ProfileLocation getProfileLocation();
|
||||
|
||||
/**
|
||||
* Return the associated DTO bean type.
|
||||
*/
|
||||
|
||||
@@ -133,6 +133,11 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanLoader,
|
||||
*/
|
||||
void externalModification(TransactionEventTable event);
|
||||
|
||||
/**
|
||||
* Clear an implicit transaction from the scope.
|
||||
*/
|
||||
void clearServerTransaction();
|
||||
|
||||
/**
|
||||
* Begin a managed transaction (Uses scope manager / ThreadLocal).
|
||||
*/
|
||||
|
||||
@@ -54,67 +54,69 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
/**
|
||||
* Find by Id or unique returning a single bean.
|
||||
*/
|
||||
BEAN(FIND_ONE),
|
||||
BEAN(FIND_ONE, "byId"),
|
||||
|
||||
/**
|
||||
* Find returning a List.
|
||||
*/
|
||||
LIST(FIND_MANY),
|
||||
LIST(FIND_MANY, "findList"),
|
||||
|
||||
/**
|
||||
* Find returning a Set.
|
||||
*/
|
||||
SET(FIND_MANY),
|
||||
SET(FIND_MANY, "findSet"),
|
||||
|
||||
/**
|
||||
* Find returning a Map.
|
||||
*/
|
||||
MAP(FIND_MANY),
|
||||
MAP(FIND_MANY, "findMap"),
|
||||
|
||||
/**
|
||||
* Find iterate type query - findEach(), findIterate() etc.
|
||||
*/
|
||||
ITERATE(FIND_ITERATE),
|
||||
ITERATE(FIND_ITERATE, "findEach"),
|
||||
|
||||
/**
|
||||
* Find the Id's.
|
||||
*/
|
||||
ID_LIST(FIND_ID_LIST),
|
||||
ID_LIST(FIND_ID_LIST, "findIds"),
|
||||
|
||||
/**
|
||||
* Find single attribute.
|
||||
*/
|
||||
ATTRIBUTE(FIND_ATTRIBUTE),
|
||||
ATTRIBUTE(FIND_ATTRIBUTE, "findAttribute"),
|
||||
|
||||
/**
|
||||
* Find rowCount.
|
||||
*/
|
||||
COUNT(FIND_COUNT),
|
||||
COUNT(FIND_COUNT, "findCount"),
|
||||
|
||||
/**
|
||||
* A subquery used as part of a where clause.
|
||||
*/
|
||||
SUBQUERY(FIND_SUBQUERY),
|
||||
SUBQUERY(FIND_SUBQUERY, "subquery"),
|
||||
|
||||
/**
|
||||
* Delete query.
|
||||
*/
|
||||
DELETE(FIND_DELETE, true),
|
||||
DELETE(FIND_DELETE, "delete", true),
|
||||
|
||||
/**
|
||||
* Update query.
|
||||
*/
|
||||
UPDATE(FIND_UPDATE, true);
|
||||
UPDATE(FIND_UPDATE, "update", true);
|
||||
|
||||
boolean update;
|
||||
String profileEventId;
|
||||
private boolean update;
|
||||
private String profileEventId;
|
||||
private String label;
|
||||
|
||||
Type(String profileEventId) {
|
||||
this(profileEventId, false);
|
||||
Type(String profileEventId, String label) {
|
||||
this(profileEventId, label, false);
|
||||
}
|
||||
|
||||
Type(String profileEventId, boolean update) {
|
||||
Type(String profileEventId, String label, boolean update) {
|
||||
this.profileEventId = profileEventId;
|
||||
this.label = label;
|
||||
this.update = update;
|
||||
}
|
||||
|
||||
@@ -128,6 +130,10 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
public String profileEventId() {
|
||||
return profileEventId;
|
||||
}
|
||||
|
||||
public String label() {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
enum TemporalMode {
|
||||
@@ -197,6 +203,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* Return the label manually set on the query or from the profile location.
|
||||
*/
|
||||
String getPlanLabel();
|
||||
|
||||
/**
|
||||
* Return true if this is a "find by id" query. This includes a check for a single "equal to" expression for the Id.
|
||||
*/
|
||||
@@ -418,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.
|
||||
|
||||
@@ -69,11 +69,6 @@ public interface SpiTransaction extends Transaction {
|
||||
*/
|
||||
void registerDeleteBean(Integer hash);
|
||||
|
||||
/**
|
||||
* Unregister the hash of the bean.
|
||||
*/
|
||||
void unregisterDeleteBean(Integer hash);
|
||||
|
||||
/**
|
||||
* Return true if this is a bean that has already been saved/deleted.
|
||||
*/
|
||||
@@ -103,7 +98,7 @@ public interface SpiTransaction extends Transaction {
|
||||
/**
|
||||
* Return the start timestamp for the transaction (JVM side).
|
||||
*/
|
||||
long getStartMillis();
|
||||
long getStartNanoTime();
|
||||
|
||||
/**
|
||||
* Return true if this transaction has updateAllLoadedProperties set.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.TxScope;
|
||||
import io.ebeaninternal.server.transaction.TransactionScopeManager;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@@ -9,6 +10,11 @@ import javax.sql.DataSource;
|
||||
*/
|
||||
public interface SpiTransactionManager {
|
||||
|
||||
/**
|
||||
* Return the scope manager for this server.
|
||||
*/
|
||||
TransactionScopeManager scope();
|
||||
|
||||
/**
|
||||
* Return the main DataSource.
|
||||
*/
|
||||
|
||||
@@ -29,8 +29,8 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartMillis() {
|
||||
return transaction.getStartMillis();
|
||||
public long getStartNanoTime() {
|
||||
return transaction.getStartNanoTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -183,11 +183,6 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
transaction.registerDeleteBean(hash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterDeleteBean(Integer hash) {
|
||||
transaction.unregisterDeleteBean(hash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegisteredDeleteBean(Integer hash) {
|
||||
return transaction.isRegisteredDeleteBean(hash);
|
||||
@@ -330,7 +325,7 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
|
||||
@Override
|
||||
public void depth(int diff) {
|
||||
transaction.depth();
|
||||
transaction.depth(diff);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -30,8 +30,6 @@ public class TransactionEvent implements Serializable {
|
||||
*/
|
||||
private final transient boolean local;
|
||||
|
||||
private final long startMillis;
|
||||
|
||||
private TransactionEventTable eventTables;
|
||||
|
||||
private transient List<PersistRequestBean<?>> listenerNotify;
|
||||
@@ -43,8 +41,7 @@ public class TransactionEvent implements Serializable {
|
||||
/**
|
||||
* Create the TransactionEvent, one per Transaction.
|
||||
*/
|
||||
public TransactionEvent(long startMillis) {
|
||||
this.startMillis = startMillis;
|
||||
public TransactionEvent() {
|
||||
this.local = true;
|
||||
}
|
||||
|
||||
@@ -119,7 +116,7 @@ public class TransactionEvent implements Serializable {
|
||||
}
|
||||
|
||||
if (changeSet == null) {
|
||||
changeSet = new CacheChangeSet(manager.clockNowMillis());
|
||||
changeSet = new CacheChangeSet();
|
||||
}
|
||||
if (eventTables != null && !eventTables.isEmpty()) {
|
||||
// notify cache with table based changes
|
||||
@@ -154,7 +151,7 @@ public class TransactionEvent implements Serializable {
|
||||
*/
|
||||
public CacheChangeSet obtainCacheChangeSet() {
|
||||
if (changeSet == null) {
|
||||
changeSet = new CacheChangeSet(startMillis);
|
||||
changeSet = new CacheChangeSet();
|
||||
}
|
||||
return changeSet;
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ public class DdlGenerator {
|
||||
runScript(connection, false, createAllContent, getCreateFileName());
|
||||
|
||||
if (extraDdl && jaxbPresent) {
|
||||
if (currentModel.isTablePartitioning()) {
|
||||
if (currentModel().isTablePartitioning()) {
|
||||
String extraPartitioning = ExtraDdlXmlReader.buildPartitioning(server.getDatabasePlatform().getName());
|
||||
if (extraPartitioning != null && !extraPartitioning.isEmpty()) {
|
||||
runScript(connection, false, extraPartitioning, "builtin-partitioning-ddl");
|
||||
@@ -201,7 +201,7 @@ public class DdlGenerator {
|
||||
runScript(connection, false, extraApply, "extra-ddl");
|
||||
}
|
||||
|
||||
if (currentModel.isTablePartitioning()) {
|
||||
if (currentModel().isTablePartitioning()) {
|
||||
checkInitialTablePartitions(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import io.ebean.config.DbMigrationConfig;
|
||||
import io.ebean.config.PlatformConfig;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.clickhouse.ClickHousePlatform;
|
||||
import io.ebean.config.dbplatform.cockroach.CockroachPlatform;
|
||||
import io.ebean.config.dbplatform.db2.DB2Platform;
|
||||
import io.ebean.config.dbplatform.h2.H2Platform;
|
||||
import io.ebean.config.dbplatform.hana.HanaPlatform;
|
||||
@@ -20,6 +22,7 @@ import io.ebean.config.dbplatform.sqlite.SQLitePlatform;
|
||||
import io.ebean.config.dbplatform.sqlserver.SqlServer16Platform;
|
||||
import io.ebean.config.dbplatform.sqlserver.SqlServer17Platform;
|
||||
import io.ebean.dbmigration.DbMigration;
|
||||
import io.ebean.migration.MigrationVersion;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
|
||||
import io.ebeaninternal.dbmigration.migration.Migration;
|
||||
@@ -27,7 +30,6 @@ import io.ebeaninternal.dbmigration.migrationreader.MigrationXmlWriter;
|
||||
import io.ebeaninternal.dbmigration.model.CurrentModel;
|
||||
import io.ebeaninternal.dbmigration.model.MConfiguration;
|
||||
import io.ebeaninternal.dbmigration.model.MigrationModel;
|
||||
import io.ebeaninternal.dbmigration.model.MigrationVersion;
|
||||
import io.ebeaninternal.dbmigration.model.ModelContainer;
|
||||
import io.ebeaninternal.dbmigration.model.ModelDiff;
|
||||
import io.ebeaninternal.dbmigration.model.PlatformDdlWriter;
|
||||
@@ -802,6 +804,11 @@ public class DefaultDbMigration implements DbMigration {
|
||||
return new SQLitePlatform();
|
||||
case HANA:
|
||||
return new HanaPlatform();
|
||||
case COCKROACH:
|
||||
return new CockroachPlatform();
|
||||
case CLICKHOUSE:
|
||||
return new ClickHousePlatform();
|
||||
|
||||
case GENERIC:
|
||||
return new DatabasePlatform();
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package io.ebeaninternal.dbmigration;
|
||||
|
||||
import io.ebeaninternal.dbmigration.model.MigrationVersion;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.ebean.migration.MigrationVersion;
|
||||
|
||||
/**
|
||||
* Utility to determine the last sql migration version and next version.
|
||||
*/
|
||||
|
||||
@@ -19,6 +19,11 @@ public interface DdlBuffer {
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Append a statement allowing for null or empty statements.
|
||||
*/
|
||||
DdlBuffer appendStatement(String content) throws IOException;
|
||||
|
||||
/**
|
||||
* Append DDL content to the buffer.
|
||||
*/
|
||||
|
||||
+12
-1
@@ -38,6 +38,15 @@ public class BaseDdlBuffer implements DdlBuffer {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DdlBuffer appendStatement(String content) throws IOException {
|
||||
if (content != null && !content.isEmpty()) {
|
||||
writer.append(content);
|
||||
endOfStatement();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DdlBuffer append(String content) throws IOException {
|
||||
writer.append(content);
|
||||
@@ -73,7 +82,9 @@ public class BaseDdlBuffer implements DdlBuffer {
|
||||
*/
|
||||
@Override
|
||||
public DdlBuffer end() throws IOException {
|
||||
writer.append("\n");
|
||||
if (!isEmpty()) {
|
||||
writer.append("\n");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
+62
-143
@@ -37,6 +37,8 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.ebeaninternal.dbmigration.ddlgeneration.platform.SplitColumns.split;
|
||||
|
||||
/**
|
||||
* Base implementation for 'create table' and 'alter table' statements.
|
||||
*/
|
||||
@@ -148,8 +150,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
buffer.append("-- NOTE: table has @History - special migration may be necessary").newLine();
|
||||
}
|
||||
for (String ddlScript : before) {
|
||||
buffer.append(translate(ddlScript, tableName, columnName, this.defaultValue));
|
||||
buffer.endOfStatement();
|
||||
buffer.appendStatement(translate(ddlScript, tableName, columnName, this.defaultValue));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,8 +160,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
// here we run post migration scripts
|
||||
for (String ddlScript : after) {
|
||||
buffer.append(translate(ddlScript, tableName, columnName, defaultValue));
|
||||
buffer.endOfStatement();
|
||||
buffer.appendStatement(translate(ddlScript, tableName, columnName, defaultValue));
|
||||
}
|
||||
if (!after.isEmpty()) {
|
||||
buffer.end();
|
||||
@@ -262,7 +262,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
DdlBuffer apply = writer.apply();
|
||||
apply.append(platformDdl.getCreateTableCommandPrefix()).append(" ").append(tableName).append(" (");
|
||||
writeTableColumns(apply, columns, useIdentity);
|
||||
writeCheckConstraints(apply, createTable);
|
||||
writeUniqueConstraints(apply, createTable);
|
||||
writeCompoundUniqueConstraints(apply, createTable);
|
||||
if (!pk.isEmpty()) {
|
||||
@@ -276,6 +275,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
|
||||
apply.newLine().append(")");
|
||||
addTableStorageEngine(apply, createTable);
|
||||
addTableCommentInline(apply, createTable);
|
||||
if (partitionMode != null) {
|
||||
platformDdl.addTablePartition(apply, partitionMode, createTable.getPartitionColumn());
|
||||
@@ -316,12 +316,11 @@ public class BaseTableDdl implements TableDdl {
|
||||
private void addComments(DdlBuffer apply, CreateTable createTable) throws IOException {
|
||||
if (!platformDdl.isInlineComments()) {
|
||||
String tableComment = createTable.getComment();
|
||||
if (!StringHelper.isNull(tableComment)) {
|
||||
if (hasValue(tableComment)) {
|
||||
platformDdl.addTableComment(apply, createTable.getName(), tableComment);
|
||||
}
|
||||
|
||||
List<Column> columns = createTable.getColumn();
|
||||
for (Column column : columns) {
|
||||
for (Column column : createTable.getColumn()) {
|
||||
if (!StringHelper.isNull(column.getComment())) {
|
||||
platformDdl.addColumnComment(apply, createTable.getName(), column.getName(), column.getComment());
|
||||
}
|
||||
@@ -329,6 +328,15 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the table storage engine clause.
|
||||
*/
|
||||
private void addTableStorageEngine(DdlBuffer apply, CreateTable createTable) throws IOException {
|
||||
if (platformDdl.isIncludeStorageEngine()) {
|
||||
platformDdl.tableStorageEngine(apply, createTable.getStorageEngine());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the table comment inline with the create table statement.
|
||||
*/
|
||||
@@ -358,26 +366,17 @@ public class BaseTableDdl implements TableDdl {
|
||||
uqName = col.getUnique();
|
||||
}
|
||||
String[] columnNames = {col.getName()};
|
||||
write.apply()
|
||||
.append(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, Boolean.TRUE.equals(col.isNotnull()) ? null : columnNames))
|
||||
.endOfStatement();
|
||||
|
||||
write.dropAllForeignKeys()
|
||||
.append(platformDdl.dropIndex(uqName, tableName))
|
||||
.endOfStatement();
|
||||
write.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, Boolean.TRUE.equals(col.isNotnull()) ? null : columnNames));
|
||||
write.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(uqName, tableName));
|
||||
}
|
||||
|
||||
for (UniqueConstraint constraint : externalCompoundUnique) {
|
||||
String uqName = constraint.getName();
|
||||
String[] columnNames = SplitColumns.split(constraint.getColumnNames());
|
||||
String[] nullableColumns = SplitColumns.split(constraint.getNullableColumns());
|
||||
write.apply()
|
||||
.append(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, nullableColumns))
|
||||
.endOfStatement();
|
||||
String[] columnNames = split(constraint.getColumnNames());
|
||||
String[] nullableColumns = split(constraint.getNullableColumns());
|
||||
|
||||
write.dropAllForeignKeys()
|
||||
.append(platformDdl.dropIndex(uqName, tableName))
|
||||
.endOfStatement();
|
||||
write.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, nullableColumns));
|
||||
write.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(uqName, tableName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,9 +393,9 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
|
||||
String createSeq = platformDdl.createSequence(seqName, initial, allocate);
|
||||
if (createSeq != null) {
|
||||
if (hasValue(createSeq)) {
|
||||
writer.apply().append(createSeq).newLine();
|
||||
writer.dropAll().append(platformDdl.dropSequence(seqName)).endOfStatement();
|
||||
writer.dropAll().appendStatement(platformDdl.dropSequence(seqName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,8 +424,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void writeInlineCompoundForeignKeys(DdlWrite write, CreateTable createTable) throws IOException {
|
||||
|
||||
List<ForeignKey> foreignKey = createTable.getForeignKey();
|
||||
for (ForeignKey key : foreignKey) {
|
||||
for (ForeignKey key : createTable.getForeignKey()) {
|
||||
String fkConstraint = platformDdl.tableInlineForeignKey(new WriteForeignKey(null, key));
|
||||
write.apply().append(",").newLine().append(" ").append(fkConstraint);
|
||||
}
|
||||
@@ -434,12 +432,10 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void writeAddForeignKeys(DdlWrite write, CreateTable createTable) throws IOException {
|
||||
|
||||
String tableName = createTable.getName();
|
||||
List<Column> columns = createTable.getColumn();
|
||||
for (Column column : columns) {
|
||||
for (Column column : createTable.getColumn()) {
|
||||
String references = column.getReferences();
|
||||
if (hasValue(references)) {
|
||||
writeForeignKey(write, tableName, column);
|
||||
writeForeignKey(write, createTable.getName(), column);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,11 +444,8 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void writeAddCompoundForeignKeys(DdlWrite write, CreateTable createTable) throws IOException {
|
||||
|
||||
String tableName = createTable.getName();
|
||||
|
||||
List<ForeignKey> foreignKey = createTable.getForeignKey();
|
||||
for (ForeignKey key : foreignKey) {
|
||||
writeForeignKey(write, new WriteForeignKey(tableName, key));
|
||||
for (ForeignKey key : createTable.getForeignKey()) {
|
||||
writeForeignKey(write, new WriteForeignKey(createTable.getName(), key));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,19 +459,15 @@ public class BaseTableDdl implements TableDdl {
|
||||
String tableName = lowerTableName(request.table());
|
||||
if (request.indexName() != null) {
|
||||
// no matching unique constraint so add the index
|
||||
fkeyBuffer.append(platformDdl.createIndex(request.indexName(), tableName, request.cols())).endOfStatement();
|
||||
fkeyBuffer.appendStatement(platformDdl.createIndex(request.indexName(), tableName, request.cols()));
|
||||
}
|
||||
|
||||
alterTableAddForeignKey(fkeyBuffer, request);
|
||||
|
||||
fkeyBuffer.end();
|
||||
|
||||
write.dropAllForeignKeys()
|
||||
.append(platformDdl.alterTableDropForeignKey(tableName, request.fkName())).endOfStatement();
|
||||
|
||||
if (request.indexName() != null) {
|
||||
write.dropAllForeignKeys()
|
||||
.append(platformDdl.dropIndex(request.indexName(), tableName)).endOfStatement();
|
||||
write.dropAllForeignKeys().appendStatement(platformDdl.alterTableDropForeignKey(tableName, request.fkName()));
|
||||
if (hasValue(request.indexName())) {
|
||||
write.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(request.indexName(), tableName));
|
||||
}
|
||||
|
||||
write.dropAllForeignKeys().end();
|
||||
@@ -486,10 +475,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void alterTableAddForeignKey(DdlBuffer buffer, WriteForeignKey request) throws IOException {
|
||||
|
||||
String fkConstraint = platformDdl.alterTableAddForeignKey(request);
|
||||
if (fkConstraint != null && !fkConstraint.isEmpty()) {
|
||||
buffer.append(fkConstraint).endOfStatement();
|
||||
}
|
||||
buffer.appendStatement(platformDdl.alterTableAddForeignKey(request));
|
||||
}
|
||||
|
||||
protected void appendColumns(String[] columns, DdlBuffer buffer) throws IOException {
|
||||
@@ -503,13 +489,12 @@ public class BaseTableDdl implements TableDdl {
|
||||
buffer.append(")");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add 'drop table' statement to the buffer.
|
||||
*/
|
||||
protected void dropTable(DdlBuffer buffer, String tableName) throws IOException {
|
||||
|
||||
buffer.append(platformDdl.dropTable(tableName)).endOfStatement();
|
||||
buffer.appendStatement(platformDdl.dropTable(tableName));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -517,43 +502,16 @@ public class BaseTableDdl implements TableDdl {
|
||||
*/
|
||||
protected void dropSequence(DdlBuffer buffer, String sequenceName) throws IOException {
|
||||
|
||||
buffer.append(platformDdl.dropSequence(sequenceName)).endOfStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write all the check constraints.
|
||||
*/
|
||||
protected void writeCheckConstraints(DdlBuffer apply, CreateTable createTable) throws IOException {
|
||||
|
||||
List<Column> columns = createTable.getColumn();
|
||||
for (Column column : columns) {
|
||||
String checkConstraint = column.getCheckConstraint();
|
||||
if (hasValue(checkConstraint)) {
|
||||
writeCheckConstraint(apply, column, checkConstraint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a check constraint.
|
||||
*/
|
||||
protected void writeCheckConstraint(DdlBuffer buffer, Column column, String checkConstraint) throws IOException {
|
||||
|
||||
String ckName = column.getCheckConstraintName();
|
||||
|
||||
buffer.append(",").newLine();
|
||||
buffer.append(" constraint ").append(ckName);
|
||||
buffer.append(" ").append(checkConstraint);
|
||||
buffer.appendStatement(platformDdl.dropSequence(sequenceName));
|
||||
}
|
||||
|
||||
protected void writeCompoundUniqueConstraints(DdlBuffer apply, CreateTable createTable) throws IOException {
|
||||
|
||||
List<UniqueConstraint> uniqueConstraints = createTable.getUniqueConstraint();
|
||||
boolean inlineUniqueWhenNull = platformDdl.isInlineUniqueWhenNullable();
|
||||
for (UniqueConstraint uniqueConstraint : uniqueConstraints) {
|
||||
for (UniqueConstraint uniqueConstraint : createTable.getUniqueConstraint()) {
|
||||
if (inlineUniqueWhenNull) {
|
||||
String uqName = uniqueConstraint.getName();
|
||||
String[] columns = SplitColumns.split(uniqueConstraint.getColumnNames());
|
||||
String[] columns = split(uniqueConstraint.getColumnNames());
|
||||
apply.append(",").newLine();
|
||||
apply.append(" constraint ").append(uqName).append(" unique");
|
||||
appendColumns(columns, apply);
|
||||
@@ -650,54 +608,34 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
@Override
|
||||
public void generate(DdlWrite writer, CreateIndex createIndex) throws IOException {
|
||||
|
||||
String[] cols = SplitColumns.split(createIndex.getColumns());
|
||||
writer.apply()
|
||||
.append(platformDdl.createIndex(createIndex.getIndexName(), createIndex.getTableName(), cols))
|
||||
.endOfStatement();
|
||||
|
||||
writer.dropAll()
|
||||
.append(platformDdl.dropIndex(createIndex.getIndexName(), createIndex.getTableName()))
|
||||
.endOfStatement();
|
||||
writer.apply().appendStatement(platformDdl.createIndex(createIndex.getIndexName(), createIndex.getTableName(), split(createIndex.getColumns())));
|
||||
writer.dropAll().appendStatement(platformDdl.dropIndex(createIndex.getIndexName(), createIndex.getTableName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(DdlWrite writer, DropIndex dropIndex) throws IOException {
|
||||
|
||||
writer.apply()
|
||||
.append(platformDdl.dropIndex(dropIndex.getIndexName(), dropIndex.getTableName()))
|
||||
.endOfStatement();
|
||||
writer.apply().appendStatement(platformDdl.dropIndex(dropIndex.getIndexName(), dropIndex.getTableName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(DdlWrite writer, AddUniqueConstraint constraint) throws IOException {
|
||||
|
||||
if (DdlHelp.isDropConstraint(constraint.getColumnNames())) {
|
||||
String ddl = platformDdl.alterTableDropUniqueConstraint(constraint.getTableName(), constraint.getConstraintName());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropUniqueConstraint(constraint.getTableName(), constraint.getConstraintName()));
|
||||
|
||||
} else {
|
||||
String[] cols = SplitColumns.split(constraint.getColumnNames());
|
||||
String[] nullableColumns = SplitColumns.split(constraint.getNullableColumns());
|
||||
String ddl = platformDdl.alterTableAddUniqueConstraint(constraint.getTableName(), constraint.getConstraintName(), cols, nullableColumns);
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
String[] cols = split(constraint.getColumnNames());
|
||||
String[] nullableColumns = split(constraint.getNullableColumns());
|
||||
writer.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(constraint.getTableName(), constraint.getConstraintName(), cols, nullableColumns));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(DdlWrite writer, AlterForeignKey alterForeignKey) throws IOException {
|
||||
if (DdlHelp.isDropForeignKey(alterForeignKey.getColumnNames())) {
|
||||
String ddl = platformDdl.alterTableDropForeignKey(alterForeignKey.getTableName(), alterForeignKey.getName());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropForeignKey(alterForeignKey.getTableName(), alterForeignKey.getName()));
|
||||
} else {
|
||||
String ddl = platformDdl.alterTableAddForeignKey(new WriteForeignKey(alterForeignKey));
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterTableAddForeignKey(new WriteForeignKey(alterForeignKey)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -902,12 +840,13 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
/**
|
||||
* This is mysql specific - alter all the base attributes of the column together.
|
||||
* Will be called, if there is a type, dbdefault or notnull change.
|
||||
*/
|
||||
protected void alterColumnBaseAttributes(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterColumnBaseAttributes(alter);
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
writer.apply().appendStatement(ddl);
|
||||
|
||||
if (isTrue(alter.isWithHistory()) && alter.getType() != null && historySupport == HistorySupport.TRIGGER_BASED) {
|
||||
// mysql and sql server column type change allowing nulls in the history table column
|
||||
@@ -919,53 +858,41 @@ public class BaseTableDdl implements TableDdl {
|
||||
String histColumnDdl = platformDdl.alterColumnBaseAttributes(alterHistoryColumn);
|
||||
|
||||
// write the apply to history table
|
||||
writer.apply().append(histColumnDdl).endOfStatement();
|
||||
writer.apply().appendStatement(histColumnDdl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void alterColumnDefaultValue(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterColumnDefaultValue(alter.getTableName(), alter.getColumnName(), alter.getDefaultValue());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterColumnDefaultValue(alter.getTableName(), alter.getColumnName(), alter.getDefaultValue()));
|
||||
}
|
||||
|
||||
protected void dropCheckConstraint(DdlWrite writer, AlterColumn alter, String constraintName) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterTableDropConstraint(alter.getTableName(), constraintName);
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropConstraint(alter.getTableName(), constraintName));
|
||||
}
|
||||
|
||||
protected void addCheckConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterTableAddCheckConstraint(alter.getTableName(), alter.getCheckConstraintName(), alter.getCheckConstraint());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterTableAddCheckConstraint(alter.getTableName(), alter.getCheckConstraintName(), alter.getCheckConstraint()));
|
||||
}
|
||||
|
||||
protected void alterColumnNotnull(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterColumnNotnull(alter.getTableName(), alter.getColumnName(), alter.isNotnull());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterColumnNotnull(alter.getTableName(), alter.getColumnName(), alter.isNotnull()));
|
||||
}
|
||||
|
||||
protected void alterColumnType(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterColumnType(alter.getTableName(), alter.getColumnName(), alter.getType());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
writer.apply().appendStatement(ddl);
|
||||
if (isTrue(alter.isWithHistory()) && historySupport == HistorySupport.TRIGGER_BASED) {
|
||||
regenerateHistoryTriggers(alter.getTableName(), HistoryTableUpdate.Change.ALTER, alter.getColumnName());
|
||||
// apply same type change to matching column in the history table
|
||||
ddl = platformDdl.alterColumnType(historyTable(alter.getTableName()), alter.getColumnName(), alter.getType());
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
writer.apply().appendStatement(ddl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -977,17 +904,13 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void alterColumnDropForeignKey(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
writer.apply()
|
||||
.append(platformDdl.alterTableDropForeignKey(alter.getTableName(), alter.getDropForeignKey()))
|
||||
.endOfStatement();
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropForeignKey(alter.getTableName(), alter.getDropForeignKey()));
|
||||
}
|
||||
|
||||
|
||||
protected void alterColumnDropUniqueConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
writer.apply()
|
||||
.append(platformDdl.alterTableDropUniqueConstraint(alter.getTableName(), alter.getDropUnique()))
|
||||
.endOfStatement();
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropUniqueConstraint(alter.getTableName(), alter.getDropUnique()));
|
||||
}
|
||||
|
||||
protected void alterColumnAddUniqueOneToOneConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
@@ -1004,13 +927,9 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
String[] cols = {alter.getColumnName()};
|
||||
boolean notNull = alter.isNotnull() != null ? alter.isNotnull() : Boolean.TRUE.equals(alter.isNotnull());
|
||||
writer.apply()
|
||||
.append(platformDdl.alterTableAddUniqueConstraint(alter.getTableName(), uqName, cols, notNull ? null : cols))
|
||||
.endOfStatement();
|
||||
writer.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(alter.getTableName(), uqName, cols, notNull ? null : cols));
|
||||
|
||||
writer.dropAllForeignKeys()
|
||||
.append(platformDdl.dropIndex(uqName, alter.getTableName()))
|
||||
.endOfStatement();
|
||||
writer.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(uqName, alter.getTableName()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Logical array type to ClickHouse array type conversion.
|
||||
*/
|
||||
class ClickHouseDbArray {
|
||||
|
||||
private static final Map<String, String> mapping = new HashMap<>();
|
||||
static {
|
||||
mapping.put("uuid[]", "Array(UUID)");
|
||||
mapping.put("varchar[]", "Array(String)");
|
||||
mapping.put("integer[]", "Array(UInt32)");
|
||||
mapping.put("bigint[]", "Array(UInt64)");
|
||||
}
|
||||
|
||||
/**
|
||||
* Covert the 'logical' array type to a native one (for Postgres and Cockroach).
|
||||
*/
|
||||
static String logicalToNative(String logicalArrayType) {
|
||||
int colonPos = logicalArrayType.indexOf(':');
|
||||
if (colonPos > -1) {
|
||||
logicalArrayType = logicalArrayType.substring(0, colonPos);
|
||||
}
|
||||
String clickHouseType = mapping.get(logicalArrayType);
|
||||
if (clickHouseType == null) {
|
||||
throw new IllegalStateException("No mapping for logical array type " + logicalArrayType);
|
||||
}
|
||||
return clickHouseType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ClickHouseDdl extends PlatformDdl {
|
||||
|
||||
private static final String LOG_TABLE = "ENGINE = Log()";
|
||||
|
||||
public ClickHouseDdl(DatabasePlatform platform) {
|
||||
super(platform);
|
||||
this.includeStorageEngine = true;
|
||||
this.identitySuffix = "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public DdlHandler createDdlHandler(ServerConfig serverConfig) {
|
||||
return new ClickHouseDdlHandler(serverConfig, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String convertArrayType(String logicalArrayType) {
|
||||
return ClickHouseDbArray.logicalToNative(logicalArrayType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an table storage engine to the create table statement.
|
||||
*/
|
||||
@Override
|
||||
public void tableStorageEngine(DdlBuffer apply, String storageEngine) throws IOException {
|
||||
if (storageEngine == null) {
|
||||
// default to Log() table but really should all be explicit (need arguments for MergeTree etc)
|
||||
storageEngine = LOG_TABLE;
|
||||
}
|
||||
apply.append(" ").append(storageEngine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String alterTableAddForeignKey(WriteForeignKey request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String alterTableDropForeignKey(String tableName, String fkName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tableInlineForeignKey(WriteForeignKey request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dropIndex(String indexName, String tableName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createIndex(String indexName, String tableName, String[] columns) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createCheckConstraint(String ckName, String checkConstraint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeColumnNotNull(DdlBuffer buffer) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTableComment(DdlBuffer apply, String tableName, String tableComment) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addColumnComment(DdlBuffer apply, String table, String column, String comment) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInlineComments() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.BaseDdlHandler;
|
||||
|
||||
public class ClickHouseDdlHandler extends BaseDdlHandler {
|
||||
|
||||
public ClickHouseDdlHandler(ServerConfig serverConfig, PlatformDdl platformDdl) {
|
||||
super(serverConfig, platformDdl, new ClickHouseTableDdl(serverConfig, platformDdl));
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import io.ebeaninternal.dbmigration.migration.CreateTable;
|
||||
|
||||
public class ClickHouseTableDdl extends BaseTableDdl {
|
||||
|
||||
public ClickHouseTableDdl(ServerConfig serverConfig, PlatformDdl platformDdl) {
|
||||
super(serverConfig, platformDdl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writePrimaryKeyConstraint(DdlBuffer buffer, String pkName, String[] pkColumns) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeCompoundUniqueConstraints(DdlBuffer apply, CreateTable createTable) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeUniqueConstraints(DdlBuffer apply, CreateTable createTable) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,10 +30,7 @@ public class HanaTableDdl extends BaseTableDdl {
|
||||
|
||||
@Override
|
||||
protected void alterColumnDefaultValue(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
String ddl = platformDdl.alterColumnBaseAttributes(alter);
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
// done in alterColumnBaseAttributes
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -14,6 +14,10 @@ import java.util.Collection;
|
||||
*/
|
||||
public class MySqlDdl extends PlatformDdl {
|
||||
|
||||
// check constraint support is disabled by default. See https://groups.google.com/forum/#!topic/ebean/luFN-2xBkUw
|
||||
// this flag is for compatibility. Use it with care.
|
||||
private static final boolean USE_CHECK_CONSTRAINT = Boolean.getBoolean("ebean.mysql.useCheckConstraint");
|
||||
|
||||
public MySqlDdl(DatabasePlatform platform) {
|
||||
super(platform);
|
||||
this.alterColumn = "modify";
|
||||
@@ -48,11 +52,40 @@ public class MySqlDdl extends PlatformDdl {
|
||||
buffer.append("CALL usp_ebean_drop_column('").append(tableName).append("', '").append(columnName).append("')").endOfStatement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createCheckConstraint(String ckName, String checkConstraint) {
|
||||
if (USE_CHECK_CONSTRAINT) {
|
||||
return super.createCheckConstraint(ckName, checkConstraint);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public String alterTableAddCheckConstraint(String tableName, String checkConstraintName, String checkConstraint) {
|
||||
if (USE_CHECK_CONSTRAINT) {
|
||||
return super.alterTableAddCheckConstraint(tableName, checkConstraintName, checkConstraint);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String alterTableDropConstraint(String tableName, String constraintName) {
|
||||
// drop constraint not supported in MySQL 5.7 and 8.0 but starting with MariaDB 10.2.1 CHECK is evaluated
|
||||
// TODO: Implement for MariaDB >= 10.2.1
|
||||
return null;
|
||||
// drop constraint not supported in MySQL 5.7 and 8.0 but starting with MariaDB
|
||||
// 10.2.1 CHECK is evaluated
|
||||
if (USE_CHECK_CONSTRAINT) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// statement for MySQL >= 8.0.16
|
||||
sb.append("/*!80016 alter table ").append(tableName);
|
||||
sb.append(" drop check ").append(constraintName).append(" */;\n");
|
||||
// statement for MariaDB >= 10.2.1
|
||||
sb.append("/*M!100201 ");
|
||||
sb.append(super.alterTableDropConstraint(tableName, constraintName));
|
||||
sb.append(" */");
|
||||
return sb.toString();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,7 +112,9 @@ public class MySqlDdl extends PlatformDdl {
|
||||
|
||||
@Override
|
||||
public String alterColumnBaseAttributes(AlterColumn alter) {
|
||||
if (DdlHelp.isDropDefault(alter.getDefaultValue())) {
|
||||
if (alter.getType() == null && alter.isNotnull() == null) {
|
||||
// No type change or notNull change
|
||||
// defaultValue change already handled in alterColumnDefaultValue
|
||||
return null;
|
||||
}
|
||||
String tableName = alter.getTableName();
|
||||
@@ -104,7 +139,6 @@ public class MySqlDdl extends PlatformDdl {
|
||||
}
|
||||
buffer.append(String.format(" comment '%s'", comment));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+71
-18
@@ -118,6 +118,8 @@ public class PlatformDdl {
|
||||
*/
|
||||
protected boolean inlineForeignKeys;
|
||||
|
||||
protected boolean includeStorageEngine;
|
||||
|
||||
protected final DbDefaultValue dbDefaultValue;
|
||||
|
||||
protected String fallbackArrayType = "varchar(1000)";
|
||||
@@ -191,6 +193,13 @@ public class PlatformDdl {
|
||||
return inlineComments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the platform includes storage engine clause.
|
||||
*/
|
||||
public boolean isIncludeStorageEngine() {
|
||||
return includeStorageEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if foreign key reference constraints need to inlined with create table.
|
||||
* Ideally we don't do this as then the constraints are not named. Do this for SQLite.
|
||||
@@ -204,10 +213,21 @@ public class PlatformDdl {
|
||||
*/
|
||||
public void writeTableColumns(DdlBuffer apply, List<Column> columns, boolean useIdentity) throws IOException {
|
||||
for (int i = 0; i < columns.size(); i++) {
|
||||
if (i > 0) {
|
||||
apply.append(",");
|
||||
}
|
||||
apply.newLine();
|
||||
writeColumnDefinition(apply, columns.get(i), useIdentity);
|
||||
if (i < columns.size() - 1) {
|
||||
apply.append(",");
|
||||
}
|
||||
|
||||
for (Column column : columns) {
|
||||
String checkConstraint = column.getCheckConstraint();
|
||||
if (hasValue(checkConstraint)) {
|
||||
checkConstraint = createCheckConstraint(maxConstraintName(column.getCheckConstraintName()), checkConstraint);
|
||||
if (hasValue(checkConstraint)) {
|
||||
apply.append(",").newLine();
|
||||
apply.append(checkConstraint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,13 +250,27 @@ public class PlatformDdl {
|
||||
}
|
||||
}
|
||||
if (isTrue(column.isNotnull()) || isTrue(column.isPrimaryKey())) {
|
||||
buffer.append(" not null");
|
||||
writeColumnNotNull(buffer);
|
||||
}
|
||||
|
||||
// add check constraints later as we really want to give them a nice name
|
||||
// so that the database can potentially provide a nice SQL error
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow for platform overriding (e.g. ClickHouse).
|
||||
*/
|
||||
protected void writeColumnNotNull(DdlBuffer buffer) throws IOException {
|
||||
buffer.append(" not null");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the check constraint.
|
||||
*/
|
||||
public String createCheckConstraint(String ckName, String checkConstraint) {
|
||||
return " constraint " + ckName + " " + checkConstraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the DB column default literal to platform specific.
|
||||
*/
|
||||
@@ -409,13 +443,17 @@ public class PlatformDdl {
|
||||
}
|
||||
|
||||
protected String translate(ConstraintMode mode) {
|
||||
switch(mode) {
|
||||
case SET_NULL: return "set null";
|
||||
case SET_DEFAULT: return "set default";
|
||||
case RESTRICT: return "restrict";
|
||||
case CASCADE: return "cascade";
|
||||
switch (mode) {
|
||||
case SET_NULL:
|
||||
return "set null";
|
||||
case SET_DEFAULT:
|
||||
return "set default";
|
||||
case RESTRICT:
|
||||
return "restrict";
|
||||
case CASCADE:
|
||||
return "cascade";
|
||||
default:
|
||||
throw new IllegalStateException("Unknown mode "+mode);
|
||||
throw new IllegalStateException("Unknown mode " + mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,16 +503,17 @@ public class PlatformDdl {
|
||||
|
||||
if (!onHistoryTable) {
|
||||
if (isTrue(column.isNotnull())) {
|
||||
buffer.append(" not null");
|
||||
writeColumnNotNull(buffer);
|
||||
}
|
||||
buffer.append(addColumnSuffix);
|
||||
buffer.endOfStatement();
|
||||
|
||||
// check constraints cannot be added in one statement for h2
|
||||
if (!StringHelper.isNull(column.getCheckConstraint())) {
|
||||
String ddl = alterTableAddCheckConstraint(tableName, column.getCheckConstraintName(),
|
||||
column.getCheckConstraint());
|
||||
buffer.append(ddl).endOfStatement();
|
||||
String ddl = alterTableAddCheckConstraint(tableName, column.getCheckConstraintName(), column.getCheckConstraint());
|
||||
if (hasValue(ddl)) {
|
||||
buffer.append(ddl).endOfStatement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buffer.append(addColumnSuffix);
|
||||
@@ -485,7 +524,7 @@ public class PlatformDdl {
|
||||
|
||||
public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException {
|
||||
buffer.append("alter table ").append(tableName).append(" ").append(dropColumn).append(" ").append(columnName)
|
||||
.append(dropColumnSuffix).endOfStatement();
|
||||
.append(dropColumnSuffix).endOfStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -560,7 +599,7 @@ public class PlatformDdl {
|
||||
}
|
||||
|
||||
protected void appendWithSpace(String content, StringBuilder buffer) {
|
||||
if (content != null && !content.isEmpty()) {
|
||||
if (hasValue(content)) {
|
||||
buffer.append(" ").append(content);
|
||||
}
|
||||
}
|
||||
@@ -593,6 +632,13 @@ public class PlatformDdl {
|
||||
return updateNullWithDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if null or trimmed string is empty.
|
||||
*/
|
||||
protected boolean hasValue(String value) {
|
||||
return value != null && !value.trim().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Null safe Boolean true test.
|
||||
*/
|
||||
@@ -607,6 +653,13 @@ public class PlatformDdl {
|
||||
// do nothing by default (MySql only)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an table storage engine to the create table statement.
|
||||
*/
|
||||
public void tableStorageEngine(DdlBuffer apply, String storageEngine) throws IOException {
|
||||
// do nothing by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Add table comment as a separate statement (from the create table statement).
|
||||
*/
|
||||
@@ -643,10 +696,10 @@ public class PlatformDdl {
|
||||
|
||||
/**
|
||||
* Shortens the given name to the maximum constraint name length of the platform in a deterministic way.
|
||||
*
|
||||
* <p>
|
||||
* First, all vowels are removed, If the string is still to long, 31 bits are taken from the hash code
|
||||
* of the string and base36 encoded (10 digits and 26 chars) string.
|
||||
*
|
||||
* <p>
|
||||
* As 36^6 > 31^2, the resulting string is never longer as 6 chars.
|
||||
*/
|
||||
protected String maxConstraintName(String name) {
|
||||
@@ -654,7 +707,7 @@ public class PlatformDdl {
|
||||
int hash = name.hashCode() & 0x7FFFFFFF;
|
||||
name = VowelRemover.trim(name, 4);
|
||||
if (name.length() > platform.getMaxConstraintNameLength()) {
|
||||
return name.substring(0, platform.getMaxConstraintNameLength()-7) + "_" + Integer.toString(hash, 36);
|
||||
return name.substring(0, platform.getMaxConstraintNameLength() - 7) + "_" + Integer.toString(hash, 36);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
|
||||
@@ -149,7 +149,9 @@ public class SqlServerDdl extends PlatformDdl {
|
||||
|
||||
@Override
|
||||
public String alterColumnBaseAttributes(AlterColumn alter) {
|
||||
if (DdlHelp.isDropDefault(alter.getDefaultValue())) {
|
||||
if (alter.getType() == null && alter.isNotnull() == null) {
|
||||
// No type change or notNull change
|
||||
// defaultValue change already handled in alterColumnDefaultValue
|
||||
return null;
|
||||
}
|
||||
String tableName = alter.getTableName();
|
||||
|
||||
@@ -77,6 +77,8 @@ public class CreateTable {
|
||||
protected BigInteger sequenceAllocate;
|
||||
@XmlAttribute(name = "pkName")
|
||||
protected String pkName;
|
||||
@XmlAttribute(name = "storageEngine")
|
||||
protected String storageEngine;
|
||||
@XmlAttribute(name = "tablespace")
|
||||
protected String tablespace;
|
||||
@XmlAttribute(name = "indexTablespace")
|
||||
@@ -365,6 +367,26 @@ public class CreateTable {
|
||||
this.pkName = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the storageEngine property.
|
||||
*
|
||||
* @return possible object is
|
||||
* {@link String }
|
||||
*/
|
||||
public String getStorageEngine() {
|
||||
return storageEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the storageEngine property.
|
||||
*
|
||||
* @param value allowed object is
|
||||
* {@link String }
|
||||
*/
|
||||
public void setStorageEngine(String value) {
|
||||
this.storageEngine = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the tablespace property.
|
||||
*
|
||||
|
||||
@@ -67,14 +67,14 @@ public class CurrentModel {
|
||||
* Return true if the model contains tables that are partitioned.
|
||||
*/
|
||||
public boolean isTablePartitioning() {
|
||||
return model.isTablePartitioning();
|
||||
return read().isTablePartitioning();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the tables that have partitioning.
|
||||
*/
|
||||
public List<MTable> getPartitionedTables() {
|
||||
return model.getPartitionedTables();
|
||||
return read().getPartitionedTables();
|
||||
}
|
||||
|
||||
private static DbConstraintNaming.MaxLength maxLength(SpiEbeanServer server, DbConstraintNaming naming) {
|
||||
|
||||
@@ -79,6 +79,8 @@ public class MTable {
|
||||
*/
|
||||
private String tablespace;
|
||||
|
||||
private String storageEngine;
|
||||
|
||||
/**
|
||||
* Tablespace to use for indexes on this table.
|
||||
*/
|
||||
@@ -159,6 +161,7 @@ public class MTable {
|
||||
this.name = createTable.getName();
|
||||
this.pkName = createTable.getPkName();
|
||||
this.comment = createTable.getComment();
|
||||
this.storageEngine = createTable.getStorageEngine();
|
||||
this.tablespace = createTable.getTablespace();
|
||||
this.indexTablespace = createTable.getIndexTablespace();
|
||||
this.withHistory = Boolean.TRUE.equals(createTable.isWithHistory());
|
||||
@@ -244,6 +247,7 @@ public class MTable {
|
||||
createTable.setPartitionMode(partitionMeta.getMode().name());
|
||||
createTable.setPartitionColumn(partitionMeta.getProperty());
|
||||
}
|
||||
createTable.setStorageEngine(storageEngine);
|
||||
createTable.setTablespace(tablespace);
|
||||
createTable.setIndexTablespace(indexTablespace);
|
||||
createTable.setSequenceName(sequenceName);
|
||||
@@ -452,6 +456,10 @@ public class MTable {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
public void setStorageEngine(String storageEngine) {
|
||||
this.storageEngine = storageEngine;
|
||||
}
|
||||
|
||||
public String getTablespace() {
|
||||
return tablespace;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package io.ebeaninternal.dbmigration.model;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.ebean.migration.MigrationVersion;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.dbmigration.model;
|
||||
|
||||
import io.ebean.migration.MigrationVersion;
|
||||
import io.ebeaninternal.dbmigration.migration.Migration;
|
||||
import io.ebeaninternal.dbmigration.migrationreader.MigrationXmlReader;
|
||||
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
package io.ebeaninternal.dbmigration.model;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* The version of a migration used so that migrations are processed in order.
|
||||
*/
|
||||
public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
|
||||
private static final Pattern SECTION_SPLITTER = Pattern.compile("[\\.-]");
|
||||
|
||||
private static final int[] REPEAT_ORDERING = {Integer.MAX_VALUE};
|
||||
|
||||
private static final boolean[] REPEAT_UNDERSCORES = {false};
|
||||
|
||||
/**
|
||||
* The raw version text.
|
||||
*/
|
||||
private final String raw;
|
||||
|
||||
/**
|
||||
* The ordering parts.
|
||||
*/
|
||||
private final int[] ordering;
|
||||
|
||||
private final boolean[] underscores;
|
||||
|
||||
private final String comment;
|
||||
|
||||
/**
|
||||
* Construct for "repeatable" version.
|
||||
*/
|
||||
private MigrationVersion(String raw, String comment) {
|
||||
this.raw = raw;
|
||||
this.comment = comment;
|
||||
this.ordering = REPEAT_ORDERING;
|
||||
this.underscores = REPEAT_UNDERSCORES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for "normal" version.
|
||||
*/
|
||||
private MigrationVersion(String raw, int[] ordering, boolean[] underscores, String comment) {
|
||||
this.raw = raw;
|
||||
this.ordering = ordering;
|
||||
this.underscores = underscores;
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a "repeatable" version.
|
||||
*/
|
||||
public boolean isRepeatable() {
|
||||
// Clarification: The comparison here is intended to compare object references and not the content of the arrays.
|
||||
// This is a kind of shortcut to see if some configuration is in place that results in the use of something other than the default.
|
||||
return ordering == REPEAT_ORDERING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full version.
|
||||
*/
|
||||
public String getFull() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the version comment.
|
||||
*/
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the version in raw form.
|
||||
*/
|
||||
public String getRaw() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the trimmed version excluding version comment and un-parsable string.
|
||||
*/
|
||||
public String asString() {
|
||||
return formattedVersion(false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the trimmed version with any underscores replaced with '.'
|
||||
*/
|
||||
public String normalised() {
|
||||
return formattedVersion(true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next version based on this version.
|
||||
*/
|
||||
public String nextVersion() {
|
||||
return formattedVersion(false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version part of the string.
|
||||
* <p>
|
||||
* Normalised means always use '.' delimiters (no underscores).
|
||||
* NextVersion means bump/increase the last version number by 1.
|
||||
*/
|
||||
private String formattedVersion(boolean normalised, boolean nextVersion) {
|
||||
|
||||
// Clarification: The comparison here is intended to compare object references and not the content of the arrays.
|
||||
// This is a kind of shortcut to see if some configuration is in place that results in the use of something other than the default.
|
||||
if (ordering == REPEAT_ORDERING) {
|
||||
return "R";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < ordering.length; i++) {
|
||||
if (i < ordering.length - 1) {
|
||||
sb.append(ordering[i]);
|
||||
if (normalised) {
|
||||
sb.append('.');
|
||||
} else {
|
||||
sb.append(underscores[i] ? '_' : '.');
|
||||
}
|
||||
} else {
|
||||
sb.append((nextVersion) ? ordering[i] + 1 : ordering[i]);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(MigrationVersion other) {
|
||||
|
||||
int otherLength = other.ordering.length;
|
||||
for (int i = 0; i < ordering.length; i++) {
|
||||
if (i >= otherLength) {
|
||||
// considered greater
|
||||
return 1;
|
||||
}
|
||||
if (ordering[i] != other.ordering[i]) {
|
||||
return (ordering[i] > other.ordering[i]) ? 1 : -1;
|
||||
}
|
||||
}
|
||||
return comment.compareTo(other.comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the raw version string and just return the leading version number;
|
||||
*/
|
||||
public static String trim(String raw) {
|
||||
return parse(raw).asString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the raw version string into a MigrationVersion.
|
||||
*/
|
||||
public static MigrationVersion parse(String raw) {
|
||||
|
||||
if (raw.startsWith("V") || raw.startsWith("v")) {
|
||||
raw = raw.substring(1);
|
||||
}
|
||||
|
||||
String comment = "";
|
||||
String value = raw;
|
||||
int commentStart = raw.indexOf("__");
|
||||
if (commentStart > -1) {
|
||||
// trim off the trailing comment
|
||||
comment = raw.substring(commentStart + 2);
|
||||
value = value.substring(0, commentStart);
|
||||
}
|
||||
|
||||
value = value.replace('_', '.');
|
||||
|
||||
String[] sections = SECTION_SPLITTER.split(value);
|
||||
|
||||
if ("r".equalsIgnoreCase(sections[0])) {
|
||||
// a "repeatable" version (does not have a version number)
|
||||
return new MigrationVersion(raw, comment);
|
||||
}
|
||||
|
||||
boolean[] underscores = new boolean[sections.length];
|
||||
int[] ordering = new int[sections.length];
|
||||
|
||||
int delimiterPos = 0;
|
||||
int stopIndex = 0;
|
||||
for (int i = 0; i < sections.length; i++) {
|
||||
try {
|
||||
ordering[i] = Integer.parseInt(sections[i]);
|
||||
stopIndex++;
|
||||
|
||||
delimiterPos += sections[i].length();
|
||||
underscores[i] = (delimiterPos < raw.length() - 1 && raw.charAt(delimiterPos) == '_');
|
||||
delimiterPos++;
|
||||
} catch (NumberFormatException e) {
|
||||
// stop parsing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int[] actualOrder = Arrays.copyOf(ordering, stopIndex);
|
||||
boolean[] actualUnderscores = Arrays.copyOf(underscores, stopIndex);
|
||||
|
||||
return new MigrationVersion(raw, actualOrder, actualUnderscores, comment);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.dbmigration.model;
|
||||
|
||||
import io.ebean.migration.MigrationVersion;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.DdlHelp;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.SplitColumns;
|
||||
import io.ebeaninternal.dbmigration.migration.AddColumn;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.dbmigration.model;
|
||||
|
||||
import io.ebean.migration.MigrationVersion;
|
||||
import io.ebeaninternal.dbmigration.migration.ChangeSet;
|
||||
import io.ebeaninternal.dbmigration.migration.ChangeSetType;
|
||||
import io.ebeaninternal.dbmigration.migration.DropColumn;
|
||||
|
||||
@@ -36,6 +36,7 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
|
||||
}
|
||||
|
||||
MTable table = new MTable(descriptor.getBaseTable());
|
||||
table.setStorageEngine(descriptor.getStorageEngine());
|
||||
table.setPartitionMeta(descriptor.getPartitionMeta());
|
||||
table.setComment(descriptor.getDbComment());
|
||||
if (descriptor.isHistorySupport()) {
|
||||
|
||||
@@ -36,14 +36,18 @@ public class ModifyAwareList<E> implements List<E>, ModifyAwareOwner {
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof ModifyAwareList)) return false;
|
||||
ModifyAwareList<?> that = (ModifyAwareList<?>) o;
|
||||
return Objects.equals(list, that.list);
|
||||
if (o instanceof ModifyAwareList) {
|
||||
ModifyAwareList<?> that = (ModifyAwareList<?>) o;
|
||||
return Objects.equals(list, that.list);
|
||||
}
|
||||
if (!(o instanceof List)) return false;
|
||||
List<?> that = (List<?>) o;
|
||||
return Objects.equals(list, that);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(list);
|
||||
return list.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -38,14 +38,18 @@ public class ModifyAwareMap<K, V> implements Map<K, V>, ModifyAwareOwner {
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof ModifyAwareMap)) return false;
|
||||
ModifyAwareMap<?, ?> that = (ModifyAwareMap<?, ?>) o;
|
||||
return Objects.equals(map, that.map);
|
||||
if (o instanceof ModifyAwareMap) {
|
||||
ModifyAwareMap<?,?> that = (ModifyAwareMap<?,?>) o;
|
||||
return Objects.equals(map, that.map);
|
||||
}
|
||||
if (!(o instanceof Map)) return false;
|
||||
Map<?,?> that = (Map<?,?>) o;
|
||||
return Objects.equals(map, that);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(map);
|
||||
return map.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -55,14 +55,18 @@ public class ModifyAwareSet<E> implements Set<E>, ModifyAwareOwner {
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof ModifyAwareSet)) return false;
|
||||
ModifyAwareSet<?> that = (ModifyAwareSet<?>) o;
|
||||
return Objects.equals(set, that.set);
|
||||
if (o instanceof ModifyAwareSet) {
|
||||
ModifyAwareSet<?> that = (ModifyAwareSet<?>) o;
|
||||
return Objects.equals(set, that.set);
|
||||
}
|
||||
if (!(o instanceof Set)) return false;
|
||||
Set<?> that = (Set<?>) o;
|
||||
return Objects.equals(set, that);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(set);
|
||||
return set.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -41,7 +41,7 @@ public class BaseQueryTuner {
|
||||
*/
|
||||
private final boolean skipAll;
|
||||
|
||||
public BaseQueryTuner(AutoTuneConfig config, SpiEbeanServer server, ProfilingListener profilingListener) {
|
||||
BaseQueryTuner(AutoTuneConfig config, SpiEbeanServer server, ProfilingListener profilingListener) {
|
||||
this.server = server;
|
||||
this.profilingListener = profilingListener;
|
||||
this.mode = config.getMode();
|
||||
@@ -84,12 +84,17 @@ public class BaseQueryTuner {
|
||||
/**
|
||||
* Auto tune the query and enable profiling.
|
||||
*/
|
||||
public boolean tuneQuery(SpiQuery<?> query) {
|
||||
boolean tuneQuery(SpiQuery<?> query) {
|
||||
|
||||
if (skipAll || !tunableQuery(query)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (query.getProfilingListener() != null) {
|
||||
// profiling secondary query
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!useTuning(query)) {
|
||||
if (profiling) {
|
||||
profiling(query, server.createCallStack());
|
||||
@@ -138,6 +143,7 @@ public class BaseQueryTuner {
|
||||
switch (type) {
|
||||
case COUNT:
|
||||
case ID_LIST:
|
||||
case UPDATE:
|
||||
case DELETE:
|
||||
case SUBQUERY:
|
||||
return false;
|
||||
|
||||
@@ -103,8 +103,7 @@ public class ProfileManager implements ProfilingListener {
|
||||
|
||||
private ProfileOrigin getProfileOrigin(ObjectGraphOrigin originQueryPoint) {
|
||||
synchronized (monitor) {
|
||||
ProfileOrigin stats = profileMap.computeIfAbsent(originQueryPoint.getKey(), k -> new ProfileOrigin(originQueryPoint, queryTuningAddVersion, profilingBase, profilingRate));
|
||||
return stats;
|
||||
return profileMap.computeIfAbsent(originQueryPoint.getKey(), k -> new ProfileOrigin(originQueryPoint, queryTuningAddVersion, profilingBase, profilingRate));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ public class ProfileOrigin {
|
||||
}
|
||||
}
|
||||
|
||||
private OrmQueryDetail buildDetail(BeanDescriptor<?> rootDesc) {
|
||||
OrmQueryDetail buildDetail(BeanDescriptor<?> rootDesc) {
|
||||
|
||||
PathProperties pathProps = new PathProperties();
|
||||
for (ProfileOriginNodeUsage statsNode : nodeUsageMap.values()) {
|
||||
@@ -107,8 +107,7 @@ public class ProfileOrigin {
|
||||
|
||||
OrmQueryDetail detail = new OrmQueryDetail();
|
||||
|
||||
Collection<Props> pathProperties = pathProps.getPathProps();
|
||||
for (Props props : pathProperties) {
|
||||
for (Props props : pathProps.getPathProps()) {
|
||||
if (!props.isEmpty()) {
|
||||
detail.fetch(props.getPath(), props.getPropertiesAsString(), null);
|
||||
}
|
||||
@@ -151,12 +150,8 @@ public class ProfileOrigin {
|
||||
* Collect the usage information for from a instance for this node.
|
||||
*/
|
||||
public void collectUsageInfo(NodeUsageCollector profile) {
|
||||
|
||||
//logger.info("COLLECT USAGE {}", profile.toString());
|
||||
|
||||
if (!profile.isEmpty()) {
|
||||
ProfileOriginNodeUsage nodeStats = getNodeStats(profile.getNode().getPath());
|
||||
nodeStats.collectUsageInfo(profile);
|
||||
getNodeStats(profile.getNode().getPath()).collectUsageInfo(profile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,23 +55,27 @@ public class ProfileOriginNodeUsage {
|
||||
}
|
||||
}
|
||||
|
||||
BeanProperty toOneIdProperty = null;
|
||||
boolean addedToPath = false;
|
||||
|
||||
for (String propName : aggregateUsed) {
|
||||
BeanProperty beanProp = desc.findPropertyFromPath(propName);
|
||||
if (beanProp == null) {
|
||||
logger.warn("AutoTune: Can't find property[" + propName + "] for " + desc.getName());
|
||||
|
||||
} else {
|
||||
if (beanProp instanceof BeanPropertyAssoc<?>) {
|
||||
BeanPropertyAssoc<?> assocProp = (BeanPropertyAssoc<?>) beanProp;
|
||||
String targetIdProp = assocProp.getTargetIdProperty();
|
||||
String manyPath = SplitName.add(path, assocProp.getName());
|
||||
pathProps.addToPath(manyPath, targetIdProp);
|
||||
if (beanProp.isId()) {
|
||||
// remember and maybe add ToOne property to parent path
|
||||
toOneIdProperty = beanProp;
|
||||
} else if (beanProp instanceof BeanPropertyAssoc<?>) {
|
||||
// intentionally skip
|
||||
} else {
|
||||
//noinspection StatementWithEmptyBody
|
||||
if (beanProp.isLob() && !beanProp.isFetchEager()) {
|
||||
// AutoTune will not include Lob's marked FetchLazy
|
||||
// (which is the default for Lob's so typical).
|
||||
} else {
|
||||
addedToPath = true;
|
||||
pathProps.addToPath(path, beanProp.getName());
|
||||
}
|
||||
}
|
||||
@@ -81,9 +85,16 @@ public class ProfileOriginNodeUsage {
|
||||
if ((modified || addVersionProperty) && desc != null) {
|
||||
BeanProperty versionProp = desc.getVersionProperty();
|
||||
if (versionProp != null) {
|
||||
addedToPath = true;
|
||||
pathProps.addToPath(path, versionProp.getName());
|
||||
}
|
||||
}
|
||||
|
||||
if (toOneIdProperty != null && !addedToPath) {
|
||||
// add ToOne property to parent path
|
||||
ElPropertyValue assocOne = rootDesc.getElGetValue(path);
|
||||
pathProps.addToPath(SplitName.parent(path), assocOne.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,13 +28,10 @@ public class CacheChangeSet {
|
||||
|
||||
private final Map<ManyKey, ManyChange> manyChangeMap = new HashMap<>();
|
||||
|
||||
private final long modificationTimestamp;
|
||||
|
||||
/**
|
||||
* Construct specifying if we also need to process invalidation for entities based on views.
|
||||
*/
|
||||
public CacheChangeSet(long modificationTimestamp) {
|
||||
this.modificationTimestamp = modificationTimestamp;
|
||||
public CacheChangeSet() {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,6 +83,7 @@ public class CacheChangeSet {
|
||||
*/
|
||||
public void addClearQuery(BeanDescriptor<?> descriptor) {
|
||||
queryCaches.add(descriptor);
|
||||
touchedTables.add(descriptor.getBaseTable());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,13 +170,6 @@ public class CacheChangeSet {
|
||||
return manyChangeMap.computeIfAbsent(key, ManyChange::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the modification timestamp for these changes.
|
||||
*/
|
||||
public long modificationTimestamp() {
|
||||
return modificationTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes for a specific many property.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -144,15 +144,13 @@ public abstract class AbstractSqlQueryRequest {
|
||||
* Prepare and execute the SQL using the Binder.
|
||||
*/
|
||||
public void executeSql(Binder binder, SpiQuery.Type type) throws SQLException {
|
||||
startNano = System.nanoTime();
|
||||
executeAsSql(binder);
|
||||
}
|
||||
|
||||
protected void executeAsSql(Binder binder) throws SQLException {
|
||||
|
||||
startNano = System.nanoTime();
|
||||
|
||||
prepareSql();
|
||||
|
||||
Connection conn = trans.getInternalConnection();
|
||||
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
|
||||
@@ -78,6 +78,15 @@ public abstract class BeanRequest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the transaction from the thread local for implicit transactions.
|
||||
*/
|
||||
public void clearTransIfRequired() {
|
||||
if (createdTransaction) {
|
||||
ebeanServer.clearServerTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the server processing the request. Made available for
|
||||
* BeanController and BeanFinder.
|
||||
|
||||
@@ -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.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.clickhouse.ClickHousePlatform;
|
||||
import io.ebean.config.dbplatform.cockroach.CockroachPlatform;
|
||||
import io.ebean.config.dbplatform.db2.DB2Platform;
|
||||
import io.ebean.config.dbplatform.h2.H2Platform;
|
||||
@@ -104,6 +105,9 @@ public class DatabasePlatformFactory {
|
||||
if (dbName.equals("db2")) {
|
||||
return new DB2Platform();
|
||||
}
|
||||
if (dbName.equals("clickhouse")) {
|
||||
return new ClickHousePlatform();
|
||||
}
|
||||
if (dbName.equals("sqlite")) {
|
||||
return new SQLitePlatform();
|
||||
}
|
||||
@@ -162,6 +166,8 @@ public class DatabasePlatformFactory {
|
||||
return new SqlAnywherePlatform();
|
||||
} else if (dbProductName.contains("hdb")) {
|
||||
return new HanaPlatform();
|
||||
} else if (dbProductName.contains("clickhouse")) {
|
||||
return new ClickHousePlatform();
|
||||
}
|
||||
|
||||
// use the standard one
|
||||
|
||||
@@ -28,7 +28,7 @@ public class DefaultCallStackFactory implements CallStackFactory {
|
||||
|
||||
// find the first non-avaje stackElement
|
||||
for (; startIndex < stackTrace.length; startIndex++) {
|
||||
if (!stackTrace[startIndex].getClassName().startsWith(IO_EBEAN)) {
|
||||
if (!ignore(stackTrace[startIndex])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,16 @@ public class DefaultCallStackFactory implements CallStackFactory {
|
||||
return createCallStack(finalTrace);
|
||||
}
|
||||
|
||||
private boolean ignore(StackTraceElement element) {
|
||||
if (element.getClassName().startsWith(IO_EBEAN)) {
|
||||
return true;
|
||||
}
|
||||
if (element.getMethodName().startsWith("_ebean_")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private CallStack createCallStack(StackTraceElement[] finalTrace) {
|
||||
return new CallStack(finalTrace, finalTrace[0].hashCode(), pathHash(finalTrace));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -10,6 +11,7 @@ import io.ebean.meta.MetaQueryPlan;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
import io.ebean.meta.ServerMetrics;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -35,6 +37,11 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
server.visitMetrics(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerMetrics collectMetrics() {
|
||||
return visitBasic();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicMetricVisitor visitBasic() {
|
||||
BasicMetricVisitor basic = new BasicMetricVisitor();
|
||||
@@ -65,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
|
||||
@@ -82,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;
|
||||
@@ -505,6 +506,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private void shutdownPlugins() {
|
||||
|
||||
if (serverConfig.isDumpMetricsOnShutdown()) {
|
||||
new DumpMetrics(this, serverConfig.getDumpMetricsOptions()).dump();
|
||||
}
|
||||
|
||||
for (Plugin plugin : serverPlugins) {
|
||||
try {
|
||||
plugin.shutdown();
|
||||
@@ -786,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());
|
||||
@@ -813,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
|
||||
@@ -833,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();
|
||||
@@ -847,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();
|
||||
@@ -899,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);
|
||||
@@ -1129,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();
|
||||
@@ -1159,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1233,8 +1179,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(spiQuery, t);
|
||||
request.profileLocationById();
|
||||
SpiOrmQueryRequest<T> request = buildQueryRequest(spiQuery, t);
|
||||
request.prepareQuery();
|
||||
if (request.isUseDocStore()) {
|
||||
return docStore().find(request);
|
||||
}
|
||||
@@ -1587,16 +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);
|
||||
request.profileLocationAll();
|
||||
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);
|
||||
}
|
||||
@@ -2287,6 +2235,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
} catch (RuntimeException e) {
|
||||
wrap.endIfCreated();
|
||||
throw e;
|
||||
} finally {
|
||||
wrap.clearIfCreated();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2306,6 +2256,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return new ObtainedTransactionImplicit(trans, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearServerTransaction() {
|
||||
transactionManager.clearServerTransaction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiTransaction beginServerTransaction() {
|
||||
return transactionManager.beginServerTransaction();
|
||||
@@ -2441,6 +2396,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);
|
||||
|
||||
@@ -37,6 +37,7 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
super(server, query, null);
|
||||
this.queryEngine = engine;
|
||||
this.query = query;
|
||||
query.obtainLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,6 +45,7 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
*/
|
||||
@Override
|
||||
public void executeSql(Binder binder, SpiQuery.Type type) throws SQLException {
|
||||
startNano = System.nanoTime();
|
||||
SpiQuery<?> ormQuery = query.getOrmQuery();
|
||||
if (ormQuery != null) {
|
||||
ormQuery.setType(type);
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
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;
|
||||
import io.ebean.meta.ServerMetrics;
|
||||
import io.ebean.meta.SortMetric;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
class DumpMetrics {
|
||||
|
||||
private final SpiEbeanServer server;
|
||||
private final String options;
|
||||
|
||||
private final String nameFormat;
|
||||
private final String nameFormatTimed;
|
||||
|
||||
private boolean dumpHash;
|
||||
private boolean dumpSql;
|
||||
private boolean dumpLoc;
|
||||
|
||||
private Comparator<MetaTimedMetric> sortBy = SortMetric.NAME;
|
||||
|
||||
DumpMetrics(SpiEbeanServer server, String options) {
|
||||
this.server = server;
|
||||
this.options = options;
|
||||
|
||||
int width = 0;
|
||||
|
||||
if (options != null) {
|
||||
dumpLoc = options.contains("loc");
|
||||
dumpSql = options.contains("sql");
|
||||
dumpHash = options.contains("hash");
|
||||
for (int i = 5; i < 10; i++) {
|
||||
width = Math.max(width, optionWidth(i * 10));
|
||||
}
|
||||
for (String option : new String[]{"Total", "Count", "Mean", "Max"}) {
|
||||
sortOption(option);
|
||||
}
|
||||
}
|
||||
if (width == 0) {
|
||||
width = 80;
|
||||
}
|
||||
|
||||
nameFormat = "%1$-" + width + "s";
|
||||
nameFormatTimed = "%1$-" + (width + 6) + "s";
|
||||
}
|
||||
|
||||
private int optionWidth(int check) {
|
||||
return options.contains("w" + check) ? check : 0;
|
||||
}
|
||||
|
||||
private void sortOption(String option) {
|
||||
if (options.contains("sort" + option)) {
|
||||
sortBy = setSortOption(option);
|
||||
}
|
||||
}
|
||||
|
||||
private Comparator<MetaTimedMetric> setSortOption(String option) {
|
||||
switch (option.toUpperCase()) {
|
||||
case "TOTAL":
|
||||
return SortMetric.TOTAL;
|
||||
case "COUNT":
|
||||
return SortMetric.COUNT;
|
||||
case "MEAN":
|
||||
return SortMetric.MEAN;
|
||||
case "MAX":
|
||||
return SortMetric.MAX;
|
||||
}
|
||||
return SortMetric.NAME;
|
||||
}
|
||||
|
||||
void dump() {
|
||||
|
||||
out("-- Dumping metrics for " + server.getName() + " -- ");
|
||||
ServerMetrics serverMetrics = server.getMetaInfoManager().collectMetrics();
|
||||
|
||||
for (MetaTimedMetric metric : serverMetrics.getTimedMetrics()) {
|
||||
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 --");
|
||||
ormQueryMetrics.sort(sortBy);
|
||||
for (MetaOrmQueryMetric metric : ormQueryMetrics) {
|
||||
logQuery(metric);
|
||||
}
|
||||
}
|
||||
|
||||
List<MetaQueryMetric> dtoQueryMetrics = serverMetrics.getDtoQueryMetrics();
|
||||
if (!dtoQueryMetrics.isEmpty()) {
|
||||
out("\n-- DTO queries --");
|
||||
dtoQueryMetrics.sort(sortBy);
|
||||
for (MetaQueryMetric metric : dtoQueryMetrics) {
|
||||
logDtoQuery(metric);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private void logQuery(MetaOrmQueryMetric metric) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
appendQueryName(metric, sb);
|
||||
appendCounters(metric, sb);
|
||||
if (dumpHash) {
|
||||
sb.append("\n hash:").append(metric.getSqlHash());
|
||||
}
|
||||
appendProfileAndSql(metric, sb);
|
||||
out(sb.toString());
|
||||
}
|
||||
|
||||
|
||||
private void logDtoQuery(MetaQueryMetric metric) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
appendQueryName(metric, sb);
|
||||
appendCounters(metric, sb);
|
||||
appendProfileAndSql(metric, sb);
|
||||
out(sb.toString());
|
||||
}
|
||||
|
||||
private void appendQueryName(MetaQueryMetric metric, StringBuilder sb) {
|
||||
sb.append("query:").append(padName(metric.getName())).append(" ");
|
||||
}
|
||||
|
||||
private void appendProfileAndSql(MetaQueryMetric metric, StringBuilder sb) {
|
||||
ProfileLocation profileLocation = metric.getProfileLocation();
|
||||
if (dumpLoc && profileLocation != null) {
|
||||
sb.append("\n loc:").append(profileLocation.shortDescription());
|
||||
}
|
||||
|
||||
if (dumpSql) {
|
||||
sb.append(" \n\n sql:").append(metric.getSql()).append("\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
private void log(MetaTimedMetric metric) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(padNameTimed(metric.getName())).append(" ");
|
||||
appendCounters(metric, sb);
|
||||
out(sb.toString());
|
||||
}
|
||||
|
||||
private void appendCounters(MetaTimedMetric timedMetric, StringBuilder sb) {
|
||||
sb.append(" count:").append(pad(timedMetric.getCount()))
|
||||
.append(" total:").append(pad(timedMetric.getTotal()))
|
||||
.append(" mean:").append(pad(timedMetric.getMean()))
|
||||
.append(" max:").append(pad(timedMetric.getMax()));
|
||||
}
|
||||
|
||||
private String padName(String name) {
|
||||
return String.format(nameFormat, name);
|
||||
}
|
||||
|
||||
private String padNameTimed(String name) {
|
||||
return String.format(nameFormatTimed, name);
|
||||
}
|
||||
|
||||
private String pad(long value) {
|
||||
return String.format("%1$-8s", value);
|
||||
}
|
||||
}
|
||||
@@ -169,7 +169,7 @@ public class InternalConfiguration {
|
||||
this.online = online;
|
||||
this.serverConfig = serverConfig;
|
||||
this.clockService = new ClockService(serverConfig.getClock());
|
||||
this.tableModState = new TableModState(clockService);
|
||||
this.tableModState = new TableModState();
|
||||
this.logManager = initLogManager();
|
||||
this.docStoreFactory = initDocStoreFactory(serverConfig.service(DocStoreFactory.class));
|
||||
this.jsonFactory = serverConfig.getJsonFactory();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user