mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
97
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49b568e7d4 | ||
|
|
628af31ddd | ||
|
|
31cac60393 | ||
|
|
6a067c0cf7 | ||
|
|
842292ef6b | ||
|
|
6db9a60f4f | ||
|
|
d9125db957 | ||
|
|
4bd9e67703 | ||
|
|
2a986ab590 | ||
|
|
7c477827fe | ||
|
|
b2967dec1e | ||
|
|
a95fd063ae | ||
|
|
17a54e3804 | ||
|
|
f1b2a09b30 | ||
|
|
e4d9e4f2de | ||
|
|
6474f306b5 | ||
|
|
f1c2f134ad | ||
|
|
0f7158ea18 | ||
|
|
3fdb76d18f | ||
|
|
b1027a2f25 | ||
|
|
e5a1f9f7cc | ||
|
|
f78f24136a | ||
|
|
5b2aa0c74d | ||
|
|
175714046a | ||
|
|
73b2cc55f3 | ||
|
|
cebe375856 | ||
|
|
2f47235018 | ||
|
|
17abd6d977 | ||
|
|
cc5f3689f3 | ||
|
|
abcc651e7f | ||
|
|
caa36262c1 | ||
|
|
66b6dba921 | ||
|
|
fc01f230f3 | ||
|
|
7fe3e657d7 | ||
|
|
9bade12c9d | ||
|
|
7a816ae2da | ||
|
|
b83412281e | ||
|
|
478bba292d | ||
|
|
77a4e684bb | ||
|
|
a8254c4295 | ||
|
|
2a5d0084ef | ||
|
|
f9404639d7 | ||
|
|
bbef5a6bbb | ||
|
|
4645fd9725 | ||
|
|
823cb45221 | ||
|
|
b2bbb05b83 | ||
|
|
5ff71324d0 | ||
|
|
851f1c98b0 | ||
|
|
a92e620f7e | ||
|
|
f9cd955f7a | ||
|
|
71f66005e7 | ||
|
|
b682293a32 | ||
|
|
5bd7853ce8 | ||
|
|
276c56161a | ||
|
|
6b01821623 | ||
|
|
8495ccced6 | ||
|
|
2d929837c4 | ||
|
|
6754992b1c | ||
|
|
08b7f4a95f | ||
|
|
07e2605a30 | ||
|
|
9a0b742f14 | ||
|
|
bce745e88d | ||
|
|
51d2e4c600 | ||
|
|
4e33027fdf | ||
|
|
0a809b0fb3 | ||
|
|
13ddb915c8 | ||
|
|
07cdf64ece | ||
|
|
b9ae76d465 | ||
|
|
82f5c21d26 | ||
|
|
14105eb1eb | ||
|
|
bab27f25ca | ||
|
|
eed8d69715 | ||
|
|
7b93df2df7 | ||
|
|
bb205ced54 | ||
|
|
0d4c50e19c | ||
|
|
8cff85fdda | ||
|
|
932986a6b5 | ||
|
|
01539091ef | ||
|
|
7e69f71b51 | ||
|
|
58d6b2c24e | ||
|
|
3c7ef7d46e | ||
|
|
db5db26fca | ||
|
|
7110f4a59b | ||
|
|
9f91d8bcf9 | ||
|
|
ac97444ad1 | ||
|
|
f6e9afcab6 | ||
|
|
a7226079fe | ||
|
|
fc915010d9 | ||
|
|
c1818bc309 | ||
|
|
4a4134ee93 | ||
|
|
e07bd8f736 | ||
|
|
4d0b29624e | ||
|
|
c7f4b56dd1 | ||
|
|
69c045443c | ||
|
|
fd2d083971 | ||
|
|
b9380aa5b8 | ||
|
|
3a1368a342 |
+11
@@ -9,16 +9,27 @@ git:
|
||||
|
||||
addons:
|
||||
postgresql: "9.6"
|
||||
# apt:
|
||||
# packages:
|
||||
# - mysql-server-5.7
|
||||
# - mysql-client-core-5.7
|
||||
# - mysql-client-5.7
|
||||
|
||||
services:
|
||||
- docker
|
||||
- postgresql
|
||||
- mysql
|
||||
|
||||
before_script:
|
||||
- ./.travis/setup_database
|
||||
- mysql -u root -e 'CREATE DATABASE test_ebean;'
|
||||
- mysql -u root -e "CREATE USER 'test_ebean'@'localhost' IDENTIFIED BY 'test';"
|
||||
- mysql -u root -e "GRANT ALL ON test_ebean.* TO 'test_ebean'@'localhost';"
|
||||
|
||||
env:
|
||||
- EBEAN_DB=h2
|
||||
- EBEAN_DB=pg
|
||||
# - EBEAN_DB=mysql
|
||||
|
||||
install: true
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>11.6.1</version>
|
||||
<version>11.10.5</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ebean</name>
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-11.6.1</tag>
|
||||
<tag>ebean-11.10.5</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -72,6 +72,17 @@
|
||||
</profiles>
|
||||
<dependencies>
|
||||
|
||||
<!--
|
||||
Projects are expected to explicit depend on version
|
||||
of slf4j that they want to use
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.25</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
Class retention Nonnull and Nullable annotations
|
||||
to assist with IDE auto-completion with Ebean API
|
||||
@@ -92,43 +103,25 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-annotation</artifactId>
|
||||
<version>3.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-datasource-api</artifactId>
|
||||
<version>1.1</version>
|
||||
<version>3.6</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-datasource</artifactId>
|
||||
<version>2.1.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-classpath-scanner-api</artifactId>
|
||||
<version>2.2</version>
|
||||
<version>3.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-classpath-scanner</artifactId>
|
||||
<version>2.2.4</version>
|
||||
<version>3.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-migration</artifactId>
|
||||
<version>11.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>[1.7.1,1.7.99)</version>
|
||||
<version>11.3.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -205,6 +198,20 @@
|
||||
<!--<scope>test</scope>-->
|
||||
<!--</dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-docker-run</artifactId>
|
||||
<version>1.5.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.2.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-agentloader</artifactId>
|
||||
@@ -215,7 +222,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-agent</artifactId>
|
||||
<version>11.5.1</version>
|
||||
<version>11.9.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -288,7 +295,7 @@
|
||||
<plugin>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-maven-plugin</artifactId>
|
||||
<version>11.5.1</version>
|
||||
<version>11.9.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
|
||||
@@ -56,18 +56,18 @@ public enum CacheMode {
|
||||
AUTO(true, true),
|
||||
|
||||
/**
|
||||
* Do not read from cache, but load retrieved beans into the cache.
|
||||
* Do not read from cache, but put beans into the cache and invalidate parts of the cache as necessary.
|
||||
* <p>
|
||||
* Use this, if you want to get the fresh value from database into the cache. Typically a CacheMode.ON query
|
||||
* will follow.
|
||||
* Use this on a query if you want to get the fresh value from database and put it into the cache.
|
||||
*/
|
||||
RECACHE(false, true),
|
||||
PUT(false, true),
|
||||
|
||||
/**
|
||||
* Query the cache for value. If it is there, use it and otherwise hit database but do NOT put the value
|
||||
* into the cache. Note that there are not many use case for this mode.
|
||||
* GET only from the cache.
|
||||
* <p>
|
||||
* This mode does not put entries into the cache or invalidate parts of the cache.
|
||||
*/
|
||||
QUERY_ONLY(true, false);
|
||||
GET(true, false);
|
||||
|
||||
private boolean get;
|
||||
private boolean put;
|
||||
|
||||
@@ -5,10 +5,10 @@ import io.ebean.cache.ServerCacheManager;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.text.csv.CsvReader;
|
||||
import io.ebean.text.json.JsonContext;
|
||||
import javax.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.Collection;
|
||||
@@ -131,8 +131,7 @@ public final class Ebean {
|
||||
private final ConcurrentHashMap<String, EbeanServer> concMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Cache for synchronized read, creation and put. Protected by the monitor
|
||||
* object.
|
||||
* Cache for synchronized read, creation and put. Protected by the monitor object.
|
||||
*/
|
||||
private final HashMap<String, EbeanServer> syncMap = new HashMap<>();
|
||||
|
||||
@@ -146,14 +145,7 @@ public final class Ebean {
|
||||
private ServerManager() {
|
||||
|
||||
try {
|
||||
// skipDefaultServer is set by EbeanServerFactory
|
||||
// ... when it is creating the primaryServer
|
||||
if (PrimaryServer.isSkip()) {
|
||||
// primary server being created by EbeanServerFactory
|
||||
// ... so we should not try and create it here
|
||||
logger.debug("PrimaryServer.isSkip()");
|
||||
|
||||
} else {
|
||||
if (!PrimaryServer.isSkip()) {
|
||||
// look to see if there is a default server defined
|
||||
String defaultName = PrimaryServer.getDefaultServerName();
|
||||
logger.debug("defaultName:{}", defaultName);
|
||||
|
||||
@@ -65,10 +65,8 @@ public class EbeanServerFactory {
|
||||
|
||||
EbeanServer server = createInternal(config);
|
||||
|
||||
if (config.isDefaultServer()) {
|
||||
PrimaryServer.setSkip(true);
|
||||
}
|
||||
if (config.isRegister()) {
|
||||
PrimaryServer.setSkip(true);
|
||||
Ebean.register(server, config.isDefaultServer());
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,8 @@ class PrimaryServer {
|
||||
private static String determineDefaultServerName() {
|
||||
|
||||
String defaultServerName = System.getenv("EBEAN_DB");
|
||||
defaultServerName = System.getProperty("db", defaultServerName);
|
||||
defaultServerName = System.getProperty("ebean_db", defaultServerName);
|
||||
if (isEmpty(defaultServerName)) {
|
||||
defaultServerName = System.getProperty("datasource.default");
|
||||
if (isEmpty(defaultServerName)) {
|
||||
@@ -74,6 +76,7 @@ class PrimaryServer {
|
||||
if (defaultServerName == null) {
|
||||
defaultServerName = "db";
|
||||
}
|
||||
System.setProperty("ebean_db", defaultServerName);
|
||||
return defaultServerName;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package io.ebean;
|
||||
|
||||
/**
|
||||
* A location for profiling transactions and queries.
|
||||
* <p>
|
||||
* Typically represents a class method in the form of class file and line of code that started
|
||||
* the transaction or invoked the query.
|
||||
* </p>
|
||||
*/
|
||||
public interface ProfileLocation {
|
||||
|
||||
/**
|
||||
* Create and return a new ProfileLocation.
|
||||
*/
|
||||
static ProfileLocation create() {
|
||||
return XServiceProvider.profileLocationFactory().create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new ProfileLocation with a given lineNumber and label.
|
||||
*/
|
||||
static ProfileLocation create(int lineNumber, String label) {
|
||||
return XServiceProvider.profileLocationFactory().create(lineNumber, label);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new ProfileLocation with a given location.
|
||||
*/
|
||||
static ProfileLocation createAt(String location) {
|
||||
return XServiceProvider.profileLocationFactory().createAt(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the location description.
|
||||
*/
|
||||
String obtain();
|
||||
|
||||
/**
|
||||
* Return a short version of the location description.
|
||||
*/
|
||||
String shortDescription();
|
||||
|
||||
/**
|
||||
* Add execution time.
|
||||
*/
|
||||
void add(long executionTime);
|
||||
}
|
||||
@@ -1369,6 +1369,12 @@ public interface Query<T> {
|
||||
*/
|
||||
Query<T> setProfileId(int profileId);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
Query<T> setProfileLocation(ProfileLocation profileLocation);
|
||||
|
||||
/**
|
||||
* Set to true if this query should execute against the doc store.
|
||||
* <p>
|
||||
@@ -1430,9 +1436,12 @@ public interface Query<T> {
|
||||
String getGeneratedSql();
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to forUpdate().
|
||||
*
|
||||
* executed the select with "for update" which should lock the record
|
||||
* "on read"
|
||||
*/
|
||||
@Deprecated
|
||||
Query<T> setForUpdate(boolean forUpdate);
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,7 @@ public interface RawSqlBuilder {
|
||||
* resultSet.
|
||||
*/
|
||||
static RawSql resultSet(ResultSet resultSet, String... propertyNames) {
|
||||
return XServiceProvider.get().resultSet(resultSet, propertyNames);
|
||||
return XServiceProvider.rawSql().resultSet(resultSet, propertyNames);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,7 +30,7 @@ public interface RawSqlBuilder {
|
||||
* this query.
|
||||
*/
|
||||
static RawSqlBuilder unparsed(String sql) {
|
||||
return XServiceProvider.get().unparsed(sql);
|
||||
return XServiceProvider.rawSql().unparsed(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +47,7 @@ public interface RawSqlBuilder {
|
||||
* </p>
|
||||
*/
|
||||
static RawSqlBuilder parse(String sql) {
|
||||
return XServiceProvider.get().parsed(sql);
|
||||
return XServiceProvider.rawSql().parsed(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,23 @@ import java.sql.Connection;
|
||||
*/
|
||||
public interface Transaction extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* Return the current transaction (of the default server) or null if there is
|
||||
* no current transaction in scope.
|
||||
* <p>
|
||||
* This is the same as <code>Ebean.currentTransaction()</code>
|
||||
* </p>
|
||||
* <p>
|
||||
* This returns the current transaction for the 'default server'. If you are using
|
||||
* multiple EbeanServer's then use {@link EbeanServer#currentTransaction()}.
|
||||
* </p>
|
||||
* @see Ebean#currentTransaction()
|
||||
* @see EbeanServer#currentTransaction()
|
||||
*/
|
||||
static Transaction current() {
|
||||
return Ebean.currentTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read Committed transaction isolation. Same as
|
||||
* java.sql.Connection.TRANSACTION_READ_COMMITTED.
|
||||
@@ -42,6 +59,14 @@ public interface Transaction extends AutoCloseable {
|
||||
*/
|
||||
void register(TransactionCallback callback);
|
||||
|
||||
/**
|
||||
* Set a label on the transaction.
|
||||
* <p>
|
||||
* This label is used to group transaction execution times for performance metrics reporting.
|
||||
* </p>
|
||||
*/
|
||||
void setLabel(String label);
|
||||
|
||||
/**
|
||||
* Return true if this transaction is read only.
|
||||
*/
|
||||
|
||||
@@ -48,10 +48,16 @@ public final class TxScope {
|
||||
*/
|
||||
private boolean flushOnQuery = true;
|
||||
|
||||
private boolean skipCache;
|
||||
|
||||
private String label;
|
||||
|
||||
private ArrayList<Class<? extends Throwable>> rollbackFor;
|
||||
|
||||
private ArrayList<Class<? extends Throwable>> noRollbackFor;
|
||||
|
||||
private ProfileLocation profileLocation;
|
||||
|
||||
/**
|
||||
* Helper method to create a TxScope with REQUIRES.
|
||||
*/
|
||||
@@ -185,6 +191,51 @@ public final class TxScope {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the profile location.
|
||||
*/
|
||||
public ProfileLocation getProfileLocation() {
|
||||
return profileLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the profile location.
|
||||
*/
|
||||
public TxScope setProfileLocation(ProfileLocation profileLocation) {
|
||||
this.profileLocation = profileLocation;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the L2 cache should be skipped for this transaction.
|
||||
*/
|
||||
public boolean isSkipCache() {
|
||||
return skipCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if the transaction should skip L2 cache access.
|
||||
*/
|
||||
public TxScope setSkipCache(boolean skipCache) {
|
||||
this.skipCache = skipCache;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the label for the transaction.
|
||||
*/
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a label for the transaction.
|
||||
*/
|
||||
public TxScope setLabel(String label) {
|
||||
this.label = label;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch mode.
|
||||
*/
|
||||
|
||||
@@ -148,6 +148,12 @@ public interface UpdateQuery<T> {
|
||||
*/
|
||||
UpdateQuery<T> setRaw(String propertyExpression, Object... values);
|
||||
|
||||
/**
|
||||
* Set the profile location of this update query. This is used to relate query execution metrics
|
||||
* back to a location like a specific line of code.
|
||||
*/
|
||||
UpdateQuery<T> setProfileLocation(ProfileLocation profileLocation);
|
||||
|
||||
/**
|
||||
* Return the query expression list to add predicates to.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebean;
|
||||
|
||||
import io.ebean.service.SpiProfileLocationFactory;
|
||||
import io.ebean.service.SpiRawSqlService;
|
||||
|
||||
import java.util.Iterator;
|
||||
@@ -10,9 +11,11 @@ import java.util.ServiceLoader;
|
||||
*/
|
||||
class XServiceProvider {
|
||||
|
||||
private static SpiRawSqlService builder = init();
|
||||
private static SpiRawSqlService rawSqlService = initRawSql();
|
||||
|
||||
private static SpiRawSqlService init() {
|
||||
private static SpiProfileLocationFactory profileLocationFactory = initProfileLocation();
|
||||
|
||||
private static SpiRawSqlService initRawSql() {
|
||||
|
||||
Iterator<SpiRawSqlService> loader = ServiceLoader.load(SpiRawSqlService.class).iterator();
|
||||
if (loader.hasNext()) {
|
||||
@@ -21,10 +24,27 @@ class XServiceProvider {
|
||||
throw new IllegalStateException("No service implementation found for SpiRawSqlService?");
|
||||
}
|
||||
|
||||
private static SpiProfileLocationFactory initProfileLocation() {
|
||||
|
||||
Iterator<SpiProfileLocationFactory> loader = ServiceLoader.load(SpiProfileLocationFactory.class).iterator();
|
||||
if (loader.hasNext()) {
|
||||
return loader.next();
|
||||
}
|
||||
throw new IllegalStateException("No service implementation found for SpiProfileLocationFactory?");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the RawSqlService implementation.
|
||||
*/
|
||||
static SpiRawSqlService get() {
|
||||
return builder;
|
||||
static SpiRawSqlService rawSql() {
|
||||
return rawSqlService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the RawSqlService implementation.
|
||||
*/
|
||||
static SpiProfileLocationFactory profileLocationFactory() {
|
||||
return profileLocationFactory;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -227,6 +227,11 @@ public interface BeanCollection<E> extends Serializable {
|
||||
*/
|
||||
void modifyReset();
|
||||
|
||||
/**
|
||||
* Has been modified by an addition or removal.
|
||||
*/
|
||||
boolean wasTouched();
|
||||
|
||||
/**
|
||||
* Return a shallow copy of this collection that is modifiable.
|
||||
*/
|
||||
|
||||
@@ -26,14 +26,16 @@ public final class CallStack implements Serializable {
|
||||
|
||||
private final StackTraceElement[] callStack;
|
||||
|
||||
private final int hc;
|
||||
|
||||
public CallStack(StackTraceElement[] callStack, int zeroHash, int pathHash) {
|
||||
this.callStack = callStack;
|
||||
this.zeroHash = enc(zeroHash);
|
||||
this.pathHash = enc(pathHash);
|
||||
this.hc = computeHashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
private int computeHashCode() {
|
||||
int hc = 0;
|
||||
for (StackTraceElement aCallStack : callStack) {
|
||||
hc = 92821 * hc + aCallStack.hashCode();
|
||||
@@ -41,6 +43,10 @@ public final class CallStack implements Serializable {
|
||||
return hc;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
|
||||
@@ -13,8 +13,14 @@ import java.io.Serializable;
|
||||
*/
|
||||
public interface EntityBean extends Serializable {
|
||||
|
||||
/**
|
||||
* Return all the property names in defined order.
|
||||
*/
|
||||
String[] _ebean_getPropertyNames();
|
||||
|
||||
/**
|
||||
* Return the property name at the given position.
|
||||
*/
|
||||
String _ebean_getPropertyName(int pos);
|
||||
|
||||
/**
|
||||
|
||||
@@ -90,6 +90,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
private int lazyLoadProperty = -1;
|
||||
|
||||
private Object ownerId;
|
||||
private int sortOrder;
|
||||
|
||||
/**
|
||||
* Create a intercept with a given entity.
|
||||
@@ -693,6 +694,9 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Add and return a dirty property hash recursing into embedded beans.
|
||||
*/
|
||||
private void addDirtyPropertyKey(StringBuilder sb) {
|
||||
if (sortOrder > 0) {
|
||||
sb.append("s,");
|
||||
}
|
||||
int len = getPropertyLength();
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (changedProps != null && changedProps[i]) {
|
||||
@@ -1050,4 +1054,18 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
public void setOldValue(int propertyIndex, Object oldValue) {
|
||||
setChangedPropertyValue(propertyIndex, true, oldValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sort order value for an order column.
|
||||
*/
|
||||
public int getSortOrder() {
|
||||
return sortOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sort order value for an order column.
|
||||
*/
|
||||
public void setSortOrder(int sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
}
|
||||
|
||||
+18
-4
@@ -17,9 +17,9 @@ import java.util.Set;
|
||||
*/
|
||||
public interface ServerCache {
|
||||
|
||||
default Map<Object,Object> getAll(Set<Object> keys){
|
||||
default Map<Object, Object> getAll(Set<Object> keys) {
|
||||
|
||||
Map<Object,Object> map = new LinkedHashMap<>();
|
||||
Map<Object, Object> map = new LinkedHashMap<>();
|
||||
for (Object key : keys) {
|
||||
Object value = get(key);
|
||||
if (value != null) {
|
||||
@@ -34,15 +34,29 @@ public interface ServerCache {
|
||||
*/
|
||||
Object get(Object id);
|
||||
|
||||
/**
|
||||
* Put all the values in the cache.
|
||||
*/
|
||||
default void putAll(Map<Object, Object> keyValues) {
|
||||
keyValues.forEach(this::put);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put the value in the cache with a given id.
|
||||
*/
|
||||
Object put(Object id, Object value);
|
||||
void put(Object id, Object value);
|
||||
|
||||
/**
|
||||
* Remove the entries from the cache given the id values.
|
||||
*/
|
||||
default void removeAll(Set<Object> keys) {
|
||||
keys.forEach(this::remove);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a entry from the cache given its id.
|
||||
*/
|
||||
Object remove(Object id);
|
||||
void remove(Object id);
|
||||
|
||||
/**
|
||||
* Clear all entries from the cache.
|
||||
|
||||
@@ -44,4 +44,19 @@ public interface ServerCacheManager {
|
||||
*/
|
||||
void clearAll();
|
||||
|
||||
/**
|
||||
* Clear all the local caches.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
void clearAllLocal();
|
||||
|
||||
/**
|
||||
* Clear the local caches for this bean type.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
void clearLocal(Class<?> beanType);
|
||||
}
|
||||
|
||||
@@ -213,7 +213,12 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
boolean holdsModifications() {
|
||||
return modifyHolder != null && modifyHolder.hasModifications();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean wasTouched() {
|
||||
return modifyHolder != null && modifyHolder.wasTouched();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies all relevant properties for a clone. See {@link #getShallowCopy()}
|
||||
* @param other
|
||||
|
||||
@@ -28,7 +28,10 @@ class ModifyHolder<E> implements Serializable {
|
||||
*/
|
||||
private Set<E> modifyAdditions = new LinkedHashSet<>();
|
||||
|
||||
private boolean touched;
|
||||
|
||||
void reset() {
|
||||
touched = false;
|
||||
modifyDeletions = new LinkedHashSet<>();
|
||||
modifyAdditions = new LinkedHashSet<>();
|
||||
}
|
||||
@@ -50,6 +53,7 @@ class ModifyHolder<E> implements Serializable {
|
||||
|
||||
void modifyAddition(E bean) {
|
||||
if (bean != null) {
|
||||
touched = true;
|
||||
// If it is to delete then just remove the deletion
|
||||
if (!undoDeletion(bean)) {
|
||||
// Insert
|
||||
@@ -65,6 +69,7 @@ class ModifyHolder<E> implements Serializable {
|
||||
@SuppressWarnings("unchecked")
|
||||
void modifyRemoval(Object bean) {
|
||||
if (bean != null) {
|
||||
touched = true;
|
||||
// If it is to be added then just remove the addition
|
||||
if (!undoAddition(bean)) {
|
||||
modifyDeletions.add((E) bean);
|
||||
@@ -80,6 +85,14 @@ class ModifyHolder<E> implements Serializable {
|
||||
return modifyDeletions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the collection was touched in some way. This is still true even if
|
||||
* a bean was removed and then added (which is not held as a modification).
|
||||
*/
|
||||
boolean wasTouched() {
|
||||
return touched;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there additions or removals.
|
||||
*/
|
||||
|
||||
@@ -32,6 +32,15 @@ public class AutoTuneConfig {
|
||||
public AutoTuneConfig() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we are profiling or query tuning.
|
||||
*
|
||||
* If we are not doing either then we don't need a CallStack.
|
||||
*/
|
||||
public boolean isActive() {
|
||||
return profiling || queryTuning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the file that holds the query tuning information.
|
||||
*/
|
||||
|
||||
@@ -10,22 +10,82 @@ import java.util.Properties;
|
||||
*/
|
||||
public class ContainerConfig {
|
||||
|
||||
protected boolean clusterActive;
|
||||
private boolean active;
|
||||
private String serviceName;
|
||||
private String namespace;
|
||||
private String podName;
|
||||
private int port;
|
||||
|
||||
protected Properties properties;
|
||||
private Properties properties;
|
||||
|
||||
/**
|
||||
* Return the service name.
|
||||
*/
|
||||
public String getServiceName() {
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the service name.
|
||||
*/
|
||||
public void setServiceName(String serviceName) {
|
||||
this.serviceName = serviceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the namespace.
|
||||
*/
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the namespace.
|
||||
*/
|
||||
public void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pod name.
|
||||
*/
|
||||
public String getPodName() {
|
||||
return podName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the pod name.
|
||||
*/
|
||||
public void setPodName(String podName) {
|
||||
this.podName = podName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the port to use.
|
||||
*/
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the port to use.
|
||||
*/
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if clustering is active.
|
||||
*/
|
||||
public boolean isClusterActive() {
|
||||
return clusterActive;
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true for clustering to be active.
|
||||
*/
|
||||
public void setClusterActive(boolean clusterActive) {
|
||||
this.clusterActive = clusterActive;
|
||||
public void setActive(boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +107,14 @@ public class ContainerConfig {
|
||||
*/
|
||||
public void loadFromProperties(Properties properties) {
|
||||
this.properties = properties;
|
||||
this.clusterActive = getProperty(properties, "ebean.cluster.active", clusterActive);
|
||||
this.active = getProperty(properties, "ebean.cluster.active", active);
|
||||
this.serviceName = properties.getProperty("ebean.cluster.serviceName", serviceName);
|
||||
this.namespace = properties.getProperty("ebean.cluster.namespace", namespace);
|
||||
this.podName = properties.getProperty("ebean.cluster.podName", podName);
|
||||
String portParam = properties.getProperty("ebean.cluster.port");
|
||||
if (portParam != null) {
|
||||
this.port = Integer.parseInt(portParam);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,8 @@ public class DbMigrationConfig {
|
||||
|
||||
protected static final Logger logger = LoggerFactory.getLogger(DbMigrationConfig.class);
|
||||
|
||||
protected MigrationConfig runnerConfig = new MigrationConfig();
|
||||
|
||||
/**
|
||||
* The database platform to generate migration DDL for.
|
||||
*/
|
||||
@@ -105,6 +107,15 @@ public class DbMigrationConfig {
|
||||
*/
|
||||
protected String dbPassword;
|
||||
|
||||
protected String patchInsertOn;
|
||||
|
||||
protected String patchResetChecksumOn;
|
||||
|
||||
/**
|
||||
* Mode used to check non-null columns added via migration have a default value specified etc.
|
||||
*/
|
||||
protected boolean strictMode = true;
|
||||
|
||||
/**
|
||||
* Return the DB platform to generate migration DDL for.
|
||||
* <p>
|
||||
@@ -352,6 +363,65 @@ public class DbMigrationConfig {
|
||||
this.dbPassword = dbPassword;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return migration versions that should be added to history without running.
|
||||
*/
|
||||
public String getPatchInsertOn() {
|
||||
return patchInsertOn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set migration versions that should be added to history without running.
|
||||
* <p>
|
||||
* Value can be a string containing comma delimited list of version numbers.
|
||||
* </p>
|
||||
*/
|
||||
public void setPatchInsertOn(String patchInsertOn) {
|
||||
this.patchInsertOn = patchInsertOn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return migration versions that should have their checksum reset and not run.
|
||||
*/
|
||||
public String getPatchResetChecksumOn() {
|
||||
return patchResetChecksumOn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set migration versions that should have their checksum reset and not run.
|
||||
* <p>
|
||||
* Value can be a string containing comma delimited list of version numbers.
|
||||
* </p>
|
||||
*/
|
||||
public void setPatchResetChecksumOn(String patchResetChecksumOn) {
|
||||
this.patchResetChecksumOn = patchResetChecksumOn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if strict mode is used which includes a check that non-null columns have a default value.
|
||||
*/
|
||||
public boolean isStrictMode() {
|
||||
String envValue = readEnvironment("ddl.migration.strictMode");
|
||||
if (!isEmpty(envValue)) {
|
||||
return Boolean.parseBoolean(envValue.trim());
|
||||
}
|
||||
return strictMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to false to turn off strict mode allowing non-null columns to not have a default value.
|
||||
*/
|
||||
public void setStrictMode(boolean strictMode) {
|
||||
this.strictMode = strictMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying migration runner configuration allowing for more advanced settings.
|
||||
*/
|
||||
public MigrationConfig getRunnerConfig() {
|
||||
return runnerConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the settings from the PropertiesWrapper.
|
||||
*/
|
||||
@@ -370,6 +440,8 @@ public class DbMigrationConfig {
|
||||
generate = properties.getBoolean("migration.generate", generate);
|
||||
version = properties.get("migration.version", version);
|
||||
name = properties.get("migration.name", name);
|
||||
patchInsertOn = properties.get("migration.patchInsertOn", patchInsertOn);
|
||||
patchResetChecksumOn = properties.get("migration.patchResetChecksumOn", patchResetChecksumOn);
|
||||
|
||||
runMigration = properties.getBoolean("migration.run", runMigration);
|
||||
metaTable = properties.get("migration.metaTable", metaTable);
|
||||
@@ -446,7 +518,6 @@ public class DbMigrationConfig {
|
||||
*/
|
||||
public MigrationRunner createRunner(ClassLoader classLoader, Properties properties) {
|
||||
|
||||
MigrationConfig runnerConfig = new MigrationConfig();
|
||||
runnerConfig.setMetaTable(metaTable);
|
||||
runnerConfig.setApplySuffix(applySuffix);
|
||||
runnerConfig.setMigrationPath(migrationPath);
|
||||
@@ -455,6 +526,12 @@ public class DbMigrationConfig {
|
||||
runnerConfig.setDbUsername(getDbUsername());
|
||||
runnerConfig.setDbPassword(getDbPassword());
|
||||
runnerConfig.setClassLoader(classLoader);
|
||||
if (patchInsertOn != null) {
|
||||
runnerConfig.setPatchInsertOn(patchInsertOn);
|
||||
}
|
||||
if (patchResetChecksumOn != null) {
|
||||
runnerConfig.setPatchResetChecksumOn(patchResetChecksumOn);
|
||||
}
|
||||
if (properties != null) {
|
||||
runnerConfig.load(properties);
|
||||
}
|
||||
|
||||
@@ -273,11 +273,29 @@ public class ServerConfig {
|
||||
*/
|
||||
private DataSource dataSource;
|
||||
|
||||
/**
|
||||
* The read only data source (can be null).
|
||||
*/
|
||||
private DataSource readOnlyDataSource;
|
||||
|
||||
/**
|
||||
* The data source config.
|
||||
*/
|
||||
private DataSourceConfig dataSourceConfig = new DataSourceConfig();
|
||||
|
||||
/**
|
||||
* When true create a read only DataSource using readOnlyDataSourceConfig defaulting values from dataSourceConfig.
|
||||
* I believe this will default to true in some future release (as it has a nice performance benefit).
|
||||
*
|
||||
* autoReadOnlyDataSource is an unfortunate name for this config option but I haven't come up with a better one.
|
||||
*/
|
||||
private boolean autoReadOnlyDataSource;
|
||||
|
||||
/**
|
||||
* Optional configuration for a read only data source.
|
||||
*/
|
||||
private DataSourceConfig readOnlyDataSourceConfig = new DataSourceConfig();
|
||||
|
||||
/**
|
||||
* The db migration config (migration resource path etc).
|
||||
*/
|
||||
@@ -1442,6 +1460,28 @@ public class ServerConfig {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the read only DataSource.
|
||||
*/
|
||||
public DataSource getReadOnlyDataSource() {
|
||||
return readOnlyDataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the read only DataSource.
|
||||
* <p>
|
||||
* Note that the DataSource is expected to use AutoCommit true mode avoiding the need
|
||||
* for explicit commit (or rollback).
|
||||
* </p>
|
||||
* <p>
|
||||
* This read only DataSource will be used for implicit query only transactions. It is not
|
||||
* used if the transaction is created explicitly or if the query is an update or delete query.
|
||||
* </p>
|
||||
*/
|
||||
public void setReadOnlyDataSource(DataSource readOnlyDataSource) {
|
||||
this.readOnlyDataSource = readOnlyDataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configuration to build a DataSource using Ebean's own DataSource
|
||||
* implementation.
|
||||
@@ -1458,6 +1498,42 @@ public class ServerConfig {
|
||||
this.dataSourceConfig = dataSourceConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if Ebean should create a DataSource for use with implicit read only transactions.
|
||||
*/
|
||||
public boolean isAutoReadOnlyDataSource() {
|
||||
return autoReadOnlyDataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if Ebean should create a DataSource for use with implicit read only transactions.
|
||||
*/
|
||||
public void setAutoReadOnlyDataSource(boolean autoReadOnlyDataSource) {
|
||||
this.autoReadOnlyDataSource = autoReadOnlyDataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configuration for the read only DataSource.
|
||||
* <p>
|
||||
* This is only used if autoReadOnlyDataSource is true.
|
||||
* </p>
|
||||
* <p>
|
||||
* The driver, url, username and password default to the configuration for the main DataSource if they are not
|
||||
* set on this configuration. This means there is actually no need to set any configuration here and we only
|
||||
* set configuration for url, username and password etc if it is different from the main DataSource.
|
||||
* </p>
|
||||
*/
|
||||
public DataSourceConfig getReadOnlyDataSourceConfig() {
|
||||
return readOnlyDataSourceConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the configuration for the read only DataSource.
|
||||
*/
|
||||
public void setReadOnlyDataSourceConfig(DataSourceConfig readOnlyDataSourceConfig) {
|
||||
this.readOnlyDataSourceConfig = readOnlyDataSourceConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JNDI name of the DataSource to use.
|
||||
*/
|
||||
@@ -2607,6 +2683,7 @@ public class ServerConfig {
|
||||
explicitTransactionBeginMode = p.getBoolean("explicitTransactionBeginMode", explicitTransactionBeginMode);
|
||||
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
|
||||
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
|
||||
autoReadOnlyDataSource = p.getBoolean("autoReadOnlyDataSource", autoReadOnlyDataSource);
|
||||
|
||||
backgroundExecutorSchedulePoolSize = p.getInt("backgroundExecutorSchedulePoolSize", backgroundExecutorSchedulePoolSize);
|
||||
backgroundExecutorShutdownSecs = p.getInt("backgroundExecutorShutdownSecs", backgroundExecutorShutdownSecs);
|
||||
|
||||
@@ -140,9 +140,25 @@ public class DatabasePlatform {
|
||||
private static final char BACK_TICK = '`';
|
||||
|
||||
/**
|
||||
* The like clause. Can be overridden to disable default escape character.
|
||||
* The non-escaped like clause (to stop slash being escaped on some platforms).
|
||||
* Used for the 'raw like' expression but not for startsWith, endsWith and contains expressions.
|
||||
*/
|
||||
protected String likeClause = "like ?";
|
||||
protected String likeClauseRaw = "like ? escape''";
|
||||
|
||||
/**
|
||||
* Escaped like clause for startsWith, endsWith and contains.
|
||||
*/
|
||||
protected String likeClauseEscaped = "like ? escape'|'";
|
||||
|
||||
/**
|
||||
* Escape character used for startsWith, endsWith and contains.
|
||||
*/
|
||||
protected char likeEscapeChar = '|';
|
||||
|
||||
/**
|
||||
* Characters escaped for startsWith, endsWith and contains.
|
||||
*/
|
||||
protected char[] likeSpecialCharacters = { '%', '_', '|' };
|
||||
|
||||
protected DbEncrypt dbEncrypt;
|
||||
|
||||
@@ -177,8 +193,6 @@ public class DatabasePlatform {
|
||||
|
||||
protected SqlExceptionTranslator exceptionTranslator = new SqlCodeTranslator();
|
||||
|
||||
protected char[] specialLikeCharacters = { '%', '_', '\\' };
|
||||
|
||||
/**
|
||||
* Instantiates a new database platform.
|
||||
*/
|
||||
@@ -583,8 +597,8 @@ public class DatabasePlatform {
|
||||
* <p>
|
||||
* This may include an escape clause to disable a default escape character.
|
||||
*/
|
||||
public String getLikeClause() {
|
||||
return likeClause;
|
||||
public String getLikeClause(boolean rawLikeExpression) {
|
||||
return rawLikeExpression ? likeClauseRaw : likeClauseEscaped;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -627,7 +641,7 @@ public class DatabasePlatform {
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
char ch = value.charAt(i);
|
||||
boolean escaped = false;
|
||||
for (char escapeChar: specialLikeCharacters) {
|
||||
for (char escapeChar: likeSpecialCharacters) {
|
||||
if (ch == escapeChar) {
|
||||
if (sb == null) {
|
||||
sb = new StringBuilder(value.substring(0, i));
|
||||
@@ -649,6 +663,6 @@ public class DatabasePlatform {
|
||||
}
|
||||
|
||||
protected void escapeLikeCharacter(char ch, StringBuilder sb) {
|
||||
sb.append('\\').append(ch);
|
||||
sb.append(likeEscapeChar).append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,10 +26,7 @@ public class DB2Platform extends DatabasePlatform {
|
||||
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
|
||||
this.likeClause = "like ? escape '|'";
|
||||
this.specialLikeCharacters = new char[] { '%', '_', '|' };
|
||||
|
||||
|
||||
this.exceptionTranslator =
|
||||
new SqlErrorCodes()
|
||||
.addAcquireLock("40001","57033") // key -911/-913
|
||||
|
||||
@@ -49,8 +49,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.likeClause = "like binary ? escape '|'";
|
||||
this.specialLikeCharacters = new char[] { '%', '_', '|' };
|
||||
this.likeClauseRaw = "like binary ? escape ''";
|
||||
this.likeClauseEscaped = "like binary ? escape '|'";
|
||||
|
||||
this.forwardOnlyHintOnFindIterate = true;
|
||||
this.booleanDbType = Types.BIT;
|
||||
|
||||
@@ -10,12 +10,13 @@ import io.ebean.config.dbplatform.DbType;
|
||||
import io.ebean.config.dbplatform.IdType;
|
||||
import io.ebean.config.dbplatform.PlatformIdGenerator;
|
||||
import io.ebean.config.dbplatform.RownumSqlLimiter;
|
||||
import io.ebean.config.dbplatform.SqlErrorCodes;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* Oracle10 and greater specific platform.
|
||||
* Oracle specific platform.
|
||||
*/
|
||||
public class OraclePlatform extends DatabasePlatform {
|
||||
|
||||
@@ -29,17 +30,21 @@ public class OraclePlatform extends DatabasePlatform {
|
||||
this.basicSqlLimiter = new BasicSqlAnsiLimiter();
|
||||
this.historySupport = new OracleDbHistorySupport();
|
||||
|
||||
// Not using getGeneratedKeys as instead we will
|
||||
// batch load sequences which enables JDBC batch execution
|
||||
dbIdentity.setSupportsGetGeneratedKeys(false);
|
||||
dbIdentity.setIdType(IdType.SEQUENCE);
|
||||
dbIdentity.setSupportsSequence(true);
|
||||
dbIdentity.setSupportsIdentity(true);
|
||||
dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
|
||||
this.treatEmptyStringsAsNull = true;
|
||||
this.likeClauseRaw = "like ?";
|
||||
|
||||
this.exceptionTranslator =
|
||||
new SqlErrorCodes()
|
||||
//.addAcquireLock("")
|
||||
.addDuplicateKey("1")
|
||||
.addDataIntegrity("2291")
|
||||
.build();
|
||||
|
||||
this.likeClause = "like ? escape '|'";
|
||||
this.specialLikeCharacters = new char[] { '%', '_', '|' };
|
||||
|
||||
this.openQuote = "\"";
|
||||
this.closeQuote = "\"";
|
||||
|
||||
@@ -81,7 +86,7 @@ public class OraclePlatform extends DatabasePlatform {
|
||||
return sql + " for update";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void escapeLikeCharacter(char ch, StringBuilder sb) {
|
||||
sb.append('|').append(ch);
|
||||
|
||||
@@ -19,12 +19,17 @@ public class SQLitePlatform extends DatabasePlatform {
|
||||
this.dbIdentity.setSelectLastInsertedIdTemplate("select last_insert_rowid()");
|
||||
|
||||
this.booleanDbType = Types.INTEGER;
|
||||
this.likeClauseRaw = "like ?";
|
||||
this.likeClauseEscaped = "like ?";
|
||||
|
||||
dbTypeMap.put(DbType.BIT, new DbPlatformType("int default 0"));
|
||||
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("int default 0"));
|
||||
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("integer"));
|
||||
dbTypeMap.put(DbType.SMALLINT, new DbPlatformType("integer"));
|
||||
}
|
||||
|
||||
protected void escapeLikeCharacter(char ch, StringBuilder sb) {
|
||||
sb.append(ch);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,8 +39,9 @@ public class SqlServerPlatform extends DatabasePlatform {
|
||||
|
||||
this.openQuote = "[";
|
||||
this.closeQuote = "]";
|
||||
this.specialLikeCharacters = new char[]{'%', '_', '['};
|
||||
this.likeClause = "like ? COLLATE Latin1_General_BIN";
|
||||
this.likeSpecialCharacters = new char[]{'%', '_', '['};
|
||||
this.likeClauseRaw = "like ? collate Latin1_General_BIN";
|
||||
this.likeClauseEscaped = "like ? collate Latin1_General_BIN";
|
||||
|
||||
booleanDbType = Types.INTEGER;
|
||||
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("bit default 0"));
|
||||
|
||||
@@ -83,6 +83,16 @@ public interface DbMigration {
|
||||
*/
|
||||
void setPlatform(DatabasePlatform databasePlatform);
|
||||
|
||||
/**
|
||||
* Set to false to turn off strict mode.
|
||||
* <p>
|
||||
* Strict mode checks that a column changed to non-null on an existing table via DB migration has a default
|
||||
* value specified. Set this to false if that isn't the case but it is known that all the existing rows have
|
||||
* a value specified (there are no existing null values for the column).
|
||||
* </p>
|
||||
*/
|
||||
void setStrictMode(boolean strictMode);
|
||||
|
||||
/**
|
||||
* Add an additional platform to write the migration DDL.
|
||||
* <p>
|
||||
@@ -101,7 +111,6 @@ public interface DbMigration {
|
||||
* <pre>{@code
|
||||
*
|
||||
* DbMigration migration = DbMigration.create();
|
||||
*
|
||||
* migration.setPlatform(Platform.POSTGRES);
|
||||
*
|
||||
* migration.generateMigration();
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface MetaBeanInfo {
|
||||
|
||||
/**
|
||||
* Collect the current query plan statistics return the non-empty statistics.
|
||||
*/
|
||||
List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset);
|
||||
|
||||
/**
|
||||
* Collect the current query plan statistics return all the statistics (include query plans that haven't had query executions).
|
||||
*/
|
||||
List<MetaQueryPlanStatistic> collectAllQueryPlanStatistics(boolean reset);
|
||||
|
||||
}
|
||||
@@ -8,17 +8,12 @@ import java.util.List;
|
||||
public interface MetaInfoManager {
|
||||
|
||||
/**
|
||||
* Return the MetaBeanInfo for a bean type.
|
||||
* Collect and return the transaction execution metrics.
|
||||
*/
|
||||
MetaBeanInfo getMetaBeanInfo(Class<?> beanClass);
|
||||
List<MetaTimedMetric> collectTransactionStatistics(boolean reset);
|
||||
|
||||
/**
|
||||
* Return all the MetaBeanInfo.
|
||||
*/
|
||||
List<MetaBeanInfo> getMetaBeanInfoList();
|
||||
|
||||
/**
|
||||
* Collect and return the query plan statistics for all the beans.
|
||||
* Collect and return the non-empty query plan statistics for all the beans.
|
||||
* <p>
|
||||
* Note that this excludes the query plan statistics where there has been no
|
||||
* executions (since the last collection with reset).
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -14,6 +16,11 @@ public interface MetaQueryPlanStatistic {
|
||||
*/
|
||||
Class<?> getBeanType();
|
||||
|
||||
/**
|
||||
* Return the profile location.
|
||||
*/
|
||||
ProfileLocation getProfileLocation();
|
||||
|
||||
/**
|
||||
* Return true if this query plan was tuned by AutoTune.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
|
||||
/**
|
||||
* Timed execution statistics.
|
||||
*/
|
||||
public interface MetaTimedMetric {
|
||||
|
||||
/**
|
||||
* Return the metric name.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Return the metric location if defined.
|
||||
*/
|
||||
String getLocation();
|
||||
|
||||
/**
|
||||
* Return the time the counters started from.
|
||||
*/
|
||||
long getStartTime();
|
||||
|
||||
/**
|
||||
* Return the total count.
|
||||
*/
|
||||
long getCount();
|
||||
|
||||
/**
|
||||
* Return the total execution time.
|
||||
*/
|
||||
long getTotal();
|
||||
|
||||
/**
|
||||
* Return the max execution time.
|
||||
*/
|
||||
long getMax();
|
||||
|
||||
/**
|
||||
* Return the mean execution time.
|
||||
*/
|
||||
long getMean();
|
||||
}
|
||||
@@ -47,4 +47,9 @@ public interface SpiServer extends EbeanServer {
|
||||
*/
|
||||
DataSource getDataSource();
|
||||
|
||||
/**
|
||||
* Return the associated read only DataSource for this EbeanServer instance (can be null).
|
||||
*/
|
||||
DataSource getReadOnlyDataSource();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.ebean.service;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
|
||||
/**
|
||||
* Factory for creating profile locations.
|
||||
*/
|
||||
public interface SpiProfileLocationFactory {
|
||||
|
||||
/**
|
||||
* Create a profile location.
|
||||
*/
|
||||
ProfileLocation create();
|
||||
|
||||
/**
|
||||
* Create a profile location with a line number.
|
||||
*/
|
||||
ProfileLocation create(int lineNumber, String label);
|
||||
|
||||
/**
|
||||
* Create a known location.
|
||||
*/
|
||||
ProfileLocation createAt(String location);
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* A hash for a query plan.
|
||||
*/
|
||||
public class HashQueryPlan {
|
||||
|
||||
private final String rawSql;
|
||||
|
||||
private final int planHash;
|
||||
|
||||
private final int bindCount;
|
||||
|
||||
public HashQueryPlan(String rawSql, int planHash, int bindCount) {
|
||||
this.rawSql = rawSql;
|
||||
this.planHash = planHash;
|
||||
this.bindCount = bindCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return planHash + ":" + bindCount + (rawSql != null ? ":r" : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return as a partial key. For rawSql hash the sql is part of the key and as such
|
||||
* needs to be included in order to have a complete key. Typically the MD5 of the sql
|
||||
* can be used as a shot form proxy for the actual sql.
|
||||
*/
|
||||
public String getPartialKey() {
|
||||
return planHash + "_" + bindCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hc = planHash;
|
||||
hc = hc * 92821 + bindCount;
|
||||
hc = hc * 92821 + (rawSql == null ? 0 : rawSql.hashCode());
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof HashQueryPlan)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HashQueryPlan e = (HashQueryPlan) obj;
|
||||
//noinspection StringEquality
|
||||
return e.planHash == planHash
|
||||
&& e.bindCount == bindCount
|
||||
&& ((e.rawSql == rawSql) || (e.rawSql != null && e.rawSql.equals(rawSql)));
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Used to build HashQueryPlan instances.
|
||||
*/
|
||||
public class HashQueryPlanBuilder {
|
||||
|
||||
private int planHash;
|
||||
|
||||
private int bindCount;
|
||||
|
||||
public HashQueryPlanBuilder() {
|
||||
this.planHash = 92821;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return planHash + ":" + bindCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(Class<?> cls) {
|
||||
planHash = planHash * 92821 + cls.getName().hashCode();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an object to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(Object object) {
|
||||
planHash = planHash * 92821 + (object == null ? 0 : object.hashCode());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the set with order being important.
|
||||
*/
|
||||
public HashQueryPlanBuilder addOrdered(Set<?> set) {
|
||||
if (set == null) {
|
||||
add(false);
|
||||
} else {
|
||||
add(true);
|
||||
for (Object o : set) {
|
||||
add(o);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an integer to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(int hashValue) {
|
||||
planHash = planHash * 92821 + (hashValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a boolean to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(boolean booleanValue) {
|
||||
planHash = planHash * 92821 + (booleanValue ? 92821 : 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a number to the bind count for the hash.
|
||||
*/
|
||||
public void bind(int extraBindCount) {
|
||||
bindCount += extraBindCount;
|
||||
}
|
||||
|
||||
public void bindIfNotNull(Object someValue) {
|
||||
if (someValue != null) {
|
||||
bindCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and return the calculated HashQueryPlan.
|
||||
*/
|
||||
public String build() {
|
||||
return planHash + "_" + bindCount;
|
||||
}
|
||||
|
||||
public int getPlanHash() {
|
||||
return planHash;
|
||||
}
|
||||
|
||||
public int getBindCount() {
|
||||
return bindCount;
|
||||
}
|
||||
}
|
||||
@@ -157,9 +157,9 @@ public class LoadBeanRequest extends LoadRequest {
|
||||
for (Object aList : list) {
|
||||
EntityBean loadedBean = (EntityBean) aList;
|
||||
loadedIds.add(desc.getId(loadedBean));
|
||||
if (isLoadCache()) {
|
||||
desc.cacheBeanPut(loadedBean);
|
||||
}
|
||||
}
|
||||
if (isLoadCache()) {
|
||||
desc.cacheBeanPutAll(list);
|
||||
}
|
||||
|
||||
if (lazyLoadProperty != null) {
|
||||
|
||||
@@ -49,8 +49,10 @@ public class NaturalKeyEntry {
|
||||
}
|
||||
|
||||
private void load(List<NaturalKeyEq> eqList) {
|
||||
for (NaturalKeyEq eq : eqList) {
|
||||
map.put(eq.property, eq.value);
|
||||
if (eqList != null) {
|
||||
for (NaturalKeyEq eq : eqList) {
|
||||
map.put(eq.property, eq.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,11 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
*/
|
||||
void clearQueryStatistics();
|
||||
|
||||
/**
|
||||
* Return the transaction manager.
|
||||
*/
|
||||
SpiTransactionManager getTransactionManager();
|
||||
|
||||
/**
|
||||
* Return all the descriptors.
|
||||
*/
|
||||
|
||||
@@ -64,7 +64,7 @@ public interface SpiExpressionRequest {
|
||||
/**
|
||||
* Append a DB Like clause.
|
||||
*/
|
||||
void appendLike();
|
||||
void appendLike(boolean rawLikeExpression);
|
||||
|
||||
/**
|
||||
* Escapes a string to use it as exact match in Like clause.
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.ebean.EbeanServer;
|
||||
import io.ebean.ExpressionList;
|
||||
import io.ebean.OrderBy;
|
||||
import io.ebean.PersistenceContextScope;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.bean.CallStack;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
@@ -98,17 +99,30 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
/**
|
||||
* Delete query.
|
||||
*/
|
||||
DELETE(FIND_DELETE),
|
||||
DELETE(FIND_DELETE, true),
|
||||
|
||||
/**
|
||||
* Update query.
|
||||
*/
|
||||
UPDATE(FIND_UPDATE);
|
||||
UPDATE(FIND_UPDATE, true);
|
||||
|
||||
boolean update;
|
||||
String profileEventId;
|
||||
|
||||
Type(String profileEventId) {
|
||||
this(profileEventId, false);
|
||||
}
|
||||
|
||||
Type(String profileEventId, boolean update) {
|
||||
this.profileEventId = profileEventId;
|
||||
this.update = update;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is an Update or Delete query (not read only).
|
||||
*/
|
||||
public boolean isUpdate() {
|
||||
return update;
|
||||
}
|
||||
|
||||
public String profileEventId() {
|
||||
@@ -161,9 +175,19 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
short getProfileId();
|
||||
|
||||
/**
|
||||
* Check for a single "equal to" expression for the Id.
|
||||
* Return the profile location for this query.
|
||||
*/
|
||||
void checkIdEqualTo();
|
||||
ProfileLocation getProfileLocation();
|
||||
|
||||
/**
|
||||
* Return true if this is a "find by id" query. This includes a check for a single "equal to" expression for the Id.
|
||||
*/
|
||||
boolean isFindById();
|
||||
|
||||
/**
|
||||
* Return true if this is a "find all" query. Used to set a "find all" profile location if necessary.
|
||||
*/
|
||||
boolean isFindAll();
|
||||
|
||||
/**
|
||||
* Return true if AutoTune should be attempted on this query.
|
||||
@@ -369,7 +393,7 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
/**
|
||||
* Reset AUTO mode to OFF for findList(). Expect explicit cache use with findList().
|
||||
*/
|
||||
void resetBeanCacheAutoMode();
|
||||
void resetBeanCacheAutoMode(boolean findOne);
|
||||
|
||||
/**
|
||||
* Collect natural key data for this query or null if the query does not match
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
@@ -24,6 +25,11 @@ import java.sql.SQLException;
|
||||
*/
|
||||
public interface SpiTransaction extends Transaction {
|
||||
|
||||
/**
|
||||
* Return the user defined label for the transaction.
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* Return the string prefix with the transaction id and label used in logging.
|
||||
*/
|
||||
@@ -294,8 +300,23 @@ public interface SpiTransaction extends Transaction {
|
||||
*/
|
||||
void profileEvent(SpiProfileTransactionEvent event);
|
||||
|
||||
/**
|
||||
* Set the profileStream to catch and time all the events for this transaction.
|
||||
*/
|
||||
void setProfileStream(ProfileStream profileStream);
|
||||
|
||||
/**
|
||||
* Return the stream that profiling events are written to.
|
||||
*/
|
||||
ProfileStream profileStream();
|
||||
|
||||
/**
|
||||
* Set the profile location for this transaction.
|
||||
*/
|
||||
void setProfileLocation(ProfileLocation profileLocation);
|
||||
|
||||
/**
|
||||
* Return the profile location for this transaction.
|
||||
*/
|
||||
ProfileLocation getProfileLocation();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.TxScope;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* Service provider interface for the transaction manager.
|
||||
*/
|
||||
public interface SpiTransactionManager {
|
||||
|
||||
/**
|
||||
* Return the main DataSource.
|
||||
*/
|
||||
DataSource getDataSource();
|
||||
|
||||
/**
|
||||
* Return the read only DataSource (if defined).
|
||||
*/
|
||||
DataSource getReadOnlyDataSource();
|
||||
|
||||
/**
|
||||
* Return the currently active transaction (can be null).
|
||||
*/
|
||||
SpiTransaction getActive();
|
||||
|
||||
/**
|
||||
* Push an externally managed transaction into scope (e.g. Spring managed transaction).
|
||||
*/
|
||||
ScopedTransaction externalBeginTransaction(SpiTransaction transaction, TxScope txScope);
|
||||
|
||||
/**
|
||||
* Called when an externally managed transaction has completed.
|
||||
*/
|
||||
void externalRemoveTransaction();
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.TransactionCallback;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
@@ -29,6 +30,16 @@ abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
return transaction.translate(message, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLabel(String label) {
|
||||
transaction.setLabel(label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return transaction.getLabel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commitAndContinue() {
|
||||
transaction.commitAndContinue();
|
||||
@@ -49,11 +60,26 @@ abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
transaction.profileEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProfileStream(ProfileStream profileStream) {
|
||||
transaction.setProfileStream(profileStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileStream profileStream() {
|
||||
return transaction.profileStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProfileLocation(ProfileLocation profileLocation) {
|
||||
transaction.setProfileLocation(profileLocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileLocation getProfileLocation() {
|
||||
return transaction.getProfileLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTenantId(Object tenantId) {
|
||||
transaction.setTenantId(tenantId);
|
||||
|
||||
@@ -2,21 +2,22 @@ package io.ebeaninternal.dbmigration;
|
||||
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.dbmigration.DbMigration;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.config.DbConstraintNaming;
|
||||
import io.ebean.config.DbMigrationConfig;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.db2.DB2Platform;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.db2.DB2Platform;
|
||||
import io.ebean.config.dbplatform.h2.H2Platform;
|
||||
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
|
||||
import io.ebean.config.dbplatform.sqlserver.SqlServerPlatform;
|
||||
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
|
||||
import io.ebean.config.dbplatform.oracle.OraclePlatform;
|
||||
import io.ebean.config.dbplatform.postgres.PostgresPlatform;
|
||||
import io.ebean.config.dbplatform.sqlanywhere.SqlAnywherePlatform;
|
||||
import io.ebean.config.dbplatform.sqlite.SQLitePlatform;
|
||||
import io.ebean.config.dbplatform.sqlserver.SqlServerPlatform;
|
||||
import io.ebean.dbmigration.DbMigration;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
|
||||
import io.ebeaninternal.dbmigration.migration.Migration;
|
||||
import io.ebeaninternal.dbmigration.migrationreader.MigrationXmlWriter;
|
||||
@@ -27,7 +28,6 @@ import io.ebeaninternal.dbmigration.model.MigrationVersion;
|
||||
import io.ebeaninternal.dbmigration.model.ModelContainer;
|
||||
import io.ebeaninternal.dbmigration.model.ModelDiff;
|
||||
import io.ebeaninternal.dbmigration.model.PlatformDdlWriter;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.extraddl.model.DdlScript;
|
||||
import io.ebeaninternal.extraddl.model.ExtraDdl;
|
||||
import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
|
||||
@@ -53,7 +53,7 @@ import java.util.List;
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* DefaultDbMigration migration = new DefaultDbMigration();
|
||||
* DbMigration migration = DbMigration.create();
|
||||
* migration.setPathToResources("src/main/resources");
|
||||
* migration.setPlatform(DbPlatformName.ORACLE);
|
||||
*
|
||||
@@ -88,6 +88,8 @@ public class DefaultDbMigration implements DbMigration {
|
||||
|
||||
protected DbConstraintNaming constraintNaming;
|
||||
|
||||
protected Boolean strictMode;
|
||||
|
||||
/**
|
||||
* Create for offline migration generation.
|
||||
*/
|
||||
@@ -139,6 +141,11 @@ public class DefaultDbMigration implements DbMigration {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStrictMode(boolean strictMode) {
|
||||
this.strictMode = strictMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the specific platform to generate DDL for.
|
||||
* <p>
|
||||
@@ -184,7 +191,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
* <h3>Example: Run for a single specific platform</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* DefaultDbMigration migration = new DefaultDbMigration();
|
||||
* DbMigration migration = DbMigration.create();
|
||||
* migration.setPathToResources("src/main/resources");
|
||||
* migration.setPlatform(DbPlatformName.ORACLE);
|
||||
*
|
||||
@@ -195,7 +202,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
* <h3>Example: Run migration generating DDL for multiple platforms</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* DefaultDbMigration migration = new DefaultDbMigration();
|
||||
* DbMigration migration = DbMigration.create();
|
||||
* migration.setPathToResources("src/main/resources");
|
||||
*
|
||||
* migration.addPlatform(DbPlatformName.POSTGRES, "pg");
|
||||
@@ -486,6 +493,9 @@ public class DefaultDbMigration implements DbMigration {
|
||||
databasePlatform = server.getDatabasePlatform();
|
||||
logger.debug("set platform to {}", databasePlatform.getName());
|
||||
}
|
||||
if (strictMode != null && migrationConfig != null) {
|
||||
migrationConfig.setStrictMode(strictMode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+13
-17
@@ -5,6 +5,7 @@ import io.ebean.config.NamingConvention;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DbHistorySupport;
|
||||
import io.ebean.config.dbplatform.IdType;
|
||||
import io.ebean.util.StringHelper;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.TableDdl;
|
||||
@@ -24,7 +25,6 @@ import io.ebeaninternal.dbmigration.migration.DropTable;
|
||||
import io.ebeaninternal.dbmigration.migration.ForeignKey;
|
||||
import io.ebeaninternal.dbmigration.migration.UniqueConstraint;
|
||||
import io.ebeaninternal.dbmigration.model.MTable;
|
||||
import io.ebean.util.StringHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
@@ -74,7 +74,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
*/
|
||||
protected Map<String, HistoryTableUpdate> regenerateHistoryTriggers = new LinkedHashMap<>();
|
||||
|
||||
private boolean strict;
|
||||
private boolean strictMode;
|
||||
|
||||
private final boolean sql2011History;
|
||||
|
||||
@@ -91,25 +91,24 @@ public class BaseTableDdl implements TableDdl {
|
||||
/**
|
||||
* Constructor for DdlMigrationHelp when adding a NEW column.
|
||||
*/
|
||||
DdlMigrationHelp(String tableName, Column column) throws IOException {
|
||||
DdlMigrationHelp(String tableName, Column column) {
|
||||
this.tableName = tableName;
|
||||
this.columnName = column.getName();
|
||||
this.defaultValue = platformDdl.convertDefaultValue(column.getDefaultValue());
|
||||
boolean alterNotNull = Boolean.TRUE.equals(column.isNotnull());
|
||||
|
||||
if (column.getBefore().isEmpty() && alterNotNull && defaultValue == null) {
|
||||
handleStrictError("non-null column has no default value: " + tableName + "." + columnName);
|
||||
handleStrictError(tableName, columnName);
|
||||
}
|
||||
|
||||
before = getScriptsForPlatform(column.getBefore(), platformDdl.getPlatform().getName());
|
||||
after = getScriptsForPlatform(column.getAfter(), platformDdl.getPlatform().getName());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for DdlMigrationHelp when altering a column.
|
||||
*/
|
||||
DdlMigrationHelp(AlterColumn alter) throws IOException {
|
||||
DdlMigrationHelp(AlterColumn alter) {
|
||||
this.tableName = alter.getTableName();
|
||||
this.columnName = alter.getColumnName();
|
||||
|
||||
@@ -120,18 +119,16 @@ public class BaseTableDdl implements TableDdl {
|
||||
// here we add the platform's default update script
|
||||
if (alter.getBefore().isEmpty() && alterNotNull) {
|
||||
if (defaultValue == null) {
|
||||
handleStrictError("non-null column has no default value: " + tableName + "." + columnName);
|
||||
handleStrictError(tableName, columnName);
|
||||
}
|
||||
before = Arrays.asList(platformDdl.getUpdateNullWithDefault());
|
||||
} else {
|
||||
before = getScriptsForPlatform(alter.getBefore(), platformDdl.getPlatform().getName());
|
||||
}
|
||||
|
||||
after = getScriptsForPlatform(alter.getAfter(), platformDdl.getPlatform().getName());
|
||||
|
||||
}
|
||||
|
||||
public void writeBefore(DdlBuffer buffer) throws IOException {
|
||||
void writeBefore(DdlBuffer buffer) throws IOException {
|
||||
if (!before.isEmpty()) {
|
||||
buffer.end();
|
||||
}
|
||||
@@ -141,8 +138,8 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
}
|
||||
|
||||
public void writeAfter(DdlBuffer buffer) throws IOException {
|
||||
// here we run postmigration scripts
|
||||
void writeAfter(DdlBuffer buffer) throws IOException {
|
||||
// here we run post migration scripts
|
||||
for (String ddlScript : after) {
|
||||
buffer.append(translate(ddlScript, tableName, columnName, defaultValue));
|
||||
buffer.endOfStatement();
|
||||
@@ -175,11 +172,10 @@ public class BaseTableDdl implements TableDdl {
|
||||
return StringHelper.replaceString(ret, "${default}", defaultValue);
|
||||
}
|
||||
|
||||
private void handleStrictError(String message) {
|
||||
if (strict) {
|
||||
private void handleStrictError(String tableName, String columnName) {
|
||||
if (strictMode) {
|
||||
String message = "DB Migration of non-null column with no default value specified for: " + tableName + "." + columnName+" Use @DbDefault to specify a default value or specify dbMigration.setStrictMode(false)";
|
||||
throw new IllegalArgumentException(message);
|
||||
} else {
|
||||
System.err.println("Error in DDL: " + message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +194,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
this.historyTableSuffix = serverConfig.getHistoryTableSuffix();
|
||||
this.platformDdl = platformDdl;
|
||||
this.platformDdl.configure(serverConfig);
|
||||
this.strict = true; // TODO RPr serverConfig.getMigrationConfig().isStrict();
|
||||
this.strictMode = serverConfig.getMigrationConfig().isStrictMode();
|
||||
DbHistorySupport hist = platformDdl.getPlatform().getHistorySupport();
|
||||
this.sql2011History = hist != null && hist.isStandardsBased();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ public class Oracle10Ddl extends PlatformDdl {
|
||||
this.columnSetNotnull = "not null";
|
||||
this.columnSetNull = "null";
|
||||
this.columnSetDefault = "default";
|
||||
this.identitySuffix = " generated always as identity";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-2
@@ -11,6 +11,7 @@ import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.IndexDefinition;
|
||||
import io.ebeaninternal.server.deploy.InheritInfo;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.deploy.TableJoinColumn;
|
||||
import io.ebeaninternal.server.deploy.id.ImportedId;
|
||||
|
||||
@@ -148,8 +149,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
|
||||
TableJoinColumn[] columns = p.getTableJoin().columns();
|
||||
if (columns.length == 0) {
|
||||
String msg = "No join columns for " + p.getFullBeanName();
|
||||
throw new RuntimeException(msg);
|
||||
throw new RuntimeException("No join columns for " + p.getFullBeanName());
|
||||
}
|
||||
|
||||
ImportedId importedId = p.getImportedId();
|
||||
@@ -231,6 +231,12 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
if (p.getBeanDescriptor().isUseIdGenerator()) {
|
||||
col.setIdentity(true);
|
||||
}
|
||||
TableJoin primaryKeyJoin = p.getBeanDescriptor().getPrimaryKeyJoin();
|
||||
if (primaryKeyJoin != null) {
|
||||
TableJoinColumn[] columns = primaryKeyJoin.columns();
|
||||
col.setReferences(primaryKeyJoin.getTable() + "." + columns[0].getForeignDbColumn());
|
||||
col.setForeignKeyName(determineForeignKeyConstraintName(col.getName()));
|
||||
}
|
||||
} else {
|
||||
col.setDefaultValue(p.getDbColumnDefault());
|
||||
col.setDbMigrationInfos(p.getDbMigrationInfos());
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Modify aware wrapper of a list.
|
||||
@@ -32,6 +33,19 @@ public class ModifyAwareList<E> implements List<E>, ModifyAwareOwner {
|
||||
return list.toString();
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMarkedDirty() {
|
||||
return owner.isMarkedDirty();
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.ebeaninternal.json;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -19,14 +20,14 @@ public class ModifyAwareMap<K, V> implements Map<K, V>, ModifyAwareOwner {
|
||||
*/
|
||||
final Map<K, V> map;
|
||||
|
||||
public ModifyAwareMap(Map<K, V> underyling) {
|
||||
this.map = underyling;
|
||||
public ModifyAwareMap(Map<K, V> underlying) {
|
||||
this.map = underlying;
|
||||
this.owner = new ModifyAwareFlag();
|
||||
}
|
||||
|
||||
public ModifyAwareMap(ModifyAwareOwner owner, Map<K, V> underyling) {
|
||||
public ModifyAwareMap(ModifyAwareOwner owner, Map<K, V> underlying) {
|
||||
this.owner = owner;
|
||||
this.map = underyling;
|
||||
this.map = underlying;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -34,6 +35,19 @@ public class ModifyAwareMap<K, V> implements Map<K, V>, ModifyAwareOwner {
|
||||
return map.toString();
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMarkedDirty() {
|
||||
return owner.isMarkedDirty();
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.json;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -51,6 +52,19 @@ public class ModifyAwareSet<E> implements Set<E>, ModifyAwareOwner {
|
||||
return set.toString();
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E o) {
|
||||
if (set.add(o)) {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.ebeaninternal.metric;
|
||||
|
||||
/**
|
||||
* Factory to create timed metric counters.
|
||||
*/
|
||||
public interface MetricFactory {
|
||||
|
||||
/**
|
||||
* Return the factory instance.
|
||||
*/
|
||||
static MetricFactory get() {
|
||||
return MetricServiceProvider.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a timed metric group.
|
||||
*/
|
||||
TimedMetricMap createTimedMetricMap(String name);
|
||||
|
||||
/**
|
||||
* Create a Timed metric.
|
||||
*/
|
||||
TimedMetric createTimedMetric(String name);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package io.ebeaninternal.metric;
|
||||
|
||||
import io.ebeaninternal.server.profile.DMetricFactory;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* Lookup MetricFactory service.
|
||||
*/
|
||||
class MetricServiceProvider {
|
||||
|
||||
private static MetricFactory metricFactory = init();
|
||||
|
||||
private static MetricFactory init() {
|
||||
|
||||
Iterator<MetricFactory> loader = ServiceLoader.load(MetricFactory.class).iterator();
|
||||
if (loader.hasNext()) {
|
||||
return loader.next();
|
||||
}
|
||||
return new DMetricFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the MetricFactory implementation.
|
||||
*/
|
||||
static MetricFactory get() {
|
||||
return metricFactory;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package io.ebeaninternal.metric;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Metric for timed events like transaction execution times.
|
||||
*/
|
||||
public interface TimedMetric {
|
||||
|
||||
/**
|
||||
* Add a time event (usually in microseconds).
|
||||
*/
|
||||
void add(long value);
|
||||
|
||||
/**
|
||||
* Return true if there are no metrics collected since the last collection.
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Collect the timed metric statistics.
|
||||
*/
|
||||
TimedMetricStats collect(boolean reset);
|
||||
|
||||
/**
|
||||
* Add non empty metrics to the result.
|
||||
*/
|
||||
void collect(boolean reset, List<MetaTimedMetric> result);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.ebeaninternal.metric;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A map of timed metrics keyed by a string.
|
||||
*/
|
||||
public interface TimedMetricMap {
|
||||
|
||||
/**
|
||||
* Add an execution for the given key.
|
||||
*/
|
||||
void add(String key, long exeMicros);
|
||||
|
||||
/**
|
||||
* Add non empty metrics to the given result.
|
||||
*/
|
||||
void collect(boolean reset, List<MetaTimedMetric> result);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.ebeaninternal.metric;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
|
||||
/**
|
||||
* Extend public MetaTimedMetric with ability to set details from profile location.
|
||||
*/
|
||||
public interface TimedMetricStats extends MetaTimedMetric {
|
||||
|
||||
/**
|
||||
* Additionally set the location.
|
||||
*/
|
||||
void setLocation(String location);
|
||||
}
|
||||
@@ -122,8 +122,7 @@ public class CacheChangeSet {
|
||||
*/
|
||||
private ManyChange many(BeanDescriptor<?> desc, String manyProperty) {
|
||||
ManyKey key = new ManyKey(desc, manyProperty);
|
||||
ManyChange manyChange = manyChangeMap.computeIfAbsent(key, ManyChange::new);
|
||||
return manyChange;
|
||||
return manyChangeMap.computeIfAbsent(key, ManyChange::new);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package io.ebeaninternal.server.cache;
|
||||
|
||||
import io.ebean.cache.ServerCacheFactory;
|
||||
import io.ebean.cache.ServerCacheOptions;
|
||||
import io.ebean.config.CurrentTenantProvider;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebeaninternal.server.cluster.ClusterManager;
|
||||
|
||||
/**
|
||||
* Configuration options when creating the default cache manager.
|
||||
*/
|
||||
public class CacheManagerOptions {
|
||||
|
||||
private final ClusterManager clusterManager;
|
||||
|
||||
private final ServerConfig serverConfig;
|
||||
|
||||
private final boolean localL2Caching;
|
||||
|
||||
private CurrentTenantProvider currentTenantProvider;
|
||||
|
||||
private ServerCacheFactory cacheFactory = new DefaultServerCacheFactory();
|
||||
private ServerCacheOptions beanDefault = new ServerCacheOptions();
|
||||
private ServerCacheOptions queryDefault = new ServerCacheOptions();
|
||||
|
||||
CacheManagerOptions() {
|
||||
this.localL2Caching = true;
|
||||
this.clusterManager = null;
|
||||
this.serverConfig = null;
|
||||
this.cacheFactory = new DefaultServerCacheFactory();
|
||||
this.beanDefault = new ServerCacheOptions();
|
||||
this.queryDefault = new ServerCacheOptions();
|
||||
}
|
||||
|
||||
public CacheManagerOptions(ClusterManager clusterManager, ServerConfig serverConfig, boolean localL2Caching) {
|
||||
this.clusterManager = clusterManager;
|
||||
this.serverConfig = serverConfig;
|
||||
this.localL2Caching = localL2Caching;
|
||||
this.currentTenantProvider = serverConfig.getCurrentTenantProvider();
|
||||
}
|
||||
|
||||
public CacheManagerOptions with(ServerCacheOptions beanDefault, ServerCacheOptions queryDefault) {
|
||||
this.beanDefault = beanDefault;
|
||||
this.queryDefault = queryDefault;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CacheManagerOptions with(ServerCacheFactory cacheFactory) {
|
||||
this.cacheFactory = cacheFactory;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CacheManagerOptions with(CurrentTenantProvider currentTenantProvider) {
|
||||
this.currentTenantProvider = currentTenantProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getServerName() {
|
||||
return (serverConfig == null) ? "db" : serverConfig.getName();
|
||||
}
|
||||
|
||||
public boolean isLocalL2Caching() {
|
||||
return localL2Caching;
|
||||
}
|
||||
|
||||
public ServerCacheFactory getCacheFactory() {
|
||||
return cacheFactory;
|
||||
}
|
||||
|
||||
public ServerCacheOptions getBeanDefault() {
|
||||
return beanDefault;
|
||||
}
|
||||
|
||||
public ServerCacheOptions getQueryDefault() {
|
||||
return queryDefault;
|
||||
}
|
||||
|
||||
public CurrentTenantProvider getCurrentTenantProvider() {
|
||||
return currentTenantProvider;
|
||||
}
|
||||
|
||||
public ClusterManager getClusterManager() {
|
||||
return clusterManager;
|
||||
}
|
||||
}
|
||||
@@ -44,12 +44,21 @@ public class DefaultCacheAdapter implements ServerCacheManager {
|
||||
|
||||
@Override
|
||||
public void clear(Class<?> beanType) {
|
||||
cacheManager.getBeanCache(beanType).clear();
|
||||
cacheManager.getQueryCache(beanType).clear();
|
||||
cacheManager.clear(beanType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAll() {
|
||||
cacheManager.clearAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAllLocal() {
|
||||
cacheManager.clearAllLocal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearLocal(Class<?> beanType) {
|
||||
cacheManager.clearLocal(beanType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,16 +8,24 @@ import io.ebean.cache.ServerCacheOptions;
|
||||
import io.ebean.cache.ServerCacheType;
|
||||
import io.ebean.config.CurrentTenantProvider;
|
||||
import io.ebean.util.AnnotationUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
|
||||
/**
|
||||
* Manages the construction of caches.
|
||||
*/
|
||||
class DefaultCacheHolder {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger("io.ebean.cache.ALL");
|
||||
|
||||
private final ConcurrentHashMap<String, ServerCache> allCaches = new ConcurrentHashMap<>();
|
||||
|
||||
private final ConcurrentHashMap<String, Set<String>> collectIdCaches = new ConcurrentHashMap<>();
|
||||
|
||||
private final ServerCacheFactory cacheFactory;
|
||||
|
||||
private final ServerCacheOptions beanDefault;
|
||||
@@ -25,6 +33,10 @@ class DefaultCacheHolder {
|
||||
|
||||
private final CurrentTenantProvider tenantProvider;
|
||||
|
||||
DefaultCacheHolder(CacheManagerOptions builder) {
|
||||
this(builder.getCacheFactory(), builder.getBeanDefault(), builder.getQueryDefault(), builder.getCurrentTenantProvider());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create with a cache factory and default cache options.
|
||||
*
|
||||
@@ -42,7 +54,6 @@ class DefaultCacheHolder {
|
||||
ServerCache getCache(Class<?> beanType, String cacheKey, ServerCacheType type) {
|
||||
|
||||
return getCacheInternal(beanType, cacheKey, type);
|
||||
|
||||
}
|
||||
|
||||
private String key(String cacheKey, ServerCacheType type) {
|
||||
@@ -60,16 +71,43 @@ class DefaultCacheHolder {
|
||||
|
||||
private ServerCache createCache(Class<?> beanType, ServerCacheType type, String key) {
|
||||
ServerCacheOptions options = getCacheOptions(beanType, type);
|
||||
|
||||
if (type == ServerCacheType.COLLECTION_IDS) {
|
||||
synchronized (this) {
|
||||
collectIdCaches.computeIfAbsent(beanType.getName(), s -> new ConcurrentSkipListSet<>()).add(key);
|
||||
}
|
||||
}
|
||||
return cacheFactory.createCache(type, key, tenantProvider, options);
|
||||
}
|
||||
|
||||
void clearAll() {
|
||||
log.debug("clearAll");
|
||||
for (ServerCache serverCache : allCaches.values()) {
|
||||
serverCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void clear(String name) {
|
||||
log.debug("clear {}", name);
|
||||
clearIfExists(key(name, ServerCacheType.QUERY));
|
||||
clearIfExists(key(name, ServerCacheType.BEAN));
|
||||
clearIfExists(key(name, ServerCacheType.NATURAL_KEY));
|
||||
Set<String> keys = collectIdCaches.get(name);
|
||||
if (keys != null) {
|
||||
for (String collectionIdKey : keys) {
|
||||
clearIfExists(collectionIdKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void clearIfExists(String fullKey) {
|
||||
ServerCache cache = allCaches.get(fullKey);
|
||||
if (cache != null) {
|
||||
log.trace("clear cache {}", fullKey);
|
||||
cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cache options for a given bean type.
|
||||
*/
|
||||
|
||||
@@ -6,15 +6,14 @@ import io.ebean.cache.ServerCacheOptions;
|
||||
import io.ebean.cache.ServerCacheStatistics;
|
||||
import io.ebean.cache.TenantAwareKey;
|
||||
import io.ebean.config.CurrentTenantProvider;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -217,20 +216,23 @@ public class DefaultServerCache implements ServerCache {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<Object, Object> keyValues) {
|
||||
keyValues.forEach(this::put);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value into the cache.
|
||||
*/
|
||||
@Override
|
||||
public Object put(Object id, Object value) {
|
||||
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();
|
||||
return null;
|
||||
} else {
|
||||
updateCount.increment();
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,14 +240,11 @@ public class DefaultServerCache implements ServerCache {
|
||||
* Remove an entry from the cache.
|
||||
*/
|
||||
@Override
|
||||
public Object remove(Object id) {
|
||||
public void remove(Object id) {
|
||||
|
||||
CacheEntry entry = map.remove(key(id));
|
||||
if (entry == null) {
|
||||
return null;
|
||||
} else {
|
||||
if (entry != null) {
|
||||
removeCount.increment();
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,10 +358,8 @@ public class DefaultServerCache implements ServerCache {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public int compare(CacheEntry entry1, CacheEntry entry2) {
|
||||
long x = entry1.getLastAccessTime();
|
||||
long y = entry2.getLastAccessTime();
|
||||
return (x < y) ? -1 : ((x == y) ? 0 : 1);
|
||||
public int compare(CacheEntry e1, CacheEntry e2) {
|
||||
return Long.compare(e1.getLastAccessTime(), e2.getLastAccessTime());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+32
-8
@@ -1,34 +1,37 @@
|
||||
package io.ebeaninternal.server.cache;
|
||||
|
||||
import io.ebean.cache.ServerCache;
|
||||
import io.ebean.cache.ServerCacheFactory;
|
||||
import io.ebean.cache.ServerCacheOptions;
|
||||
import io.ebean.cache.ServerCacheType;
|
||||
import io.ebean.config.CurrentTenantProvider;
|
||||
import io.ebeaninternal.server.cluster.ClusterManager;
|
||||
|
||||
/**
|
||||
* Manages the bean and query caches.
|
||||
*/
|
||||
public class DefaultServerCacheManager implements SpiCacheManager {
|
||||
|
||||
private final ClusterManager clusterManager;
|
||||
|
||||
private final DefaultCacheHolder cacheHolder;
|
||||
|
||||
private final boolean localL2Caching;
|
||||
|
||||
private final String serverName;
|
||||
|
||||
/**
|
||||
* Create with a cache factory and default cache options.
|
||||
*/
|
||||
public DefaultServerCacheManager(boolean localL2Caching, CurrentTenantProvider tenantProvider, ServerCacheFactory cacheFactory,
|
||||
ServerCacheOptions beanDefault, ServerCacheOptions queryDefault) {
|
||||
this.localL2Caching = localL2Caching;
|
||||
this.cacheHolder = new DefaultCacheHolder(cacheFactory, beanDefault, queryDefault, tenantProvider);
|
||||
public DefaultServerCacheManager(CacheManagerOptions builder) {
|
||||
this.clusterManager = builder.getClusterManager();
|
||||
this.serverName = builder.getServerName();
|
||||
this.localL2Caching = builder.isLocalL2Caching();
|
||||
this.cacheHolder = new DefaultCacheHolder(builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct when l2 cache is disabled.
|
||||
*/
|
||||
public DefaultServerCacheManager() {
|
||||
this(true, null, new DefaultServerCacheFactory(), new ServerCacheOptions(), new ServerCacheOptions());
|
||||
this(new CacheManagerOptions());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -42,6 +45,27 @@ public class DefaultServerCacheManager implements SpiCacheManager {
|
||||
@Override
|
||||
public void clearAll() {
|
||||
cacheHolder.clearAll();
|
||||
if (clusterManager != null) {
|
||||
clusterManager.cacheClearAll(serverName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAllLocal() {
|
||||
cacheHolder.clearAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(Class<?> beanType) {
|
||||
cacheHolder.clear(name(beanType));
|
||||
if (clusterManager != null) {
|
||||
clusterManager.cacheClear(serverName, beanType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearLocal(Class<?> beanType) {
|
||||
cacheHolder.clear(name(beanType));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package io.ebeaninternal.server.cache;
|
||||
|
||||
import io.ebeaninternal.server.cluster.BinaryMessage;
|
||||
import io.ebeaninternal.server.cluster.BinaryMessageList;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Cache events broadcast across the cluster.
|
||||
*/
|
||||
public class RemoteCacheEvent {
|
||||
|
||||
private boolean clearAll;
|
||||
|
||||
private List<String> clearCaches;
|
||||
|
||||
/**
|
||||
* Clear all the caches.
|
||||
*/
|
||||
public RemoteCacheEvent(boolean clearAll) {
|
||||
this.clearAll = clearAll;
|
||||
this.clearCaches = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear caches for the given bean type.
|
||||
*/
|
||||
public RemoteCacheEvent(Class<?> beanType) {
|
||||
this.clearAll = false;
|
||||
this.clearCaches = new ArrayList<>(1);
|
||||
this.clearCaches.add(beanType.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create with the read options.
|
||||
*/
|
||||
public RemoteCacheEvent(boolean clearAll, List<String> beanTypes) {
|
||||
this.clearAll = clearAll;
|
||||
this.clearCaches = beanTypes;
|
||||
}
|
||||
|
||||
public boolean isClearAll() {
|
||||
return clearAll;
|
||||
}
|
||||
|
||||
public List<String> getClearCaches() {
|
||||
return clearCaches;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "clearAll:" + clearAll + " caches:" + clearCaches;
|
||||
}
|
||||
|
||||
public static RemoteCacheEvent readBinaryMessage(DataInput dataInput) throws IOException {
|
||||
|
||||
boolean clearAll = dataInput.readBoolean();
|
||||
int size = dataInput.readInt();
|
||||
|
||||
List<String> clearCache = null;
|
||||
if (size > 0) {
|
||||
clearCache = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
clearCache.add(dataInput.readUTF());
|
||||
}
|
||||
}
|
||||
|
||||
return new RemoteCacheEvent(clearAll, clearCache);
|
||||
}
|
||||
|
||||
public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
|
||||
|
||||
int bufferSize = (clearCaches == null) ? 0 : clearCaches.size() * 30;
|
||||
|
||||
BinaryMessage msg = new BinaryMessage(bufferSize + 10);
|
||||
DataOutputStream os = msg.getOs();
|
||||
os.writeInt(BinaryMessage.TYPE_CACHE);
|
||||
os.writeBoolean(clearAll);
|
||||
if (clearCaches == null) {
|
||||
os.writeInt(0);
|
||||
} else {
|
||||
os.writeInt(clearCaches.size());
|
||||
for (String cacheName : clearCaches) {
|
||||
os.writeUTF(cacheName);
|
||||
}
|
||||
}
|
||||
msgList.add(msg);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -36,9 +36,24 @@ public interface SpiCacheManager {
|
||||
*/
|
||||
ServerCache getQueryCache(Class<?> beanType);
|
||||
|
||||
/**
|
||||
* Clear the caches for the given bean type.
|
||||
*/
|
||||
void clear(Class<?> beanType);
|
||||
|
||||
/**
|
||||
* Clear all the caches.
|
||||
*/
|
||||
void clearAll();
|
||||
|
||||
/**
|
||||
* Clear all local caches.
|
||||
*/
|
||||
void clearAllLocal();
|
||||
|
||||
/**
|
||||
* Clear local caches for the given bean type.
|
||||
*/
|
||||
void clearLocal(Class<?> beanType);
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ public class BinaryMessage {
|
||||
public static final int TYPE_MSGCONTROL = 0;
|
||||
public static final int TYPE_BEANIUD = 1;
|
||||
public static final int TYPE_TABLEIUD = 2;
|
||||
public static final int TYPE_CACHE = 3;
|
||||
|
||||
public static final int TYPE_MSGACK = 8;
|
||||
public static final int TYPE_MSGRESEND = 9;
|
||||
|
||||
@@ -5,12 +5,10 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* Holds a List of BinaryMessage's.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class BinaryMessageList {
|
||||
|
||||
final ArrayList<BinaryMessage> list = new ArrayList<>();
|
||||
final List<BinaryMessage> list = new ArrayList<>();
|
||||
|
||||
public void add(BinaryMessage msg) {
|
||||
list.add(msg);
|
||||
|
||||
@@ -24,5 +24,4 @@ public interface ClusterBroadcast {
|
||||
* Send a transaction event to all the members of the cluster.
|
||||
*/
|
||||
void broadcast(RemoteTransactionEvent remoteTransEvent);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.server.cluster;
|
||||
|
||||
import java.util.Properties;
|
||||
import io.ebean.config.ContainerConfig;
|
||||
|
||||
/**
|
||||
* Factory to create the cluster broadcast service.
|
||||
@@ -10,5 +10,5 @@ public interface ClusterBroadcastFactory {
|
||||
/**
|
||||
* Create the cluster transport with the manager and deployment properties.
|
||||
*/
|
||||
ClusterBroadcast create(ClusterManager manager, Properties properties);
|
||||
ClusterBroadcast create(ClusterManager manager, ContainerConfig config);
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@ public class ClusterManager {
|
||||
|
||||
private static final Logger clusterLogger = LoggerFactory.getLogger("io.ebean.Cluster");
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ClusterManager.class);
|
||||
|
||||
private final ConcurrentHashMap<String, EbeanServer> serverMap = new ConcurrentHashMap<>();
|
||||
|
||||
private final Object monitor = new Object();
|
||||
@@ -27,12 +25,14 @@ public class ClusterManager {
|
||||
|
||||
private boolean started;
|
||||
|
||||
private boolean shutdown;
|
||||
|
||||
public ClusterManager(ContainerConfig config) {
|
||||
if (!config.isClusterActive()) {
|
||||
broadcast = null;
|
||||
ClusterBroadcastFactory factory = createFactory();
|
||||
if (factory != null) {
|
||||
broadcast = factory.create(this, config);
|
||||
} else {
|
||||
ClusterBroadcastFactory factory = createFactory();
|
||||
broadcast = factory.create(this, config.getProperties());
|
||||
broadcast = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,10 +47,6 @@ public class ClusterManager {
|
||||
if (iterator.hasNext()) {
|
||||
factory = iterator.next();
|
||||
}
|
||||
if (factory == null) {
|
||||
throw new IllegalStateException("No ClusterTransportFactory found in classpath. "
|
||||
+ " Probably need to add the avaje-ebeanorm-cluster dependency");
|
||||
}
|
||||
return factory;
|
||||
}
|
||||
|
||||
@@ -76,6 +72,24 @@ public class ClusterManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast a cache clear all event to the cluster.
|
||||
*/
|
||||
public void cacheClearAll(String serverName) {
|
||||
if (broadcast != null) {
|
||||
broadcast.broadcast(new RemoteTransactionEvent(serverName).cacheClearAll());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast a cache clear event to the cluster.
|
||||
*/
|
||||
public void cacheClear(String serverName, Class<?> beanType) {
|
||||
if (broadcast != null) {
|
||||
broadcast.broadcast(new RemoteTransactionEvent(serverName).cacheClear(beanType));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if clustering is on.
|
||||
*/
|
||||
@@ -99,8 +113,8 @@ public class ClusterManager {
|
||||
* Shutdown the service and Deregister from the cluster.
|
||||
*/
|
||||
public void shutdown() {
|
||||
if (broadcast != null) {
|
||||
logger.info("ClusterManager shutdown ");
|
||||
if (broadcast != null && !shutdown) {
|
||||
shutdown = true;
|
||||
broadcast.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,14 @@ public class CObjectGraphNodeStatistics {
|
||||
|
||||
private final AtomicLong startTime = new AtomicLong(System.currentTimeMillis());
|
||||
|
||||
public CObjectGraphNodeStatistics(ObjectGraphNode node) {
|
||||
CObjectGraphNodeStatistics(ObjectGraphNode node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return count.sum() == 0;
|
||||
}
|
||||
|
||||
public void add(long beanCount, long exeMicros) {
|
||||
count.increment();
|
||||
totalTime.add(exeMicros);
|
||||
|
||||
@@ -10,5 +10,5 @@ public interface CallStackFactory {
|
||||
/**
|
||||
* Create and return the CallStack given the stack trace elements.
|
||||
*/
|
||||
CallStack createCallStack(StackTraceElement[] finalTrace);
|
||||
CallStack createCallStack();
|
||||
}
|
||||
|
||||
@@ -2,13 +2,56 @@ package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.bean.CallStack;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Default CallStackFactory where the Hash function for StackTraceElement includes the line number.
|
||||
*/
|
||||
public class DefaultCallStackFactory implements CallStackFactory {
|
||||
|
||||
private static final int IGNORE_LEADING_ELEMENTS = 5;
|
||||
|
||||
private static final String IO_EBEAN = "io.ebean";
|
||||
|
||||
private final int maxCallStack;
|
||||
|
||||
DefaultCallStackFactory(int maxCallStack) {
|
||||
this.maxCallStack = maxCallStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallStack createCallStack(StackTraceElement[] finalTrace) {
|
||||
public CallStack createCallStack() {
|
||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||
|
||||
// ignore the first 6 as they are always avaje stack elements
|
||||
int startIndex = IGNORE_LEADING_ELEMENTS;
|
||||
|
||||
// find the first non-avaje stackElement
|
||||
for (; startIndex < stackTrace.length; startIndex++) {
|
||||
if (!stackTrace[startIndex].getClassName().startsWith(IO_EBEAN)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int stackLength = stackTrace.length - startIndex;
|
||||
if (stackLength > maxCallStack) {
|
||||
// maximum of maxCallStack stackTrace elements
|
||||
stackLength = maxCallStack;
|
||||
}
|
||||
|
||||
// create the 'interesting' part of the stackTrace
|
||||
StackTraceElement[] finalTrace = new StackTraceElement[stackLength];
|
||||
System.arraycopy(stackTrace, startIndex, finalTrace, 0, stackLength);
|
||||
|
||||
if (stackLength < 1) {
|
||||
// this should not really happen
|
||||
throw new RuntimeException("StackTraceElement size 0? stack: " + Arrays.toString(stackTrace));
|
||||
}
|
||||
|
||||
return createCallStack(finalTrace);
|
||||
}
|
||||
|
||||
private CallStack createCallStack(StackTraceElement[] finalTrace) {
|
||||
return new CallStack(finalTrace, finalTrace[0].hashCode(), pathHash(finalTrace));
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.cache.ServerCacheFactory;
|
||||
import io.ebean.cache.ServerCacheOptions;
|
||||
import io.ebean.cache.ServerCachePlugin;
|
||||
import io.ebean.service.SpiContainer;
|
||||
import io.ebean.config.ContainerConfig;
|
||||
import io.ebean.config.PropertyMap;
|
||||
import io.ebean.config.ServerConfig;
|
||||
@@ -12,10 +11,12 @@ import io.ebean.config.TenantMode;
|
||||
import io.ebean.config.UnderscoreNamingConvention;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.h2.H2Platform;
|
||||
import io.ebeaninternal.dbmigration.DbOffline;
|
||||
import io.ebean.service.SpiContainer;
|
||||
import io.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import io.ebeaninternal.api.SpiContainerBootup;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.dbmigration.DbOffline;
|
||||
import io.ebeaninternal.server.cache.CacheManagerOptions;
|
||||
import io.ebeaninternal.server.cache.DefaultServerCacheManager;
|
||||
import io.ebeaninternal.server.cache.DefaultServerCachePlugin;
|
||||
import io.ebeaninternal.server.cache.SpiCacheManager;
|
||||
@@ -71,6 +72,7 @@ public class DefaultContainer implements SpiContainer {
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
clusterManager.shutdown();
|
||||
ShutdownManager.shutdown();
|
||||
}
|
||||
|
||||
@@ -201,7 +203,11 @@ public class DefaultContainer implements SpiContainer {
|
||||
}
|
||||
|
||||
ServerCacheFactory factory = plugin.create(serverConfig, executor);
|
||||
return new DefaultServerCacheManager(localL2Caching, serverConfig.getCurrentTenantProvider(), factory, beanOptions, queryOptions);
|
||||
|
||||
CacheManagerOptions builder = new CacheManagerOptions(clusterManager, serverConfig, localL2Caching)
|
||||
.with(beanOptions, queryOptions)
|
||||
.with(factory);
|
||||
return new DefaultServerCacheManager(builder);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -272,18 +278,21 @@ public class DefaultContainer implements SpiContainer {
|
||||
*/
|
||||
private void setDataSource(ServerConfig config) {
|
||||
if (config.getDataSource() == null) {
|
||||
config.setDataSource(getDataSourceFromConfig(config));
|
||||
config.setDataSource(getDataSourceFromConfig(config, false));
|
||||
}
|
||||
if (config.getReadOnlyDataSource() == null && config.isAutoReadOnlyDataSource()) {
|
||||
config.setReadOnlyDataSource(getDataSourceFromConfig(config, true));
|
||||
}
|
||||
}
|
||||
|
||||
private DataSource getDataSourceFromConfig(ServerConfig config) {
|
||||
private DataSource getDataSourceFromConfig(ServerConfig config, boolean readOnly) {
|
||||
|
||||
if (isOfflineMode(config)) {
|
||||
logger.debug("... DbOffline using platform [{}]", DbOffline.getPlatform());
|
||||
return null;
|
||||
}
|
||||
|
||||
if (config.getDataSourceJndiName() != null) {
|
||||
if (!readOnly && config.getDataSourceJndiName() != null) {
|
||||
DataSource ds = jndiDataSourceFactory.lookup(config.getDataSourceJndiName());
|
||||
if (ds == null) {
|
||||
throw new PersistenceException("JNDI lookup for DataSource " + config.getDataSourceJndiName() + " returned null.");
|
||||
@@ -292,7 +301,7 @@ public class DefaultContainer implements SpiContainer {
|
||||
}
|
||||
}
|
||||
|
||||
DataSourceConfig dsConfig = config.getDataSourceConfig();
|
||||
DataSourceConfig dsConfig = (readOnly) ? config.getReadOnlyDataSourceConfig() : config.getDataSourceConfig();
|
||||
if (dsConfig == null) {
|
||||
throw new PersistenceException("No DataSourceConfig defined for " + config.getName());
|
||||
}
|
||||
@@ -317,7 +326,15 @@ public class DefaultContainer implements SpiContainer {
|
||||
|
||||
attachListener(config, dsConfig);
|
||||
|
||||
return factory.createPool(config.getName(), dsConfig);
|
||||
if (readOnly) {
|
||||
// setup to use AutoCommit such that we skip explicit commit
|
||||
dsConfig.setAutoCommit(true);
|
||||
//dsConfig.setReadOnly(true);
|
||||
dsConfig.setDefaults(config.getDataSourceConfig());
|
||||
dsConfig.setIsolationLevel(config.getDataSourceConfig().getIsolationLevel());
|
||||
}
|
||||
String poolName = config.getName() + (readOnly ? "-ro" : "");
|
||||
return factory.createPool(poolName, dsConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.meta.MetaBeanInfo;
|
||||
import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.meta.MetaObjectGraphNodeStats;
|
||||
import io.ebean.meta.MetaQueryPlanStatistic;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.query.CQueryPlanStatsCollector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -15,41 +17,32 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
|
||||
private final DefaultServer server;
|
||||
|
||||
public DefaultMetaInfoManager(DefaultServer server) {
|
||||
DefaultMetaInfoManager(DefaultServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaBeanInfo getMetaBeanInfo(Class<?> beanClass) {
|
||||
return server.getBeanDescriptor(beanClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaBeanInfo> getMetaBeanInfoList() {
|
||||
|
||||
return new ArrayList<>(server.getBeanDescriptors());
|
||||
public List<MetaTimedMetric> collectTransactionStatistics(boolean reset) {
|
||||
return server.collectTransactionStatistics(reset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset) {
|
||||
|
||||
List<MetaQueryPlanStatistic> list = new ArrayList<>();
|
||||
for (MetaBeanInfo metaBeanInfo : getMetaBeanInfoList()) {
|
||||
list.addAll(metaBeanInfo.collectQueryPlanStatistics(reset));
|
||||
CQueryPlanStatsCollector collector = new CQueryPlanStatsCollector(reset);
|
||||
for (BeanDescriptor<?> desc : server.getBeanDescriptors()) {
|
||||
desc.collectQueryPlanStatistics(collector);
|
||||
}
|
||||
return list;
|
||||
return collector.getList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaObjectGraphNodeStats> collectNodeStatistics(boolean reset) {
|
||||
|
||||
List<MetaObjectGraphNodeStats> list = new ArrayList<>();
|
||||
|
||||
for (CObjectGraphNodeStatistics nodeStatistics : server.objectGraphStats.values()) {
|
||||
MetaObjectGraphNodeStats nodeStats = nodeStatistics.get(reset);
|
||||
if (nodeStats.getCount() > 0) {
|
||||
// Only collection non-empty statistics
|
||||
list.add(nodeStats);
|
||||
if (!nodeStatistics.isEmpty()) {
|
||||
list.add(nodeStatistics.get(reset));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
|
||||
@@ -12,6 +12,7 @@ import io.ebean.FutureList;
|
||||
import io.ebean.FutureRowCount;
|
||||
import io.ebean.PagedList;
|
||||
import io.ebean.PersistenceContextScope;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.QueryIterator;
|
||||
import io.ebean.SqlQuery;
|
||||
@@ -25,7 +26,6 @@ import io.ebean.UpdateQuery;
|
||||
import io.ebean.ValuePair;
|
||||
import io.ebean.Version;
|
||||
import io.ebean.annotation.TxIsolation;
|
||||
import io.ebean.annotation.TxType;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.CallStack;
|
||||
import io.ebean.bean.EntityBean;
|
||||
@@ -46,6 +46,7 @@ import io.ebean.event.BeanPersistController;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.plugin.Plugin;
|
||||
import io.ebean.plugin.SpiServer;
|
||||
@@ -53,7 +54,6 @@ import io.ebean.text.csv.CsvReader;
|
||||
import io.ebean.text.json.JsonContext;
|
||||
import io.ebeaninternal.api.LoadBeanRequest;
|
||||
import io.ebeaninternal.api.LoadManyRequest;
|
||||
import io.ebeaninternal.api.ScopeTrans;
|
||||
import io.ebeaninternal.api.ScopedTransaction;
|
||||
import io.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
@@ -61,10 +61,12 @@ import io.ebeaninternal.api.SpiJsonContext;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiQuery.Type;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiTransactionManager;
|
||||
import io.ebeaninternal.api.TransactionEventTable;
|
||||
import io.ebeaninternal.dbmigration.DdlGenerator;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
|
||||
import io.ebeaninternal.server.autotune.AutoTuneService;
|
||||
import io.ebeaninternal.server.cache.RemoteCacheEvent;
|
||||
import io.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
@@ -91,7 +93,6 @@ import io.ebeaninternal.server.text.csv.TCsvReader;
|
||||
import io.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
import io.ebeaninternal.server.transaction.RemoteTransactionEvent;
|
||||
import io.ebeaninternal.server.transaction.TransactionManager;
|
||||
import io.ebeaninternal.server.transaction.TransactionScopeManager;
|
||||
import io.ebeaninternal.util.ParamTypeHelper;
|
||||
import io.ebeaninternal.util.ParamTypeHelper.TypeInfo;
|
||||
import io.ebeanservice.docstore.api.DocStoreIntegration;
|
||||
@@ -102,7 +103,6 @@ import javax.persistence.NonUniqueResultException;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -122,10 +122,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultServer.class);
|
||||
|
||||
private static final int IGNORE_LEADING_ELEMENTS = 5;
|
||||
|
||||
private static final String IO_EBEAN = "io.ebean";
|
||||
|
||||
private final ServerConfig serverConfig;
|
||||
|
||||
private final String serverName;
|
||||
@@ -134,19 +130,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private final TransactionManager transactionManager;
|
||||
|
||||
private final TransactionScopeManager transactionScopeManager;
|
||||
|
||||
private final DataTimeZone dataTimeZone;
|
||||
|
||||
private final CallStackFactory callStackFactory = new DefaultCallStackFactory();
|
||||
|
||||
private final int maxCallStack;
|
||||
|
||||
/**
|
||||
* Ebean defaults this to true but for EJB compatible behaviour set this to
|
||||
* false;
|
||||
*/
|
||||
private final boolean rollbackOnChecked;
|
||||
private final CallStackFactory callStackFactory;
|
||||
|
||||
/**
|
||||
* Handles the save, delete, updateSql CallableSql.
|
||||
@@ -253,9 +239,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
this.updateAllPropertiesInBatch = serverConfig.isUpdateAllPropertiesInBatch();
|
||||
this.collectQueryOrigins = serverConfig.isCollectQueryOrigins();
|
||||
this.collectQueryStatsByNode = serverConfig.isCollectQueryStatsByNode();
|
||||
this.maxCallStack = serverConfig.getMaxCallStack();
|
||||
|
||||
this.rollbackOnChecked = serverConfig.isTransactionRollbackOnChecked();
|
||||
this.callStackFactory = initCallStackFactory(serverConfig);
|
||||
|
||||
this.persister = config.createPersister(this);
|
||||
this.queryEngine = config.createOrmQueryEngine();
|
||||
@@ -271,7 +255,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
DocStoreIntegration docStoreComponents = config.createDocStoreIntegration(this);
|
||||
this.transactionManager = config.createTransactionManager(docStoreComponents.updateProcessor());
|
||||
this.transactionScopeManager = config.createTransactionScopeManager(transactionManager);
|
||||
this.documentStore = docStoreComponents.documentStore();
|
||||
|
||||
this.serverPlugins = config.getPlugins();
|
||||
@@ -283,6 +266,17 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
ShutdownManager.registerEbeanServer(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the CallStackFactory depending if AutoTune is being used.
|
||||
*/
|
||||
private CallStackFactory initCallStackFactory(ServerConfig serverConfig) {
|
||||
if (!serverConfig.getAutoTuneConfig().isActive()) {
|
||||
// use a common CallStack for performance as we don't care with no AutoTune
|
||||
return new NoopCallStackFactory();
|
||||
}
|
||||
return new DefaultCallStackFactory(serverConfig.getMaxCallStack());
|
||||
}
|
||||
|
||||
private void configureServerPlugins() {
|
||||
|
||||
autoTuneService.startup();
|
||||
@@ -379,6 +373,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return transactionManager.getDataSource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource getReadOnlyDataSource() {
|
||||
return transactionManager.getReadOnlyDataSource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadAuditPrepare getReadAuditPrepare() {
|
||||
return readAuditPrepare;
|
||||
@@ -553,12 +552,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public void externalModification(TransactionEventTable tableEvent) {
|
||||
SpiTransaction t = transactionScopeManager.get();
|
||||
if (t != null) {
|
||||
t.getEvent().add(tableEvent);
|
||||
} else {
|
||||
transactionManager.externalModification(tableEvent);
|
||||
}
|
||||
transactionManager.externalModification(tableEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -619,7 +613,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
id = desc.convertId(id);
|
||||
|
||||
PersistenceContext pc = null;
|
||||
SpiTransaction t = transactionScopeManager.get();
|
||||
SpiTransaction t = transactionManager.getActive();
|
||||
if (t != null) {
|
||||
pc = t.getPersistenceContext();
|
||||
Object existing = desc.contextGet(pc, id);
|
||||
@@ -658,8 +652,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public Transaction createTransaction() {
|
||||
|
||||
return transactionManager.createTransaction(0, true, -1);
|
||||
return transactionManager.createTransaction(true, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -670,8 +663,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public Transaction createTransaction(TxIsolation isolation) {
|
||||
|
||||
return transactionManager.createTransaction(0, true, isolation.getLevel());
|
||||
return transactionManager.createTransaction(true, isolation.getLevel());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -681,7 +673,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> T executeCall(TxScope scope, Callable<T> c) {
|
||||
ScopedTransaction scopeTrans = scopedTransaction(scope);
|
||||
ScopedTransaction scopeTrans = transactionManager.beginScopedTransaction(scope);
|
||||
try {
|
||||
return c.call();
|
||||
|
||||
@@ -703,7 +695,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public void execute(TxScope scope, Runnable r) {
|
||||
ScopedTransaction t = scopedTransaction(scope);
|
||||
ScopedTransaction t = transactionManager.beginScopedTransaction(scope);
|
||||
try {
|
||||
r.run();
|
||||
|
||||
@@ -718,43 +710,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether to create a new transaction or not.
|
||||
* <p>
|
||||
* This will also potentially throw exceptions for MANDATORY and NEVER types.
|
||||
* </p>
|
||||
*/
|
||||
private boolean createNewTransaction(SpiTransaction current, TxType type) {
|
||||
switch (type) {
|
||||
case REQUIRED:
|
||||
return current == null;
|
||||
|
||||
case REQUIRES_NEW:
|
||||
return true;
|
||||
|
||||
case MANDATORY:
|
||||
if (current == null) {
|
||||
throw new PersistenceException("Transaction missing when MANDATORY");
|
||||
}
|
||||
return false;
|
||||
|
||||
case SUPPORTS:
|
||||
return current == null;
|
||||
|
||||
case NEVER:
|
||||
if (current != null) {
|
||||
throw new PersistenceException("Transaction exists for Transactional NEVER");
|
||||
}
|
||||
return true; // always use NoTransaction instance
|
||||
|
||||
case NOT_SUPPORTED:
|
||||
return true; // always use NoTransaction instance
|
||||
|
||||
default:
|
||||
throw new RuntimeException("Should never get here?");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scopedTransactionEnter(TxScope txScope) {
|
||||
beginTransaction(txScope);
|
||||
@@ -762,12 +717,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public void scopedTransactionExit(Object returnOrThrowable, int opCode) {
|
||||
ScopedTransaction st = (ScopedTransaction) transactionScopeManager.getScoped();
|
||||
if (st != null) {
|
||||
// can be null for Supports as that can start as a 'No Transaction' and then
|
||||
// effectively be replaced by transactions inside the scope
|
||||
st.complete(returnOrThrowable, opCode);
|
||||
}
|
||||
transactionManager.exitScopedTransaction(returnOrThrowable, opCode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -775,7 +725,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public SpiTransaction currentServerTransaction() {
|
||||
return transactionScopeManager.get();
|
||||
return transactionManager.getActive();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -794,54 +744,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public Transaction beginTransaction(TxScope txScope) {
|
||||
return scopedTransaction(txScope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Scoped transaction which internally can 'nest' transactions on it's own stack.
|
||||
*/
|
||||
ScopedTransaction scopedTransaction(TxScope txScope) {
|
||||
|
||||
txScope = initTxScope(txScope);
|
||||
|
||||
boolean setToScope = false;
|
||||
ScopedTransaction txnContainer = (ScopedTransaction) transactionScopeManager.get();
|
||||
if (txnContainer == null) {
|
||||
setToScope = true;
|
||||
txnContainer = new ScopedTransaction(transactionScopeManager);
|
||||
}
|
||||
|
||||
SpiTransaction transaction = txnContainer.current();
|
||||
|
||||
TxType type = txScope.getType();
|
||||
boolean createTransaction = createNewTransaction(transaction, type);
|
||||
if (createTransaction) {
|
||||
switch (type) {
|
||||
case SUPPORTS:
|
||||
case NOT_SUPPORTED:
|
||||
case NEVER:
|
||||
transaction = NoTransaction.INSTANCE;
|
||||
break;
|
||||
default:
|
||||
transaction = transactionManager.createTransaction(txScope.getProfileId(), true, txScope.getIsolationLevel());
|
||||
}
|
||||
}
|
||||
|
||||
txnContainer.push(new ScopeTrans(rollbackOnChecked, createTransaction, transaction, txScope));
|
||||
if (setToScope) {
|
||||
transactionScopeManager.set(txnContainer);
|
||||
}
|
||||
return txnContainer;
|
||||
}
|
||||
|
||||
private TxScope initTxScope(TxScope txScope) {
|
||||
if (txScope == null) {
|
||||
return new TxScope();
|
||||
} else {
|
||||
// check for implied batch mode via setting batchSize
|
||||
txScope.checkBatchMode();
|
||||
return txScope;
|
||||
}
|
||||
return transactionManager.beginScopedTransaction(txScope);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -853,9 +756,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Override
|
||||
public Transaction beginTransaction(TxIsolation isolation) {
|
||||
// start an explicit transaction
|
||||
SpiTransaction t = transactionManager.createTransaction(0, true, isolation.getLevel());
|
||||
SpiTransaction t = transactionManager.createTransaction(true, isolation.getLevel());
|
||||
try {
|
||||
transactionScopeManager.set(t);
|
||||
// note that we are not supporting nested scoped transactions in this case
|
||||
transactionManager.set(t);
|
||||
} catch (PersistenceException existingTransactionError) {
|
||||
t.end();
|
||||
throw existingTransactionError;
|
||||
@@ -869,7 +773,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public Transaction currentTransaction() {
|
||||
return transactionScopeManager.get();
|
||||
return transactionManager.getActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -882,7 +786,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public void commitTransaction() {
|
||||
transactionScopeManager.commit();
|
||||
transactionManager.scope().commit();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -890,7 +794,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public void rollbackTransaction() {
|
||||
transactionScopeManager.rollback();
|
||||
transactionManager.scope().rollback();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -923,7 +827,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public void endTransaction() {
|
||||
transactionScopeManager.end();
|
||||
transactionManager.scope().end();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1104,6 +1008,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private <T> SpiOrmQueryRequest<T> createQueryRequest(SpiQuery<T> query, Transaction t) {
|
||||
|
||||
if (t == null) {
|
||||
t = currentServerTransaction();
|
||||
}
|
||||
query.setDefaultRawSqlIfRequired();
|
||||
if (query.isAutoTunable() && !autoTuneService.tuneQuery(query)) {
|
||||
// use deployment FetchType.LAZY/EAGER annotations
|
||||
@@ -1113,6 +1020,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
query.selectAllForLazyLoadProperty();
|
||||
|
||||
ProfileLocation profileLocation = query.getProfileLocation();
|
||||
if (profileLocation != null) {
|
||||
profileLocation.obtain();
|
||||
}
|
||||
// if determine cost and no origin for AutoTune
|
||||
if (query.getParentNode() == null) {
|
||||
query.setOrigin(createCallStack());
|
||||
@@ -1193,6 +1104,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(spiQuery, t);
|
||||
request.profileLocationById();
|
||||
if (request.isUseDocStore()) {
|
||||
return docStore().find(request);
|
||||
}
|
||||
@@ -1214,9 +1126,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
public <T> T findOne(Query<T> query, Transaction transaction) {
|
||||
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
spiQuery.checkIdEqualTo();
|
||||
Object id = spiQuery.getId();
|
||||
if (id != null) {
|
||||
if (spiQuery.isFindById()) {
|
||||
// actually a find by Id query
|
||||
return findId(query, transaction);
|
||||
}
|
||||
@@ -1517,12 +1427,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
private <T> List<T> findList(Query<T> query, Transaction t, boolean findOne) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, t);
|
||||
if (!findOne) {
|
||||
request.resetBeanCacheAutoMode();
|
||||
Object result = request.getFromQueryCache();
|
||||
if (result != null) {
|
||||
return (List<T>) result;
|
||||
}
|
||||
request.profileLocationAll();
|
||||
request.resetBeanCacheAutoMode(findOne);
|
||||
Object result = request.getFromQueryCache();
|
||||
if (result != null) {
|
||||
return (List<T>) result;
|
||||
}
|
||||
if ((t == null || !t.isSkipCache()) && request.getFromBeanCache()) {
|
||||
return request.getBeanCacheHits();
|
||||
@@ -1577,7 +1486,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
public List<SqlRow> findList(SqlQuery query, Transaction t) {
|
||||
|
||||
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t);
|
||||
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return request.findList();
|
||||
@@ -1965,6 +1873,14 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return beanDescriptorManager.getBeanDescriptorList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the transaction manager.
|
||||
*/
|
||||
@Override
|
||||
public SpiTransactionManager getTransactionManager() {
|
||||
return transactionManager;
|
||||
}
|
||||
|
||||
public void register(BeanPersistController c) {
|
||||
List<BeanDescriptor<?>> list = beanDescriptorManager.getBeanDescriptorList();
|
||||
for (BeanDescriptor<?> aList : list) {
|
||||
@@ -2072,6 +1988,30 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Override
|
||||
public void remoteTransactionEvent(RemoteTransactionEvent event) {
|
||||
transactionManager.remoteTransactionEvent(event);
|
||||
processRemoteCacheEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a cache event coming from another server in the cluster.
|
||||
*/
|
||||
private void processRemoteCacheEvent(RemoteTransactionEvent event) {
|
||||
RemoteCacheEvent cacheEvent = event.getRemoteCacheEvent();
|
||||
if (cacheEvent != null) {
|
||||
if (cacheEvent.isClearAll()) {
|
||||
serverCacheManager.clearAllLocal();
|
||||
} else {
|
||||
List<String> caches = cacheEvent.getClearCaches();
|
||||
if (caches != null) {
|
||||
for (String cache : caches) {
|
||||
try {
|
||||
serverCacheManager.clearLocal(Class.forName(cache));
|
||||
} catch (Exception e) {
|
||||
logger.error("Error clearing local cache for type " + cache, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <P> P executeInTrans(Function<SpiTransaction, P> fun, Transaction t) {
|
||||
@@ -2095,7 +2035,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
if (t != null) {
|
||||
return new ObtainedTransaction((SpiTransaction) t);
|
||||
}
|
||||
SpiTransaction trans = transactionScopeManager.get();
|
||||
SpiTransaction trans = transactionManager.getActive();
|
||||
if (trans != null) {
|
||||
return new ObtainedTransaction(trans);
|
||||
}
|
||||
@@ -2105,9 +2045,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public SpiTransaction beginServerTransaction() {
|
||||
SpiTransaction t = transactionManager.createTransaction(0, false, -1);
|
||||
transactionScopeManager.set(t);
|
||||
return t;
|
||||
return transactionManager.beginServerTransaction();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2124,35 +2062,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public CallStack createCallStack() {
|
||||
|
||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||
|
||||
// ignore the first 6 as they are always avaje stack elements
|
||||
int startIndex = IGNORE_LEADING_ELEMENTS;
|
||||
|
||||
// find the first non-avaje stackElement
|
||||
for (; startIndex < stackTrace.length; startIndex++) {
|
||||
if (!stackTrace[startIndex].getClassName().startsWith(IO_EBEAN)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int stackLength = stackTrace.length - startIndex;
|
||||
if (stackLength > maxCallStack) {
|
||||
// maximum of maxCallStack stackTrace elements
|
||||
stackLength = maxCallStack;
|
||||
}
|
||||
|
||||
// create the 'interesting' part of the stackTrace
|
||||
StackTraceElement[] finalTrace = new StackTraceElement[stackLength];
|
||||
System.arraycopy(stackTrace, startIndex, finalTrace, 0, stackLength);
|
||||
|
||||
if (stackLength < 1) {
|
||||
// this should not really happen
|
||||
throw new RuntimeException("StackTraceElement size 0? stack: " + Arrays.toString(stackTrace));
|
||||
}
|
||||
|
||||
return callStackFactory.createCallStack(finalTrace);
|
||||
return callStackFactory.createCallStack();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2194,4 +2104,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<MetaTimedMetric> collectTransactionStatistics(boolean reset) {
|
||||
return transactionManager.collectTransactionStatistics(reset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,10 +371,11 @@ public class InternalConfiguration {
|
||||
*/
|
||||
public TransactionManager createTransactionManager(DocStoreUpdateProcessor indexUpdateProcessor) {
|
||||
|
||||
TransactionScopeManager scopeManager = createTransactionScopeManager();
|
||||
boolean localL2 = cacheManager.isLocalL2Caching();
|
||||
|
||||
TransactionManagerOptions options =
|
||||
new TransactionManagerOptions(localL2, serverConfig, clusterManager, backgroundExecutor,
|
||||
new TransactionManagerOptions(localL2, serverConfig, scopeManager, clusterManager, backgroundExecutor,
|
||||
indexUpdateProcessor, beanDescriptorManager, dataSource(), profileHandler());
|
||||
|
||||
if (serverConfig.isExplicitTransactionBeginMode()) {
|
||||
@@ -410,11 +411,11 @@ public class InternalConfiguration {
|
||||
case DB:
|
||||
return new MultiTenantDbSupplier(serverConfig.getCurrentTenantProvider(), serverConfig.getTenantDataSourceProvider());
|
||||
case SCHEMA:
|
||||
return new MultiTenantDbSchemaSupplier(serverConfig.getCurrentTenantProvider(), serverConfig.getDataSource(), serverConfig.getTenantSchemaProvider());
|
||||
return new MultiTenantDbSchemaSupplier(serverConfig.getCurrentTenantProvider(), serverConfig.getDataSource(), serverConfig.getReadOnlyDataSource(), serverConfig.getTenantSchemaProvider());
|
||||
case CATALOG:
|
||||
return new MultiTenantDbCatalogSupplier(serverConfig.getCurrentTenantProvider(), serverConfig.getDataSource(), serverConfig.getTenantCatalogProvider());
|
||||
return new MultiTenantDbCatalogSupplier(serverConfig.getCurrentTenantProvider(), serverConfig.getDataSource(), serverConfig.getReadOnlyDataSource(), serverConfig.getTenantCatalogProvider());
|
||||
default:
|
||||
return new SimpleDataSourceProvider(serverConfig.getDataSource());
|
||||
return new SimpleDataSourceProvider(serverConfig.getDataSource(), serverConfig.getReadOnlyDataSource());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,18 +434,17 @@ public class InternalConfiguration {
|
||||
/**
|
||||
* Create the TransactionScopeManager taking into account JTA or external transaction manager.
|
||||
*/
|
||||
public TransactionScopeManager createTransactionScopeManager(TransactionManager transactionManager) {
|
||||
public TransactionScopeManager createTransactionScopeManager() {
|
||||
|
||||
ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager();
|
||||
if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) {
|
||||
externalTransactionManager = new JtaTransactionManager();
|
||||
}
|
||||
if (externalTransactionManager != null) {
|
||||
externalTransactionManager.setTransactionManager(transactionManager);
|
||||
logger.info("Using Transaction Manager [" + externalTransactionManager.getClass() + "]");
|
||||
return new ExternalTransactionScopeManager(transactionManager, externalTransactionManager);
|
||||
return new ExternalTransactionScopeManager(serverConfig.getName(), externalTransactionManager);
|
||||
} else {
|
||||
return new DefaultTransactionScopeManager(transactionManager);
|
||||
return new DefaultTransactionScopeManager(serverConfig.getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,19 +17,21 @@ import java.util.logging.Logger;
|
||||
*/
|
||||
public class MultiTenantDbCatalogSupplier implements DataSourceSupplier {
|
||||
|
||||
private final CurrentTenantProvider tenantProvider;
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
private final TenantCatalogProvider catalogProvider;
|
||||
private final DataSource readOnlyDataSource;
|
||||
|
||||
private final CatalogDataSource catalogDataSource;
|
||||
private final CatalogDataSource readOnly;
|
||||
|
||||
MultiTenantDbCatalogSupplier(CurrentTenantProvider tenantProvider, DataSource dataSource, TenantCatalogProvider catalogProvider) {
|
||||
this.tenantProvider = tenantProvider;
|
||||
MultiTenantDbCatalogSupplier(CurrentTenantProvider tenantProvider, DataSource dataSource, DataSource readOnlyDataSource, TenantCatalogProvider catalogProvider) {
|
||||
this.dataSource = dataSource;
|
||||
this.catalogProvider = catalogProvider;
|
||||
this.catalogDataSource = new CatalogDataSource();
|
||||
this.readOnlyDataSource = readOnlyDataSource;
|
||||
this.catalogDataSource = new CatalogDataSource(dataSource, tenantProvider, catalogProvider);
|
||||
if (readOnlyDataSource == null) {
|
||||
this.readOnly = null;
|
||||
} else {
|
||||
this.readOnly = new CatalogDataSource(readOnlyDataSource, tenantProvider, catalogProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -37,28 +39,48 @@ public class MultiTenantDbCatalogSupplier implements DataSourceSupplier {
|
||||
return catalogDataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource getReadOnlyDataSource() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection(Object tenantId) throws SQLException {
|
||||
return catalogDataSource.getConnectionForTenant(tenantId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getReadOnlyConnection(Object tenantId) throws SQLException {
|
||||
return readOnly.getConnectionForTenant(tenantId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown(boolean deregisterDriver) {
|
||||
if (readOnlyDataSource instanceof DataSourcePool) {
|
||||
((DataSourcePool) readOnlyDataSource).shutdown(false);
|
||||
}
|
||||
if (dataSource instanceof DataSourcePool) {
|
||||
((DataSourcePool) dataSource).shutdown(deregisterDriver);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DB catalog for the current user Tenant Id.
|
||||
*/
|
||||
private String tenantCatalog() {
|
||||
return catalogProvider.catalog(tenantProvider.currentId());
|
||||
}
|
||||
|
||||
private class CatalogDataSource implements DataSource {
|
||||
|
||||
CatalogDataSource() {
|
||||
final DataSource dataSource;
|
||||
final CurrentTenantProvider tenantProvider;
|
||||
final TenantCatalogProvider catalogProvider;
|
||||
|
||||
CatalogDataSource(DataSource dataSource, CurrentTenantProvider tenantProvider, TenantCatalogProvider catalogProvider) {
|
||||
this.dataSource = dataSource;
|
||||
this.tenantProvider = tenantProvider;
|
||||
this.catalogProvider = catalogProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DB catalog for the current user Tenant Id.
|
||||
*/
|
||||
private String tenantCatalog() {
|
||||
return catalogProvider.catalog(tenantProvider.currentId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,19 +17,21 @@ import java.util.logging.Logger;
|
||||
*/
|
||||
class MultiTenantDbSchemaSupplier implements DataSourceSupplier {
|
||||
|
||||
private final CurrentTenantProvider tenantProvider;
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
private final TenantSchemaProvider schemaProvider;
|
||||
private final DataSource readOnlyDataSource;
|
||||
|
||||
private final SchemaDataSource schemaDataSource;
|
||||
private final SchemaDataSource readOnly;
|
||||
|
||||
MultiTenantDbSchemaSupplier(CurrentTenantProvider tenantProvider, DataSource dataSource, TenantSchemaProvider schemaProvider) {
|
||||
this.tenantProvider = tenantProvider;
|
||||
MultiTenantDbSchemaSupplier(CurrentTenantProvider tenantProvider, DataSource dataSource, DataSource readOnlyDataSource, TenantSchemaProvider schemaProvider) {
|
||||
this.dataSource = dataSource;
|
||||
this.schemaProvider = schemaProvider;
|
||||
this.schemaDataSource = new SchemaDataSource();
|
||||
this.readOnlyDataSource = readOnlyDataSource;
|
||||
this.schemaDataSource = new SchemaDataSource(dataSource, schemaProvider, tenantProvider);
|
||||
if (readOnlyDataSource == null) {
|
||||
this.readOnly = null;
|
||||
} else {
|
||||
this.readOnly = new SchemaDataSource(readOnlyDataSource, schemaProvider, tenantProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -37,28 +39,44 @@ class MultiTenantDbSchemaSupplier implements DataSourceSupplier {
|
||||
return schemaDataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource getReadOnlyDataSource() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection(Object tenantId) throws SQLException {
|
||||
return schemaDataSource.getConnectionForTenant(tenantId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getReadOnlyConnection(Object tenantId) throws SQLException {
|
||||
return readOnly.getConnectionForTenant(tenantId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown(boolean deregisterDriver) {
|
||||
if (readOnlyDataSource instanceof DataSourcePool) {
|
||||
((DataSourcePool) readOnlyDataSource).shutdown(false);
|
||||
}
|
||||
if (dataSource instanceof DataSourcePool) {
|
||||
((DataSourcePool) dataSource).shutdown(deregisterDriver);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DB schema for the current user Tenant Id.
|
||||
* Tenant schema aware DataSource.
|
||||
*/
|
||||
private String tenantSchema() {
|
||||
return schemaProvider.schema(tenantProvider.currentId());
|
||||
}
|
||||
private static class SchemaDataSource implements DataSource {
|
||||
|
||||
private class SchemaDataSource implements DataSource {
|
||||
private final DataSource dataSource;
|
||||
private final TenantSchemaProvider schemaProvider;
|
||||
private final CurrentTenantProvider tenantProvider;
|
||||
|
||||
SchemaDataSource() {
|
||||
SchemaDataSource(DataSource dataSource, TenantSchemaProvider schemaProvider, CurrentTenantProvider tenantProvider) {
|
||||
this.dataSource = dataSource;
|
||||
this.schemaProvider = schemaProvider;
|
||||
this.tenantProvider = tenantProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,6 +88,13 @@ class MultiTenantDbSchemaSupplier implements DataSourceSupplier {
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DB schema for the current user Tenant Id.
|
||||
*/
|
||||
private String tenantSchema() {
|
||||
return schemaProvider.schema(tenantProvider.currentId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the connection with the appropriate DB schema set.
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,12 @@ class MultiTenantDbSupplier implements DataSourceSupplier {
|
||||
this.dataSourceProvider = dataSourceProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource getReadOnlyDataSource() {
|
||||
// read only datasource not supported with DB per tenant at this stage
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource getDataSource() {
|
||||
return dataSourceProvider.dataSource(tenantProvider.currentId());
|
||||
@@ -32,6 +38,11 @@ class MultiTenantDbSupplier implements DataSourceSupplier {
|
||||
return dataSourceProvider.dataSource(tenantId).getConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getReadOnlyConnection(Object tenantId) throws SQLException {
|
||||
throw new SQLException("Not currently supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown(boolean deregisterDriver) {
|
||||
dataSourceProvider.shutdown(deregisterDriver);
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.bean.CallStack;
|
||||
|
||||
/**
|
||||
* A CallStackFactory we can use when we don't use AutoTune.
|
||||
*/
|
||||
class NoopCallStackFactory implements CallStackFactory {
|
||||
|
||||
private final CallStack COMMON = new CallStack(Thread.currentThread().getStackTrace(), 0, 0);
|
||||
|
||||
@Override
|
||||
public CallStack createCallStack() {
|
||||
return COMMON;
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,12 @@ import io.ebean.event.BeanFindController;
|
||||
import io.ebean.event.BeanQueryAdapter;
|
||||
import io.ebean.event.BeanQueryRequest;
|
||||
import io.ebean.text.json.JsonReadOptions;
|
||||
import io.ebeaninternal.api.BeanCacheResult;
|
||||
import io.ebeaninternal.api.CQueryPlanKey;
|
||||
import io.ebeaninternal.api.HashQuery;
|
||||
import io.ebeaninternal.api.LoadContext;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
import io.ebeaninternal.api.NaturalKeySet;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiQuery.Type;
|
||||
@@ -30,9 +33,6 @@ import io.ebeaninternal.server.deploy.DeployPropertyParserMap;
|
||||
import io.ebeaninternal.server.loadcontext.DLoadContext;
|
||||
import io.ebeaninternal.server.query.CQueryPlan;
|
||||
import io.ebeaninternal.server.query.CancelableQuery;
|
||||
import io.ebeaninternal.api.BeanCacheResult;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
import io.ebeaninternal.api.NaturalKeySet;
|
||||
import io.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -96,6 +96,20 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
return queryEngine.translate(this, bindLog, sql, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void profileLocationById() {
|
||||
if (query.getProfileLocation() == null) {
|
||||
query.setProfileLocation(beanDescriptor.profileLocationById());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void profileLocationAll() {
|
||||
if (query.getProfileLocation() == null && query.isFindAll()) {
|
||||
query.setProfileLocation(beanDescriptor.profileLocationAll());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMultiValueIdSupported() {
|
||||
return beanDescriptor.isMultiValueIdSupported();
|
||||
@@ -118,8 +132,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
* Return the database platform like clause.
|
||||
*/
|
||||
@Override
|
||||
public String getDBLikeClause() {
|
||||
return ebeanServer.getDatabasePlatform().getLikeClause();
|
||||
public String getDBLikeClause(boolean rawLikeExpression) {
|
||||
return ebeanServer.getDatabasePlatform().getLikeClause(rawLikeExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -241,13 +255,15 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
public void initTransIfRequired() {
|
||||
// first check if the query requires its own transaction
|
||||
if (transaction == null) {
|
||||
// maybe a current one
|
||||
transaction = ebeanServer.currentServerTransaction();
|
||||
if (transaction == null) {
|
||||
if (query.getType().isUpdate()) {
|
||||
// bulk update or delete query
|
||||
transaction = ebeanServer.beginServerTransaction();
|
||||
} else {
|
||||
// create an implicit transaction to execute this query
|
||||
// potentially using read-only DataSource with autoCommit
|
||||
transaction = ebeanServer.createQueryTransaction(query.getTenantId());
|
||||
createdTransaction = true;
|
||||
}
|
||||
createdTransaction = true;
|
||||
}
|
||||
persistenceContext = getPersistenceContext(query, transaction);
|
||||
loadContext = new DLoadContext(this, secondaryQueries);
|
||||
@@ -503,12 +519,16 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetBeanCacheAutoMode() {
|
||||
query.resetBeanCacheAutoMode();
|
||||
public void resetBeanCacheAutoMode(boolean findOne) {
|
||||
query.resetBeanCacheAutoMode(findOne);
|
||||
}
|
||||
|
||||
public boolean isQueryCachePut() {
|
||||
return cacheKey != null && query.getUseQueryCache().isPut();
|
||||
}
|
||||
|
||||
public boolean isBeanCachePut() {
|
||||
return query.isBeanCachePut();
|
||||
return !transaction.isSkipCache() && query.isBeanCachePut();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -580,7 +600,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object getFromQueryCache() {
|
||||
|
||||
if (query.getUseQueryCache() == CacheMode.OFF) {
|
||||
if (query.getUseQueryCache() == CacheMode.OFF || (transaction != null && transaction.isSkipCache())) {
|
||||
return null;
|
||||
} else {
|
||||
cacheKey = query.queryHash();
|
||||
|
||||
@@ -61,7 +61,7 @@ public final class RelationalQueryRequest {
|
||||
/**
|
||||
* Create the BeanFindRequest.
|
||||
*/
|
||||
public RelationalQueryRequest(SpiEbeanServer server, RelationalQueryEngine engine, SqlQuery q, Transaction t) {
|
||||
RelationalQueryRequest(SpiEbeanServer server, RelationalQueryEngine engine, SqlQuery q, Transaction t) {
|
||||
this.ebeanServer = server;
|
||||
this.queryEngine = engine;
|
||||
this.query = (SpiSqlQuery) q;
|
||||
|
||||
@@ -14,8 +14,11 @@ class SimpleDataSourceProvider implements DataSourceSupplier {
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
SimpleDataSourceProvider(DataSource dataSource) {
|
||||
private final DataSource readOnlyDataSource;
|
||||
|
||||
SimpleDataSourceProvider(DataSource dataSource, DataSource readOnlyDataSource) {
|
||||
this.dataSource = dataSource;
|
||||
this.readOnlyDataSource = readOnlyDataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -23,13 +26,26 @@ class SimpleDataSourceProvider implements DataSourceSupplier {
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource getReadOnlyDataSource() {
|
||||
return readOnlyDataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection(Object tenantId) throws SQLException {
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getReadOnlyConnection(Object tenantId) throws SQLException {
|
||||
return readOnlyDataSource.getConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown(boolean deregisterDriver) {
|
||||
if (readOnlyDataSource instanceof DataSourcePool){
|
||||
((DataSourcePool) readOnlyDataSource).shutdown(false);
|
||||
}
|
||||
if (dataSource instanceof DataSourcePool){
|
||||
((DataSourcePool) dataSource).shutdown(deregisterDriver);
|
||||
}
|
||||
|
||||
@@ -134,12 +134,12 @@ public interface SpiOrmQueryRequest<T> extends DocQueryRequest<T> {
|
||||
/**
|
||||
* Reset Bean cache mode AUTO - require explicit setting for bean cache use with findList().
|
||||
*/
|
||||
void resetBeanCacheAutoMode();
|
||||
void resetBeanCacheAutoMode(boolean findOne);
|
||||
|
||||
/**
|
||||
* Return the Database platform like clause.
|
||||
*/
|
||||
String getDBLikeClause();
|
||||
String getDBLikeClause(boolean rawLikeExpression);
|
||||
|
||||
/**
|
||||
* Escapes a string to use it as exact match in Like clause.
|
||||
@@ -155,4 +155,14 @@ public interface SpiOrmQueryRequest<T> extends DocQueryRequest<T> {
|
||||
* Return true if this query is expected to use the doc store.
|
||||
*/
|
||||
boolean isUseDocStore();
|
||||
|
||||
/**
|
||||
* Set profile location for "find by id" if not set.
|
||||
*/
|
||||
void profileLocationById();
|
||||
|
||||
/**
|
||||
* Set profile location for "find all" if not set.
|
||||
*/
|
||||
void profileLocationAll();
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public class BeanCascadeInfo {
|
||||
}
|
||||
}
|
||||
|
||||
private void setType(CascadeType type) {
|
||||
public void setType(CascadeType type) {
|
||||
switch (type) {
|
||||
case ALL:
|
||||
save = true;
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.OrderBy;
|
||||
import io.ebean.PersistenceContextScope;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.Transaction;
|
||||
@@ -27,12 +28,12 @@ import io.ebean.event.changelog.ChangeType;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.event.readaudit.ReadEvent;
|
||||
import io.ebean.meta.MetaBeanInfo;
|
||||
import io.ebean.meta.MetaQueryPlanStatistic;
|
||||
import io.ebean.plugin.BeanDocType;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.plugin.ExpressionPath;
|
||||
import io.ebean.plugin.Property;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.BeanCacheResult;
|
||||
import io.ebeaninternal.api.CQueryPlanKey;
|
||||
import io.ebeaninternal.api.ConcurrencyMode;
|
||||
import io.ebeaninternal.api.LoadContext;
|
||||
@@ -61,9 +62,7 @@ import io.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.persist.DmlUtil;
|
||||
import io.ebeaninternal.server.query.CQueryPlan;
|
||||
import io.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.BeanCacheResult;
|
||||
import io.ebeaninternal.server.query.CQueryPlanStatsCollector;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import io.ebeaninternal.server.rawsql.SpiRawSql;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
@@ -100,7 +99,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
/**
|
||||
* Describes Beans including their deployment information.
|
||||
*/
|
||||
public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
public class BeanDescriptor<T> implements BeanType<T> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanDescriptor.class);
|
||||
|
||||
@@ -119,6 +118,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
private final Map<String, String> namedQuery;
|
||||
|
||||
private final short profileBeanId;
|
||||
private final ProfileLocation locationById;
|
||||
private final ProfileLocation locationAll;
|
||||
|
||||
private final boolean multiValueSupported;
|
||||
|
||||
@@ -178,6 +179,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
private final String baseTableAsOf;
|
||||
private final String baseTableVersionsBetween;
|
||||
private final boolean historySupport;
|
||||
private final TableJoin primaryKeyJoin;
|
||||
|
||||
private final BeanProperty softDeleteProperty;
|
||||
private final boolean softDelete;
|
||||
@@ -306,6 +308,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
|
||||
|
||||
private final BeanPropertyAssocOne<?> unidirectional;
|
||||
private final BeanProperty orderColumn;
|
||||
|
||||
/**
|
||||
* list of properties that are Lists/Sets/Maps (Derived).
|
||||
@@ -415,6 +418,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
this.name = InternString.intern(deploy.getName());
|
||||
this.baseTableAlias = "t0";
|
||||
this.fullName = InternString.intern(deploy.getFullName());
|
||||
this.locationById = ProfileLocation.createAt(fullName+".byId");
|
||||
this.locationAll = ProfileLocation.createAt(fullName+".all");
|
||||
this.profileBeanId = deploy.getProfileId();
|
||||
this.beanType = deploy.getBeanType();
|
||||
this.rootBeanType = PersistenceContextUtil.root(beanType);
|
||||
@@ -451,6 +456,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
this.draftTable = deploy.getDraftTable();
|
||||
this.baseTable = InternString.intern(deploy.getBaseTable());
|
||||
this.baseTableAsOf = deploy.getBaseTableAsOf();
|
||||
this.primaryKeyJoin = deploy.getPrimaryKeyJoin();
|
||||
this.baseTableVersionsBetween = deploy.getBaseTableVersionsBetween();
|
||||
this.dependentTables = deploy.getDependentTables();
|
||||
this.dbComment = deploy.getDbComment();
|
||||
@@ -475,6 +481,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
this.propertiesLocal = listHelper.getLocal();
|
||||
this.propertiesMutable = listHelper.getMutable();
|
||||
this.unidirectional = listHelper.getUnidirectional();
|
||||
this.orderColumn = listHelper.getOrderColumn();
|
||||
this.propertiesOne = listHelper.getOnes();
|
||||
this.propertiesOneExportedSave = listHelper.getOneExportedSave();
|
||||
this.propertiesOneExportedDelete = listHelper.getOneExportedDelete();
|
||||
@@ -537,6 +544,20 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a location for "find by id".
|
||||
*/
|
||||
public ProfileLocation profileLocationById() {
|
||||
return locationById;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a location for "find all".
|
||||
*/
|
||||
public ProfileLocation profileLocationAll() {
|
||||
return locationAll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the id used in profiling to identify the bean type.
|
||||
*/
|
||||
@@ -1348,6 +1369,17 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
cacheHelp.beanCachePut(bean);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void cacheBeanPutAll(Collection<?> beans) {
|
||||
if (!beans.isEmpty()) {
|
||||
cacheHelp.beanPutAll((Collection<EntityBean>)beans);
|
||||
}
|
||||
}
|
||||
|
||||
void cacheBeanPutAllDirect(Collection<EntityBean> beans) {
|
||||
cacheHelp.beanCachePutAllDirect(beans);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a bean into the cache as the correct type.
|
||||
*/
|
||||
@@ -1517,25 +1549,12 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
return new DeployUpdateParser(this).parse(ormUpdateStatement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset) {
|
||||
return collectQueryPlanStatisticsInternal(reset, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlanStatistic> collectAllQueryPlanStatistics(boolean reset) {
|
||||
return collectQueryPlanStatisticsInternal(reset, false);
|
||||
}
|
||||
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatisticsInternal(boolean reset, boolean collectAll) {
|
||||
List<MetaQueryPlanStatistic> list = new ArrayList<>(queryPlanCache.size());
|
||||
public void collectQueryPlanStatistics(CQueryPlanStatsCollector collector) {
|
||||
for (CQueryPlan queryPlan : queryPlanCache.values()) {
|
||||
Snapshot snapshot = queryPlan.getSnapshot(reset);
|
||||
if (collectAll || snapshot.getExecutionCount() > 0) {
|
||||
list.add(snapshot);
|
||||
if (!queryPlan.isEmptyStats()) {
|
||||
collector.add(queryPlan.getSnapshot(collector.isReset()));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1908,6 +1927,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
return owner.getBeanDescriptor(otherType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the order column property.
|
||||
*/
|
||||
public BeanProperty getOrderColumn() {
|
||||
return orderColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the "shadow" property to support unidirectional relationships.
|
||||
* <p>
|
||||
@@ -2873,6 +2899,10 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public TableJoin getPrimaryKeyJoin() {
|
||||
return primaryKeyJoin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanProperty getIdProperty() {
|
||||
return idProperty;
|
||||
|
||||
@@ -418,6 +418,22 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
return CachedBeanDataFromBean.extract(targetDesc, bean);
|
||||
}
|
||||
|
||||
void beanPutAll(Collection<EntityBean> beans) {
|
||||
if (desc.inheritInfo != null) {
|
||||
Class<?> aClass = theClassOf(beans);
|
||||
desc.descOf(aClass).cacheBeanPutAllDirect(beans);
|
||||
} else {
|
||||
beanCachePutAllDirect(beans);
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> theClassOf(Collection<EntityBean> beans) {
|
||||
if (beans instanceof List) {
|
||||
return ((List)beans).get(0).getClass();
|
||||
}
|
||||
return beans.iterator().next().getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a bean into the bean cache.
|
||||
*/
|
||||
@@ -430,9 +446,41 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
}
|
||||
|
||||
void beanCachePutAllDirect(Collection<EntityBean> beans) {
|
||||
|
||||
Map<Object,Object> natKeys = null;
|
||||
if (naturalKey != null) {
|
||||
natKeys = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
Map<Object,Object> map = new LinkedHashMap<>();
|
||||
for (EntityBean bean : beans) {
|
||||
CachedBeanData beanData = beanExtractData(desc, bean);
|
||||
Object id = desc.getId(bean);
|
||||
map.put(id, beanData);
|
||||
if (naturalKey != null) {
|
||||
Object naturalKey = calculateNaturalKey(beanData);
|
||||
if (naturalKey != null) {
|
||||
natKeys.put(naturalKey, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" PUT ALL {}({})", cacheName, map.keySet());
|
||||
}
|
||||
getBeanCache().putAll(map);
|
||||
|
||||
if (natKeys != null && !natKeys.isEmpty()) {
|
||||
if (natLog.isDebugEnabled()) {
|
||||
natLog.debug(" PUT ALL {}({}, {})", cacheName, naturalKey, natKeys.keySet());
|
||||
}
|
||||
naturalKeyCache.putAll(natKeys);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Put the bean into the bean cache.
|
||||
*/
|
||||
* Put the bean into the bean cache.
|
||||
*/
|
||||
void beanCachePutDirect(EntityBean bean) {
|
||||
|
||||
CachedBeanData beanData = beanExtractData(desc, bean);
|
||||
@@ -494,6 +542,9 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" GET {}({}) - hit", cacheName, id);
|
||||
}
|
||||
return convertToBean(id, readOnly, context, data);
|
||||
}
|
||||
|
||||
@@ -558,9 +609,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
ebi.setPersistenceContext(context);
|
||||
desc.contextPut(context, id, bean);
|
||||
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" GET {}({}) - hit", cacheName, id);
|
||||
}
|
||||
if (desc.isReadAuditing()) {
|
||||
desc.readAuditBean("l2", "", bean);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanTable;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployOrderColumn;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
|
||||
import io.ebeaninternal.server.deploy.parse.DeployBeanInfo;
|
||||
import io.ebeaninternal.server.deploy.parse.DeployCreateProperties;
|
||||
@@ -50,6 +51,7 @@ import io.ebeaninternal.server.properties.BeanPropertiesReader;
|
||||
import io.ebeaninternal.server.properties.BeanPropertyAccess;
|
||||
import io.ebeaninternal.server.properties.EnhanceBeanPropertyAccess;
|
||||
import io.ebeaninternal.server.query.CQueryPlan;
|
||||
import io.ebeaninternal.server.type.ScalarTypeInteger;
|
||||
import io.ebeaninternal.xmlmapping.XmlMappingReader;
|
||||
import io.ebeaninternal.xmlmapping.model.XmAliasMapping;
|
||||
import io.ebeaninternal.xmlmapping.model.XmColumnMapping;
|
||||
@@ -797,8 +799,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
// We only perform 'circular' checks etc after we have
|
||||
// all the DeployBeanDescriptors created and in the map.
|
||||
|
||||
List<DeployBeanPropertyAssocOne<?>> primaryKeyJoinCheck = new ArrayList<>();
|
||||
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
|
||||
checkMappedBy(info);
|
||||
checkMappedBy(info, primaryKeyJoinCheck);
|
||||
}
|
||||
for (DeployBeanPropertyAssocOne<?> prop : primaryKeyJoinCheck) {
|
||||
checkUniDirectionalPrimaryKeyJoin(prop);
|
||||
}
|
||||
|
||||
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
|
||||
@@ -868,12 +874,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* relationships.
|
||||
* </p>
|
||||
*/
|
||||
private void checkMappedBy(DeployBeanInfo<?> info) {
|
||||
private void checkMappedBy(DeployBeanInfo<?> info, List<DeployBeanPropertyAssocOne<?>> primaryKeyJoinCheck) {
|
||||
|
||||
for (DeployBeanPropertyAssocOne<?> oneProp : info.getDescriptor().propertiesAssocOne()) {
|
||||
if (!oneProp.isTransient()) {
|
||||
if (oneProp.getMappedBy() != null) {
|
||||
checkMappedByOneToOne(oneProp);
|
||||
} else if (oneProp.isPrimaryKeyJoin()) {
|
||||
primaryKeyJoinCheck.add(oneProp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -973,6 +981,23 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
|
||||
private void makeOrderColumn(DeployBeanPropertyAssocMany<?> oneToMany) {
|
||||
|
||||
DeployBeanDescriptor<?> targetDesc = getTargetDescriptor(oneToMany);
|
||||
|
||||
DeployOrderColumn orderColumn = oneToMany.getOrderColumn();
|
||||
DeployBeanProperty orderProperty = new DeployBeanProperty(targetDesc, Integer.class, ScalarTypeInteger.INSTANCE, null);
|
||||
|
||||
orderProperty.setName(DeployOrderColumn.LOGICAL_NAME);
|
||||
orderProperty.setDbColumn(orderColumn.getName());
|
||||
orderProperty.setNullable(orderColumn.isNullable());
|
||||
orderProperty.setDbInsertable(orderColumn.isInsertable());
|
||||
orderProperty.setDbUpdateable(orderColumn.isUpdatable());
|
||||
orderProperty.setDbRead(true);
|
||||
|
||||
targetDesc.setOrderColumn(orderProperty);
|
||||
}
|
||||
|
||||
/**
|
||||
* A OneToMany with no matching mappedBy property in the target so must be
|
||||
* unidirectional.
|
||||
@@ -1071,6 +1096,21 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
DeployTableJoin otherTableJoin = mappedAssocOne.getTableJoin();
|
||||
otherTableJoin.copyWithoutType(tableJoin, true, tableJoin.getTable());
|
||||
}
|
||||
|
||||
if (mappedAssocOne.isPrimaryKeyJoin()) {
|
||||
// bi-directional PrimaryKeyJoin ...
|
||||
mappedAssocOne.setPrimaryKeyJoin(false);
|
||||
prop.setPrimaryKeyExport();
|
||||
addPrimaryKeyJoin(prop);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkUniDirectionalPrimaryKeyJoin(DeployBeanPropertyAssocOne<?> prop) {
|
||||
if (prop.isPrimaryKeyJoin()) {
|
||||
// uni-directional PrimaryKeyJoin ...
|
||||
prop.setPrimaryKeyExport();
|
||||
addPrimaryKeyJoin(prop);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1091,6 +1131,10 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
prop.getCascadeInfo().setSaveDelete(true, true);
|
||||
}
|
||||
|
||||
if (prop.hasOrderColumn()) {
|
||||
makeOrderColumn(prop);
|
||||
}
|
||||
|
||||
if (prop.getMappedBy() == null) {
|
||||
// if we are doc store only we are done
|
||||
// this allowes the use of @OneToMany in @DocStore - Entities
|
||||
@@ -1392,7 +1436,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
Integer pos = reflectProps.getPropertyIndex(propName);
|
||||
if (pos == null) {
|
||||
if (isPersistentField(prop)) {
|
||||
throw new IllegalStateException("Property " + propName + " not found in " + reflectProps + " for type " + desc.getBeanType());
|
||||
throw new IllegalStateException(
|
||||
"If you are running in an IDE with enhancement plugin try a Build -> Rebuild Project to recompile and enhance all entity beans. " +
|
||||
"Error - property " + propName + " not found in " + reflectProps + " for type " + desc.getBeanType());
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -1544,6 +1590,17 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
return changeLogListener;
|
||||
}
|
||||
|
||||
public void addPrimaryKeyJoin(DeployBeanPropertyAssocOne<?> prop) {
|
||||
|
||||
String baseTable = prop.getDesc().getBaseTable();
|
||||
DeployTableJoin inverse = prop.getTableJoin().createInverse(baseTable);
|
||||
|
||||
TableJoin inverseJoin = new TableJoin(inverse);
|
||||
|
||||
DeployBeanInfo<?> target = deployInfoMap.get(prop.getTargetType());
|
||||
target.setPrimaryKeyJoin(inverseJoin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator to sort the BeanDescriptors by name.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.text.PathProperties;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import io.ebeaninternal.server.core.InternString;
|
||||
import io.ebeaninternal.server.deploy.id.IdBinder;
|
||||
import io.ebeaninternal.server.deploy.id.ImportedId;
|
||||
@@ -10,7 +14,7 @@ import io.ebeaninternal.server.deploy.id.ImportedIdSimple;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.el.ElPropertyChainBuilder;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.server.persist.MultiValueWrapper;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeanservice.docstore.api.mapping.DocMappingBuilder;
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyMapping;
|
||||
@@ -402,17 +406,83 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
|
||||
protected void bindWhereParentId(List<Object> bindValues, Object parentId) {
|
||||
private List<Object> flattenParentIds(List<Object> parentIds) {
|
||||
List<Object> bindValues = new ArrayList<>(parentIds.size() * 3);
|
||||
for (Object parentId : parentIds) {
|
||||
flatten(bindValues, parentId);
|
||||
}
|
||||
return bindValues;
|
||||
}
|
||||
|
||||
if (exportedProperties.length == 1) {
|
||||
private List<Object> flattenParentId(Object parentId) {
|
||||
List<Object> bindValues = new ArrayList<>();
|
||||
flatten(bindValues, parentId);
|
||||
return bindValues;
|
||||
}
|
||||
|
||||
private void flatten(List<Object> bindValues, Object parentId) {
|
||||
|
||||
if (isExportedSimple()) {
|
||||
bindValues.add(parentId);
|
||||
|
||||
} else {
|
||||
EntityBean parent = (EntityBean) parentId;
|
||||
for (ExportedProperty exportedProperty : exportedProperties) {
|
||||
Object embVal = exportedProperty.getValue(parent);
|
||||
bindValues.add(embVal);
|
||||
bindValues.add(exportedProperty.getValue(parent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EbeanServer server() {
|
||||
return getBeanDescriptor().getEbeanServer();
|
||||
}
|
||||
|
||||
void bindParentIds(DefaultSqlUpdate delete, List<Object> parentIds) {
|
||||
|
||||
if (isExportedSimple()) {
|
||||
delete.addParameter(new MultiValueWrapper(parentIds));
|
||||
} else {
|
||||
// embedded ids etc
|
||||
List<Object> bindValues = flattenParentIds(parentIds);
|
||||
for (Object bindValue : bindValues) {
|
||||
delete.addParameter(bindValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bindParentId(DefaultSqlUpdate sqlUpd, Object parentId) {
|
||||
|
||||
if (isExportedSimple()) {
|
||||
sqlUpd.addParameter(parentId);
|
||||
return;
|
||||
}
|
||||
EntityBean parent = (EntityBean) parentId;
|
||||
for (ExportedProperty exportedProperty : exportedProperties) {
|
||||
sqlUpd.addParameter(exportedProperty.getValue(parent));
|
||||
}
|
||||
}
|
||||
|
||||
void bindParentIdEq(String expr, Object parentId, Query<?> q) {
|
||||
if (isExportedSimple()) {
|
||||
q.where().raw(expr, parentId);
|
||||
} else {
|
||||
// embedded ids etc
|
||||
List<Object> bindValues = flattenParentId(parentId);
|
||||
q.where().raw(expr, bindValues.toArray());
|
||||
}
|
||||
}
|
||||
|
||||
void bindParentIdsIn(String expr, List<Object> parentIds, Query<?> q) {
|
||||
if (isExportedSimple()) {
|
||||
q.where().raw(expr, new MultiValueWrapper(parentIds));
|
||||
} else {
|
||||
// embedded ids etc
|
||||
List<Object> bindValues = flattenParentIds(parentIds);
|
||||
q.where().raw(expr, bindValues.toArray());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isExportedSimple() {
|
||||
return exportedProperties.length == 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.Expression;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.Transaction;
|
||||
@@ -18,7 +17,6 @@ import io.ebeaninternal.server.deploy.id.ImportedId;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.el.ElPropertyChainBuilder;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.persist.MultiValueWrapper;
|
||||
import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
@@ -60,6 +58,11 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
*/
|
||||
private final boolean unidirectional;
|
||||
|
||||
/**
|
||||
* Flag to indicate that the target has a order column to auto populate.
|
||||
*/
|
||||
private final boolean hasOrderColumn;
|
||||
|
||||
/**
|
||||
* Flag to indicate manyToMany relationship.
|
||||
*/
|
||||
@@ -111,6 +114,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
public BeanPropertyAssocMany(BeanDescriptor<?> descriptor, DeployBeanPropertyAssocMany<T> deploy) {
|
||||
super(descriptor, deploy);
|
||||
this.unidirectional = deploy.isUnidirectional();
|
||||
this.hasOrderColumn = deploy.hasOrderColumn();
|
||||
this.manyToMany = deploy.isManyToMany();
|
||||
this.manyType = deploy.getManyType();
|
||||
this.mapKey = deploy.getMapKey();
|
||||
@@ -290,7 +294,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
private SqlUpdate deleteByParentId(Object parentId) {
|
||||
DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByParentIdSql);
|
||||
bindWhereParendId(sqlDelete, parentId);
|
||||
bindParentId(sqlDelete, parentId);
|
||||
return sqlDelete;
|
||||
}
|
||||
|
||||
@@ -308,18 +312,13 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
private List<Object> findIdsByParentId(Object parentId, Transaction t, List<Object> excludeDetailIds) {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(false, "");
|
||||
List<Object> bindValues = new ArrayList<>();
|
||||
bindWhereParentId(bindValues, parentId);
|
||||
|
||||
EbeanServer server = getBeanDescriptor().getEbeanServer();
|
||||
Query<?> q = server.find(getPropertyType())
|
||||
.where()
|
||||
.raw(rawWhere, bindValues.toArray())
|
||||
.query();
|
||||
EbeanServer server = server();
|
||||
Query<?> q = server.find(getPropertyType());
|
||||
bindParentIdEq(rawWhere, parentId, q);
|
||||
|
||||
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
|
||||
Expression idIn = q.getExpressionFactory().idIn(excludeDetailIds);
|
||||
q.where().not(idIn);
|
||||
q.where().not(q.getExpressionFactory().idIn(excludeDetailIds));
|
||||
}
|
||||
|
||||
return server.findIds(q, t);
|
||||
@@ -334,16 +333,6 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the loaded current bean to its associated parent.
|
||||
*/
|
||||
public void lazyLoadMany(EntityBean current) {
|
||||
EntityBean parentBean = childMasterProperty.getValueAsEntityBean(current);
|
||||
if (parentBean != null) {
|
||||
addBeanToCollectionWithCreate(parentBean, current, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void addWhereParentIdIn(SpiQuery<?> query, List<Object> parentIds, boolean useDocStore) {
|
||||
if (useDocStore) {
|
||||
// assumes the ManyToOne property is included
|
||||
@@ -365,59 +354,35 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
String rawWhere = deriveWhereParentIdSql(true, tableAlias);
|
||||
String expr = descriptor.getParentIdInExpr(parentIds.size(), rawWhere);
|
||||
|
||||
// Flatten the bind values if needed (embeddedId)
|
||||
List<Object> bindValues = getBindParentIds(parentIds);
|
||||
if (descriptor.isSimpleId()) {
|
||||
query.where().raw(expr, new MultiValueWrapper(bindValues));
|
||||
} else {
|
||||
query.where().raw(expr, bindValues.toArray());
|
||||
}
|
||||
bindParentIdsIn(expr, parentIds, query);
|
||||
}
|
||||
|
||||
private List<Object> findIdsByParentIdList(List<Object> parentIdList, Transaction t, List<Object> excludeDetailIds) {
|
||||
private List<Object> findIdsByParentIdList(List<Object> parentIds, Transaction t, List<Object> excludeDetailIds) {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(true, "");
|
||||
String inClause = buildInClauseBinding(parentIdList.size(), exportedPropertyBindProto);
|
||||
String inClause = buildInClauseBinding(parentIds.size(), exportedPropertyBindProto);
|
||||
|
||||
String expr = rawWhere + inClause;
|
||||
|
||||
List<Object> bindValues = new ArrayList<>();
|
||||
for (Object aParentIdList : parentIdList) {
|
||||
bindWhereParentId(bindValues, aParentIdList);
|
||||
}
|
||||
|
||||
EbeanServer server = getBeanDescriptor().getEbeanServer();
|
||||
Query<?> q = server.find(getPropertyType());
|
||||
if (descriptor.isSimpleId()) {
|
||||
q.where().raw(expr, new MultiValueWrapper(bindValues));
|
||||
} else {
|
||||
q.where().raw(expr, bindValues.toArray());
|
||||
}
|
||||
EbeanServer server = descriptor.getEbeanServer();
|
||||
Query<?> q = server.find(propertyType);
|
||||
bindParentIdsIn(expr, parentIds, q);
|
||||
|
||||
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
|
||||
Expression idIn = q.getExpressionFactory().idIn(excludeDetailIds);
|
||||
q.where().not(idIn);
|
||||
q.where().not(q.getExpressionFactory().idIn(excludeDetailIds));
|
||||
}
|
||||
|
||||
return server.findIds(q, t);
|
||||
}
|
||||
|
||||
private SqlUpdate deleteByParentIdList(List<Object> parentIdList) {
|
||||
private SqlUpdate deleteByParentIdList(List<Object> parentIds) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(100);
|
||||
sb.append(deleteByParentIdInSql);
|
||||
|
||||
String inClause = buildInClauseBinding(parentIdList.size(), exportedPropertyBindProto);
|
||||
sb.append(inClause);
|
||||
sb.append(buildInClauseBinding(parentIds.size(), exportedPropertyBindProto));
|
||||
|
||||
DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString());
|
||||
if (exportedProperties.length == 1) {
|
||||
bindWhereParendId(delete, new MultiValueWrapper(parentIdList));
|
||||
} else {
|
||||
for (Object aParentIdist : parentIdList) {
|
||||
bindWhereParendId(delete, aParentIdist);
|
||||
}
|
||||
}
|
||||
bindParentIds(delete, parentIds);
|
||||
return delete;
|
||||
}
|
||||
|
||||
@@ -506,22 +471,34 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
@Override
|
||||
public String getAssocIsEmpty(SpiExpressionRequest request, String path) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean softDelete = targetDescriptor.isSoftDelete();
|
||||
|
||||
StringBuilder sb = new StringBuilder(50);
|
||||
SpiQuery<?> query = request.getQueryRequest().getQuery();
|
||||
if (manyToMany) {
|
||||
sb.append(query.isAsDraft() ? intersectionDraftTable : intersectionPublishTable);
|
||||
} else {
|
||||
sb.append(targetDescriptor.getBaseTable(query.getTemporalMode()));
|
||||
}
|
||||
sb.append(" x where ");
|
||||
if (softDelete && manyToMany) {
|
||||
sb.append(" x join ");
|
||||
sb.append(targetDescriptor.getBaseTable(query.getTemporalMode()));
|
||||
sb.append(" x2 on ");
|
||||
inverseJoin.addJoin("x2", "x", sb);
|
||||
} else {
|
||||
sb.append(" x");
|
||||
}
|
||||
|
||||
sb.append(" where ");
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(" and ");
|
||||
}
|
||||
exportedProperties[i].appendWhere(sb, "x.", path);
|
||||
}
|
||||
if (targetDescriptor.isSoftDelete()) {
|
||||
sb.append(" and ").append(targetDescriptor.getSoftDeletePredicate("x"));
|
||||
if (softDelete) {
|
||||
String alias = (manyToMany) ? "x2" : "x";
|
||||
sb.append(" and ").append(targetDescriptor.getSoftDeletePredicate(alias));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
@@ -563,6 +540,10 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean hasOrderColumn() {
|
||||
return hasOrderColumn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssocMany() {
|
||||
return true;
|
||||
@@ -678,33 +659,6 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
return descriptor.getId(parentBean);
|
||||
}
|
||||
|
||||
public List<Object> getBindParentIds(List<Object> parentIds) {
|
||||
if (exportedProperties.length == 1) {
|
||||
return parentIds;
|
||||
}
|
||||
List<Object> expandedList = new ArrayList<>(parentIds.size() * exportedProperties.length);
|
||||
for (Object parentId : parentIds) {
|
||||
for (ExportedProperty exportedProperty : exportedProperties) {
|
||||
Object compId = parentId;
|
||||
expandedList.add(exportedProperty.getValue((EntityBean) compId));
|
||||
}
|
||||
}
|
||||
return expandedList;
|
||||
}
|
||||
|
||||
private void bindWhereParendId(DefaultSqlUpdate sqlUpd, Object parentId) {
|
||||
|
||||
if (exportedProperties.length == 1) {
|
||||
sqlUpd.addParameter(parentId);
|
||||
return;
|
||||
}
|
||||
EntityBean parent = (EntityBean) parentId;
|
||||
for (ExportedProperty exportedProperty : exportedProperties) {
|
||||
Object embVal = exportedProperty.getValue(parent);
|
||||
sqlUpd.addParameter(embVal);
|
||||
}
|
||||
}
|
||||
|
||||
public void addSelectExported(DbSqlContext ctx, String tableAlias) {
|
||||
|
||||
String alias = manyToMany ? "int_" : tableAlias;
|
||||
|
||||
@@ -7,6 +7,7 @@ import io.ebean.Transaction;
|
||||
import io.ebean.ValuePair;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.server.cache.CacheChangeSet;
|
||||
import io.ebeaninternal.server.cache.CachedBeanData;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
@@ -14,7 +15,6 @@ import io.ebeaninternal.server.deploy.id.ImportedId;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.el.ElPropertyChainBuilder;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
@@ -39,15 +39,14 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
private final boolean importedPrimaryKey;
|
||||
|
||||
private final boolean primaryKeyExport;
|
||||
|
||||
private AssocOneHelp localHelp;
|
||||
|
||||
protected final BeanProperty[] embeddedProps;
|
||||
|
||||
private final HashMap<String, BeanProperty> embeddedPropsMap;
|
||||
|
||||
/**
|
||||
* The information for Imported foreign Keys.
|
||||
*/
|
||||
protected ImportedId importedId;
|
||||
|
||||
private String deleteByParentIdSql;
|
||||
@@ -70,6 +69,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
super(descriptor, deploy);
|
||||
|
||||
primaryKeyExport = deploy.isPrimaryKeyExport();
|
||||
importedPrimaryKey = deploy.isImportedPrimaryKey();
|
||||
oneToOne = deploy.isOneToOne();
|
||||
oneToOneExported = deploy.isOneToOneExported();
|
||||
@@ -202,38 +202,29 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
}
|
||||
|
||||
private SqlUpdate deleteByParentIdList(List<Object> parentIdist) {
|
||||
private SqlUpdate deleteByParentIdList(List<Object> parentIds) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(100);
|
||||
sb.append(deleteByParentIdInSql);
|
||||
|
||||
String inClause = targetIdBinder.getIdInValueExpr(false, parentIdist.size());
|
||||
sb.append(inClause);
|
||||
sb.append(targetIdBinder.getIdInValueExpr(false, parentIds.size()));
|
||||
|
||||
DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString());
|
||||
for (Object aParentIdist : parentIdist) {
|
||||
targetIdBinder.bindId(delete, aParentIdist);
|
||||
}
|
||||
|
||||
bindParentIds(delete, parentIds);
|
||||
return delete;
|
||||
}
|
||||
|
||||
private SqlUpdate deleteByParentId(Object parentId) {
|
||||
|
||||
DefaultSqlUpdate delete = new DefaultSqlUpdate(deleteByParentIdSql);
|
||||
if (exportedProperties.length == 1) {
|
||||
delete.addParameter(parentId);
|
||||
} else {
|
||||
targetDescriptor.getIdBinder().bindId(delete, parentId);
|
||||
}
|
||||
bindParentId(delete, parentId);
|
||||
return delete;
|
||||
}
|
||||
|
||||
public List<Object> findIdsByParentId(Object parentId, List<Object> parentIdist, Transaction t) {
|
||||
public List<Object> findIdsByParentId(Object parentId, List<Object> parentIds, Transaction t) {
|
||||
if (parentId != null) {
|
||||
return findIdsByParentId(parentId, t);
|
||||
} else {
|
||||
return findIdsByParentIdList(parentIdist, t);
|
||||
return findIdsByParentIdList(parentIds, t);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,33 +232,21 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(false);
|
||||
|
||||
List<Object> bindValues = new ArrayList<>();
|
||||
bindWhereParentId(bindValues, parentId);
|
||||
|
||||
EbeanServer server = getBeanDescriptor().getEbeanServer();
|
||||
Query<?> q = server.find(getPropertyType())
|
||||
.where()
|
||||
.raw(rawWhere, bindValues.toArray())
|
||||
.query();
|
||||
|
||||
EbeanServer server = server();
|
||||
Query<?> q = server.find(getPropertyType());
|
||||
bindParentIdEq(rawWhere, parentId, q);
|
||||
return server.findIds(q, t);
|
||||
}
|
||||
|
||||
private List<Object> findIdsByParentIdList(List<Object> parentIdList, Transaction t) {
|
||||
private List<Object> findIdsByParentIdList(List<Object> parentIds, Transaction t) {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(true);
|
||||
String inClause = targetIdBinder.getIdInValueExpr(false, parentIdList.size());
|
||||
|
||||
String inClause = targetIdBinder.getIdInValueExpr(false, parentIds.size());
|
||||
String expr = rawWhere + inClause;
|
||||
|
||||
List<Object> bindValues = new ArrayList<>();
|
||||
for (Object aParentIdList : parentIdList) {
|
||||
bindWhereParentId(bindValues, aParentIdList);
|
||||
}
|
||||
|
||||
EbeanServer server = getBeanDescriptor().getEbeanServer();
|
||||
Query<?> q = server.find(getPropertyType())
|
||||
.where().raw(expr, bindValues.toArray()).query();
|
||||
EbeanServer server = server();
|
||||
Query<?> q = server.find(getPropertyType());
|
||||
bindParentIdsIn(expr, parentIds, q);
|
||||
|
||||
return server.findIds(q, t);
|
||||
}
|
||||
@@ -571,13 +550,17 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
@Override
|
||||
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
|
||||
if (!isTransient) {
|
||||
localHelp.appendSelect(ctx, subQuery);
|
||||
if (primaryKeyExport) {
|
||||
descriptor.getIdProperty().appendSelect(ctx, subQuery);
|
||||
} else {
|
||||
localHelp.appendSelect(ctx, subQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
if (!isTransient) {
|
||||
if (!isTransient && !primaryKeyExport) {
|
||||
localHelp.appendFrom(ctx, joinType);
|
||||
if (sqlFormulaJoin != null) {
|
||||
ctx.appendFormulaJoin(sqlFormulaJoin, joinType);
|
||||
@@ -755,6 +738,11 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
*/
|
||||
public void setParentBeanToChild(EntityBean parent, EntityBean child) {
|
||||
|
||||
if (primaryKeyExport) {
|
||||
Object parentId = descriptor.getId(parent);
|
||||
targetDescriptor.convertSetId(parentId, child);
|
||||
}
|
||||
|
||||
if (mappedBy != null) {
|
||||
BeanProperty beanProperty = targetDescriptor.getBeanProperty(mappedBy);
|
||||
if (beanProperty != null && beanProperty.getValue(child) == null) {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
/**
|
||||
* Bean property for synthetic sort order value / order column.
|
||||
*
|
||||
* The value of which is held on the entity bean intercept.
|
||||
*/
|
||||
public class BeanPropertyOrderColumn extends BeanProperty {
|
||||
|
||||
public BeanPropertyOrderColumn(BeanDescriptor<?> descriptor, DeployBeanProperty deploy) {
|
||||
super(descriptor, deploy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(EntityBean bean) {
|
||||
return bean._ebean_getIntercept().getSortOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueIntercept(EntityBean bean) {
|
||||
return bean._ebean_getIntercept().getSortOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(EntityBean bean, Object value) {
|
||||
bean._ebean_getIntercept().setSortOrder((int)value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueIntercept(EntityBean bean, Object value) {
|
||||
bean._ebean_getIntercept().setSortOrder((int)value);
|
||||
}
|
||||
}
|
||||
@@ -121,4 +121,13 @@ public class BeanTable {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the primary key DB column.
|
||||
*/
|
||||
public String getIdColumn() {
|
||||
if (idProperties.length != 1) {
|
||||
throw new IllegalStateException("Expecting only one Id column to join to on "+beanType);
|
||||
}
|
||||
return idProperties[0].dbColumn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ public abstract class DeployParser {
|
||||
|
||||
protected int pos;
|
||||
|
||||
protected String priorWord;
|
||||
|
||||
protected String word;
|
||||
|
||||
protected char wordTerminator;
|
||||
@@ -45,7 +47,6 @@ public abstract class DeployParser {
|
||||
|
||||
public abstract String getDeployWord(String expression);
|
||||
|
||||
|
||||
/**
|
||||
* Return the join includes.
|
||||
*/
|
||||
@@ -67,8 +68,14 @@ public abstract class DeployParser {
|
||||
this.sb = new StringBuilder(source.length() + 20);
|
||||
|
||||
while (nextWord()) {
|
||||
String deployWord = convertWord();
|
||||
sb.append(deployWord);
|
||||
if (skipWordConvert()) {
|
||||
sb.append(word);
|
||||
priorWord = word;
|
||||
} else {
|
||||
String deployWord = convertWord();
|
||||
sb.append(deployWord);
|
||||
priorWord = deployWord;
|
||||
}
|
||||
if (pos < sourceLength) {
|
||||
sb.append(wordTerminator);
|
||||
if (wordTerminator == SINGLE_QUOTE) {
|
||||
@@ -80,6 +87,10 @@ public abstract class DeployParser {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected boolean skipWordConvert() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean nextWord() {
|
||||
|
||||
if (!findWordStart()) {
|
||||
|
||||
@@ -14,12 +14,15 @@ import java.util.Set;
|
||||
*/
|
||||
public final class DeployPropertyParser extends DeployParser {
|
||||
|
||||
private static final String JOIN = "join";
|
||||
|
||||
private static final String FROM = "from";
|
||||
|
||||
private final BeanDescriptor<?> beanDescriptor;
|
||||
|
||||
private final Set<String> includes = new HashSet<>();
|
||||
|
||||
public DeployPropertyParser(BeanDescriptor<?> beanDescriptor) {
|
||||
DeployPropertyParser(BeanDescriptor<?> beanDescriptor) {
|
||||
this.beanDescriptor = beanDescriptor;
|
||||
}
|
||||
|
||||
@@ -28,6 +31,13 @@ public final class DeployPropertyParser extends DeployParser {
|
||||
return includes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip if in raw sql expression with from tableName or join tableName.
|
||||
*/
|
||||
protected boolean skipWordConvert() {
|
||||
return FROM.equalsIgnoreCase(priorWord) || JOIN.equalsIgnoreCase(priorWord);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDeployWord(String expression) {
|
||||
ElPropertyDeploy elProp = beanDescriptor.getElPropertyDeploy(expression);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.server.core.InternString;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
|
||||
/**
|
||||
@@ -145,4 +145,15 @@ public final class TableJoin {
|
||||
return joinType.autoToOuter(type);
|
||||
}
|
||||
|
||||
public void addJoin(String a1, String a2, StringBuilder sb) {
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
TableJoinColumn pair = columns[i];
|
||||
if (i > 0) {
|
||||
sb.append(" and ");
|
||||
}
|
||||
sb.append(a1).append(".").append(pair.getLocalDbColumn());
|
||||
sb.append(" = ");
|
||||
sb.append(a2).append(".").append(pair.getForeignDbColumn());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user