mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d9f9982b7 | ||
|
|
7178085680 | ||
|
|
4d82b86f81 | ||
|
|
1d99a5c5a4 | ||
|
|
41a7f2c97e | ||
|
|
c8d01828b3 | ||
|
|
a3dbd4538f | ||
|
|
3051412d90 | ||
|
|
a22eb555d0 |
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>10.1.7</version>
|
||||
<version>10.2.1</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-10.1.7</tag>
|
||||
<tag>ebean-10.2.1</tag>
|
||||
</scm>
|
||||
|
||||
<dependencies>
|
||||
@@ -160,7 +160,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-agent</artifactId>
|
||||
<version>10.1.6</version>
|
||||
<version>10.1.7</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -225,7 +225,7 @@
|
||||
<plugin>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-maven-plugin</artifactId>
|
||||
<version>10.1.6</version>
|
||||
<version>10.1.7</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.ebean;
|
||||
|
||||
import javax.persistence.PessimisticLockException;
|
||||
|
||||
/**
|
||||
* Thrown when failing to acquire a pessimistic lock.
|
||||
* <p>
|
||||
* Typically when "select for update nowait" or "select for update" is being used and
|
||||
* the lock can not be obtained (as it is held by another transaction).
|
||||
* </p>
|
||||
*/
|
||||
public class AcquireLockException extends PessimisticLockException {
|
||||
|
||||
/**
|
||||
* Create with a message and cause.
|
||||
*/
|
||||
public AcquireLockException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.ebean;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
/**
|
||||
* Thrown when a foreign key constraint is enforced.
|
||||
*/
|
||||
public class DataIntegrityException extends PersistenceException {
|
||||
|
||||
/**
|
||||
* Create with a message and cause.
|
||||
*/
|
||||
public DataIntegrityException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.ebean;
|
||||
|
||||
/**
|
||||
* Thrown when a duplicate is attempted on a unique constraint.
|
||||
*/
|
||||
public class DuplicateKeyException extends DataIntegrityException {
|
||||
|
||||
/**
|
||||
* Create with a message and cause.
|
||||
*/
|
||||
public DuplicateKeyException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -428,6 +428,24 @@ public final class Ebean {
|
||||
return serverMgr.getDefaultServer().currentTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* The batch will be flushing automatically but you can use this to explicitly
|
||||
* flush the batch if you like.
|
||||
* <p>
|
||||
* Flushing occurs automatically when:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>the batch size is reached</li>
|
||||
* <li>A query is executed on the same transaction</li>
|
||||
* <li>UpdateSql or CallableSql are mixed with bean save and delete</li>
|
||||
* <li>Transaction commit occurs</li>
|
||||
* <li>A getter method is called on a batched bean</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static void flush() {
|
||||
currentTransaction().flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a TransactionCallback on the currently active transaction.
|
||||
* <p/>
|
||||
|
||||
@@ -613,6 +613,19 @@ public interface EbeanServer {
|
||||
*/
|
||||
Transaction currentTransaction();
|
||||
|
||||
/**
|
||||
* Flush the JDBC batch on the current transaction.
|
||||
* <p>
|
||||
* This only is useful when JDBC batch is used. Flush occurs automatically when the
|
||||
* transaction commits or batch size is reached. This manually flushes the JDBC batch
|
||||
* buffer.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is the same as <code>currentTransaction().flush()</code>.
|
||||
* </p>
|
||||
*/
|
||||
void flush();
|
||||
|
||||
/**
|
||||
* Commit the current transaction.
|
||||
*/
|
||||
|
||||
@@ -118,6 +118,27 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
Query<T> asDraft();
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause which results in the DB locking the record.
|
||||
*/
|
||||
Query<T> forUpdate();
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause with No Wait option.
|
||||
* <p>
|
||||
* This is typically a Postgres and Oracle only option at this stage.
|
||||
* </p>
|
||||
*/
|
||||
Query<T> forUpdateNoWait();
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause with Skip Locked option.
|
||||
* <p>
|
||||
* This is typically a Postgres and Oracle only option at this stage.
|
||||
* </p>
|
||||
*/
|
||||
Query<T> forUpdateSkipLocked();
|
||||
|
||||
/**
|
||||
* Execute the query including soft deleted rows.
|
||||
*/
|
||||
|
||||
@@ -190,6 +190,17 @@ public abstract class Model {
|
||||
db().save(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush any batched changes to the database.
|
||||
* <p>
|
||||
* When using JDBC batch flushing occurs automatically at commit() time or when the batch size
|
||||
* is reached. This provides the ability to manually flush the batch.
|
||||
* </p>
|
||||
*/
|
||||
public void flush() {
|
||||
db().flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this entity.
|
||||
*
|
||||
|
||||
@@ -194,6 +194,26 @@ import java.util.function.Predicate;
|
||||
*/
|
||||
public interface Query<T> {
|
||||
|
||||
/**
|
||||
* For update mode.
|
||||
*/
|
||||
enum ForUpdate {
|
||||
/**
|
||||
* Standard For update clause.
|
||||
*/
|
||||
BASE,
|
||||
|
||||
/**
|
||||
* For update with No Wait option.
|
||||
*/
|
||||
NOWAIT,
|
||||
|
||||
/**
|
||||
* For update with Skip Locked option.
|
||||
*/
|
||||
SKIPLOCKED
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the RawSql that was set to use for this query.
|
||||
*/
|
||||
@@ -1355,11 +1375,37 @@ public interface Query<T> {
|
||||
*/
|
||||
Query<T> setForUpdate(boolean forUpdate);
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause which results in the DB locking the record.
|
||||
*/
|
||||
Query<T> forUpdate();
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause with "no wait" option.
|
||||
* <p>
|
||||
* This is typically a Postgres and Oracle only option at this stage.
|
||||
* </p>
|
||||
*/
|
||||
Query<T> forUpdateNoWait();
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause with "skip locked" option.
|
||||
* <p>
|
||||
* This is typically a Postgres and Oracle only option at this stage.
|
||||
* </p>
|
||||
*/
|
||||
Query<T> forUpdateSkipLocked();
|
||||
|
||||
/**
|
||||
* Return true if this query has forUpdate set.
|
||||
*/
|
||||
boolean isForUpdate();
|
||||
|
||||
/**
|
||||
* Return the "for update" mode to use.
|
||||
*/
|
||||
ForUpdate getForUpdateMode();
|
||||
|
||||
/**
|
||||
* Set root table alias.
|
||||
*/
|
||||
|
||||
@@ -5,14 +5,12 @@ import io.ebean.config.DocStoreConfig;
|
||||
import io.ebean.config.ServerConfig;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.RollbackException;
|
||||
import java.io.Closeable;
|
||||
import java.sql.Connection;
|
||||
|
||||
/**
|
||||
* The Transaction object. Typically representing a JDBC or JTA transaction.
|
||||
*/
|
||||
public interface Transaction extends Closeable {
|
||||
public interface Transaction extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* Read Committed transaction isolation. Same as
|
||||
@@ -67,7 +65,7 @@ public interface Transaction extends Closeable {
|
||||
* <li>Perform post-commit processing updating L2 cache, ElasticSearch etc</li>
|
||||
* </ul>
|
||||
*/
|
||||
void commitAndContinue() throws RollbackException;
|
||||
void commitAndContinue();
|
||||
|
||||
/**
|
||||
* Commit the transaction.
|
||||
@@ -85,7 +83,7 @@ public interface Transaction extends Closeable {
|
||||
* <li>Mark the transaction as "Inactive"</li>
|
||||
* </ul>
|
||||
*/
|
||||
void commit() throws RollbackException;
|
||||
void commit();
|
||||
|
||||
/**
|
||||
* Rollback the transaction.
|
||||
@@ -125,7 +123,13 @@ public interface Transaction extends Closeable {
|
||||
/**
|
||||
* If the transaction is active then perform rollback. Otherwise do nothing.
|
||||
*/
|
||||
void end() throws PersistenceException;
|
||||
void end();
|
||||
|
||||
|
||||
/**
|
||||
* Synonym for end() to support AutoClosable.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Return true if the transaction is active.
|
||||
@@ -423,8 +427,17 @@ public interface Transaction extends Closeable {
|
||||
* <li>A query is executed on the same transaction</li>
|
||||
* <li>UpdateSql or CallableSql are mixed with bean save and delete</li>
|
||||
* <li>Transaction commit occurs</li>
|
||||
* <li>A getter method is called on a batched bean</li>
|
||||
* </ul>
|
||||
*/
|
||||
void flush() throws PersistenceException;
|
||||
|
||||
/**
|
||||
* This is a synonym for flush() and will be deprecated.
|
||||
* <p>
|
||||
* flush() is preferred as it matches the JPA flush() method.
|
||||
* </p>
|
||||
*/
|
||||
void flushBatch() throws PersistenceException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package io.ebean.config.dbplatform;
|
||||
|
||||
/**
|
||||
* Specific persistence error types we wish to map.
|
||||
*/
|
||||
public enum DataErrorType {
|
||||
|
||||
/**
|
||||
* Error trying to acquire lock (e.g. failure executing select for update nowait)
|
||||
*/
|
||||
AcquireLock,
|
||||
|
||||
/**
|
||||
* Error with a duplicate primary or unique key.
|
||||
*/
|
||||
DuplicateKey,
|
||||
|
||||
/**
|
||||
* Data integrity error like an invalid foreign key.
|
||||
*/
|
||||
DataIntegrity
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import io.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
@@ -172,12 +173,21 @@ public class DatabasePlatform {
|
||||
|
||||
protected boolean supportsNativeIlike;
|
||||
|
||||
protected SqlExceptionTranslator exceptionTranslator = new SqlCodeTranslator();
|
||||
|
||||
/**
|
||||
* Instantiates a new database platform.
|
||||
*/
|
||||
public DatabasePlatform() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate the SQLException into a specific persistence exception if possible.
|
||||
*/
|
||||
public PersistenceException translate(String message, SQLException e) {
|
||||
return exceptionTranslator.translate(message, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure UUID Storage etc based on ServerConfig settings.
|
||||
*/
|
||||
@@ -554,17 +564,16 @@ public class DatabasePlatform {
|
||||
}
|
||||
|
||||
public String completeSql(String sql, Query<?> query) {
|
||||
if (Boolean.TRUE.equals(query.isForUpdate())) {
|
||||
sql = withForUpdate(sql);
|
||||
if (query.isForUpdate()) {
|
||||
sql = withForUpdate(sql, query.getForUpdateMode());
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
protected String withForUpdate(String sql) {
|
||||
protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) {
|
||||
// silently assume the database does not support the "for update" clause.
|
||||
logger.info("it seems your database does not support the 'for update' clause");
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package io.ebean.config.dbplatform;
|
||||
|
||||
import io.ebean.AcquireLockException;
|
||||
import io.ebean.DataIntegrityException;
|
||||
import io.ebean.DuplicateKeyException;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Translate SQLException based on SQLState codes.
|
||||
*/
|
||||
public class SqlCodeTranslator implements SqlExceptionTranslator {
|
||||
|
||||
private final Map<String,DataErrorType> map;
|
||||
|
||||
/**
|
||||
* Create given the map of SQLState codes to error types.
|
||||
*/
|
||||
public SqlCodeTranslator(Map<String,DataErrorType> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create "No-op" implementation.
|
||||
*/
|
||||
public SqlCodeTranslator() {
|
||||
this.map = Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistenceException translate(String message, SQLException e) {
|
||||
|
||||
DataErrorType errorType = map.get(e.getSQLState());
|
||||
if (errorType != null) {
|
||||
switch (errorType) {
|
||||
case AcquireLock:
|
||||
return new AcquireLockException(message, e);
|
||||
case DuplicateKey:
|
||||
return new DuplicateKeyException(message, e);
|
||||
case DataIntegrity:
|
||||
return new DataIntegrityException(message, e);
|
||||
}
|
||||
}
|
||||
// return a generic exception
|
||||
return new PersistenceException(message, e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package io.ebean.config.dbplatform;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Used to build a SQLCodeTranslator given DB platform specific codes.
|
||||
*/
|
||||
public class SqlErrorCodes {
|
||||
|
||||
private Map<String,DataErrorType> map = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Map the codes to AcquireLockException.
|
||||
*/
|
||||
public SqlErrorCodes addAcquireLock(String... codes) {
|
||||
return add(DataErrorType.AcquireLock, codes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the codes to DataIntegrityException.
|
||||
*/
|
||||
public SqlErrorCodes addDataIntegrity(String... codes) {
|
||||
return add(DataErrorType.DataIntegrity, codes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the codes to DuplicateKeyException.
|
||||
*/
|
||||
public SqlErrorCodes addDuplicateKey(String... codes) {
|
||||
return add(DataErrorType.DuplicateKey, codes);
|
||||
}
|
||||
|
||||
private SqlErrorCodes add(DataErrorType type, String... codes) {
|
||||
for (String code : codes) {
|
||||
map.put(code, type);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and return the SQLCodeTranslator with the mapped codes.
|
||||
*/
|
||||
public SqlCodeTranslator build() {
|
||||
return new SqlCodeTranslator(map);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package io.ebean.config.dbplatform;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Used to translate SQLExceptions to specific persistence exceptions.
|
||||
*/
|
||||
public interface SqlExceptionTranslator {
|
||||
|
||||
/**
|
||||
* Translate the given exception.
|
||||
*/
|
||||
PersistenceException translate(String message, SQLException e);
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.config.dbplatform.DbType;
|
||||
import io.ebean.config.dbplatform.PlatformIdGenerator;
|
||||
import io.ebean.config.dbplatform.SqlErrorCodes;
|
||||
import io.ebean.dbmigration.ddlgeneration.platform.DB2Ddl;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
@@ -27,6 +28,13 @@ public class DB2Platform extends DatabasePlatform {
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
|
||||
this.exceptionTranslator =
|
||||
new SqlErrorCodes()
|
||||
//.addAcquireLock("")
|
||||
.addDuplicateKey("-803")
|
||||
.addDataIntegrity("-407","-530","-531","-532","-543","-544","-545","-603","-667")
|
||||
.build();
|
||||
|
||||
booleanDbType = Types.BOOLEAN;
|
||||
dbTypeMap.put(DbType.REAL, new DbPlatformType("real"));
|
||||
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
|
||||
|
||||
@@ -2,11 +2,13 @@ package io.ebean.config.dbplatform.h2;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.Platform;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.config.dbplatform.DbType;
|
||||
import io.ebean.config.dbplatform.IdType;
|
||||
import io.ebean.config.dbplatform.PlatformIdGenerator;
|
||||
import io.ebean.config.dbplatform.SqlErrorCodes;
|
||||
import io.ebean.dbmigration.ddlgeneration.platform.H2Ddl;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
@@ -26,6 +28,13 @@ public class H2Platform extends DatabasePlatform {
|
||||
this.dbDefaultValue.setNow("now()");
|
||||
this.columnAliasPrefix = null;
|
||||
|
||||
this.exceptionTranslator =
|
||||
new SqlErrorCodes()
|
||||
.addAcquireLock("50200")
|
||||
.addDuplicateKey("23001","23505")
|
||||
.addDataIntegrity("22001","22003","22012","22018","22025","23000","23002","23003","23502","23503","23506","23507","23513")
|
||||
.build();
|
||||
|
||||
this.dbIdentity.setIdType(IdType.IDENTITY);
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
@@ -50,7 +59,8 @@ public class H2Platform extends DatabasePlatform {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String withForUpdate(String sql) {
|
||||
protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) {
|
||||
// NOWAIT and SKIP LOCKED currently not supported with H2
|
||||
return sql + " for update";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@ package io.ebean.config.dbplatform.mysql;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.Platform;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.config.dbplatform.DbType;
|
||||
import io.ebean.config.dbplatform.IdType;
|
||||
import io.ebean.config.dbplatform.PlatformIdGenerator;
|
||||
import io.ebean.config.dbplatform.SqlErrorCodes;
|
||||
import io.ebean.dbmigration.ddlgeneration.platform.MySqlDdl;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
@@ -40,6 +42,13 @@ public class MySqlPlatform extends DatabasePlatform {
|
||||
this.dbIdentity.setSupportsIdentity(true);
|
||||
this.dbIdentity.setSupportsSequence(false);
|
||||
|
||||
this.exceptionTranslator =
|
||||
new SqlErrorCodes()
|
||||
.addAcquireLock("1205")
|
||||
.addDuplicateKey("1062")
|
||||
.addDataIntegrity("630","839","840","893","1169","1215","1216","1217","1364","1451","1452","1557")
|
||||
.build();
|
||||
|
||||
this.openQuote = "`";
|
||||
this.closeQuote = "`";
|
||||
|
||||
@@ -66,7 +75,8 @@ public class MySqlPlatform extends DatabasePlatform {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String withForUpdate(String sql) {
|
||||
protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) {
|
||||
// NOWAIT and SKIP LOCKED currently not supported with MySQL
|
||||
return sql + " for update";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebean.config.dbplatform.oracle;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.Platform;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.config.dbplatform.BasicSqlAnsiLimiter;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
@@ -69,7 +70,14 @@ public class OraclePlatform extends DatabasePlatform {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String withForUpdate(String sql) {
|
||||
return sql + " for update";
|
||||
protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) {
|
||||
switch (forUpdateMode) {
|
||||
case SKIPLOCKED:
|
||||
return sql + " for update skip locked";
|
||||
case NOWAIT:
|
||||
return sql + " for update nowait";
|
||||
default:
|
||||
return sql + " for update";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@ package io.ebean.config.dbplatform.postgres;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.Platform;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.config.dbplatform.DbType;
|
||||
import io.ebean.config.dbplatform.IdType;
|
||||
import io.ebean.config.dbplatform.PlatformIdGenerator;
|
||||
import io.ebean.config.dbplatform.SqlErrorCodes;
|
||||
import io.ebean.dbmigration.ddlgeneration.DdlHandler;
|
||||
import io.ebean.dbmigration.ddlgeneration.platform.PostgresDdl;
|
||||
|
||||
@@ -42,7 +44,12 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
|
||||
//this.columnAliasPrefix = "as c";
|
||||
this.exceptionTranslator =
|
||||
new SqlErrorCodes()
|
||||
.addAcquireLock("55P03")
|
||||
.addDuplicateKey("23505")
|
||||
.addDataIntegrity("23000","23502","23503","23514")
|
||||
.build();
|
||||
|
||||
this.openQuote = "\"";
|
||||
this.closeQuote = "\"";
|
||||
@@ -102,7 +109,14 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String withForUpdate(String sql) {
|
||||
return sql + " for update";
|
||||
protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) {
|
||||
switch (forUpdateMode) {
|
||||
case SKIPLOCKED:
|
||||
return sql + " for update skip locked";
|
||||
case NOWAIT:
|
||||
return sql + " for update nowait";
|
||||
default:
|
||||
return sql + " for update";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.config.dbplatform.DbType;
|
||||
import io.ebean.config.dbplatform.IdType;
|
||||
import io.ebean.config.dbplatform.SqlErrorCodes;
|
||||
import io.ebean.dbmigration.ddlgeneration.platform.SqlServerDdl;
|
||||
|
||||
import java.sql.Types;
|
||||
@@ -31,6 +32,13 @@ public class SqlServerPlatform extends DatabasePlatform {
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsIdentity(true);
|
||||
|
||||
this.exceptionTranslator =
|
||||
new SqlErrorCodes()
|
||||
.addAcquireLock("1222")
|
||||
.addDuplicateKey("2601","2627")
|
||||
.addDataIntegrity("544","8114","8115")
|
||||
.build();
|
||||
|
||||
this.openQuote = "[";
|
||||
this.closeQuote = "]";
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -159,4 +160,11 @@ public class EJson {
|
||||
}
|
||||
return ((ModifyAwareList) list).asSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json returning as a Set taking into account the current token.
|
||||
*/
|
||||
public static Set<Object> parseSet(JsonParser parser, JsonToken currentToken) throws IOException {
|
||||
return new LinkedHashSet<>(parseList(parser, currentToken));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.PersistBatch;
|
||||
import io.ebean.TransactionCallback;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.PersistBatch;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
@@ -13,20 +13,20 @@ import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeanservice.docstore.api.DocStoreTransaction;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.RollbackException;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Wrapper of a ScopeTrans request and it's underlying transaction.
|
||||
*/
|
||||
public class ScopedTransaction implements SpiTransaction {
|
||||
|
||||
final ScopeTrans scopeTrans;
|
||||
private final ScopeTrans scopeTrans;
|
||||
|
||||
final SpiTransaction transaction;
|
||||
private final SpiTransaction transaction;
|
||||
|
||||
boolean committed;
|
||||
private boolean committed;
|
||||
|
||||
public ScopedTransaction(ScopeTrans scopeTrans) {
|
||||
this.scopeTrans = scopeTrans;
|
||||
@@ -34,12 +34,17 @@ public class ScopedTransaction implements SpiTransaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commitAndContinue() throws RollbackException {
|
||||
public PersistenceException translate(String message, SQLException cause) {
|
||||
return transaction.translate(message, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commitAndContinue() {
|
||||
transaction.commitAndContinue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() throws RollbackException {
|
||||
public void commit() {
|
||||
scopeTrans.commitTransaction();
|
||||
committed = true;
|
||||
}
|
||||
@@ -290,9 +295,14 @@ public class ScopedTransaction implements SpiTransaction {
|
||||
return transaction.isBatchFlushOnQuery();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws PersistenceException {
|
||||
transaction.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushBatch() throws PersistenceException {
|
||||
transaction.flushBatch();
|
||||
flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -406,7 +416,7 @@ public class ScopedTransaction implements SpiTransaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
public void close() {
|
||||
transaction.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +158,11 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
String getNativeSql();
|
||||
|
||||
/**
|
||||
* Return the ForUpdate mode.
|
||||
*/
|
||||
ForUpdate getForUpdateMode();
|
||||
|
||||
/**
|
||||
* Return the bean descriptor for this query.
|
||||
*/
|
||||
|
||||
@@ -11,7 +11,9 @@ import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeanservice.docstore.api.DocStoreTransaction;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Extends Transaction with additional API required on server.
|
||||
@@ -242,6 +244,11 @@ public interface SpiTransaction extends Transaction {
|
||||
*/
|
||||
void flushBatchOnRollback();
|
||||
|
||||
/**
|
||||
* Translate the SQLException.
|
||||
*/
|
||||
PersistenceException translate(String message, SQLException cause);
|
||||
|
||||
/**
|
||||
* Mark the transaction explicitly as not being query only.
|
||||
*/
|
||||
|
||||
@@ -840,6 +840,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return transactionScopeManager.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
currentTransaction().flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit the current transaction.
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,8 @@ import io.ebean.QueryIterator;
|
||||
import io.ebean.Version;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -55,4 +57,9 @@ public interface OrmQueryEngine {
|
||||
* Execute the query as a update statement.
|
||||
*/
|
||||
<T> int update(OrmQueryRequest<T> request);
|
||||
|
||||
/**
|
||||
* Translate the SQLException to a specific persistence exception type if possible.
|
||||
*/
|
||||
<T> PersistenceException translate(OrmQueryRequest<T> request, String bindLog, String sql, SQLException e);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import io.ebeaninternal.server.query.CancelableQuery;
|
||||
import io.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -76,6 +77,10 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
this.readOnly = query.isReadOnly();
|
||||
}
|
||||
|
||||
public PersistenceException translate(String bindLog, String sql, SQLException e) {
|
||||
return queryEngine.translate(this, bindLog, sql, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the transaction as not being query only.
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeaninternal.server.persist.BatchPostExecute;
|
||||
import io.ebeaninternal.server.persist.BatchedSqlException;
|
||||
import io.ebeaninternal.server.persist.PersistExecute;
|
||||
|
||||
/**
|
||||
@@ -15,14 +16,14 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
|
||||
INSERT, UPDATE, DELETE, SOFT_DELETE, DELETE_PERMANENT, UPDATESQL, CALLABLESQL
|
||||
}
|
||||
|
||||
protected boolean persistCascade;
|
||||
boolean persistCascade;
|
||||
|
||||
/**
|
||||
* One of INSERT, UPDATE, DELETE, UPDATESQL or CALLABLESQL.
|
||||
*/
|
||||
protected Type type;
|
||||
|
||||
protected final PersistExecute persistExecute;
|
||||
final PersistExecute persistExecute;
|
||||
|
||||
/**
|
||||
* Used by CallableSqlRequest and UpdateSqlRequest.
|
||||
@@ -63,24 +64,27 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
|
||||
/**
|
||||
* Execute the statement.
|
||||
*/
|
||||
public int executeStatement() {
|
||||
int executeStatement() {
|
||||
|
||||
boolean batch = isBatchThisRequest();
|
||||
|
||||
int rows;
|
||||
BatchControl control = transaction.getBatchControl();
|
||||
if (control != null) {
|
||||
rows = control.executeStatementOrBatch(this, batch);
|
||||
try {
|
||||
int rows;
|
||||
BatchControl control = transaction.getBatchControl();
|
||||
if (control != null) {
|
||||
rows = control.executeStatementOrBatch(this, batch);
|
||||
|
||||
} else if (batch) {
|
||||
// need to create the BatchControl
|
||||
control = persistExecute.createBatchControl(transaction);
|
||||
rows = control.executeStatementOrBatch(this, true);
|
||||
} else {
|
||||
rows = executeNow();
|
||||
} else if (batch) {
|
||||
// need to create the BatchControl
|
||||
control = persistExecute.createBatchControl(transaction);
|
||||
rows = control.executeStatementOrBatch(this, true);
|
||||
} else {
|
||||
rows = executeNow();
|
||||
}
|
||||
return rows;
|
||||
} catch (BatchedSqlException e) {
|
||||
throw transaction.translate(e.getMessage(), e.getCause());
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
public void initTransIfRequired() {
|
||||
|
||||
@@ -20,6 +20,7 @@ import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.id.ImportedId;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeaninternal.server.persist.BatchedSqlException;
|
||||
import io.ebeaninternal.server.persist.PersistExecute;
|
||||
import io.ebeaninternal.server.transaction.BeanPersistIdMap;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdate;
|
||||
@@ -499,10 +500,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
}
|
||||
|
||||
public BeanManager<T> getBeanManager() {
|
||||
return beanManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the BeanDescriptor for the associated bean.
|
||||
*/
|
||||
@@ -697,17 +694,20 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
public int executeOrQueue() {
|
||||
|
||||
boolean batch = isBatchThisRequest();
|
||||
try {
|
||||
BatchControl control = transaction.getBatchControl();
|
||||
if (control != null) {
|
||||
return control.executeOrQueue(this, batch);
|
||||
}
|
||||
if (batch) {
|
||||
control = persistExecute.createBatchControl(transaction);
|
||||
return control.executeOrQueue(this, true);
|
||||
|
||||
BatchControl control = transaction.getBatchControl();
|
||||
if (control != null) {
|
||||
return control.executeOrQueue(this, batch);
|
||||
}
|
||||
if (batch) {
|
||||
control = persistExecute.createBatchControl(transaction);
|
||||
return control.executeOrQueue(this, true);
|
||||
|
||||
} else {
|
||||
return executeNow();
|
||||
} else {
|
||||
return executeNow();
|
||||
}
|
||||
} catch (BatchedSqlException e) {
|
||||
throw transaction.translate(e.getMessage(), e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -264,8 +264,8 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
EbeanServer server = getBeanDescriptor().getEbeanServer();
|
||||
Query<?> q = (Query<?>) server.find(getPropertyType())
|
||||
.where().raw(expr, bindValues.toArray());
|
||||
Query<?> q = server.find(getPropertyType())
|
||||
.where().raw(expr, bindValues.toArray()).query();
|
||||
|
||||
return server.findIds(q, t);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import io.ebeaninternal.server.type.DataEncryptSupport;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.ScalarTypeArrayList;
|
||||
import io.ebeaninternal.server.type.ScalarTypeArray;
|
||||
import io.ebeaninternal.server.type.ScalarTypeEnumStandard;
|
||||
import io.ebeaninternal.server.type.SimpleAesEncryptor;
|
||||
import io.ebeaninternal.server.type.TypeManager;
|
||||
@@ -244,8 +244,8 @@ public class DeployUtil {
|
||||
int dbType = scalarType.getJdbcType();
|
||||
prop.setDbType(dbType);
|
||||
prop.setScalarType(scalarType);
|
||||
if (scalarType instanceof ScalarTypeArrayList) {
|
||||
prop.setDbColumnDefn(((ScalarTypeArrayList) scalarType).getDbColumnDefn());
|
||||
if (scalarType instanceof ScalarTypeArray) {
|
||||
prop.setDbColumnDefn(((ScalarTypeArray) scalarType).getDbColumnDefn());
|
||||
}
|
||||
if (dbType == Types.VARCHAR) {
|
||||
// determine the db column size
|
||||
|
||||
@@ -419,6 +419,21 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return query.filterMany(prop);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdate() {
|
||||
return query.forUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdateNoWait() {
|
||||
return query.forUpdateNoWait();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdateSkipLocked() {
|
||||
return query.forUpdateSkipLocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> select(String fetchProperties) {
|
||||
return query.select(fetchProperties);
|
||||
|
||||
@@ -419,6 +419,21 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
|
||||
return exprList.findUnique();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdate() {
|
||||
return exprList.forUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdateNoWait() {
|
||||
return exprList.forUpdateNoWait();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> forUpdateSkipLocked() {
|
||||
return exprList.forUpdateSkipLocked();
|
||||
}
|
||||
|
||||
/**
|
||||
* Path exists - for the given path in a JSON document.
|
||||
*/
|
||||
|
||||
@@ -5,7 +5,6 @@ import io.ebeaninternal.server.core.PersistRequest;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@@ -123,7 +122,7 @@ public final class BatchControl {
|
||||
* to the depth.
|
||||
* </p>
|
||||
*/
|
||||
public int executeStatementOrBatch(PersistRequest request, boolean batch) {
|
||||
public int executeStatementOrBatch(PersistRequest request, boolean batch) throws BatchedSqlException {
|
||||
if (!batch || (batchFlushOnMixed && !isBeansEmpty())) {
|
||||
// flush when mixing beans and updateSql
|
||||
flush();
|
||||
@@ -148,7 +147,7 @@ public final class BatchControl {
|
||||
* immediately or queue it for batch processing later. The queue is flushedIntercept
|
||||
* according to the depth (object graph depth).
|
||||
*/
|
||||
public int executeOrQueue(PersistRequestBean<?> request, boolean batch) {
|
||||
public int executeOrQueue(PersistRequestBean<?> request, boolean batch) throws BatchedSqlException {
|
||||
|
||||
if (!batch || (batchFlushOnMixed && !pstmtHolder.isEmpty())) {
|
||||
// flush when mixing beans and updateSql
|
||||
@@ -167,7 +166,7 @@ public final class BatchControl {
|
||||
/**
|
||||
* Add the request to the batch and return true if we should flush.
|
||||
*/
|
||||
private boolean addToBatch(PersistRequestBean<?> request) {
|
||||
private boolean addToBatch(PersistRequestBean<?> request) throws BatchedSqlException {
|
||||
|
||||
BatchedBeanHolder beanHolder = getBeanHolder(request);
|
||||
int bufferSize = beanHolder.append(request);
|
||||
@@ -193,14 +192,14 @@ public final class BatchControl {
|
||||
/**
|
||||
* Flush any batched PreparedStatements.
|
||||
*/
|
||||
protected void flushPstmtHolder() {
|
||||
protected void flushPstmtHolder() throws BatchedSqlException {
|
||||
pstmtHolder.flush(getGeneratedKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all the requests contained in the list.
|
||||
*/
|
||||
protected void executeNow(ArrayList<PersistRequest> list) {
|
||||
protected void executeNow(ArrayList<PersistRequest> list) throws BatchedSqlException {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (i % batchSize == 0) {
|
||||
// hit the batch size so flush
|
||||
@@ -214,14 +213,14 @@ public final class BatchControl {
|
||||
/**
|
||||
* Flush without resetting the topOrder (maintains the depth info).
|
||||
*/
|
||||
public void flush() throws PersistenceException {
|
||||
public void flush() throws BatchedSqlException {
|
||||
flush(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush with a reset the topOrder (fully empty the batch).
|
||||
*/
|
||||
public void flushReset() throws PersistenceException {
|
||||
public void flushReset() throws BatchedSqlException {
|
||||
flush(true);
|
||||
}
|
||||
|
||||
@@ -236,31 +235,38 @@ public final class BatchControl {
|
||||
/**
|
||||
* execute all the requests currently queued or batched.
|
||||
*/
|
||||
private void flush(boolean resetTop) throws PersistenceException {
|
||||
private void flush(boolean resetTop) throws BatchedSqlException {
|
||||
|
||||
if (!pstmtHolder.isEmpty()) {
|
||||
// Flush existing pstmts (updateSql or callableSql)
|
||||
flushPstmtHolder();
|
||||
}
|
||||
if (isEmpty()) {
|
||||
// Nothing in queue to flush
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!pstmtHolder.isEmpty()) {
|
||||
// Flush existing pstmts (updateSql or callableSql)
|
||||
flushPstmtHolder();
|
||||
}
|
||||
if (isEmpty()) {
|
||||
// Nothing in queue to flush
|
||||
return;
|
||||
}
|
||||
|
||||
// convert entry map to array for sorting
|
||||
BatchedBeanHolder[] bsArray = getBeanHolderArray();
|
||||
// sort the entries by depth
|
||||
Arrays.sort(bsArray, depthComparator);
|
||||
// convert entry map to array for sorting
|
||||
BatchedBeanHolder[] bsArray = getBeanHolderArray();
|
||||
// sort the entries by depth
|
||||
Arrays.sort(bsArray, depthComparator);
|
||||
|
||||
if (transaction.isLogSummary()) {
|
||||
transaction.logSummary("BatchControl flush " + Arrays.toString(bsArray));
|
||||
}
|
||||
for (BatchedBeanHolder aBsArray : bsArray) {
|
||||
aBsArray.executeNow();
|
||||
}
|
||||
if (transaction.isLogSummary()) {
|
||||
transaction.logSummary("BatchControl flush " + Arrays.toString(bsArray));
|
||||
}
|
||||
for (BatchedBeanHolder aBsArray : bsArray) {
|
||||
aBsArray.executeNow();
|
||||
}
|
||||
|
||||
if (resetTop) {
|
||||
beanHoldMap.clear();
|
||||
if (resetTop) {
|
||||
beanHoldMap.clear();
|
||||
}
|
||||
} catch (BatchedSqlException e) {
|
||||
// clear the batch on error in case we want to
|
||||
// catch, rollback and continue processing
|
||||
clear();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +274,7 @@ public final class BatchControl {
|
||||
* Return an entry for the given type description. The type description is
|
||||
* typically the bean class name (or table name for MapBeans).
|
||||
*/
|
||||
private BatchedBeanHolder getBeanHolder(PersistRequestBean<?> request) {
|
||||
private BatchedBeanHolder getBeanHolder(PersistRequestBean<?> request) throws BatchedSqlException {
|
||||
|
||||
BeanDescriptor<?> beanDescriptor = request.getBeanDescriptor();
|
||||
BatchedBeanHolder batchBeanHolder = beanHoldMap.get(beanDescriptor.getFullName());
|
||||
|
||||
@@ -79,7 +79,7 @@ public class BatchedBeanHolder {
|
||||
* and then execute them.
|
||||
* </p>
|
||||
*/
|
||||
public void executeNow() {
|
||||
public void executeNow() throws BatchedSqlException {
|
||||
// process the requests. Creates one or more PreparedStatements
|
||||
// with binding addBatch() for each request.
|
||||
// Note updates and deletes can result in many PreparedStatements
|
||||
|
||||
@@ -121,7 +121,7 @@ public class BatchedPstmt {
|
||||
list.get(index).setGeneratedKey(idValue);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ public class BatchedPstmtHolder {
|
||||
*
|
||||
* @param getGeneratedKeys if true try to get generated keys for inserts
|
||||
*/
|
||||
public void flush(boolean getGeneratedKeys) throws PersistenceException {
|
||||
public void flush(boolean getGeneratedKeys) throws BatchedSqlException {
|
||||
|
||||
SQLException firstError = null;
|
||||
String errorSql = null;
|
||||
@@ -96,7 +96,7 @@ public class BatchedPstmtHolder {
|
||||
} catch (SQLException ex) {
|
||||
SQLException next = ex.getNextException();
|
||||
while (next != null) {
|
||||
logger.error("Next Exception during batch execution", next);
|
||||
logger.trace("Next Exception during batch execution", next);
|
||||
next = next.getNextException();
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ public class BatchedPstmtHolder {
|
||||
|
||||
if (firstError != null) {
|
||||
String msg = "Error when batch flush on sql: " + errorSql;
|
||||
throw new PersistenceException(msg, firstError);
|
||||
throw new BatchedSqlException(msg, firstError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package io.ebeaninternal.server.persist;
|
||||
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Holds the first SQLException found when executing a JDBC batch.
|
||||
*/
|
||||
public class BatchedSqlException extends Exception {
|
||||
|
||||
private SQLException cause;
|
||||
|
||||
BatchedSqlException(String message, SQLException cause) {
|
||||
super(message, cause);
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLException getCause() {
|
||||
return cause;
|
||||
}
|
||||
}
|
||||
@@ -653,7 +653,9 @@ public final class DefaultPersister implements Persister {
|
||||
executeSqlUpdate(sqlDelete, t);
|
||||
} else {
|
||||
List<Object> childIds = expOne.findIdsByParentId(id, idList, t);
|
||||
deleteChildrenById(t, targetDesc, childIds, softDelete);
|
||||
if (childIds != null && !childIds.isEmpty()) {
|
||||
deleteChildrenById(t, targetDesc, childIds, softDelete);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package io.ebeaninternal.server.persist.dml;
|
||||
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.util.StringHelper;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.persist.BeanPersister;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
@@ -18,15 +18,16 @@ import java.sql.SQLException;
|
||||
*/
|
||||
public final class DmlBeanPersister implements BeanPersister {
|
||||
|
||||
private final DatabasePlatform dbPlatform;
|
||||
|
||||
private final UpdateMeta updateMeta;
|
||||
|
||||
private final InsertMeta insertMeta;
|
||||
|
||||
private final DeleteMeta deleteMeta;
|
||||
|
||||
|
||||
public DmlBeanPersister(UpdateMeta updateMeta, InsertMeta insertMeta, DeleteMeta deleteMeta) {
|
||||
|
||||
public DmlBeanPersister(DatabasePlatform dbPlatform, UpdateMeta updateMeta, InsertMeta insertMeta, DeleteMeta deleteMeta) {
|
||||
this.dbPlatform = dbPlatform;
|
||||
this.updateMeta = updateMeta;
|
||||
this.insertMeta = insertMeta;
|
||||
this.deleteMeta = deleteMeta;
|
||||
@@ -37,9 +38,7 @@ public final class DmlBeanPersister implements BeanPersister {
|
||||
*/
|
||||
@Override
|
||||
public int delete(PersistRequestBean<?> request) {
|
||||
|
||||
DeleteHandler delete = new DeleteHandler(request, deleteMeta);
|
||||
return execute(request, delete);
|
||||
return execute(request, new DeleteHandler(request, deleteMeta));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,9 +46,7 @@ public final class DmlBeanPersister implements BeanPersister {
|
||||
*/
|
||||
@Override
|
||||
public void insert(PersistRequestBean<?> request) {
|
||||
|
||||
InsertHandler insert = new InsertHandler(request, insertMeta);
|
||||
execute(request, insert);
|
||||
execute(request, new InsertHandler(request, insertMeta));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,9 +54,7 @@ public final class DmlBeanPersister implements BeanPersister {
|
||||
*/
|
||||
@Override
|
||||
public void update(PersistRequestBean<?> request) {
|
||||
|
||||
UpdateHandler update = new UpdateHandler(request, updateMeta);
|
||||
execute(request, update);
|
||||
execute(request, new UpdateHandler(request, updateMeta));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,12 +75,12 @@ public final class DmlBeanPersister implements BeanPersister {
|
||||
|
||||
} catch (SQLException e) {
|
||||
// log the error to the transaction log
|
||||
String errMsg = StringHelper.replaceStringMulti(e.getMessage(), new String[]{"\r", "\n"}, "\\n ");
|
||||
String msg = "ERROR executing DML bindLog[" + handler.getBindLog() + "] error[" + errMsg + "]";
|
||||
String errMsg = StringHelper.replaceStringMulti(e.getMessage(), new String[]{"\r", "\n"}, " ");
|
||||
String msg = "Error[" + errMsg + "]";
|
||||
if (request.getTransaction().isLogSummary()) {
|
||||
request.getTransaction().logSummary(msg);
|
||||
}
|
||||
throw new PersistenceException(msg, e);
|
||||
throw dbPlatform.translate(msg, e);
|
||||
|
||||
} finally {
|
||||
if (!batched) {
|
||||
|
||||
@@ -10,9 +10,12 @@ import io.ebeaninternal.server.persist.BeanPersisterFactory;
|
||||
*/
|
||||
public class DmlBeanPersisterFactory implements BeanPersisterFactory {
|
||||
|
||||
private final DatabasePlatform dbPlatform;
|
||||
|
||||
private final MetaFactory metaFactory;
|
||||
|
||||
public DmlBeanPersisterFactory(DatabasePlatform dbPlatform) {
|
||||
this.dbPlatform = dbPlatform;
|
||||
this.metaFactory = new MetaFactory(dbPlatform);
|
||||
}
|
||||
|
||||
@@ -29,7 +32,7 @@ public class DmlBeanPersisterFactory implements BeanPersisterFactory {
|
||||
UpdateMeta updMeta = metaFactory.createUpdate(desc);
|
||||
DeleteMeta delMeta = metaFactory.createDelete(desc);
|
||||
InsertMeta insMeta = metaFactory.createInsert(desc);
|
||||
return new DmlBeanPersister(updMeta, insMeta, delMeta);
|
||||
return new DmlBeanPersister(dbPlatform, updMeta, insMeta, delMeta);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,12 +10,10 @@ import io.ebean.bean.NodeUsageListener;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.event.readaudit.ReadEvent;
|
||||
import io.ebean.util.StringHelper;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiQuery.Mode;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.autotune.ProfilingListener;
|
||||
import io.ebeaninternal.server.core.Message;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.core.SpiOrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanCollectionHelp;
|
||||
@@ -665,28 +663,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
|
||||
* Create a PersistenceException including interesting information like the bindLog and sql used.
|
||||
*/
|
||||
PersistenceException createPersistenceException(SQLException e) {
|
||||
|
||||
return createPersistenceException(e, getTransaction(), bindLog, sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a PersistenceException including interesting information like the bindLog and sql used.
|
||||
*/
|
||||
static PersistenceException createPersistenceException(SQLException e, SpiTransaction t, String bindLog, String sql) {
|
||||
|
||||
if (t.isLogSummary()) {
|
||||
// log the error to the transaction log
|
||||
String errMsg = StringHelper.replaceStringMulti(e.getMessage(), new String[]{"\r", "\n"}, "\\n ");
|
||||
String msg = "ERROR executing query: bindLog[" + bindLog + "] error[" + errMsg + "]";
|
||||
t.logSummary(msg);
|
||||
}
|
||||
|
||||
// ensure 'rollback' is logged if queryOnly transaction
|
||||
t.getConnection();
|
||||
|
||||
// build a decent error message for the exception
|
||||
String m = Message.msg("fetch.sqlerror", e.getMessage(), bindLog, sql);
|
||||
return new PersistenceException(m, e);
|
||||
return request.translate(bindLog, sql, e);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,8 +8,11 @@ import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.util.StringHelper;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.core.DiffHelp;
|
||||
import io.ebeaninternal.server.core.Message;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.lib.util.Str;
|
||||
@@ -18,6 +21,7 @@ import io.ebeaninternal.server.transaction.TransactionManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -45,7 +49,10 @@ public class CQueryEngine {
|
||||
|
||||
private final CQueryHistorySupport historySupport;
|
||||
|
||||
private final DatabasePlatform dbPlatform;
|
||||
|
||||
public CQueryEngine(ServerConfig serverConfig, DatabasePlatform dbPlatform, Binder binder, Map<String, String> asOfTableMapping, Map<String, String> draftTableMap) {
|
||||
this.dbPlatform = dbPlatform;
|
||||
this.defaultFetchSizeFindEach = serverConfig.getJdbcFetchSizeFindEach();
|
||||
this.defaultFetchSizeFindList = serverConfig.getJdbcFetchSizeFindList();
|
||||
this.forwardOnlyHintOnFindIterate = dbPlatform.isForwardOnlyHintOnFindIterate();
|
||||
@@ -83,7 +90,7 @@ public class CQueryEngine {
|
||||
return rows;
|
||||
|
||||
} catch (SQLException e) {
|
||||
throw CQuery.createPersistenceException(e, request.getTransaction(), query.getBindLog(), query.getGeneratedSql());
|
||||
throw translate(request, query.getBindLog(), query.getGeneratedSql(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,10 +116,30 @@ public class CQueryEngine {
|
||||
return list;
|
||||
|
||||
} catch (SQLException e) {
|
||||
throw CQuery.createPersistenceException(e, request.getTransaction(), rcQuery.getBindLog(), rcQuery.getGeneratedSql());
|
||||
throw translate(request, rcQuery.getBindLog(), rcQuery.getGeneratedSql(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate the SQLException into a PersistenceException.
|
||||
*/
|
||||
<T> PersistenceException translate(OrmQueryRequest<T> request, String bindLog, String sql, SQLException e) {
|
||||
SpiTransaction t = request.getTransaction();
|
||||
if (t.isLogSummary()) {
|
||||
// log the error to the transaction log
|
||||
String errMsg = StringHelper.replaceStringMulti(e.getMessage(), new String[]{"\r", "\n"}, "\\n ");
|
||||
String msg = "ERROR executing query, bindLog[" + bindLog + "] error[" + errMsg + "]";
|
||||
t.logSummary(msg);
|
||||
}
|
||||
|
||||
// ensure 'rollback' is logged if queryOnly transaction
|
||||
t.getConnection();
|
||||
|
||||
// build a decent error message for the exception
|
||||
String m = Message.msg("fetch.sqlerror", e.getMessage(), bindLog, sql);
|
||||
return dbPlatform.translate(m, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and execute the find Id's query.
|
||||
*/
|
||||
@@ -155,7 +182,7 @@ public class CQueryEngine {
|
||||
return count;
|
||||
|
||||
} catch (SQLException e) {
|
||||
throw CQuery.createPersistenceException(e, request.getTransaction(), rcQuery.getBindLog(), rcQuery.getGeneratedSql());
|
||||
throw translate(request, rcQuery.getBindLog(), rcQuery.getGeneratedSql(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ import io.ebeaninternal.server.core.OrmQueryEngine;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -28,10 +30,14 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine {
|
||||
* Create the Finder.
|
||||
*/
|
||||
public DefaultOrmQueryEngine(CQueryEngine queryEngine) {
|
||||
|
||||
this.queryEngine = queryEngine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> PersistenceException translate(OrmQueryRequest<T> request, String bindLog, String sql, SQLException e) {
|
||||
return queryEngine.translate(request, bindLog, sql, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the jdbc batch by default unless explicitly turned off on the transaction.
|
||||
*/
|
||||
|
||||
@@ -206,9 +206,9 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
private Boolean autoTune;
|
||||
|
||||
/**
|
||||
* Allow to fetch a record "for update" which should lock it on read
|
||||
* For update mode.
|
||||
*/
|
||||
private boolean forUpdate;
|
||||
private ForUpdate forUpdate;
|
||||
|
||||
private boolean singleAttribute;
|
||||
|
||||
@@ -579,7 +579,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
// includes joins and we use - delete ... where id in (...)
|
||||
maxRows = 0;
|
||||
firstRow = 0;
|
||||
forUpdate = false;
|
||||
forUpdate = null;
|
||||
rootTableAlias = "${RTA}"; // alias we remove later
|
||||
setSelectId();
|
||||
}
|
||||
@@ -791,11 +791,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return autoTune;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForUpdate() {
|
||||
return forUpdate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultRawSqlIfRequired() {
|
||||
if (beanDescriptor.isRawSqlBased() && rawSql == null) {
|
||||
@@ -811,11 +806,42 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setForUpdate(boolean forUpdate) {
|
||||
this.forUpdate = forUpdate;
|
||||
this.forUpdate = (forUpdate) ? ForUpdate.BASE : null;
|
||||
this.excludeBeanCache = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> forUpdate() {
|
||||
return setForUpdateWithMode(ForUpdate.BASE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> forUpdateNoWait() {
|
||||
return setForUpdateWithMode(ForUpdate.NOWAIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> forUpdateSkipLocked() {
|
||||
return setForUpdateWithMode(ForUpdate.SKIPLOCKED);
|
||||
}
|
||||
|
||||
private DefaultOrmQuery<T> setForUpdateWithMode(ForUpdate mode) {
|
||||
this.forUpdate = mode;
|
||||
this.excludeBeanCache = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForUpdate() {
|
||||
return forUpdate != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForUpdate getForUpdateMode() {
|
||||
return forUpdate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfilingListener getProfilingListener() {
|
||||
return profilingListener;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.querydefn;
|
||||
|
||||
import io.ebean.OrderBy;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.RawSql;
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.CQueryPlanKey;
|
||||
@@ -14,61 +15,69 @@ import io.ebeaninternal.server.deploy.TableJoin;
|
||||
*/
|
||||
class OrmQueryPlanKey implements CQueryPlanKey {
|
||||
|
||||
private final String m2mIncludeTable;
|
||||
private final String orderByAsSting;
|
||||
private final SpiExpression where;
|
||||
private final SpiExpression having;
|
||||
private final RawSql.Key rawSqlKey;
|
||||
private final boolean hasIdValue;
|
||||
private final SpiQuery.Type type;
|
||||
private final int maxRows;
|
||||
private final int firstRow;
|
||||
private final boolean disableLazyLoading;
|
||||
private final boolean distinct;
|
||||
private final boolean sqlDistinct;
|
||||
private final String mapKey;
|
||||
private final SpiQuery.TemporalMode temporalMode;
|
||||
private final boolean forUpdate;
|
||||
private final String rootTableAlias;
|
||||
private final OrmUpdateProperties updateProperties;
|
||||
|
||||
private final int planHash;
|
||||
private final int bindCount;
|
||||
private final String options;
|
||||
|
||||
OrmQueryPlanKey(TableJoin m2mIncludeTable, SpiQuery.Type type, OrmQueryDetail detail, int maxRows, int firstRow, boolean disableLazyLoading, OrderBy<?> orderBy, boolean distinct, boolean sqlDistinct, String mapKey, Object id, BindParams bindParams, SpiExpression whereExpressions, SpiExpression havingExpressions, SpiQuery.TemporalMode temporalMode, boolean forUpdate, String rootTableAlias, RawSql rawSql, OrmUpdateProperties updateProperties) {
|
||||
OrmQueryPlanKey(TableJoin m2mIncludeTable, SpiQuery.Type type, OrmQueryDetail detail, int maxRows, int firstRow, boolean disableLazyLoading,
|
||||
OrderBy<?> orderBy, boolean distinct, boolean sqlDistinct, String mapKey, Object id, BindParams bindParams,
|
||||
SpiExpression whereExpressions, SpiExpression havingExpressions, SpiQuery.TemporalMode temporalMode,
|
||||
Query.ForUpdate forUpdate, String rootTableAlias, RawSql rawSql, OrmUpdateProperties updateProperties) {
|
||||
|
||||
this.m2mIncludeTable = m2mIncludeTable == null ? null : m2mIncludeTable.getTable();
|
||||
this.type = type;
|
||||
StringBuilder sb = new StringBuilder(300);
|
||||
if (type != null) {
|
||||
sb.append("t:").append(type.ordinal());
|
||||
}
|
||||
if (temporalMode != SpiQuery.TemporalMode.CURRENT) {
|
||||
sb.append(",temp:").append(temporalMode.ordinal());
|
||||
}
|
||||
if (forUpdate != null) {
|
||||
sb.append(",forUpd:").append(forUpdate.ordinal());
|
||||
}
|
||||
if (id != null) {
|
||||
sb.append(",id:");
|
||||
}
|
||||
if (distinct) {
|
||||
sb.append(",dist:");
|
||||
}
|
||||
if (sqlDistinct) {
|
||||
sb.append(",sqlD:");
|
||||
}
|
||||
if (disableLazyLoading) {
|
||||
sb.append(",disLazy:");
|
||||
}
|
||||
if (rootTableAlias != null) {
|
||||
sb.append(",root:").append(rootTableAlias);
|
||||
}
|
||||
if (orderBy != null) {
|
||||
sb.append(",orderBy:").append(orderBy.toStringFormat());
|
||||
}
|
||||
if (m2mIncludeTable != null) {
|
||||
sb.append(",m2m:").append(m2mIncludeTable.getTable());
|
||||
}
|
||||
if (mapKey != null) {
|
||||
sb.append(",mapKey:").append(mapKey);
|
||||
}
|
||||
this.options = sb.toString();
|
||||
this.maxRows = maxRows;
|
||||
this.firstRow = firstRow;
|
||||
this.disableLazyLoading = disableLazyLoading;
|
||||
this.orderByAsSting = (orderBy == null) ? null : orderBy.toStringFormat();
|
||||
this.distinct = distinct;
|
||||
this.sqlDistinct = sqlDistinct;
|
||||
this.mapKey = mapKey;
|
||||
this.hasIdValue = (id != null);
|
||||
this.where = (whereExpressions == null) ? null : whereExpressions.copyForPlanKey();
|
||||
this.having = (havingExpressions == null) ? null : havingExpressions.copyForPlanKey();
|
||||
this.temporalMode = temporalMode;
|
||||
this.forUpdate = forUpdate;
|
||||
this.rootTableAlias = rootTableAlias;
|
||||
this.updateProperties = updateProperties;
|
||||
this.rawSqlKey = (rawSql == null) ? null : rawSql.getKey();
|
||||
|
||||
// exclude bind values and things unrelated to the sql being generated
|
||||
HashQueryPlanBuilder builder = new HashQueryPlanBuilder();
|
||||
|
||||
builder.add((type == null ? 0 : type.ordinal() + 1));
|
||||
builder.add(distinct).add(sqlDistinct);
|
||||
builder.add(options.hashCode());
|
||||
builder.add(firstRow).add(maxRows);
|
||||
builder.add(orderBy).add(forUpdate);
|
||||
builder.add(mapKey);
|
||||
builder.add(disableLazyLoading);
|
||||
builder.add(hasIdValue);
|
||||
builder.add(temporalMode);
|
||||
builder.add(rawSqlKey == null ? 0 : rawSqlKey.hashCode());
|
||||
builder.add(this.m2mIncludeTable);
|
||||
builder.add(rootTableAlias);
|
||||
|
||||
if (detail != null) {
|
||||
detail.queryPlanHash(builder);
|
||||
@@ -111,23 +120,10 @@ class OrmQueryPlanKey implements CQueryPlanKey {
|
||||
if (bindCount != that.bindCount) return false;
|
||||
if (maxRows != that.maxRows) return false;
|
||||
if (firstRow != that.firstRow) return false;
|
||||
if (disableLazyLoading != that.disableLazyLoading) return false;
|
||||
if (distinct != that.distinct) return false;
|
||||
if (sqlDistinct != that.sqlDistinct) return false;
|
||||
if (forUpdate != that.forUpdate) return false;
|
||||
if (hasIdValue != that.hasIdValue) return false;
|
||||
if (type != that.type) return false;
|
||||
if (temporalMode != that.temporalMode) return false;
|
||||
if (m2mIncludeTable != null ? !m2mIncludeTable.equals(that.m2mIncludeTable) : that.m2mIncludeTable != null)
|
||||
return false;
|
||||
if (orderByAsSting != null ? !orderByAsSting.equals(that.orderByAsSting) : that.orderByAsSting != null)
|
||||
return false;
|
||||
if (!options.equals(that.options)) return false;
|
||||
if (where != null ? !where.isSameByPlan(that.where) : that.where != null) return false;
|
||||
if (having != null ? !having.isSameByPlan(that.having) : that.having != null) return false;
|
||||
if (updateProperties != null ? !updateProperties.isSameByPlan(that.updateProperties) : that.updateProperties != null)
|
||||
return false;
|
||||
if (rawSqlKey != null ? !rawSqlKey.equals(that.rawSqlKey) : that.rawSqlKey != null) return false;
|
||||
if (mapKey != null ? !mapKey.equals(that.mapKey) : that.mapKey != null) return false;
|
||||
return rootTableAlias != null ? rootTableAlias.equals(that.rootTableAlias) : that.rootTableAlias == null;
|
||||
if (updateProperties != null ? !updateProperties.isSameByPlan(that.updateProperties) : that.updateProperties != null) return false;
|
||||
return rawSqlKey != null ? rawSqlKey.equals(that.rawSqlKey) : that.rawSqlKey == null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.RollbackException;
|
||||
import java.sql.Connection;
|
||||
|
||||
/**
|
||||
@@ -43,7 +42,7 @@ public class ExternalJdbcTransaction extends JdbcTransaction {
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public void commit() throws RollbackException {
|
||||
public void commit() {
|
||||
throw new PersistenceException("This is an external transaction so must be committed externally");
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import io.ebeaninternal.server.core.PersistRequest;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.lib.util.Str;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeaninternal.server.persist.BatchedSqlException;
|
||||
import io.ebeanservice.docstore.api.DocStoreTransaction;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -620,28 +621,45 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
@Override
|
||||
public void flushBatchOnCollection() {
|
||||
if (batchOnCascadeSet) {
|
||||
if (batchControl != null) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("... flushBatchOnCollection");
|
||||
}
|
||||
batchControl.flushReset();
|
||||
}
|
||||
batchFlushReset();
|
||||
// restore the previous batch mode of NONE
|
||||
batchMode = PersistBatch.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
private void batchFlush() {
|
||||
if (batchControl != null) {
|
||||
try {
|
||||
batchControl.flush();
|
||||
} catch (BatchedSqlException e) {
|
||||
throw translate(e.getMessage(), e.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void batchFlushReset() {
|
||||
if (batchControl != null) {
|
||||
try {
|
||||
batchControl.flushReset();
|
||||
} catch (BatchedSqlException e) {
|
||||
throw translate(e.getMessage(), e.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PersistenceException translate(String message, SQLException cause) {
|
||||
if (manager != null) {
|
||||
return manager.translate(message, cause);
|
||||
}
|
||||
return new PersistenceException(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush after completing persist cascade.
|
||||
*/
|
||||
@Override
|
||||
public void flushBatchOnCascade() {
|
||||
if (batchControl != null) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("... flushBatchOnCascade");
|
||||
}
|
||||
batchControl.flushReset();
|
||||
}
|
||||
batchFlushReset();
|
||||
// restore the previous batch mode
|
||||
batchMode = oldBatchMode;
|
||||
}
|
||||
@@ -670,21 +688,13 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
// escalate up to batch mode for this request (and cascade)
|
||||
oldBatchMode = batchMode;
|
||||
batchMode = PersistBatch.ALL;
|
||||
if (batchControl != null) {
|
||||
// flush with reset so that this request goes into it's own batch buffer
|
||||
batchControl.flushReset();
|
||||
}
|
||||
batchFlushReset();
|
||||
// skip using jdbc batch for the top level bean (no gain there)
|
||||
request.setSkipBatchForTopLevel();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (batchControl != null && !batchControl.isEmpty()) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("... flush from batchOnCascade ");
|
||||
}
|
||||
batchControl.flushReset();
|
||||
}
|
||||
batchFlushReset();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -721,20 +731,23 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public void flushBatch() {
|
||||
public void flush() {
|
||||
if (!isActive()) {
|
||||
throw new IllegalStateException(illegalStateMessage);
|
||||
}
|
||||
internalBatchFlush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushBatch() {
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the JDBC batch and execute derived relationship statements if necessary.
|
||||
*/
|
||||
private void internalBatchFlush() {
|
||||
if (batchControl != null) {
|
||||
batchControl.flush();
|
||||
}
|
||||
batchFlush();
|
||||
if (deferredList != null) {
|
||||
for (PersistDeferredRelationship deferred : deferredList) {
|
||||
deferred.execute(this);
|
||||
@@ -935,7 +948,7 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public void commitAndContinue() throws RollbackException {
|
||||
public void commitAndContinue() {
|
||||
if (rollbackOnly) {
|
||||
return;
|
||||
}
|
||||
@@ -951,7 +964,7 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
|
||||
} catch (Exception e) {
|
||||
doRollback(e);
|
||||
throw new RollbackException(e);
|
||||
throw wrapIfNeeded(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -959,7 +972,7 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
* Commit the transaction.
|
||||
*/
|
||||
@Override
|
||||
public void commit() throws RollbackException {
|
||||
public void commit() {
|
||||
if (rollbackOnly) {
|
||||
rollback();
|
||||
return;
|
||||
@@ -976,13 +989,24 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
|
||||
} catch (Exception e) {
|
||||
doRollback(e);
|
||||
throw new RollbackException(e);
|
||||
throw wrapIfNeeded(e);
|
||||
|
||||
} finally {
|
||||
deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to keep specific exceptions and otherwise wrap as RollbackException.
|
||||
*/
|
||||
private RuntimeException wrapIfNeeded(Exception e) {
|
||||
if (e instanceof PersistenceException) {
|
||||
// keep more specific exception if we have it
|
||||
return (PersistenceException)e;
|
||||
}
|
||||
return new RollbackException(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the transaction manager.
|
||||
*/
|
||||
@@ -1125,11 +1149,7 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
* Alias for end(), which enables this class to be used in try-with-resources.
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
end();
|
||||
} catch (PersistenceException ex) {
|
||||
throw new IOException(ex);
|
||||
}
|
||||
public void close() {
|
||||
end();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.transaction;
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.config.CurrentTenantProvider;
|
||||
import io.ebean.PersistBatch;
|
||||
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;
|
||||
@@ -19,8 +20,10 @@ import io.ebeanservice.docstore.api.DocStoreUpdates;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -100,11 +103,14 @@ public class TransactionManager {
|
||||
|
||||
private final TransactionFactory transactionFactory;
|
||||
|
||||
private final DatabasePlatform databasePlatform;
|
||||
|
||||
/**
|
||||
* Create the TransactionManager
|
||||
*/
|
||||
public TransactionManager(TransactionManagerOptions options) {
|
||||
|
||||
this.databasePlatform = options.config.getDatabasePlatform();
|
||||
this.skipCacheAfterWrite = options.config.isSkipCacheAfterWrite();
|
||||
this.localL2Caching = options.localL2Caching;
|
||||
this.persistBatch = options.config.getPersistBatch();
|
||||
@@ -132,6 +138,13 @@ public class TransactionManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate the SQLException into a specific exception if possible based on the DB platform.
|
||||
*/
|
||||
public PersistenceException translate(String message, SQLException cause) {
|
||||
return databasePlatform.translate(message, cause);
|
||||
}
|
||||
|
||||
public void shutdown(boolean shutdownDataSource, boolean deregisterDriver) {
|
||||
if (shutdownDataSource) {
|
||||
dataSourceSupplier.shutdown(deregisterDriver);
|
||||
|
||||
@@ -165,7 +165,8 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
*/
|
||||
private ScalarType<?> jsonNodeJsonb;
|
||||
|
||||
private final PlatformArrayTypeFactory arrayTypeFactory;
|
||||
private final PlatformArrayTypeFactory arrayTypeListFactory;
|
||||
private final PlatformArrayTypeFactory arrayTypeSetFactory;
|
||||
|
||||
/**
|
||||
* Create the DefaultTypeManager.
|
||||
@@ -182,7 +183,8 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
this.extraTypeFactory = new DefaultTypeFactory(config);
|
||||
this.postgres = isPostgres(config.getDatabasePlatform());
|
||||
this.arrayTypeFactory = arrayTypeFactory(postgres, config.getDatabasePlatform());
|
||||
this.arrayTypeListFactory = arrayTypeListFactory(postgres, config.getDatabasePlatform());
|
||||
this.arrayTypeSetFactory = arrayTypeSetFactory(postgres, config.getDatabasePlatform());
|
||||
|
||||
this.offlineMigrationGeneration = DbOffline.isGenerateMigration();
|
||||
|
||||
@@ -203,7 +205,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
/**
|
||||
* Return the factory to use to support DB ARRAY types.
|
||||
*/
|
||||
private PlatformArrayTypeFactory arrayTypeFactory(boolean postgres, DatabasePlatform databasePlatform) {
|
||||
private PlatformArrayTypeFactory arrayTypeListFactory(boolean postgres, DatabasePlatform databasePlatform) {
|
||||
if (postgres) {
|
||||
return ScalarTypeArrayList.factory();
|
||||
} else if (databasePlatform.isPlatform(Platform.H2)) {
|
||||
@@ -213,6 +215,19 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the factory to use to support DB ARRAY types.
|
||||
*/
|
||||
private PlatformArrayTypeFactory arrayTypeSetFactory(boolean postgres, DatabasePlatform databasePlatform) {
|
||||
if (postgres) {
|
||||
return ScalarTypeArraySet.factory();
|
||||
} else if (databasePlatform.isPlatform(Platform.H2)) {
|
||||
return ScalarTypeArraySetH2.factory();
|
||||
}
|
||||
// not supported for this DB platform
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load custom scalar types registered via ExtraTypeFactory and ServiceLoader.
|
||||
*/
|
||||
@@ -318,13 +333,19 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
@Override
|
||||
public ScalarType<?> getArrayScalarType(Class<?> type, DbArray dbArray, Type genericType) {
|
||||
|
||||
Type valueType = getValueType(genericType);
|
||||
if (type.equals(List.class)) {
|
||||
if (arrayTypeFactory != null) {
|
||||
Type valueType = getValueType(genericType);
|
||||
return arrayTypeFactory.typeFor(valueType);
|
||||
if (arrayTypeListFactory != null) {
|
||||
return arrayTypeListFactory.typeFor(valueType);
|
||||
}
|
||||
// fallback to JSON storage in VARCHAR column
|
||||
return new ScalarTypeJsonList.Varchar(getDocType(getValueType(genericType)));
|
||||
return new ScalarTypeJsonList.Varchar(getDocType(valueType));
|
||||
} else if (type.equals(Set.class)) {
|
||||
if (arrayTypeSetFactory != null) {
|
||||
return arrayTypeSetFactory.typeFor(valueType);
|
||||
}
|
||||
// fallback to JSON storage in VARCHAR column
|
||||
return new ScalarTypeJsonSet.Varchar(getDocType(valueType));
|
||||
}
|
||||
throw new IllegalStateException("Type [" + type + "] not supported for @DbArray");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
/**
|
||||
* DB Array types.
|
||||
*/
|
||||
public interface ScalarTypeArray {
|
||||
|
||||
/**
|
||||
* Return the underlying DB column type.
|
||||
*/
|
||||
String getDbColumnDefn();
|
||||
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import java.util.UUID;
|
||||
/**
|
||||
* Type mapped for DB ARRAY type (Postgres only effectively).
|
||||
*/
|
||||
public class ScalarTypeArrayList extends ScalarTypeJsonCollection<List> {
|
||||
public class ScalarTypeArrayList extends ScalarTypeJsonCollection<List> implements ScalarTypeArray {
|
||||
|
||||
private static ScalarTypeArrayList UUID = new ScalarTypeArrayList("uuid", DocPropertyType.UUID, ArrayElementConverter.UUID);
|
||||
private static ScalarTypeArrayList LONG = new ScalarTypeArrayList("bigint", DocPropertyType.LONG, ArrayElementConverter.LONG);
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.ebean.text.json.EJson;
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.sql.Array;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Type mapped for DB ARRAY type (Postgres only effectively).
|
||||
*/
|
||||
public class ScalarTypeArraySet extends ScalarTypeJsonCollection<Set> implements ScalarTypeArray {
|
||||
|
||||
private static ScalarTypeArraySet UUID = new ScalarTypeArraySet("uuid", DocPropertyType.UUID, ArrayElementConverter.UUID);
|
||||
private static ScalarTypeArraySet LONG = new ScalarTypeArraySet("bigint", DocPropertyType.LONG, ArrayElementConverter.LONG);
|
||||
private static ScalarTypeArraySet INTEGER = new ScalarTypeArraySet("integer", DocPropertyType.INTEGER, ArrayElementConverter.INTEGER);
|
||||
private static ScalarTypeArraySet DOUBLE = new ScalarTypeArraySet("float", DocPropertyType.DOUBLE, ArrayElementConverter.DOUBLE);
|
||||
private static ScalarTypeArraySet STRING = new ScalarTypeArraySet("varchar", DocPropertyType.TEXT, ArrayElementConverter.STRING);
|
||||
|
||||
static PlatformArrayTypeFactory factory() {
|
||||
return new Factory();
|
||||
}
|
||||
|
||||
static class Factory implements PlatformArrayTypeFactory {
|
||||
|
||||
/**
|
||||
* Return the ScalarType to use based on the List's generic parameter type.
|
||||
*/
|
||||
@Override
|
||||
public ScalarTypeArraySet typeFor(Type valueType) {
|
||||
if (valueType.equals(UUID.class)) {
|
||||
return UUID;
|
||||
}
|
||||
if (valueType.equals(Long.class)) {
|
||||
return LONG;
|
||||
}
|
||||
if (valueType.equals(Integer.class)) {
|
||||
return INTEGER;
|
||||
}
|
||||
if (valueType.equals(Double.class)) {
|
||||
return DOUBLE;
|
||||
}
|
||||
if (valueType.equals(String.class)) {
|
||||
return STRING;
|
||||
}
|
||||
throw new IllegalArgumentException("Type [" + valueType + "] not supported for @DbArray mapping on set");
|
||||
}
|
||||
}
|
||||
|
||||
private final String arrayType;
|
||||
|
||||
private final ArrayElementConverter converter;
|
||||
|
||||
public ScalarTypeArraySet(String arrayType, DocPropertyType docPropertyType, ArrayElementConverter converter) {
|
||||
super(Set.class, Types.ARRAY, docPropertyType);
|
||||
this.arrayType = arrayType;
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocPropertyType getDocType() {
|
||||
return docPropertyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DB column definition for DDL generation.
|
||||
*/
|
||||
public String getDbColumnDefn() {
|
||||
return arrayType + "[]";
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Set fromArray(Object[] array1) {
|
||||
Set set = new LinkedHashSet();
|
||||
for (Object element : array1) {
|
||||
set.add(converter.toElement(element));
|
||||
}
|
||||
return new ModifyAwareSet(set);
|
||||
}
|
||||
|
||||
protected Object[] toArray(Set value) {
|
||||
return value.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set read(DataReader reader) throws SQLException {
|
||||
Array array = reader.getArray();
|
||||
if (array == null) {
|
||||
return null;
|
||||
} else {
|
||||
return fromArray((Object[]) array.getArray());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBind bind, Set value) throws SQLException {
|
||||
if (value == null) {
|
||||
bind.setNull(Types.ARRAY);
|
||||
} else {
|
||||
bind.setArray(arrayType, toArray(value));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(Set value) {
|
||||
try {
|
||||
return EJson.write(value);
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException("Failed to format List into JSON content", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set parse(String value) {
|
||||
try {
|
||||
return EJson.parseSet(value, false);
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException("Failed to parse JSON content as List: [" + value + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set jsonRead(JsonParser parser) throws IOException {
|
||||
return EJson.parseSet(parser, parser.getCurrentToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, Set value) throws IOException {
|
||||
EJson.write(value, writer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* H2 database support for DB ARRAY.
|
||||
*/
|
||||
class ScalarTypeArraySetH2 extends ScalarTypeArraySet {
|
||||
|
||||
private static ScalarTypeArraySetH2 UUID = new ScalarTypeArraySetH2("uuid", DocPropertyType.UUID, ArrayElementConverter.UUID);
|
||||
private static ScalarTypeArraySetH2 LONG = new ScalarTypeArraySetH2("bigint", DocPropertyType.LONG, ArrayElementConverter.LONG);
|
||||
private static ScalarTypeArraySetH2 INTEGER = new ScalarTypeArraySetH2("integer", DocPropertyType.INTEGER, ArrayElementConverter.INTEGER);
|
||||
private static ScalarTypeArraySetH2 DOUBLE = new ScalarTypeArraySetH2("double", DocPropertyType.DOUBLE, ArrayElementConverter.DOUBLE);
|
||||
private static ScalarTypeArraySetH2 STRING = new ScalarTypeArraySetH2("varchar", DocPropertyType.TEXT, ArrayElementConverter.STRING);
|
||||
|
||||
static PlatformArrayTypeFactory factory() {
|
||||
return new ScalarTypeArraySetH2.Factory();
|
||||
}
|
||||
|
||||
static class Factory implements PlatformArrayTypeFactory {
|
||||
|
||||
/**
|
||||
* Return the ScalarType to use based on the List's generic parameter type.
|
||||
*/
|
||||
@Override
|
||||
public ScalarTypeArraySetH2 typeFor(Type valueType) {
|
||||
if (valueType.equals(java.util.UUID.class)) {
|
||||
return UUID;
|
||||
}
|
||||
if (valueType.equals(Integer.class)) {
|
||||
return INTEGER;
|
||||
}
|
||||
if (valueType.equals(Long.class)) {
|
||||
return LONG;
|
||||
}
|
||||
if (valueType.equals(Double.class)) {
|
||||
return DOUBLE;
|
||||
}
|
||||
if (valueType.equals(String.class)) {
|
||||
return STRING;
|
||||
}
|
||||
throw new IllegalArgumentException("Type [" + valueType + "] not supported for @DbArray mapping");
|
||||
}
|
||||
}
|
||||
|
||||
private ScalarTypeArraySetH2(String arrayType, DocPropertyType docPropertyType, ArrayElementConverter converter) {
|
||||
super(arrayType, docPropertyType, converter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDbColumnDefn() {
|
||||
return "array";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBind bind, Set value) throws SQLException {
|
||||
if (value == null) {
|
||||
bind.setNull(Types.ARRAY);
|
||||
} else {
|
||||
bind.setObject(toArray(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ public class ScalarTypeJsonSet {
|
||||
/**
|
||||
* List mapped to DB VARCHAR.
|
||||
*/
|
||||
private static class Varchar extends ScalarTypeJsonSet.Base {
|
||||
public static class Varchar extends ScalarTypeJsonSet.Base {
|
||||
public Varchar(DocPropertyType docPropertyType) {
|
||||
super(Types.VARCHAR, docPropertyType);
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ fetch.bind.error=error binding parameter [{0}][{1}]
|
||||
fetch.bind.datatype=Datatype [{0}] not handled for parameter [{1}][{2}]
|
||||
fetch.bind.datatype2=Datatype for [{0}] not handled.
|
||||
|
||||
fetch.sqlerror=Query threw SQLException:{0} \r\nBind values:[{1}] \r\nQuery was:\r\n{2} \r\n\r\n
|
||||
fetch.error=Query threw SQLException:{0} Query was:\r\n{1}\r\n\r\n
|
||||
fetch.sqlerror=Query threw SQLException:{0} Bind values:[{1}] Query was:{2}
|
||||
fetch.error=Query threw SQLException:{0} Query was:{1}
|
||||
fetch.limit.orderby=You must specify an OrderBy if limiting the ResultSet
|
||||
|
||||
fetch.many.depth=Many property [{0}] can not be included due to the wrong depth.
|
||||
|
||||
@@ -460,6 +460,11 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commitTransaction() {
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.querydefn;
|
||||
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import org.tests.model.basic.Customer;
|
||||
import org.tests.model.basic.Order;
|
||||
@@ -8,7 +9,7 @@ import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class DefaultOrmQueryTest {
|
||||
public class DefaultOrmQueryTest extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void when_forUpdate_then_excludeFromBeanCache() {
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.querydefn;
|
||||
|
||||
|
||||
import io.ebean.OrderBy;
|
||||
import io.ebean.Query;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionList;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
@@ -21,8 +22,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
@Test
|
||||
public void equals_when_defaults() {
|
||||
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
|
||||
assertSame(key1, key2);
|
||||
}
|
||||
@@ -32,8 +33,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
|
||||
TableJoin tableJoin = tableJoin("table", "id", "customer_id");
|
||||
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(tableJoin, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(tableJoin, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
@@ -44,8 +45,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
TableJoin tableJoin1 = tableJoin("one", "id", "customer_id");
|
||||
TableJoin tableJoin2 = tableJoin("two", "id", "customer_id");
|
||||
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(tableJoin1, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(tableJoin2, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(tableJoin1, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(tableJoin2, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
@@ -56,8 +57,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
TableJoin tableJoin1 = tableJoin("one", "id", "customer_id");
|
||||
TableJoin tableJoin2 = tableJoin("one", "id", "customer_id");
|
||||
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(tableJoin1, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(tableJoin2, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(tableJoin1, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(tableJoin2, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
|
||||
assertSame(key1, key2);
|
||||
}
|
||||
@@ -73,8 +74,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
@Test
|
||||
public void equals_when_diffQueryType() {
|
||||
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.LIST, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.LIST, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
@@ -82,8 +83,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
@Test
|
||||
public void equals_when_firstRowsDifferent() {
|
||||
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 10, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 10, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
@@ -91,8 +92,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
@Test
|
||||
public void equals_when_maxRowsDifferent() {
|
||||
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 10, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 10, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
@@ -100,8 +101,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
@Test
|
||||
public void equals_when_firstRowsMaxRowsSame() {
|
||||
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 10, 20, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 10, 20, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 10, 20, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 10, 20, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
|
||||
assertSame(key1, key2);
|
||||
}
|
||||
@@ -109,8 +110,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
@Test
|
||||
public void equals_when_diffDisableLazyLoading() {
|
||||
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, true, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, true, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
@@ -119,8 +120,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
public void equals_when_diffOrderByNull() {
|
||||
|
||||
OrderBy<Object> o1 = new OrderBy<>("id");
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, o1, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, o1, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
@@ -130,107 +131,121 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
|
||||
OrderBy<Object> o1 = new OrderBy<>("id, name");
|
||||
OrderBy<Object> o2 = new OrderBy<>("id, name");
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, o1, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, o2, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, o1, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, o2, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
|
||||
assertSame(key1, key2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffDistinct() {
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, true, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, true, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_sameDistinct() {
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, true, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, true, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, true, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, true, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertSame(key1, key2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffSqlDistinct() {
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, true, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, true, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_sameSqlDistinct() {
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, true, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, true, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, true, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, true, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertSame(key1, key2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffMapKeyNull() {
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, "mapKey", null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, "mapKey", null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffMapKey() {
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, "mapKey", null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, "diff", null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, "mapKey", null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, "diff", null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_sameMapKey() {
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, "mapKey", null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, "mapKey", null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, "mapKey", null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, "mapKey", null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertSame(key1, key2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffIdNull() {
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, 42, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, 42, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_idBothGiven() {
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, 42, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, 23, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, 42, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, 23, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertSame(key1, key2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffTemporalMode() {
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.DRAFT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.DRAFT, null, null, null, null);
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffForUpdate() {
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, true, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, Query.ForUpdate.BASE, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffForUpdate_NoWait() {
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, Query.ForUpdate.BASE, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, Query.ForUpdate.NOWAIT, null, null, null);
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffForUpdate_SkipLocked() {
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, Query.ForUpdate.BASE, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, Query.ForUpdate.SKIPLOCKED, null, null, null);
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffRootAliasNull() {
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, "rootAlias", null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, "rootAlias", null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffRootAlias() {
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, "rootAlias", null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, "diff", null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, "rootAlias", null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, "diff", null, null);
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_sameRootAlias() {
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, "rootAlias", null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, "rootAlias", null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, "rootAlias", null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, "rootAlias", null, null);
|
||||
assertSame(key1, key2);
|
||||
}
|
||||
|
||||
@@ -257,8 +272,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
SpiExpressionList<Customer> list1 = list_id_eq_42();
|
||||
SpiExpressionList<Customer> list2 = list_id_eq_43();
|
||||
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, list1, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, list2, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, list1, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, list2, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertSame(key1, key2);
|
||||
}
|
||||
|
||||
@@ -269,8 +284,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
SpiExpressionList<Customer> where1 = list_id_eq_42();
|
||||
SpiExpressionList<Customer> where2 = list_id_eq_42_and_name_eq_rob();
|
||||
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, where1, null, SpiQuery.TemporalMode.DRAFT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, where2, null, SpiQuery.TemporalMode.DRAFT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, where1, null, SpiQuery.TemporalMode.DRAFT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, where2, null, SpiQuery.TemporalMode.DRAFT, null, null, null, null);
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
|
||||
@@ -279,8 +294,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
|
||||
SpiExpressionList<Customer> list1 = list_id_eq_42();
|
||||
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, list1, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, list1, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
|
||||
@@ -289,8 +304,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
|
||||
SpiExpressionList<Customer> list1 = list_id_eq_42();
|
||||
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, list1, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, list1, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
|
||||
@@ -300,8 +315,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
SpiExpression having1 = list_id_eq_42().copyForPlanKey();
|
||||
SpiExpression having2 = list_id_eq_42_and_name_eq_rob().copyForPlanKey();
|
||||
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, having1, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, having2, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, having1, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, having2, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
|
||||
@@ -311,8 +326,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
SpiExpression having1 = list_id_eq_42().copyForPlanKey();
|
||||
SpiExpression having2 = list_id_eq_42().copyForPlanKey();
|
||||
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, having1, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, having2, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, having1, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, having2, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertSame(key1, key2);
|
||||
}
|
||||
|
||||
@@ -321,8 +336,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
|
||||
SpiExpression having1 = list_id_eq_42().copyForPlanKey();
|
||||
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, having1, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, having1, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
|
||||
@@ -331,8 +346,8 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
|
||||
SpiExpression having1 = list_id_eq_42().copyForPlanKey();
|
||||
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, having1, SpiQuery.TemporalMode.CURRENT, false, null, null, null);
|
||||
OrmQueryPlanKey key1 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, null, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
OrmQueryPlanKey key2 = new OrmQueryPlanKey(null, SpiQuery.Type.BEAN, null, 0, 0, false, null, false, false, null, null, null, null, having1, SpiQuery.TemporalMode.CURRENT, null, null, null, null);
|
||||
assertDifferent(key1, key2);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
package org.tests.basic;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.Query;
|
||||
import org.tests.model.basic.Order;
|
||||
import org.tests.model.basic.ResetBasicData;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestQuery extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void testCountOrderBy() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class).setAutoTune(false).order().asc("orderDate")
|
||||
.order().desc("id");
|
||||
// .orderBy("orderDate");
|
||||
|
||||
int rc = query.findList().size();
|
||||
Assert.assertTrue(rc > 0);
|
||||
// String generatedSql = query.getGeneratedSql();
|
||||
// Assert.assertFalse(generatedSql.contains("order by"));
|
||||
|
||||
}
|
||||
|
||||
public void testForUpdate() {
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class).setAutoTune(false).setForUpdate(false)
|
||||
.setMaxRows(1).order().asc("orderDate").order().desc("id");
|
||||
|
||||
int rc = query.findList().size();
|
||||
Assert.assertTrue(rc > 0);
|
||||
Assert.assertTrue(!query.getGeneratedSql().toLowerCase().contains("for update"));
|
||||
|
||||
query = Ebean.find(Order.class).setAutoTune(false).setForUpdate(true).setMaxRows(1).order()
|
||||
.asc("orderDate").order().desc("id");
|
||||
|
||||
rc = query.findList().size();
|
||||
Assert.assertTrue(rc > 0);
|
||||
Assert.assertTrue(query.getGeneratedSql().toLowerCase().contains("for update"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package org.tests.basic;
|
||||
|
||||
import io.ebean.AcquireLockException;
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.Transaction;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Customer;
|
||||
import org.tests.model.basic.ResetBasicData;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class TestQueryForUpdate extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void testForUpdate() {
|
||||
|
||||
if (isH2() || isPostgres()) {
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Customer> query = Ebean.find(Customer.class)
|
||||
.forUpdate()
|
||||
.setMaxRows(1)
|
||||
.order().desc("id");
|
||||
|
||||
query.findList();
|
||||
assertThat(sqlOf(query)).contains("for update");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForUpdate_noWait() {
|
||||
|
||||
if (isPostgres() || isOracle()) {
|
||||
ResetBasicData.reset();
|
||||
|
||||
EbeanServer server = Ebean.getDefaultServer();
|
||||
|
||||
Ebean.beginTransaction();
|
||||
try {
|
||||
Query<Customer> query = Ebean.find(Customer.class)
|
||||
.forUpdateNoWait()
|
||||
.setMaxRows(1)
|
||||
.order().desc("id");
|
||||
|
||||
List<Customer> list = query.findList();
|
||||
Customer first = list.get(0);
|
||||
|
||||
assertThat(sqlOf(query)).contains("for update nowait");
|
||||
|
||||
// create a 2nd transaction to test that the
|
||||
// row is locked and we can't acquire it
|
||||
Transaction txn2 = server.createTransaction();
|
||||
try {
|
||||
logger.info("... attempt another acquire using 2nd transaction");
|
||||
Query<Customer> query2 =
|
||||
server.find(Customer.class)
|
||||
.where().idEq(first.getId())
|
||||
.forUpdateNoWait();
|
||||
|
||||
server.findUnique(query2, txn2);
|
||||
assertTrue(false); // never get here
|
||||
} catch (AcquireLockException e) {
|
||||
logger.info("... got AcquireLockException " + e);
|
||||
} finally {
|
||||
txn2.end();
|
||||
}
|
||||
|
||||
} finally {
|
||||
Ebean.endTransaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.tests.insert;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.DataIntegrityException;
|
||||
import io.ebean.Ebean;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Customer;
|
||||
import org.tests.model.basic.Order;
|
||||
import org.tests.model.basic.ResetBasicData;
|
||||
|
||||
public class TestInsertDataIntegrityException extends BaseTestCase {
|
||||
|
||||
|
||||
@Test(expected = DataIntegrityException.class)
|
||||
public void insert_invalidForeignKey() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
// an invalid foreign key value
|
||||
Customer invalidCustomer = Ebean.getReference(Customer.class, 900000);
|
||||
|
||||
Order order = new Order();
|
||||
order.setStatus(Order.Status.NEW);
|
||||
order.setCustomer(invalidCustomer);
|
||||
|
||||
Ebean.save(order);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package org.tests.insert;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.DuplicateKeyException;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.annotation.Transactional;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tests.model.draftable.Document;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestInsertDuplicateKey extends BaseTestCase {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(TestInsertDuplicateKey.class);
|
||||
|
||||
@Test(expected = DuplicateKeyException.class)
|
||||
public void insert_duplicateKey() {
|
||||
|
||||
Document doc1 = new Document();
|
||||
doc1.setTitle("ThisIsAUniqueKey");
|
||||
doc1.setBody("one");
|
||||
|
||||
doc1.save();
|
||||
|
||||
Document doc2 = new Document();
|
||||
doc2.setTitle("ThisIsAUniqueKey");
|
||||
doc2.setBody("clashes with doc1");
|
||||
|
||||
doc2.save();
|
||||
}
|
||||
|
||||
@Transactional(batchSize = 100)
|
||||
@Test(expected = DuplicateKeyException.class)
|
||||
public void insertBatch_duplicateKey() {
|
||||
|
||||
Document doc1 = new Document();
|
||||
doc1.setTitle("ThisIsASecondUniqueKey");
|
||||
doc1.setBody("one");
|
||||
|
||||
doc1.save();
|
||||
|
||||
Document doc2 = new Document();
|
||||
doc2.setTitle("ThisIsASecondUniqueKey");
|
||||
doc2.setBody("clash when batch flushed");
|
||||
|
||||
doc2.save();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertBatch_duplicateKey_catchAndContinue() {
|
||||
|
||||
insertTheBatch_duplicateKey_catchAndContinue();
|
||||
|
||||
List<Document> found = Ebean.getDefaultServer()
|
||||
.find(Document.class)
|
||||
.asDraft()
|
||||
.where().startsWith("body", "insertTheBatch_duplicateKey_catchAndContinue")
|
||||
.findList();
|
||||
|
||||
assertThat(found).hasSize(1);
|
||||
assertThat(found.get(0).getTitle()).isEqualTo("ThisIsAThirdUniqueKey");
|
||||
assertThat(found.get(0).getBody()).isEqualTo("insertTheBatch_duplicateKey_catchAndContinue-1");
|
||||
}
|
||||
|
||||
@Transactional//(batchSize = 100)
|
||||
private void insertTheBatch_duplicateKey_catchAndContinue() {
|
||||
|
||||
Document doc1 = new Document();
|
||||
doc1.setTitle("ThisIsAThirdUniqueKey");
|
||||
doc1.setBody("insertTheBatch_duplicateKey_catchAndContinue-1");
|
||||
doc1.save();
|
||||
|
||||
try {
|
||||
Document doc2 = new Document();
|
||||
doc2.setTitle("ThisIsAThirdUniqueKey");
|
||||
doc2.setBody("insertTheBatch_duplicateKey_catchAndContinue-2");
|
||||
doc2.save();
|
||||
|
||||
// flush at this point, fails
|
||||
Ebean.getDefaultServer().currentTransaction().flushBatch();
|
||||
} catch (DuplicateKeyException e) {
|
||||
log.info("duplicate failed but just continue" + e.getMessage());
|
||||
try {
|
||||
// typically we would use transaction.commitAndContinue()
|
||||
// ... this is a rollback and continue type scenario
|
||||
// ... more sensible to use a second transaction that do this
|
||||
Ebean.getDefaultServer().currentTransaction().getConnection().rollback();
|
||||
} catch (SQLException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
Document doc0 = new Document();
|
||||
doc0.setTitle("ThisIsAThirdUniqueKey");
|
||||
doc0.setBody("insertTheBatch_duplicateKey_catchAndContinue-1");
|
||||
doc0.save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package org.tests.model.array;
|
||||
|
||||
|
||||
import io.ebean.annotation.DbArray;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
public class EArraySetBean {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
String name;
|
||||
|
||||
@DbArray(length = 300)
|
||||
Set<String> phoneNumbers = new LinkedHashSet<>();
|
||||
|
||||
@DbArray
|
||||
Set<UUID> uids = new LinkedHashSet<>();
|
||||
|
||||
@DbArray
|
||||
Set<Long> otherIds = new LinkedHashSet<>();
|
||||
|
||||
@DbArray
|
||||
Set<Double> doubs;
|
||||
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<String> getPhoneNumbers() {
|
||||
return phoneNumbers;
|
||||
}
|
||||
|
||||
public void setPhoneNumbers(Set<String> phoneNumbers) {
|
||||
this.phoneNumbers = phoneNumbers;
|
||||
}
|
||||
|
||||
public Set<UUID> getUids() {
|
||||
return uids;
|
||||
}
|
||||
|
||||
public void setUids(Set<UUID> uids) {
|
||||
this.uids = uids;
|
||||
}
|
||||
|
||||
public Set<Long> getOtherIds() {
|
||||
return otherIds;
|
||||
}
|
||||
|
||||
public void setOtherIds(Set<Long> otherIds) {
|
||||
this.otherIds = otherIds;
|
||||
}
|
||||
|
||||
public Set<Double> getDoubs() {
|
||||
return doubs;
|
||||
}
|
||||
|
||||
public void setDoubs(Set<Double> doubs) {
|
||||
this.doubs = doubs;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package org.tests.model.array;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.Query;
|
||||
import org.avaje.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class TestDbArray_asSet extends BaseTestCase {
|
||||
|
||||
private EArraySetBean bean = new EArraySetBean();
|
||||
|
||||
private EArraySetBean found;
|
||||
|
||||
@Test
|
||||
public void insert() {
|
||||
|
||||
bean.setName("some stuff");
|
||||
|
||||
Set<String> phNumbers = bean.getPhoneNumbers();
|
||||
phNumbers.add("4321");
|
||||
phNumbers.add("9823");
|
||||
|
||||
|
||||
Set<Double> doubles = new LinkedHashSet<>();
|
||||
doubles.add(1.3);
|
||||
doubles.add(2.4);
|
||||
|
||||
UUID first = UUID.randomUUID();
|
||||
bean.getUids().add(first);
|
||||
bean.getUids().add(UUID.randomUUID());
|
||||
bean.getOtherIds().add(95L);
|
||||
bean.getOtherIds().add(96L);
|
||||
bean.getOtherIds().add(97L);
|
||||
bean.setDoubs(doubles);
|
||||
|
||||
Ebean.save(bean);
|
||||
|
||||
found = Ebean.find(EArraySetBean.class, bean.getId());
|
||||
|
||||
assertThat(found.getPhoneNumbers()).containsExactly("4321", "9823");
|
||||
|
||||
if (isPostgres()) {
|
||||
Query<EArraySetBean> query = Ebean.find(EArraySetBean.class)
|
||||
.where()
|
||||
.arrayContains("otherIds", 96L, 97L)
|
||||
.arrayContains("uids", first)
|
||||
.arrayContains("phoneNumbers", "9823")
|
||||
.arrayIsNotEmpty("phoneNumbers")
|
||||
.query();
|
||||
|
||||
List<EArraySetBean> list = query.findList();
|
||||
|
||||
assertThat(query.getGeneratedSql()).contains(" t0.other_ids @> array[?,?]::bigint[] ");
|
||||
assertThat(query.getGeneratedSql()).contains(" t0.uids @> array[?] ");
|
||||
assertThat(query.getGeneratedSql()).contains(" t0.phone_numbers @> array[?] ");
|
||||
assertThat(query.getGeneratedSql()).contains(" coalesce(cardinality(t0.phone_numbers),0) <> 0");
|
||||
assertThat(list).hasSize(1);
|
||||
|
||||
query = Ebean.find(EArraySetBean.class)
|
||||
.where()
|
||||
.arrayIsEmpty("otherIds")
|
||||
.arrayNotContains("uids", first)
|
||||
.query();
|
||||
query.findList();
|
||||
|
||||
assertThat(query.getGeneratedSql()).contains(" coalesce(cardinality(t0.other_ids),0) = 0");
|
||||
assertThat(query.getGeneratedSql()).contains(" not (t0.uids @> array[?])");
|
||||
}
|
||||
|
||||
json_parse_format();
|
||||
update_when_notDirty();
|
||||
update_when_dirty();
|
||||
}
|
||||
|
||||
//@Test//(dependsOnMethods = "insert")
|
||||
public void json_parse_format() {
|
||||
|
||||
String asJson = Ebean.json().toJson(found);
|
||||
assertThat(asJson).contains("\"phoneNumbers\":[\"4321\",\"9823\"]");
|
||||
assertThat(asJson).contains("\"id\":");
|
||||
|
||||
EArraySetBean fromJson = Ebean.json().toBean(EArraySetBean.class, asJson);
|
||||
assertEquals(found.getId(), fromJson.getId());
|
||||
assertEquals(found.getId(), fromJson.getId());
|
||||
assertEquals(found.getName(), fromJson.getName());
|
||||
assertThat(fromJson.getPhoneNumbers()).containsExactly("4321", "9823");
|
||||
}
|
||||
|
||||
//@Test//(dependsOnMethods = "insert")
|
||||
public void update_when_notDirty() {
|
||||
|
||||
found.setName("jack");
|
||||
LoggedSqlCollector.start();
|
||||
Ebean.save(found);
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
|
||||
// we don't update the phone numbers (as they are not dirty)
|
||||
assertThat(sql.get(0)).contains("update earray_set_bean set name=?, version=? where");
|
||||
}
|
||||
|
||||
//@Test//(dependsOnMethods = "update_when_notDirty")
|
||||
public void update_when_dirty() {
|
||||
|
||||
found.getPhoneNumbers().add("9987");
|
||||
found.getUids().add(UUID.randomUUID());
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
Ebean.save(found);
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
|
||||
assertThat(sql.get(0)).contains("update earray_set_bean set phone_numbers=?, uids=?, version=? where");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertNulls() {
|
||||
|
||||
EArraySetBean bean = new EArraySetBean();
|
||||
bean.setName("some nulls");
|
||||
bean.setPhoneNumbers(null);
|
||||
bean.setOtherIds(null);
|
||||
bean.setUids(null);
|
||||
|
||||
Ebean.save(bean);
|
||||
Ebean.delete(bean);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertAll_when_hasNulls() {
|
||||
|
||||
EArraySetBean bean = new EArraySetBean();
|
||||
bean.setName("some nulls");
|
||||
bean.setPhoneNumbers(null);
|
||||
bean.setOtherIds(null);
|
||||
bean.setUids(null);
|
||||
|
||||
Set<EArraySetBean> all = new HashSet<>();
|
||||
all.add(bean);
|
||||
|
||||
Ebean.saveAll(all);
|
||||
Ebean.deleteAll(all);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.tests.model.draftable;
|
||||
|
||||
import io.ebean.Finder;
|
||||
import io.ebean.annotation.DraftOnly;
|
||||
import io.ebean.annotation.Draftable;
|
||||
|
||||
@@ -15,6 +16,8 @@ import java.util.List;
|
||||
@Entity
|
||||
public class Document extends BaseDomain {
|
||||
|
||||
public static DocumentFinder find = new DocumentFinder();
|
||||
|
||||
@Column(unique = true)
|
||||
String title;
|
||||
|
||||
@@ -72,4 +75,14 @@ public class Document extends BaseDomain {
|
||||
public void setWhenPublish(Timestamp whenPublish) {
|
||||
this.whenPublish = whenPublish;
|
||||
}
|
||||
|
||||
public static class DocumentFinder extends Finder<Long,Document> {
|
||||
DocumentFinder() {
|
||||
super(Document.class);
|
||||
}
|
||||
|
||||
public Document asDraft(Long id) {
|
||||
return query().asDraft().setId(id).findUnique();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.tests.transaction;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.annotation.Transactional;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.m2m.MnyB;
|
||||
|
||||
public class TestBatchModelFlush extends BaseTestCase {
|
||||
|
||||
@Transactional(batchSize = 50)
|
||||
@Test
|
||||
public void insert() {
|
||||
|
||||
new MnyB("TestBatchModelFlush_0").save();
|
||||
new MnyB("TestBatchModelFlush_1").save();
|
||||
|
||||
MnyB bean = new MnyB("TestBatchModelFlush_2");
|
||||
bean.save();
|
||||
bean.db().currentTransaction().flush();
|
||||
|
||||
MnyB bean2 = new MnyB("TestBatchModelFlush_3");
|
||||
bean2.save();
|
||||
bean2.db().flush();
|
||||
|
||||
new MnyB("TestBatchModelFlush_4").save();
|
||||
Ebean.flush();
|
||||
|
||||
// the rest is flushed on commit
|
||||
new MnyB("TestBatchModelFlush_5").save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.tests.transaction;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.DuplicateKeyException;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.Transaction;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tests.model.draftable.Document;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestTransactionTryResources extends BaseTestCase {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(TestTransactionTryResources.class);
|
||||
|
||||
@Test
|
||||
public void tryWithResources_simple() {
|
||||
|
||||
Document doc = new Document();
|
||||
|
||||
try (Transaction transaction = Ebean.beginTransaction()) {
|
||||
|
||||
doc.setTitle("tryWithResources");
|
||||
doc.setBody("stuff");
|
||||
doc.save();
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
Document document = Document.find.asDraft(doc.getId());
|
||||
assertThat(document).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tryWithResources_catch() {
|
||||
|
||||
try (Transaction transaction = Ebean.beginTransaction()) {
|
||||
|
||||
Document doc = new Document();
|
||||
doc.setTitle("tryWithResources_catch");
|
||||
doc.setBody("tryWithResources_catch_1");
|
||||
doc.save();
|
||||
|
||||
Document doc2 = new Document();
|
||||
doc2.setTitle("tryWithResources_catch");
|
||||
doc2.setBody("tryWithResources_catch_2");
|
||||
doc2.save();
|
||||
|
||||
transaction.commit();
|
||||
|
||||
} catch (DuplicateKeyException e) {
|
||||
|
||||
log.info("catch duplicate ... " + e);
|
||||
|
||||
Document doc3 = new Document();
|
||||
doc3.setTitle("tryWithResources_catch");
|
||||
doc3.setBody("tryWithResources_catch_3");
|
||||
doc3.save();
|
||||
|
||||
List<Document> docs = Document.find.query()
|
||||
.where().startsWith("body", "tryWithResources_catch")
|
||||
.asDraft()
|
||||
.findList();
|
||||
|
||||
assertThat(docs).hasSize(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user