mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
64
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
+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.8.1</version>
|
||||
<version>11.10.4</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.8.1</tag>
|
||||
<tag>ebean-11.10.4</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -103,7 +103,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-annotation</artifactId>
|
||||
<version>3.5</version>
|
||||
<version>3.6</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -112,22 +112,16 @@
|
||||
<version>3.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-classpath-scanner-api</artifactId>
|
||||
<version>2.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-classpath-scanner</artifactId>
|
||||
<version>2.2.5</version>
|
||||
<version>3.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-migration</artifactId>
|
||||
<version>11.2.1</version>
|
||||
<version>11.3.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -204,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>
|
||||
@@ -214,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>
|
||||
|
||||
@@ -287,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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -174,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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -331,6 +331,7 @@ public class DefaultContainer implements SpiContainer {
|
||||
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,6 +61,7 @@ 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;
|
||||
@@ -92,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;
|
||||
@@ -130,18 +130,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private final TransactionManager transactionManager;
|
||||
|
||||
private final TransactionScopeManager transactionScopeManager;
|
||||
|
||||
private final DataTimeZone dataTimeZone;
|
||||
|
||||
private final CallStackFactory callStackFactory;
|
||||
|
||||
/**
|
||||
* Ebean defaults this to true but for EJB compatible behaviour set this to
|
||||
* false;
|
||||
*/
|
||||
private final boolean rollbackOnChecked;
|
||||
|
||||
/**
|
||||
* Handles the save, delete, updateSql CallableSql.
|
||||
*/
|
||||
@@ -249,8 +241,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
this.collectQueryStatsByNode = serverConfig.isCollectQueryStatsByNode();
|
||||
this.callStackFactory = initCallStackFactory(serverConfig);
|
||||
|
||||
this.rollbackOnChecked = serverConfig.isTransactionRollbackOnChecked();
|
||||
|
||||
this.persister = config.createPersister(this);
|
||||
this.queryEngine = config.createOrmQueryEngine();
|
||||
this.relationalQueryEngine = config.createRelationalQueryEngine();
|
||||
@@ -265,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();
|
||||
@@ -563,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -629,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);
|
||||
@@ -668,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -680,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
|
||||
@@ -691,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();
|
||||
|
||||
@@ -713,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();
|
||||
|
||||
@@ -728,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);
|
||||
@@ -772,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -785,7 +725,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public SpiTransaction currentServerTransaction() {
|
||||
return transactionScopeManager.get();
|
||||
return transactionManager.getActive();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -804,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -863,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;
|
||||
@@ -879,7 +773,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public Transaction currentTransaction() {
|
||||
return transactionScopeManager.get();
|
||||
return transactionManager.getActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -892,7 +786,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public void commitTransaction() {
|
||||
transactionScopeManager.commit();
|
||||
transactionManager.scope().commit();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -900,7 +794,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public void rollbackTransaction() {
|
||||
transactionScopeManager.rollback();
|
||||
transactionManager.scope().rollback();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -933,7 +827,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public void endTransaction() {
|
||||
transactionScopeManager.end();
|
||||
transactionManager.scope().end();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1114,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
|
||||
@@ -1123,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());
|
||||
@@ -1203,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);
|
||||
}
|
||||
@@ -1224,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);
|
||||
}
|
||||
@@ -1527,6 +1427,7 @@ 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);
|
||||
request.profileLocationAll();
|
||||
request.resetBeanCacheAutoMode(findOne);
|
||||
Object result = request.getFromQueryCache();
|
||||
if (result != null) {
|
||||
@@ -1585,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();
|
||||
@@ -1973,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) {
|
||||
@@ -2127,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);
|
||||
}
|
||||
@@ -2137,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
|
||||
@@ -2198,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()) {
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,19 +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;
|
||||
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;
|
||||
}
|
||||
persistenceContext = getPersistenceContext(query, transaction);
|
||||
loadContext = new DLoadContext(this, secondaryQueries);
|
||||
@@ -513,8 +523,12 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
query.resetBeanCacheAutoMode(findOne);
|
||||
}
|
||||
|
||||
public boolean isQueryCachePut() {
|
||||
return cacheKey != null && query.getUseQueryCache().isPut();
|
||||
}
|
||||
|
||||
public boolean isBeanCachePut() {
|
||||
return query.isBeanCachePut();
|
||||
return !transaction.isSkipCache() && query.isBeanCachePut();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -586,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;
|
||||
|
||||
@@ -139,7 +139,7 @@ public interface SpiOrmQueryRequest<T> extends DocQueryRequest<T> {
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
@@ -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,8 +28,6 @@ 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;
|
||||
@@ -63,7 +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.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;
|
||||
|
||||
@@ -415,6 +416,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);
|
||||
@@ -537,6 +540,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.
|
||||
*/
|
||||
@@ -1528,25 +1545,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1392,7 +1392,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 {
|
||||
|
||||
@@ -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;
|
||||
@@ -290,7 +288,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 +306,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 +327,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 +348,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 +465,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();
|
||||
}
|
||||
@@ -678,33 +649,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;
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ public class DeployInheritInfo {
|
||||
public int getColumnLength(InheritInfo parent) {
|
||||
if (columnLength == 0) {
|
||||
if (parent == null) {
|
||||
columnLength = 10;
|
||||
columnLength = 31;
|
||||
} else {
|
||||
columnLength = parent.getColumnLength();
|
||||
}
|
||||
|
||||
@@ -112,30 +112,16 @@ public class DeployUtil {
|
||||
if (!enumType.isEnum()) {
|
||||
throw new IllegalArgumentException("Class [" + enumType + "] is Not a Enum?");
|
||||
}
|
||||
ScalarType<?> scalarType = typeManager.getScalarType(enumType);
|
||||
if (enumOverrideDefaultMapping(enumerated, scalarType)) {
|
||||
logger.debug("override default enum mapping for type {}", enumType);
|
||||
scalarType = null;
|
||||
}
|
||||
if (scalarType == null) {
|
||||
// look for @DbEnumValue or @EnumValue annotations etc
|
||||
Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) enumType;
|
||||
EnumType type = enumerated != null ? enumerated.value() : null;
|
||||
scalarType = typeManager.createEnumScalarType(enumClass, type);
|
||||
}
|
||||
prop.setScalarType(scalarType);
|
||||
prop.setDbType(scalarType.getJdbcType());
|
||||
}
|
||||
try {
|
||||
Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) enumType;
|
||||
EnumType type = enumerated != null ? enumerated.value() : null;
|
||||
ScalarType<?> scalarType = typeManager.createEnumScalarType(enumClass, type);
|
||||
prop.setScalarType(scalarType);
|
||||
prop.setDbType(scalarType.getJdbcType());
|
||||
|
||||
/**
|
||||
* Return true if there is an existing default mapping for the enum that needs
|
||||
* to be overridden (for example, DayOfWeek defaults to Integer mapping 1 to 7
|
||||
* and some might want this mapped to 'MONDAY' etc).
|
||||
*/
|
||||
private boolean enumOverrideDefaultMapping(Enumerated enumerated, ScalarType<?> scalarType) {
|
||||
return enumerated != null && scalarType != null
|
||||
&& enumerated.value() == EnumType.STRING
|
||||
&& scalarType.getJdbcType() != Types.VARCHAR;
|
||||
} catch (IllegalStateException e) {
|
||||
throw new PersistenceException("Error mapping property " + prop.getFullBeanName() + " - " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,7 +290,7 @@ public class DeployUtil {
|
||||
prop.setScalarType(scalarType);
|
||||
}
|
||||
|
||||
public boolean isClobType(Class<?> type) {
|
||||
private boolean isClobType(Class<?> type) {
|
||||
return type.equals(String.class);
|
||||
}
|
||||
|
||||
|
||||
@@ -91,9 +91,9 @@ public class DefaultExpressionRequest implements SpiExpressionRequest {
|
||||
* Append the database platform like clause.
|
||||
*/
|
||||
@Override
|
||||
public void appendLike() {
|
||||
public void appendLike(boolean rawLikeExpression) {
|
||||
sql.append(" ");
|
||||
sql.append(queryRequest.getDBLikeClause());
|
||||
sql.append(queryRequest.getDBLikeClause(rawLikeExpression));
|
||||
sql.append(" ");
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ class LikeExpression extends AbstractValueExpression {
|
||||
request.append(" = ? ");
|
||||
} else {
|
||||
// append db platform like clause
|
||||
request.appendLike();
|
||||
request.appendLike(type == LikeType.RAW);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,10 @@ public class BatchedBeanHolder {
|
||||
// with binding addBatch() for each request.
|
||||
// Note updates and deletes can result in many PreparedStatements
|
||||
// if their where clauses differ via use of IS NOT NULL.
|
||||
if (deletes != null && !deletes.isEmpty()) {
|
||||
control.executeNow(deletes);
|
||||
deletes.clear();
|
||||
}
|
||||
if (inserts != null && !inserts.isEmpty()) {
|
||||
control.executeNow(inserts);
|
||||
inserts.clear();
|
||||
@@ -92,10 +96,6 @@ public class BatchedBeanHolder {
|
||||
control.executeNow(updates);
|
||||
updates.clear();
|
||||
}
|
||||
if (deletes != null && !deletes.isEmpty()) {
|
||||
control.executeNow(deletes);
|
||||
deletes.clear();
|
||||
}
|
||||
persistedBeans.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
|
||||
/**
|
||||
* Fixed / given location. Used internally for find by id and find all.
|
||||
*/
|
||||
class BasicProfileLocation implements ProfileLocation {
|
||||
|
||||
private final String location;
|
||||
private final String shortDescription;
|
||||
|
||||
BasicProfileLocation(String location) {
|
||||
this.location = location;
|
||||
this.shortDescription = shortDesc(location);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return shortDescription;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(long executionTime) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public String obtain() {
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String shortDescription() {
|
||||
return shortDescription;
|
||||
}
|
||||
|
||||
private String shortDesc(String location) {
|
||||
int lastPer = location.lastIndexOf('.');
|
||||
if (lastPer > -1) {
|
||||
lastPer = location.lastIndexOf('.', lastPer-1);
|
||||
if (lastPer > -1) {
|
||||
return location.substring(lastPer+1);
|
||||
}
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetricMap;
|
||||
|
||||
/**
|
||||
* Default metric factory implementation.
|
||||
*/
|
||||
public class DMetricFactory implements MetricFactory {
|
||||
|
||||
@Override
|
||||
public TimedMetricMap createTimedMetricMap(String name) {
|
||||
return new DTimedMetricMap(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimedMetric createTimedMetric(String name) {
|
||||
return new DTimedMetric(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
|
||||
/**
|
||||
* Default profile location that uses stack trace.
|
||||
*/
|
||||
class DProfileLocation implements ProfileLocation {
|
||||
|
||||
private static final String IO_EBEAN = "io.ebean";
|
||||
|
||||
private static final String UNKNOWN = "unknown";
|
||||
|
||||
private String location;
|
||||
|
||||
private String shortDescription;
|
||||
|
||||
private final int lineNumber;
|
||||
|
||||
DProfileLocation() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create with a given line number.
|
||||
*/
|
||||
DProfileLocation(int lineNumber) {
|
||||
this.lineNumber = lineNumber;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "location: " + location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(long executionTime) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public String obtain() {
|
||||
// atomic assignment so happy with this
|
||||
if (location == null) {
|
||||
location = create();
|
||||
shortDescription = shortDesc(location);
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String shortDescription() {
|
||||
return shortDescription;
|
||||
}
|
||||
|
||||
private String create() {
|
||||
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
|
||||
for (int i = 3; i < trace.length; i++) {
|
||||
if (!trace[i].getClassName().startsWith(IO_EBEAN)) {
|
||||
return withLineNumber(trace[i].toString());
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
private String withLineNumber(String traceLine) {
|
||||
if (lineNumber == 0) {
|
||||
return traceLine;
|
||||
} else if (traceLine.endsWith(":1)")) {
|
||||
return traceLine.substring(0, traceLine.length() - 3) + ":" + lineNumber + ")";
|
||||
} else if (traceLine.contains(":")) {
|
||||
return traceLine;
|
||||
} else {
|
||||
return traceLine.substring(0, traceLine.length() - 1) + ":" + lineNumber + ")";
|
||||
}
|
||||
}
|
||||
|
||||
private String shortDesc(String location) {
|
||||
int pos = location.lastIndexOf('(');
|
||||
if (pos == -1) {
|
||||
pos = location.length();
|
||||
}
|
||||
|
||||
pos = location.lastIndexOf('.', pos);
|
||||
if (pos > -1) {
|
||||
pos = location.lastIndexOf('.', pos - 1);
|
||||
if (pos > -1) {
|
||||
return location.substring(pos + 1);
|
||||
}
|
||||
}
|
||||
return location;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.service.SpiProfileLocationFactory;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
|
||||
/**
|
||||
* Default implementation of the profile location factory.
|
||||
*/
|
||||
public class DProfileLocationFactory implements SpiProfileLocationFactory {
|
||||
|
||||
@Override
|
||||
public ProfileLocation create() {
|
||||
return new DProfileLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileLocation create(int lineNumber, String label) {
|
||||
|
||||
TimedMetric timedMetric = MetricFactory.get().createTimedMetric("txn.named." + label);
|
||||
|
||||
DTimedProfileLocation loc = new DTimedProfileLocation(lineNumber, label, timedMetric);
|
||||
TimedProfileLocationRegistry.register(loc);
|
||||
return loc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileLocation createAt(String location) {
|
||||
return new BasicProfileLocation(location);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebeaninternal.metric.TimedMetricStats;
|
||||
|
||||
/**
|
||||
* Snapshot of the current statistics for a Counter or TimeCounter.
|
||||
*/
|
||||
class DTimeMetricStats implements TimedMetricStats {
|
||||
|
||||
private final String name;
|
||||
|
||||
private String location;
|
||||
|
||||
private final long startTime;
|
||||
|
||||
private final long count;
|
||||
|
||||
private final long total;
|
||||
|
||||
private final long max;
|
||||
|
||||
DTimeMetricStats(String name, long collectionStart, long count, long total, long max) {
|
||||
this.name = name;
|
||||
this.startTime = collectionStart;
|
||||
this.count = count;
|
||||
this.total = total;
|
||||
// collection is racy so sanitize the max value if it has not been set
|
||||
// this most likely would happen when count = 1 so max = mean
|
||||
this.max = max != Long.MIN_VALUE ? max : (count < 1 ? 0 : Math.round(total / count));
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (location != null) {
|
||||
sb.append("loc:").append(location).append(" ");
|
||||
}
|
||||
if (name != null) {
|
||||
sb.append("name:").append(name).append(" ");
|
||||
}
|
||||
sb.append("count:").append(count)
|
||||
.append(" total:").append(total)
|
||||
.append(" max:").append(max);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void setLocation(String location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time the counter started statistics collection.
|
||||
*/
|
||||
@Override
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the count of values collected.
|
||||
*/
|
||||
@Override
|
||||
public long getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total of all the values.
|
||||
*/
|
||||
@Override
|
||||
public long getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Max value collected.
|
||||
*/
|
||||
@Override
|
||||
public long getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mean value rounded up.
|
||||
*/
|
||||
@Override
|
||||
public long getMean() {
|
||||
return (count < 1) ? 0L : Math.round((double)(total / count));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.LongAccumulator;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
|
||||
/**
|
||||
* Used to collect timed execution statistics.
|
||||
* <p>
|
||||
* It is intended for high concurrent updates to the statistics and relatively infrequent reads.
|
||||
* </p>
|
||||
*/
|
||||
class DTimedMetric implements TimedMetric {
|
||||
|
||||
protected final String name;
|
||||
|
||||
protected final LongAdder count = new LongAdder();
|
||||
|
||||
protected final LongAdder total = new LongAdder();
|
||||
|
||||
protected final LongAccumulator max = new LongAccumulator(Math::max, Long.MIN_VALUE);
|
||||
|
||||
protected final AtomicLong startTime = new AtomicLong(System.currentTimeMillis());
|
||||
|
||||
DTimedMetric(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a value. Usually the value is Time or Bytes etc.
|
||||
*/
|
||||
@Override
|
||||
public void add(long value) {
|
||||
|
||||
count.increment();
|
||||
total.add(value);
|
||||
max.accumulate(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return count.sum() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(boolean reset, List<MetaTimedMetric> result) {
|
||||
DTimeMetricStats metric = collect(reset);
|
||||
if (metric != null) {
|
||||
result.add(metric);
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
public DTimeMetricStats collect(boolean reset) {
|
||||
boolean empty = count.sum() == 0;
|
||||
if (empty) {
|
||||
if (reset) {
|
||||
startTime.set(System.currentTimeMillis());
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
return getStatistics(reset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current statistics resetting the internal values if reset is true.
|
||||
*/
|
||||
public DTimeMetricStats getStatistics(boolean reset) {
|
||||
|
||||
if (reset) {
|
||||
// Note these values are not guaranteed to be consistent wrt each other
|
||||
// but should be reasonably consistent (small time between count and total)
|
||||
final long maxVal = max.getThenReset();
|
||||
final long totalVal = total.sumThenReset();
|
||||
final long countVal = count.sumThenReset();
|
||||
final long startTimeVal = startTime.getAndSet(System.currentTimeMillis());
|
||||
return new DTimeMetricStats(name, startTimeVal, countVal, totalVal, maxVal);
|
||||
|
||||
} else {
|
||||
return new DTimeMetricStats(name, startTime.get(), count.sum(), total.sum(), max.get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all the internal counters and start time.
|
||||
*/
|
||||
public void reset() {
|
||||
startTime.set(System.currentTimeMillis());
|
||||
max.reset();
|
||||
count.reset();
|
||||
total.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the start time.
|
||||
*/
|
||||
public long getStartTime() {
|
||||
return startTime.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the count of values.
|
||||
*/
|
||||
public long getCount() {
|
||||
return count.sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total of values.
|
||||
*/
|
||||
public long getTotal() {
|
||||
return total.sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the max value.
|
||||
*/
|
||||
public long getMax() {
|
||||
return max.get();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetricMap;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
class DTimedMetricMap implements TimedMetricMap {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final ConcurrentHashMap<String, DTimedMetric> map = new ConcurrentHashMap<>();
|
||||
|
||||
DTimedMetricMap(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(String key, long exeMicros) {
|
||||
map.computeIfAbsent(key, (k)-> new DTimedMetric(name + key)).add(exeMicros);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(boolean reset, List<MetaTimedMetric> list) {
|
||||
for (DTimedMetric value : map.values()) {
|
||||
value.collect(reset, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetricStats;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Default profile location that uses stack trace.
|
||||
*/
|
||||
class DTimedProfileLocation extends DProfileLocation implements TimedProfileLocation {
|
||||
|
||||
private final String label;
|
||||
|
||||
private final TimedMetric timedMetric;
|
||||
|
||||
DTimedProfileLocation(int lineNumber, String label, TimedMetric timedMetric) {
|
||||
super(lineNumber);
|
||||
this.label = label;
|
||||
this.timedMetric = timedMetric;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimedMetric getMetric() {
|
||||
return timedMetric;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(long executionTime) {
|
||||
timedMetric.add(executionTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(boolean reset, List<MetaTimedMetric> list) {
|
||||
|
||||
TimedMetricStats collect = timedMetric.collect(reset);
|
||||
if (collect != null) {
|
||||
collect.setLocation(obtain());
|
||||
list.add(collect);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ProfileLocation that collects timing metrics.
|
||||
*/
|
||||
public interface TimedProfileLocation extends ProfileLocation {
|
||||
|
||||
/**
|
||||
* Return the label.
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* Return the metric.
|
||||
*/
|
||||
TimedMetric getMetric();
|
||||
|
||||
/**
|
||||
* Collect the metrics adding to the given list if the metrics are non empty.
|
||||
*/
|
||||
void collect(boolean reset, List<MetaTimedMetric> list);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Global registry of the TimedProfileLocation instances created.
|
||||
*/
|
||||
public class TimedProfileLocationRegistry {
|
||||
|
||||
private static final List<TimedProfileLocation> list = Collections.synchronizedList(new ArrayList<TimedProfileLocation>());
|
||||
|
||||
/**
|
||||
* Register the timed profile location instance.
|
||||
*/
|
||||
public static void register(TimedProfileLocation location) {
|
||||
list.add(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the registered timed locations.
|
||||
*/
|
||||
public static List<TimedProfileLocation> registered() {
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ class CQueryBuilder {
|
||||
if (queryPlan != null) {
|
||||
// skip building the SqlTree and Sql string
|
||||
predicates.prepare(false);
|
||||
return new CQueryUpdate(type, request, predicates, queryPlan.getSql());
|
||||
return new CQueryUpdate(type, request, predicates, queryPlan);
|
||||
}
|
||||
|
||||
predicates.prepare(true);
|
||||
@@ -125,7 +125,7 @@ class CQueryBuilder {
|
||||
// cache the query plan
|
||||
queryPlan = new CQueryPlan(request, sql, sqlTree, false, false, predicates.getLogWhereSql());
|
||||
request.putQueryPlan(queryPlan);
|
||||
return new CQueryUpdate(type, request, predicates, sql);
|
||||
return new CQueryUpdate(type, request, predicates, queryPlan);
|
||||
}
|
||||
|
||||
private <T> String buildDeleteSql(OrmQueryRequest<T> request, String rootTableAlias, CQueryPredicates predicates, SqlTree sqlTree) {
|
||||
@@ -251,8 +251,7 @@ class CQueryBuilder {
|
||||
if (queryPlan != null) {
|
||||
// skip building the SqlTree and Sql string
|
||||
predicates.prepare(false);
|
||||
String sql = queryPlan.getSql();
|
||||
return new CQueryRowCount(queryPlan, request, predicates, sql);
|
||||
return new CQueryRowCount(queryPlan, request, predicates);
|
||||
}
|
||||
|
||||
predicates.prepare(true);
|
||||
@@ -283,7 +282,7 @@ class CQueryBuilder {
|
||||
queryPlan = new CQueryPlan(request, sql, sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql());
|
||||
request.putQueryPlan(queryPlan);
|
||||
|
||||
return new CQueryRowCount(queryPlan, request, predicates, sql);
|
||||
return new CQueryRowCount(queryPlan, request, predicates);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -610,7 +609,7 @@ class CQueryBuilder {
|
||||
if (!hasWhere) {
|
||||
sb.append(" where ");
|
||||
} else {
|
||||
sb.append("and ");
|
||||
sb.append(" and ");
|
||||
}
|
||||
for (int i = 0; i < softDeletePredicates.size(); i++) {
|
||||
if (i > 0) {
|
||||
|
||||
@@ -55,7 +55,6 @@ public class CQueryEngine {
|
||||
this.defaultFetchSizeFindEach = serverConfig.getJdbcFetchSizeFindEach();
|
||||
this.defaultFetchSizeFindList = serverConfig.getJdbcFetchSizeFindList();
|
||||
this.forwardOnlyHintOnFindIterate = dbPlatform.isForwardOnlyHintOnFindIterate();
|
||||
|
||||
this.historySupport = new CQueryHistorySupport(dbPlatform.getHistorySupport(), asOfTableMapping, serverConfig.getAsOfSysPeriod());
|
||||
this.queryBuilder = new CQueryBuilder(dbPlatform, binder, historySupport, new CQueryDraftSupport(draftTableMap));
|
||||
}
|
||||
@@ -331,8 +330,6 @@ public class CQueryEngine {
|
||||
* deemed to be a be a paging query - check that the order by contains the id
|
||||
* property to ensure unique row ordering for predicable paging but only in
|
||||
* case, this is not a distinct query
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
private <T> void prepareForPaging(OrmQueryRequest<T> request) {
|
||||
SpiQuery<T> query = request.getQuery();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebeaninternal.api.SpiProfileTransactionEvent;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
@@ -18,10 +19,12 @@ import java.util.List;
|
||||
/**
|
||||
* Base compiled query request for single attribute queries.
|
||||
*/
|
||||
class CQueryFetchSingleAttribute {
|
||||
class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CQueryFetchSingleAttribute.class);
|
||||
|
||||
private final CQueryPlan queryPlan;
|
||||
|
||||
/**
|
||||
* The overall find request wrapper object.
|
||||
*/
|
||||
@@ -56,17 +59,19 @@ class CQueryFetchSingleAttribute {
|
||||
|
||||
private final ScalarType<?> scalarType;
|
||||
|
||||
private long profileOffset;
|
||||
|
||||
/**
|
||||
* Create the Sql select based on the request.
|
||||
*/
|
||||
CQueryFetchSingleAttribute(OrmQueryRequest<?> request, CQueryPredicates predicates, CQueryPlan plan) {
|
||||
CQueryFetchSingleAttribute(OrmQueryRequest<?> request, CQueryPredicates predicates, CQueryPlan queryPlan) {
|
||||
this.request = request;
|
||||
this.queryPlan = queryPlan;
|
||||
this.query = request.getQuery();
|
||||
this.sql = plan.getSql();
|
||||
this.sql = queryPlan.getSql();
|
||||
this.desc = request.getBeanDescriptor();
|
||||
this.predicates = predicates;
|
||||
this.scalarType = plan.getSingleAttributeScalarType();
|
||||
|
||||
this.scalarType = queryPlan.getSingleAttributeScalarType();
|
||||
query.setGeneratedSql(sql);
|
||||
}
|
||||
|
||||
@@ -102,6 +107,9 @@ class CQueryFetchSingleAttribute {
|
||||
|
||||
executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
|
||||
request.slowQueryCheck(executionTimeMicros, rowCount);
|
||||
queryPlan.executionTime(rowCount, executionTimeMicros, null);
|
||||
getTransaction().profileEvent(this);
|
||||
|
||||
return result;
|
||||
|
||||
} finally {
|
||||
@@ -109,6 +117,10 @@ class CQueryFetchSingleAttribute {
|
||||
}
|
||||
}
|
||||
|
||||
private SpiTransaction getTransaction() {
|
||||
return request.getTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bind log.
|
||||
*/
|
||||
@@ -125,7 +137,8 @@ class CQueryFetchSingleAttribute {
|
||||
|
||||
private void prepareExecute() throws SQLException {
|
||||
|
||||
SpiTransaction t = request.getTransaction();
|
||||
SpiTransaction t = getTransaction();
|
||||
profileOffset = t.profileOffset();
|
||||
Connection conn = t.getInternalConnection();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
|
||||
@@ -166,4 +179,10 @@ class CQueryFetchSingleAttribute {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void profile() {
|
||||
getTransaction()
|
||||
.profileStream()
|
||||
.addQueryEvent(query.profileEventId(), profileOffset, desc.getProfileId(), rowCount, query.getProfileId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.config.dbplatform.SqlLimitResponse;
|
||||
import io.ebeaninternal.api.CQueryPlanKey;
|
||||
@@ -48,6 +49,8 @@ public class CQueryPlan {
|
||||
|
||||
private final boolean autoTuned;
|
||||
|
||||
private final ProfileLocation profileLocation;
|
||||
|
||||
private final CQueryPlanKey planKey;
|
||||
|
||||
private final boolean rawSql;
|
||||
@@ -87,6 +90,7 @@ public class CQueryPlan {
|
||||
this.dataTimeZone = server.getDataTimeZone();
|
||||
this.beanType = request.getBeanDescriptor().getBeanType();
|
||||
this.planKey = request.getQueryPlanKey();
|
||||
this.profileLocation = request.getQuery().getProfileLocation();
|
||||
this.autoTuned = request.getQuery().isAutoTuned();
|
||||
this.asOfTableCount = request.getQuery().getAsOfTableCount();
|
||||
this.sql = sqlRes.getSql();
|
||||
@@ -107,6 +111,7 @@ public class CQueryPlan {
|
||||
this.server = request.getServer();
|
||||
this.dataTimeZone = server.getDataTimeZone();
|
||||
this.beanType = request.getBeanDescriptor().getBeanType();
|
||||
this.profileLocation = request.getQuery().getProfileLocation();
|
||||
this.planKey = buildPlanKey(sql, rawSql, rowNumberIncluded, logWhereSql);
|
||||
this.autoTuned = false;
|
||||
this.asOfTableCount = 0;
|
||||
@@ -134,6 +139,10 @@ public class CQueryPlan {
|
||||
return beanType;
|
||||
}
|
||||
|
||||
public ProfileLocation getProfileLocation() {
|
||||
return profileLocation;
|
||||
}
|
||||
|
||||
public DataReader createDataReader(ResultSet rset) {
|
||||
return new RsetDataReader(dataTimeZone, rset);
|
||||
}
|
||||
@@ -252,4 +261,11 @@ public class CQueryPlan {
|
||||
ScalarType<?> getSingleAttributeScalarType() {
|
||||
return sqlTree.getRootNode().getSingleAttributeScalarType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are no statistics collected since the last reset.
|
||||
*/
|
||||
public boolean isEmptyStats() {
|
||||
return stats.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.meta.MetaQueryPlanOriginCount;
|
||||
import io.ebean.meta.MetaQueryPlanStatistic;
|
||||
@@ -43,6 +44,13 @@ public final class CQueryPlanStats {
|
||||
this.origins = !collectQueryOrigins ? null : new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are no statistics collected since the last reset.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return count.sum() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a query execution to the statistics.
|
||||
*/
|
||||
@@ -188,8 +196,9 @@ public final class CQueryPlanStats {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return queryPlan + " count:" + count + " time:" + totalTime + " maxTime:" + maxTime + " beans:" + totalBeans
|
||||
+ " start:" + startTime + " lastQuery:" + lastQueryTime + " origins:" + origins;
|
||||
ProfileLocation profileLocation = queryPlan.getProfileLocation();
|
||||
String loc = (profileLocation == null) ? "" : profileLocation.shortDescription();
|
||||
return "location:" + loc + " count:" + count + " time:" + totalTime + " maxTime:" + maxTime + " beans:" + totalBeans + " sql:" + getSql();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -197,6 +206,11 @@ public final class CQueryPlanStats {
|
||||
return queryPlan.getBeanType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileLocation getProfileLocation() {
|
||||
return queryPlan.getProfileLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getExecutionCount() {
|
||||
return count;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.meta.MetaQueryPlanStatistic;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper to collect query plan execution statistics.
|
||||
*/
|
||||
public class CQueryPlanStatsCollector {
|
||||
|
||||
private final boolean reset;
|
||||
|
||||
List<MetaQueryPlanStatistic> list = new ArrayList<>();
|
||||
|
||||
public CQueryPlanStatsCollector(boolean reset) {
|
||||
this.reset = reset;
|
||||
}
|
||||
|
||||
public boolean isReset() {
|
||||
return reset;
|
||||
}
|
||||
|
||||
public void add(MetaQueryPlanStatistic planStatistic) {
|
||||
list.add(planStatistic);
|
||||
}
|
||||
|
||||
public List<MetaQueryPlanStatistic> getList() {
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebeaninternal.server.rawsql.SpiRawSql;
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.SpiExpressionList;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
@@ -12,6 +11,7 @@ import io.ebeaninternal.server.expression.DefaultExpressionRequest;
|
||||
import io.ebeaninternal.server.persist.Binder;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryProperties;
|
||||
import io.ebeaninternal.server.querydefn.OrmUpdateProperties;
|
||||
import io.ebeaninternal.server.rawsql.SpiRawSql;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
import io.ebeaninternal.server.util.BindParamsParser;
|
||||
import org.slf4j.Logger;
|
||||
@@ -318,15 +318,9 @@ public class CQueryPredicates {
|
||||
}
|
||||
|
||||
private String parse(String expr, DeployParser deployParser) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (!isEmpty(expr)) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append(" and ");
|
||||
}
|
||||
sb.append(deployParser.parse(expr));
|
||||
}
|
||||
return sb.toString();
|
||||
if (expr == null) return "";
|
||||
if (expr.isEmpty()) return expr;
|
||||
return deployParser.parse(expr);
|
||||
}
|
||||
|
||||
private String deriveHaving(DeployParser deployParser) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebeaninternal.api.SpiProfileTransactionEvent;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
@@ -14,7 +15,7 @@ import java.sql.SQLException;
|
||||
/**
|
||||
* Executes the select row count query.
|
||||
*/
|
||||
class CQueryRowCount {
|
||||
class CQueryRowCount implements SpiProfileTransactionEvent {
|
||||
|
||||
private final CQueryPlan queryPlan;
|
||||
|
||||
@@ -53,17 +54,19 @@ class CQueryRowCount {
|
||||
|
||||
private int rowCount;
|
||||
|
||||
private long profileOffset;
|
||||
|
||||
/**
|
||||
* Create the Sql select based on the request.
|
||||
*/
|
||||
CQueryRowCount(CQueryPlan queryPlan, OrmQueryRequest<?> request, CQueryPredicates predicates, String sql) {
|
||||
CQueryRowCount(CQueryPlan queryPlan, OrmQueryRequest<?> request, CQueryPredicates predicates) {
|
||||
this.queryPlan = queryPlan;
|
||||
this.request = request;
|
||||
this.query = request.getQuery();
|
||||
this.sql = sql;
|
||||
query.setGeneratedSql(sql);
|
||||
this.sql = queryPlan.getSql();
|
||||
this.desc = request.getBeanDescriptor();
|
||||
this.predicates = predicates;
|
||||
query.setGeneratedSql(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,7 +105,8 @@ class CQueryRowCount {
|
||||
|
||||
long startNano = System.nanoTime();
|
||||
try {
|
||||
SpiTransaction t = request.getTransaction();
|
||||
SpiTransaction t = getTransaction();
|
||||
profileOffset = t.profileOffset();
|
||||
Connection conn = t.getInternalConnection();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
|
||||
@@ -121,6 +125,7 @@ class CQueryRowCount {
|
||||
executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
|
||||
queryPlan.executionTime(rowCount, executionTimeMicros, query.getParentNode());
|
||||
request.slowQueryCheck(executionTimeMicros, rowCount);
|
||||
getTransaction().profileEvent(this);
|
||||
return rowCount;
|
||||
|
||||
} finally {
|
||||
@@ -128,6 +133,10 @@ class CQueryRowCount {
|
||||
}
|
||||
}
|
||||
|
||||
private SpiTransaction getTransaction() {
|
||||
return request.getTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the resources.
|
||||
*/
|
||||
@@ -138,4 +147,10 @@ class CQueryRowCount {
|
||||
pstmt = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void profile() {
|
||||
getTransaction()
|
||||
.profileStream()
|
||||
.addQueryEvent(query.profileEventId(), profileOffset, desc.getProfileId(), rowCount, query.getProfileId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebeaninternal.api.SpiProfileTransactionEvent;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
@@ -12,7 +13,9 @@ import java.sql.SQLException;
|
||||
/**
|
||||
* Executes the delete query.
|
||||
*/
|
||||
class CQueryUpdate {
|
||||
class CQueryUpdate implements SpiProfileTransactionEvent {
|
||||
|
||||
private final CQueryPlan queryPlan;
|
||||
|
||||
private final OrmQueryRequest<?> request;
|
||||
|
||||
@@ -43,17 +46,20 @@ class CQueryUpdate {
|
||||
|
||||
private int rowCount;
|
||||
|
||||
private long profileOffset;
|
||||
|
||||
/**
|
||||
* Create the Sql select based on the request.
|
||||
*/
|
||||
CQueryUpdate(String type, OrmQueryRequest<?> request, CQueryPredicates predicates, String sql) {
|
||||
CQueryUpdate(String type, OrmQueryRequest<?> request, CQueryPredicates predicates, CQueryPlan queryPlan) {
|
||||
this.type = type;
|
||||
this.request = request;
|
||||
this.queryPlan = queryPlan;
|
||||
this.query = request.getQuery();
|
||||
this.sql = sql;
|
||||
query.setGeneratedSql(sql);
|
||||
this.sql = queryPlan.getSql();
|
||||
this.desc = request.getBeanDescriptor();
|
||||
this.predicates = predicates;
|
||||
query.setGeneratedSql(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +96,8 @@ class CQueryUpdate {
|
||||
|
||||
long startNano = System.nanoTime();
|
||||
try {
|
||||
SpiTransaction t = request.getTransaction();
|
||||
SpiTransaction t = getTransaction();
|
||||
profileOffset = t.profileOffset();
|
||||
Connection conn = t.getInternalConnection();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
|
||||
@@ -103,6 +110,8 @@ class CQueryUpdate {
|
||||
|
||||
executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
|
||||
request.slowQueryCheck(executionTimeMicros, rowCount);
|
||||
queryPlan.executionTime(rowCount, executionTimeMicros, null);
|
||||
getTransaction().profileEvent(this);
|
||||
return rowCount;
|
||||
|
||||
} finally {
|
||||
@@ -110,6 +119,10 @@ class CQueryUpdate {
|
||||
}
|
||||
}
|
||||
|
||||
private SpiTransaction getTransaction() {
|
||||
return request.getTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the resources.
|
||||
*/
|
||||
@@ -118,4 +131,10 @@ class CQueryUpdate {
|
||||
pstmt = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void profile() {
|
||||
getTransaction()
|
||||
.profileStream()
|
||||
.addQueryEvent(query.profileEventId(), profileOffset, desc.getProfileId(), rowCount, query.getProfileId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine {
|
||||
|
||||
SpiQuery<T> query = request.getQuery();
|
||||
|
||||
if (query.isBeanCachePut()) {
|
||||
if (request.isBeanCachePut()) {
|
||||
// load the individual beans into the bean cache
|
||||
BeanDescriptor<T> descriptor = request.getBeanDescriptor();
|
||||
Collection<T> c = result.getActualDetails();
|
||||
@@ -160,7 +160,7 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine {
|
||||
|
||||
request.mergeCacheHits(result);
|
||||
|
||||
if (query.getUseQueryCache().isPut()) {
|
||||
if (request.isQueryCachePut()) {
|
||||
// load the query result into the query cache
|
||||
result.setReadOnly(true);
|
||||
request.putToQueryCache(result);
|
||||
|
||||
@@ -14,6 +14,7 @@ import io.ebean.OrderBy;
|
||||
import io.ebean.OrderBy.Property;
|
||||
import io.ebean.PagedList;
|
||||
import io.ebean.PersistenceContextScope;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.QueryIterator;
|
||||
import io.ebean.RawSql;
|
||||
@@ -257,6 +258,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
*/
|
||||
private short profileId;
|
||||
|
||||
private ProfileLocation profileLocation;
|
||||
|
||||
public DefaultOrmQuery(BeanDescriptor<T> desc, EbeanServer server, ExpressionFactory expressionFactory) {
|
||||
this.beanDescriptor = desc;
|
||||
this.beanType = desc.getBeanType();
|
||||
@@ -274,14 +277,20 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return beanDescriptor;
|
||||
}
|
||||
|
||||
|
||||
public boolean isFindAll() {
|
||||
return whereExpressions == null && nativeSql == null && rawSql == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkIdEqualTo() {
|
||||
public boolean isFindById() {
|
||||
if (id == null && whereExpressions != null) {
|
||||
id = whereExpressions.idEqualTo(beanDescriptor.getIdName());
|
||||
if (id != null) {
|
||||
whereExpressions = null;
|
||||
}
|
||||
}
|
||||
return id != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -304,6 +313,12 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> setProfileLocation(ProfileLocation profileLocation) {
|
||||
this.profileLocation = profileLocation;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoTunable() {
|
||||
return nativeSql == null && beanDescriptor.isAutoTunable();
|
||||
@@ -706,6 +721,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
DefaultOrmQuery<T> copy = new DefaultOrmQuery<>(beanDescriptor, server, expressionFactory);
|
||||
copy.m2mIncludeJoin = m2mIncludeJoin;
|
||||
copy.profilingListener = profilingListener;
|
||||
copy.profileLocation = profileLocation;
|
||||
|
||||
copy.rootTableAlias = rootTableAlias;
|
||||
copy.distinct = distinct;
|
||||
@@ -1732,4 +1748,9 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
public OrmUpdateProperties getUpdateProperties() {
|
||||
return updateProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileLocation getProfileLocation() {
|
||||
return profileLocation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.querydefn;
|
||||
|
||||
import io.ebean.ExpressionList;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.UpdateQuery;
|
||||
|
||||
/**
|
||||
@@ -41,6 +42,12 @@ public class DefaultUpdateQuery<T> implements UpdateQuery<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateQuery<T> setProfileLocation(ProfileLocation profileLocation) {
|
||||
query.setProfileLocation(profileLocation);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> where() {
|
||||
return query.where();
|
||||
|
||||
@@ -19,7 +19,7 @@ public class AutoCommitTransactionManager extends TransactionManager {
|
||||
* Create an autoCommit based Transaction.
|
||||
*/
|
||||
@Override
|
||||
protected SpiTransaction createTransaction(int profileId, boolean explicit, Connection c, long id) {
|
||||
protected SpiTransaction createTransaction(boolean explicit, Connection c, long id) {
|
||||
|
||||
return new AutoCommitJdbcTransaction(prefix + id, explicit, c, this);
|
||||
}
|
||||
|
||||
+9
-4
@@ -8,8 +8,13 @@ import io.ebeaninternal.api.SpiTransaction;
|
||||
public class DefaultTransactionScopeManager extends TransactionScopeManager {
|
||||
|
||||
|
||||
public DefaultTransactionScopeManager(TransactionManager transactionManager) {
|
||||
super(transactionManager);
|
||||
public DefaultTransactionScopeManager(String serverName) {
|
||||
super(serverName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(TransactionManager manager) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -23,12 +28,12 @@ public class DefaultTransactionScopeManager extends TransactionScopeManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiTransaction getScoped() {
|
||||
public SpiTransaction getInScope() {
|
||||
return DefaultTransactionThreadLocal.get(serverName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiTransaction get() {
|
||||
public SpiTransaction getActive() {
|
||||
SpiTransaction t = DefaultTransactionThreadLocal.get(serverName);
|
||||
if (t == null || !t.isActive()) {
|
||||
return null;
|
||||
|
||||
@@ -22,9 +22,9 @@ public class DocStoreTransactionManager extends TransactionManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiTransaction createTransaction(int profileId, boolean explicit, int isolationLevel) {
|
||||
public SpiTransaction createTransaction(boolean explicit, int isolationLevel) {
|
||||
long id = counter.incrementAndGet();
|
||||
return createTransaction(profileId, explicit, null, id);
|
||||
return createTransaction(explicit, null, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -33,7 +33,7 @@ public class DocStoreTransactionManager extends TransactionManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpiTransaction createTransaction(int profileId, boolean explicit, Connection c, long id) {
|
||||
protected SpiTransaction createTransaction(boolean explicit, Connection c, long id) {
|
||||
return new DocStoreOnlyTransaction(prefix + id, explicit, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ public class ExplicitTransactionManager extends TransactionManager {
|
||||
* Create a ExplicitJdbcTransaction.
|
||||
*/
|
||||
@Override
|
||||
protected SpiTransaction createTransaction(int profileId, boolean explicit, Connection c, long id) {
|
||||
protected SpiTransaction createTransaction(boolean explicit, Connection c, long id) {
|
||||
|
||||
return new ExplicitJdbcTransaction(prefix + id, explicit, c, this);
|
||||
}
|
||||
|
||||
+7
-34
@@ -6,53 +6,26 @@ import io.ebeaninternal.api.SpiTransaction;
|
||||
/**
|
||||
* A TransactionScopeManager aware of external transaction managers.
|
||||
*/
|
||||
public class ExternalTransactionScopeManager extends TransactionScopeManager {
|
||||
public class ExternalTransactionScopeManager extends DefaultTransactionScopeManager {
|
||||
|
||||
final ExternalTransactionManager externalManager;
|
||||
private final ExternalTransactionManager externalManager;
|
||||
|
||||
/**
|
||||
* Instantiates transaction scope manager.
|
||||
*
|
||||
* @param transactionManager the transaction manager
|
||||
*/
|
||||
public ExternalTransactionScopeManager(TransactionManager transactionManager, ExternalTransactionManager externalManager) {
|
||||
super(transactionManager);
|
||||
public ExternalTransactionScopeManager(String serverName, ExternalTransactionManager externalManager) {
|
||||
super(serverName);
|
||||
this.externalManager = externalManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
DefaultTransactionThreadLocal.commit(serverName);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
DefaultTransactionThreadLocal.end(serverName);
|
||||
public void register(TransactionManager manager) {
|
||||
externalManager.setTransactionManager(manager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiTransaction getScoped() {
|
||||
return get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiTransaction get() {
|
||||
public SpiTransaction getActive() {
|
||||
return (SpiTransaction) externalManager.getCurrentTransaction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(SpiTransaction trans) {
|
||||
DefaultTransactionThreadLocal.replace(serverName, trans);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
DefaultTransactionThreadLocal.rollback(serverName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(SpiTransaction trans) {
|
||||
DefaultTransactionThreadLocal.set(serverName, trans);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.TransactionCallback;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
@@ -40,6 +41,8 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
|
||||
private static final String notExpectedMessage = "Not expected on read only transaction";
|
||||
|
||||
private final TransactionManager manager;
|
||||
|
||||
/**
|
||||
* The status of the transaction.
|
||||
*/
|
||||
@@ -60,23 +63,37 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
|
||||
private Map<String, Object> userObjects;
|
||||
|
||||
private long startNanos;
|
||||
|
||||
/**
|
||||
* Create without a tenantId.
|
||||
*/
|
||||
ImplicitReadOnlyTransaction(Connection connection) {
|
||||
ImplicitReadOnlyTransaction(TransactionManager manager, Connection connection) {
|
||||
this.manager = manager;
|
||||
this.active = true;
|
||||
this.connection = connection;
|
||||
this.persistenceContext = new DefaultPersistenceContext();
|
||||
this.startNanos = System.nanoTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create with a tenantId.
|
||||
*/
|
||||
ImplicitReadOnlyTransaction(Connection connection, Object tenantId) {
|
||||
this(connection);
|
||||
ImplicitReadOnlyTransaction(TransactionManager manager, Connection connection, Object tenantId) {
|
||||
this(manager, connection);
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLabel(String label) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long profileOffset() {
|
||||
return 0;
|
||||
@@ -87,11 +104,26 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProfileStream(ProfileStream profileStream) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileStream profileStream() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProfileLocation(ProfileLocation profileLocation) {
|
||||
throw new IllegalStateException(notExpectedMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileLocation getProfileLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipCache() {
|
||||
return false;
|
||||
@@ -464,6 +496,8 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
}
|
||||
connection = null;
|
||||
active = false;
|
||||
long exeMicros = (System.nanoTime() - startNanos) / 1000L;
|
||||
manager.collectMetricReadOnly(exeMicros);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.TransactionCallback;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform.OnQueryOnly;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiProfileTransactionEvent;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.TransactionEvent;
|
||||
import io.ebeaninternal.api.TxnProfileEventCodes;
|
||||
import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
@@ -54,6 +55,11 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
*/
|
||||
protected final String id;
|
||||
|
||||
/**
|
||||
* The user defined label to group execution statistics.
|
||||
*/
|
||||
protected String label;
|
||||
|
||||
/**
|
||||
* Flag to indicate if this was an explicitly created Transaction.
|
||||
*/
|
||||
@@ -171,21 +177,17 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
|
||||
protected DocStoreTransaction docStoreTxn;
|
||||
|
||||
private final ProfileStream profileStream;
|
||||
private ProfileStream profileStream;
|
||||
|
||||
/**
|
||||
* Create without ProfileStream option (no profiling).
|
||||
*/
|
||||
public JdbcTransaction(String id, boolean explicit, Connection connection, TransactionManager manager) {
|
||||
this(null, id, explicit, connection, manager);
|
||||
}
|
||||
protected ProfileLocation profileLocation;
|
||||
|
||||
protected final long startNanos;
|
||||
|
||||
/**
|
||||
* Create a new JdbcTransaction.
|
||||
*/
|
||||
public JdbcTransaction(ProfileStream profileStream, String id, boolean explicit, Connection connection, TransactionManager manager) {
|
||||
public JdbcTransaction(String id, boolean explicit, Connection connection, TransactionManager manager) {
|
||||
try {
|
||||
this.profileStream = profileStream;
|
||||
this.active = true;
|
||||
this.id = id;
|
||||
this.logPrefix = deriveLogPrefix(id);
|
||||
@@ -193,6 +195,7 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
this.manager = manager;
|
||||
this.connection = connection;
|
||||
this.persistenceContext = new DefaultPersistenceContext();
|
||||
this.startNanos = System.nanoTime();
|
||||
|
||||
if (manager == null) {
|
||||
this.skipCacheAfterWrite = true;
|
||||
@@ -213,6 +216,16 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long profileOffset() {
|
||||
return (profileStream == null) ? 0 : profileStream.offset();
|
||||
@@ -225,11 +238,26 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProfileStream(ProfileStream profileStream) {
|
||||
this.profileStream = profileStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileStream profileStream() {
|
||||
return profileStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProfileLocation(ProfileLocation profileLocation) {
|
||||
this.profileLocation = profileLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileLocation getProfileLocation() {
|
||||
return profileLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden in AutoCommitJdbcTransaction as that expects to run/operate with autocommit true.
|
||||
*/
|
||||
@@ -967,8 +995,17 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
}
|
||||
|
||||
private void profileEnd() {
|
||||
if (profileStream != null) {
|
||||
profileStream.end(manager);
|
||||
if (manager != null) {
|
||||
long exeMicros = (System.nanoTime() - startNanos) / 1000L;
|
||||
if (profileLocation != null) {
|
||||
profileLocation.add(exeMicros);
|
||||
} else if (label != null) {
|
||||
manager.collectMetricNamed(exeMicros, label);
|
||||
}
|
||||
manager.collectMetric(exeMicros);
|
||||
if (profileStream != null) {
|
||||
profileStream.end(manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+30
-2
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.TransactionCallback;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
@@ -9,8 +10,10 @@ import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebeaninternal.api.SpiProfileTransactionEvent;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.TransactionEvent;
|
||||
import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
import io.ebeaninternal.server.core.PersistRequest;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeaninternal.server.transaction.ProfileStream;
|
||||
import io.ebeanservice.docstore.api.DocStoreTransaction;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
@@ -24,6 +27,16 @@ class NoTransaction implements SpiTransaction {
|
||||
|
||||
static final NoTransaction INSTANCE = new NoTransaction();
|
||||
|
||||
@Override
|
||||
public void setLabel(String label) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
// always false
|
||||
@@ -412,8 +425,23 @@ class NoTransaction implements SpiTransaction {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProfileStream(ProfileStream profileStream) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileStream profileStream() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProfileLocation(ProfileLocation profileLocation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileLocation getProfileLocation() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ abstract class TransactionFactory {
|
||||
/**
|
||||
* Return a new transaction.
|
||||
*/
|
||||
abstract SpiTransaction createTransaction(int profileId, boolean explicit, int isolationLevel);
|
||||
abstract SpiTransaction createTransaction(boolean explicit, int isolationLevel);
|
||||
|
||||
/**
|
||||
* Set the Transaction Isolation level if required.
|
||||
|
||||
@@ -13,13 +13,10 @@ import java.sql.SQLException;
|
||||
*/
|
||||
class TransactionFactoryBasic extends TransactionFactory {
|
||||
|
||||
final DataSourceSupplier dataSourceSupplier;
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
TransactionFactoryBasic(TransactionManager manager, DataSourceSupplier dataSourceSupplier) {
|
||||
super(manager);
|
||||
this.dataSourceSupplier = dataSourceSupplier;
|
||||
this.dataSource = dataSourceSupplier.getDataSource();
|
||||
}
|
||||
|
||||
@@ -29,7 +26,7 @@ class TransactionFactoryBasic extends TransactionFactory {
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = dataSource.getConnection();
|
||||
return create(0, false, connection);
|
||||
return create(false, connection);
|
||||
|
||||
} catch (PersistenceException ex) {
|
||||
JdbcClose.close(connection);
|
||||
@@ -40,11 +37,11 @@ class TransactionFactoryBasic extends TransactionFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiTransaction createTransaction(int profileId, boolean explicit, int isolationLevel) {
|
||||
public SpiTransaction createTransaction(boolean explicit, int isolationLevel) {
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = dataSource.getConnection();
|
||||
SpiTransaction t = create(profileId, explicit, connection);
|
||||
SpiTransaction t = create(explicit, connection);
|
||||
return setIsolationLevel(t, explicit, isolationLevel);
|
||||
} catch (PersistenceException ex) {
|
||||
JdbcClose.close(connection);
|
||||
@@ -54,8 +51,8 @@ class TransactionFactoryBasic extends TransactionFactory {
|
||||
}
|
||||
}
|
||||
|
||||
private SpiTransaction create(int profileId, boolean explicit, Connection c) {
|
||||
return manager.createTransaction(profileId, explicit, c, counter.incrementAndGet());
|
||||
private SpiTransaction create(boolean explicit, Connection c) {
|
||||
return manager.createTransaction(explicit, c, counter.incrementAndGet());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-2
@@ -18,7 +18,6 @@ import java.sql.SQLException;
|
||||
*/
|
||||
class TransactionFactoryBasicWithRead extends TransactionFactoryBasic {
|
||||
|
||||
|
||||
private final DataSource readOnlyDataSource;
|
||||
|
||||
TransactionFactoryBasicWithRead(TransactionManager manager, DataSourceSupplier dataSourceSupplier) {
|
||||
@@ -32,7 +31,7 @@ class TransactionFactoryBasicWithRead extends TransactionFactoryBasic {
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = readOnlyDataSource.getConnection();
|
||||
return new ImplicitReadOnlyTransaction(connection);
|
||||
return new ImplicitReadOnlyTransaction(manager, connection);
|
||||
|
||||
} catch (PersistenceException ex) {
|
||||
JdbcClose.close(connection);
|
||||
|
||||
@@ -25,17 +25,17 @@ class TransactionFactoryTenant extends TransactionFactory {
|
||||
|
||||
@Override
|
||||
public SpiTransaction createQueryTransaction(Object tenantId) {
|
||||
return create(0,false, tenantId);
|
||||
return create(false, tenantId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiTransaction createTransaction(int profileId, boolean explicit, int isolationLevel) {
|
||||
public SpiTransaction createTransaction(boolean explicit, int isolationLevel) {
|
||||
|
||||
SpiTransaction t = create(profileId, explicit, null);
|
||||
SpiTransaction t = create(explicit, null);
|
||||
return setIsolationLevel(t, explicit, isolationLevel);
|
||||
}
|
||||
|
||||
private SpiTransaction create(int profileId, boolean explicit, Object tenantId) {
|
||||
private SpiTransaction create(boolean explicit, Object tenantId) {
|
||||
Connection connection = null;
|
||||
try {
|
||||
if (tenantId == null) {
|
||||
@@ -43,7 +43,7 @@ class TransactionFactoryTenant extends TransactionFactory {
|
||||
tenantId = tenantProvider.currentId();
|
||||
}
|
||||
connection = dataSourceSupplier.getConnection(tenantId);
|
||||
SpiTransaction transaction = manager.createTransaction(profileId, explicit, connection, counter.incrementAndGet());
|
||||
SpiTransaction transaction = manager.createTransaction(explicit, connection, counter.incrementAndGet());
|
||||
transaction.setTenantId(tenantId);
|
||||
return transaction;
|
||||
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ class TransactionFactoryTenantWithRead extends TransactionFactoryTenant {
|
||||
tenantId = tenantProvider.currentId();
|
||||
}
|
||||
connection = dataSourceSupplier.getReadOnlyConnection(tenantId);
|
||||
return new ImplicitReadOnlyTransaction(connection, tenantId);
|
||||
return new ImplicitReadOnlyTransaction(manager, connection, tenantId);
|
||||
|
||||
} catch (PersistenceException ex) {
|
||||
JdbcClose.close(connection);
|
||||
|
||||
@@ -1,20 +1,32 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.TxScope;
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
import io.ebean.annotation.TxType;
|
||||
import io.ebean.config.CurrentTenantProvider;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform.OnQueryOnly;
|
||||
import io.ebean.event.changelog.ChangeLogListener;
|
||||
import io.ebean.event.changelog.ChangeLogPrepare;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebeaninternal.api.ScopeTrans;
|
||||
import io.ebeaninternal.api.ScopedTransaction;
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiTransactionManager;
|
||||
import io.ebeaninternal.api.TransactionEvent;
|
||||
import io.ebeaninternal.api.TransactionEventTable;
|
||||
import io.ebeaninternal.api.TransactionEventTable.TableIUD;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetricMap;
|
||||
import io.ebeaninternal.server.cluster.ClusterManager;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import io.ebeaninternal.server.profile.TimedProfileLocation;
|
||||
import io.ebeaninternal.server.profile.TimedProfileLocationRegistry;
|
||||
import io.ebeanservice.docstore.api.DocStoreTransaction;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdateProcessor;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdates;
|
||||
@@ -25,6 +37,7 @@ import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -34,7 +47,7 @@ import java.util.Set;
|
||||
* Keeps the Cache and Cluster in synch when transactions are committed.
|
||||
* </p>
|
||||
*/
|
||||
public class TransactionManager {
|
||||
public class TransactionManager implements SpiTransactionManager {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TransactionManager.class);
|
||||
|
||||
@@ -48,6 +61,12 @@ public class TransactionManager {
|
||||
|
||||
protected final BeanDescriptorManager beanDescriptorManager;
|
||||
|
||||
/**
|
||||
* Ebean defaults this to true but for EJB compatible behaviour set this to
|
||||
* false;
|
||||
*/
|
||||
private final boolean rollbackOnChecked;
|
||||
|
||||
/**
|
||||
* Prefix for transaction id's (logging).
|
||||
*/
|
||||
@@ -113,6 +132,11 @@ public class TransactionManager {
|
||||
|
||||
private final SpiProfileHandler profileHandler;
|
||||
|
||||
private final TimedMetric txnMain;
|
||||
private final TimedMetric txnReadOnly;
|
||||
private final TimedMetricMap txnNamed;
|
||||
private final TransactionScopeManager scopeManager;
|
||||
|
||||
/**
|
||||
* Create the TransactionManager
|
||||
*/
|
||||
@@ -123,6 +147,7 @@ public class TransactionManager {
|
||||
this.localL2Caching = options.localL2Caching;
|
||||
this.persistBatch = options.config.getPersistBatch();
|
||||
this.persistBatchOnCascade = options.config.appliedPersistBatchOnCascade();
|
||||
this.rollbackOnChecked = options.config.isTransactionRollbackOnChecked();
|
||||
this.beanDescriptorManager = options.descMgr;
|
||||
this.viewInvalidation = options.descMgr.requiresViewEntityCacheInvalidation();
|
||||
this.changeLogPrepare = options.descMgr.getChangeLogPrepare();
|
||||
@@ -130,6 +155,7 @@ public class TransactionManager {
|
||||
this.changeLogAsync = options.config.isChangeLogAsync();
|
||||
this.clusterManager = options.clusterManager;
|
||||
this.serverName = options.config.getName();
|
||||
this.scopeManager = options.scopeManager;
|
||||
this.backgroundExecutor = options.backgroundExecutor;
|
||||
this.dataSourceSupplier = options.dataSourceSupplier;
|
||||
this.docStoreActive = options.config.getDocStoreConfig().isActive();
|
||||
@@ -142,6 +168,55 @@ public class TransactionManager {
|
||||
|
||||
CurrentTenantProvider tenantProvider = options.config.getCurrentTenantProvider();
|
||||
this.transactionFactory = TransactionFactoryBuilder.build(this, dataSourceSupplier, tenantProvider);
|
||||
|
||||
MetricFactory metricFactory = MetricFactory.get();
|
||||
this.txnMain = metricFactory.createTimedMetric("txn.main");
|
||||
this.txnReadOnly = metricFactory.createTimedMetric("txn.readonly");
|
||||
this.txnNamed = metricFactory.createTimedMetricMap("txn.named.");
|
||||
|
||||
scopeManager.register(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new scoped transaction.
|
||||
*/
|
||||
private ScopedTransaction createScopedTransaction() {
|
||||
return new ScopedTransaction(scopeManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the scope manager.
|
||||
*/
|
||||
public TransactionScopeManager scope() {
|
||||
return scopeManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the transaction onto the scope.
|
||||
*/
|
||||
public void set(SpiTransaction txn) {
|
||||
scopeManager.set(txn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current active transaction.
|
||||
*/
|
||||
public SpiTransaction getActive() {
|
||||
return scopeManager.getActive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current active transaction as a scoped transaction.
|
||||
*/
|
||||
public ScopedTransaction getActiveScoped() {
|
||||
return (ScopedTransaction) scopeManager.getActive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current transaction from thread local scope. Note that it may be inactive.
|
||||
*/
|
||||
public SpiTransaction getInScope() {
|
||||
return scopeManager.getInScope();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,10 +327,13 @@ public class TransactionManager {
|
||||
/**
|
||||
* Create a new Transaction.
|
||||
*/
|
||||
public SpiTransaction createTransaction(int profileId, boolean explicit, int isolationLevel) {
|
||||
return transactionFactory.createTransaction(profileId, explicit, isolationLevel);
|
||||
public SpiTransaction createTransaction(boolean explicit, int isolationLevel) {
|
||||
return transactionFactory.createTransaction(explicit, isolationLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Transaction for query only purposes (can use read only datasource).
|
||||
*/
|
||||
public SpiTransaction createQueryTransaction(Object tenantId) {
|
||||
return transactionFactory.createQueryTransaction(tenantId);
|
||||
}
|
||||
@@ -263,10 +341,9 @@ public class TransactionManager {
|
||||
/**
|
||||
* Create a new transaction.
|
||||
*/
|
||||
protected SpiTransaction createTransaction(int profileId, boolean explicit, Connection c, long id) {
|
||||
protected SpiTransaction createTransaction(boolean explicit, Connection c, long id) {
|
||||
|
||||
ProfileStream profileStream = profileHandler.createProfileStream(profileId);
|
||||
return new JdbcTransaction(profileStream,prefix + id, explicit, c, this);
|
||||
return new JdbcTransaction(prefix + id, explicit, c, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -342,14 +419,16 @@ public class TransactionManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a Transaction that comes from another framework or local code.
|
||||
* <p>
|
||||
* For cases where raw SQL/JDBC or other frameworks are used this can
|
||||
* invalidate the appropriate parts of the cache.
|
||||
* </p>
|
||||
*/
|
||||
public void externalModification(TransactionEventTable tableEvents) {
|
||||
public void externalModification(TransactionEventTable tableEvent) {
|
||||
SpiTransaction t = getActive();
|
||||
if (t != null) {
|
||||
t.getEvent().add(tableEvent);
|
||||
} else {
|
||||
externalModificationEvent(tableEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private void externalModificationEvent(TransactionEventTable tableEvents) {
|
||||
|
||||
TransactionEvent event = new TransactionEvent();
|
||||
event.add(tableEvents);
|
||||
@@ -424,4 +503,185 @@ public class TransactionManager {
|
||||
public void profileCollect(TransactionProfile transactionProfile) {
|
||||
profileHandler.collectTransactionProfile(transactionProfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect execution time for an explicit transaction.
|
||||
*/
|
||||
public void collectMetric(long exeMicros) {
|
||||
txnMain.add(exeMicros);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect execution time for implicit read only transaction.
|
||||
*/
|
||||
public void collectMetricReadOnly(long exeMicros) {
|
||||
txnReadOnly.add(exeMicros);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect execution time for a named transaction.
|
||||
*/
|
||||
public void collectMetricNamed(long exeMicros, String label) {
|
||||
txnNamed.add(label, exeMicros);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the transaction execution statistics since the last reset.
|
||||
*/
|
||||
public List<MetaTimedMetric> collectTransactionStatistics(boolean reset) {
|
||||
|
||||
List<MetaTimedMetric> list = new ArrayList<>();
|
||||
|
||||
txnMain.collect(reset, list);
|
||||
txnReadOnly.collect(reset, list);
|
||||
for (TimedProfileLocation timedLocation : TimedProfileLocationRegistry.registered()) {
|
||||
timedLocation.collect(reset, list);
|
||||
}
|
||||
txnNamed.collect(reset, list);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin an implicit transaction.
|
||||
*/
|
||||
public SpiTransaction beginServerTransaction() {
|
||||
SpiTransaction t = createTransaction(false, -1);
|
||||
scopeManager.set(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit a scoped transaction (that can be inactive - already committed etc).
|
||||
*/
|
||||
public void exitScopedTransaction(Object returnOrThrowable, int opCode) {
|
||||
SpiTransaction st = getInScope();
|
||||
if (st instanceof ScopedTransaction) {
|
||||
// can be null for Supports as that can start as a 'No Transaction' and then
|
||||
// effectively be replaced by transactions inside the scope
|
||||
((ScopedTransaction)st).complete(returnOrThrowable, opCode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void externalRemoveTransaction() {
|
||||
scopeManager.replace(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push an externally created transaction into scope. This transaction is usually managed externally
|
||||
* (e.g. Spring managed transaction).
|
||||
*/
|
||||
@Override
|
||||
public ScopedTransaction externalBeginTransaction(SpiTransaction transaction, TxScope txScope) {
|
||||
ScopedTransaction scopedTxn = new ScopedTransaction(scopeManager);
|
||||
scopedTxn.push(new ScopeTrans(rollbackOnChecked, false, transaction, txScope));
|
||||
scopeManager.set(scopedTxn);
|
||||
return scopedTxn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin a scoped transaction.
|
||||
*/
|
||||
public ScopedTransaction beginScopedTransaction(TxScope txScope) {
|
||||
|
||||
txScope = initTxScope(txScope);
|
||||
|
||||
boolean setToScope = false;
|
||||
ScopedTransaction txnContainer = getActiveScoped();
|
||||
if (txnContainer == null) {
|
||||
setToScope = true;
|
||||
txnContainer = createScopedTransaction();
|
||||
}
|
||||
|
||||
SpiTransaction transaction = txnContainer.current();
|
||||
|
||||
TxType type = txScope.getType();
|
||||
boolean createTransaction = isCreateNewTransaction(transaction, type);
|
||||
if (createTransaction) {
|
||||
switch (type) {
|
||||
case SUPPORTS:
|
||||
case NOT_SUPPORTED:
|
||||
case NEVER:
|
||||
transaction = NoTransaction.INSTANCE;
|
||||
break;
|
||||
default:
|
||||
transaction = createTransaction(true, txScope.getIsolationLevel());
|
||||
initNewTransaction(transaction, txScope);
|
||||
}
|
||||
}
|
||||
|
||||
txnContainer.push(new ScopeTrans(rollbackOnChecked, createTransaction, transaction, txScope));
|
||||
if (setToScope) {
|
||||
set(txnContainer);
|
||||
}
|
||||
return txnContainer;
|
||||
}
|
||||
|
||||
private void initNewTransaction(SpiTransaction transaction, TxScope txScope) {
|
||||
|
||||
if (txScope.isSkipCache()) {
|
||||
transaction.setSkipCache(true);
|
||||
}
|
||||
String label = txScope.getLabel();
|
||||
if (label != null) {
|
||||
transaction.setLabel(label);
|
||||
}
|
||||
int profileId = txScope.getProfileId();
|
||||
if (profileId > 0) {
|
||||
transaction.setProfileStream(profileHandler.createProfileStream(profileId));
|
||||
}
|
||||
ProfileLocation profileLocation = txScope.getProfileLocation();
|
||||
if (profileLocation != null) {
|
||||
profileLocation.obtain();
|
||||
transaction.setProfileLocation(profileLocation);
|
||||
}
|
||||
}
|
||||
|
||||
private TxScope initTxScope(TxScope txScope) {
|
||||
if (txScope == null) {
|
||||
return new TxScope();
|
||||
} else {
|
||||
// check for implied batch mode via setting batchSize
|
||||
txScope.checkBatchMode();
|
||||
return txScope;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether to create a new transaction or not.
|
||||
* <p>
|
||||
* This will also potentially throw exceptions for MANDATORY and NEVER types.
|
||||
* </p>
|
||||
*/
|
||||
private boolean isCreateNewTransaction(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?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,13 +21,15 @@ public class TransactionManagerOptions {
|
||||
final BeanDescriptorManager descMgr;
|
||||
final DataSourceSupplier dataSourceSupplier;
|
||||
final SpiProfileHandler profileHandler;
|
||||
final TransactionScopeManager scopeManager;
|
||||
|
||||
public TransactionManagerOptions(boolean localL2Caching, ServerConfig config, ClusterManager clusterManager,
|
||||
BackgroundExecutor backgroundExecutor, DocStoreUpdateProcessor docStoreUpdateProcessor,
|
||||
BeanDescriptorManager descMgr, DataSourceSupplier dataSourceSupplier, SpiProfileHandler profileHandler) {
|
||||
public TransactionManagerOptions(boolean localL2Caching, ServerConfig config, TransactionScopeManager scopeManager, ClusterManager clusterManager,
|
||||
BackgroundExecutor backgroundExecutor, DocStoreUpdateProcessor docStoreUpdateProcessor,
|
||||
BeanDescriptorManager descMgr, DataSourceSupplier dataSourceSupplier, SpiProfileHandler profileHandler) {
|
||||
|
||||
this.localL2Caching = localL2Caching;
|
||||
this.config = config;
|
||||
this.scopeManager = scopeManager;
|
||||
this.clusterManager = clusterManager;
|
||||
this.backgroundExecutor = backgroundExecutor;
|
||||
this.docStoreUpdateProcessor = docStoreUpdateProcessor;
|
||||
|
||||
@@ -8,24 +8,26 @@ import io.ebeaninternal.api.SpiTransactionScopeManager;
|
||||
*/
|
||||
public abstract class TransactionScopeManager implements SpiTransactionScopeManager {
|
||||
|
||||
protected final TransactionManager transactionManager;
|
||||
|
||||
protected final String serverName;
|
||||
|
||||
public TransactionScopeManager(TransactionManager transactionManager) {
|
||||
this.transactionManager = transactionManager;
|
||||
this.serverName = transactionManager.getServerName();
|
||||
public TransactionScopeManager(String serverName) {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current Transaction allowing it to be inactive.
|
||||
* Register the transaction manager (for use by external transaction scopes).
|
||||
*/
|
||||
public abstract SpiTransaction getScoped();
|
||||
public abstract void register(TransactionManager manager);
|
||||
|
||||
/**
|
||||
* Return the current Transaction for this serverName and Thread.
|
||||
* Return the current Transaction from internal Ebean scope.
|
||||
*/
|
||||
public abstract SpiTransaction get();
|
||||
public abstract SpiTransaction getInScope();
|
||||
|
||||
/**
|
||||
* Return the current Transaction potentially looking in external scope (like Spring).
|
||||
*/
|
||||
public abstract SpiTransaction getActive();
|
||||
|
||||
/**
|
||||
* Set a new Transaction for this serverName and Thread.
|
||||
|
||||
@@ -271,6 +271,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
public void addEnumType(ScalarType<?> scalarType, Class<? extends Enum> enumClass) {
|
||||
|
||||
Set<Class<?>> mappedClasses = new HashSet<>();
|
||||
mappedClasses.add(enumClass);
|
||||
for (Object value : EnumSet.allOf(enumClass).toArray()) {
|
||||
mappedClasses.add(value.getClass());
|
||||
}
|
||||
@@ -552,7 +553,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
* Return null if the EnumValue annotations are not present/used.
|
||||
* </p>
|
||||
*/
|
||||
private ScalarType<?> createEnumScalarType2(Class<?> enumType) {
|
||||
private ScalarTypeEnum<?> createEnumScalarType2(Class<?> enumType) {
|
||||
|
||||
boolean integerType = true;
|
||||
|
||||
@@ -589,8 +590,11 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
@Override
|
||||
public ScalarType<?> createEnumScalarType(Class<? extends Enum<?>> enumType, EnumType type) {
|
||||
|
||||
ScalarType<?> scalarType = getScalarType(enumType);
|
||||
if (scalarType != null) {
|
||||
ScalarTypeEnum<?> scalarType = (ScalarTypeEnum<?>) getScalarType(enumType);
|
||||
if (scalarType != null && !scalarType.isOverrideBy(type)) {
|
||||
if (type != null && !scalarType.isCompatible(type)) {
|
||||
throw new IllegalStateException("Error mapping Enum type:"+enumType+" It is mapped using 2 different modes when only one is supported (ORDINAL, STRING or an Ebean mapping)");
|
||||
}
|
||||
return scalarType;
|
||||
}
|
||||
|
||||
@@ -603,7 +607,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
return scalarType;
|
||||
}
|
||||
|
||||
private ScalarType<?> createEnumScalarTypePerSpec(Class<?> enumType, EnumType type) {
|
||||
private ScalarTypeEnum<?> createEnumScalarTypePerSpec(Class<?> enumType, EnumType type) {
|
||||
if (type == null) {
|
||||
// default as per spec is ORDINAL
|
||||
return new ScalarTypeEnumStandard.OrdinalEnum(enumType);
|
||||
@@ -616,7 +620,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
}
|
||||
}
|
||||
|
||||
private ScalarType<?> createEnumScalarTypePerExtentions(Class<? extends Enum<?>> enumType) {
|
||||
private ScalarTypeEnum<?> createEnumScalarTypePerExtentions(Class<? extends Enum<?>> enumType) {
|
||||
|
||||
Method[] methods = enumType.getMethods();
|
||||
for (Method method : methods) {
|
||||
@@ -638,7 +642,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
* Return null if the EnumValue annotations are not present/used.
|
||||
* </p>
|
||||
*/
|
||||
private ScalarType<?> createEnumScalarTypeDbValue(Class<? extends Enum<?>> enumType, Method method, boolean integerType) {
|
||||
private ScalarTypeEnum<?> createEnumScalarTypeDbValue(Class<? extends Enum<?>> enumType, Method method, boolean integerType) {
|
||||
|
||||
Map<String, String> nameValueMap = new HashMap<>();
|
||||
|
||||
@@ -664,7 +668,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
* length create the ScalarType for the Enum.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private ScalarType<?> createEnumScalarType(Class enumType, Map<String, String> nameValueMap, boolean integerType, int dbColumnLength) {
|
||||
private ScalarTypeEnum<?> createEnumScalarType(Class enumType, Map<String, String> nameValueMap, boolean integerType, int dbColumnLength) {
|
||||
|
||||
EnumToDbValueMap<?> beanDbMap = EnumToDbValueMap.create(integerType);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import javax.persistence.EnumType;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.time.DayOfWeek;
|
||||
@@ -22,6 +23,14 @@ public class ScalarTypeDayOfWeek extends ScalarTypeEnumWithMapping {
|
||||
super(beanDbMap, DayOfWeek.class, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* We allow this to be overridden by a JPA EnumType.
|
||||
*/
|
||||
@Override
|
||||
public boolean isOverrideBy(EnumType type) {
|
||||
return type != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind DayOfWeek enum using getValue().
|
||||
*/
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import javax.persistence.EnumType;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Marker interface for the Enum scalar types.
|
||||
*/
|
||||
public interface ScalarTypeEnum {
|
||||
public interface ScalarTypeEnum<T> extends ScalarType<T> {
|
||||
|
||||
/**
|
||||
* Return the IN values for DB constraint construction.
|
||||
*/
|
||||
Set<String> getDbCheckConstraintValues();
|
||||
|
||||
/**
|
||||
* Return true if we allow this scalar enum type to be overridden.
|
||||
* Ability to override the built-in support for java time DayOfWeek and Month.
|
||||
*/
|
||||
default boolean isOverrideBy(EnumType type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the scalar type is compatible with the specified enum type.
|
||||
*/
|
||||
boolean isCompatible(EnumType enumType);
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.ebean.text.TextException;
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
|
||||
import javax.persistence.EnumType;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
@@ -40,6 +41,11 @@ public class ScalarTypeEnumStandard {
|
||||
this.length = maxValueLength(enumType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompatible(EnumType enumType) {
|
||||
return EnumType.STRING == enumType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the IN values for DB constraint construction.
|
||||
*/
|
||||
@@ -128,6 +134,11 @@ public class ScalarTypeEnumStandard {
|
||||
this.enumArray = EnumSet.allOf(enumType).toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompatible(EnumType enumType) {
|
||||
return EnumType.ORDINAL == enumType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the IN values for DB constraint construction.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import javax.persistence.EnumType;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
@@ -24,6 +25,11 @@ public class ScalarTypeEnumWithMapping extends ScalarTypeEnumStandard.EnumBase i
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompatible(EnumType enumType) {
|
||||
return enumType == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long asVersion(Object value) {
|
||||
throw new RuntimeException("not supported");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user