mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
60
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
705e4f4098 | ||
|
|
1642833fdb | ||
|
|
492bb98e0e | ||
|
|
a8456f90db | ||
|
|
103b75172d | ||
|
|
0baa3a0eac | ||
|
|
f26e4b5381 | ||
|
|
3d4de4dee5 | ||
|
|
c621435a28 | ||
|
|
7408483a43 | ||
|
|
4d15535381 | ||
|
|
787c2f90f2 | ||
|
|
e590a2149d | ||
|
|
8c77740aa7 | ||
|
|
29582b57f7 | ||
|
|
2763f333d4 | ||
|
|
8840628d34 | ||
|
|
6fb28d7ded | ||
|
|
be810aa568 | ||
|
|
8a698bd71e | ||
|
|
baa0594501 | ||
|
|
e82b8c80f0 | ||
|
|
f8e54be9a6 | ||
|
|
d3137be984 | ||
|
|
824cfa27be | ||
|
|
66cb6d5e7d | ||
|
|
8f432aa0a9 | ||
|
|
4aa4137abe | ||
|
|
f4bb4e1b6a | ||
|
|
7af4e166a4 | ||
|
|
1fa488d870 | ||
|
|
37a3a51b6b | ||
|
|
6a6e8f906f | ||
|
|
55bc498f9d | ||
|
|
4ee784c68f | ||
|
|
fc757c087a | ||
|
|
884dd939b7 | ||
|
|
6fd2f556db | ||
|
|
4e088a40d9 | ||
|
|
8ba66af878 | ||
|
|
441f51ed79 | ||
|
|
9ee891dc66 | ||
|
|
87ffcbf23e | ||
|
|
adfc54200f | ||
|
|
3c36b43449 | ||
|
|
9be1dcff6c | ||
|
|
37dff62fa9 | ||
|
|
4fd462b1c8 | ||
|
|
33ac1923ff | ||
|
|
0b87ae5eac | ||
|
|
678743cc10 | ||
|
|
c8345ce093 | ||
|
|
f3af43a9ce | ||
|
|
1df2ccb1c6 | ||
|
|
3add4472ef | ||
|
|
4613f63c66 | ||
|
|
612295fb9d | ||
|
|
4977aa2ad1 | ||
|
|
dd6a473008 | ||
|
|
ef1143bb70 |
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean api</name>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package io.ebean;
|
||||
|
||||
/**
|
||||
* Defines a cancelable query.
|
||||
* <p>
|
||||
* Typically holds a representation of the PreparedStatement to perform the
|
||||
* actual cancel.
|
||||
* </p>
|
||||
*/
|
||||
public interface CancelableQuery {
|
||||
|
||||
/**
|
||||
* Cancel the query.
|
||||
* <p>
|
||||
* For JDBC this translates to calling cancel on the PreparedStatement.
|
||||
* </p>
|
||||
*/
|
||||
void cancel();
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Query for performing native SQL queries that return DTO Bean's.
|
||||
@@ -37,7 +38,7 @@ import java.util.function.Predicate;
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public interface DtoQuery<T> {
|
||||
public interface DtoQuery<T> extends CancelableQuery {
|
||||
|
||||
/**
|
||||
* Execute the query returning a list.
|
||||
@@ -45,6 +46,26 @@ public interface DtoQuery<T> {
|
||||
@Nonnull
|
||||
List<T> findList();
|
||||
|
||||
/**
|
||||
* Execute the query iterating a row at a time.
|
||||
* <p>
|
||||
* Note that the QueryIterator holds resources related to the underlying
|
||||
* resultSet and potentially connection and MUST be closed. We should use
|
||||
* QueryIterator in a <em>try with resource block</em>.
|
||||
*/
|
||||
@Nonnull
|
||||
QueryIterator<T> findIterate();
|
||||
|
||||
/**
|
||||
* Execute the query returning a Stream.
|
||||
* <p>
|
||||
* Note that the Stream holds resources related to the underlying
|
||||
* resultSet and potentially connection and MUST be closed. We should use
|
||||
* the Stream in a <em>try with resource block</em>.
|
||||
*/
|
||||
@Nonnull
|
||||
Stream<T> findStream();
|
||||
|
||||
/**
|
||||
* Execute the query iterating a row at a time.
|
||||
* <p>
|
||||
|
||||
@@ -177,7 +177,7 @@ import java.util.stream.Stream;
|
||||
*
|
||||
* @param <T> the type of Entity bean this query will fetch.
|
||||
*/
|
||||
public interface Query<T> {
|
||||
public interface Query<T> extends CancelableQuery {
|
||||
|
||||
/**
|
||||
* The lock type (strength) to use with query FOR UPDATE row locking.
|
||||
@@ -291,15 +291,6 @@ public interface Query<T> {
|
||||
*/
|
||||
UpdateQuery<T> asUpdate();
|
||||
|
||||
/**
|
||||
* Cancel the query execution if supported by the underlying database and
|
||||
* driver.
|
||||
* <p>
|
||||
* This must be called from a different thread to the query executor.
|
||||
* </p>
|
||||
*/
|
||||
void cancel();
|
||||
|
||||
/**
|
||||
* Return a copy of the query.
|
||||
* <p>
|
||||
|
||||
@@ -56,7 +56,7 @@ import java.util.Iterator;
|
||||
*
|
||||
* @param <T> the type of entity bean in the iteration
|
||||
*/
|
||||
public interface QueryIterator<T> extends Iterator<T>, java.io.Closeable {
|
||||
public interface QueryIterator<T> extends Iterator<T>, AutoCloseable {
|
||||
|
||||
/**
|
||||
* Returns <tt>true</tt> if the iteration has more elements.
|
||||
@@ -70,12 +70,6 @@ public interface QueryIterator<T> extends Iterator<T>, java.io.Closeable {
|
||||
@Override
|
||||
T next();
|
||||
|
||||
/**
|
||||
* Remove is not allowed.
|
||||
*/
|
||||
@Override
|
||||
void remove();
|
||||
|
||||
/**
|
||||
* Close the underlying resources held by this iterator.
|
||||
*/
|
||||
|
||||
@@ -37,7 +37,7 @@ import java.util.function.Predicate;
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public interface SqlQuery extends Serializable {
|
||||
public interface SqlQuery extends Serializable, CancelableQuery {
|
||||
|
||||
/**
|
||||
* Execute the query returning a list.
|
||||
@@ -365,5 +365,10 @@ public interface SqlQuery extends Serializable {
|
||||
* Return the list of values.
|
||||
*/
|
||||
List<T> findList();
|
||||
|
||||
/**
|
||||
* Find streaming the result effectively consuming a row at a time.
|
||||
*/
|
||||
void findEach(Consumer<T> consumer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,10 @@ import java.util.Set;
|
||||
* from the Map Set or List. The purpose of gathering the additions and removals
|
||||
* is to support persisting ManyToMany objects. The additions and removals
|
||||
* become inserts and deletes from the intersection table.
|
||||
* </p>
|
||||
* <p>
|
||||
* Technically this is <em>NOT</em> an extension of
|
||||
* <em>java.util.Collection</em>. The reason being that java.util.Map is not a
|
||||
* Collection. I realise this makes this name confusing so I apologise for that.
|
||||
* </p>
|
||||
*/
|
||||
public interface BeanCollection<E> extends Serializable {
|
||||
|
||||
@@ -68,7 +66,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
* Return true if the collection is uninitialised or is empty without any held modifications.
|
||||
* <p>
|
||||
* Returning true means can safely skip cascade save for this bean collection.
|
||||
* </p>
|
||||
*/
|
||||
boolean isSkipSave();
|
||||
|
||||
@@ -93,7 +90,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
* <p>
|
||||
* That is, if the collection was not loaded due to filterMany predicates etc
|
||||
* then make sure the collection is set to empty.
|
||||
* </p>
|
||||
*/
|
||||
boolean checkEmptyLazyLoad();
|
||||
|
||||
@@ -136,10 +132,7 @@ public interface BeanCollection<E> extends Serializable {
|
||||
boolean isReadOnly();
|
||||
|
||||
/**
|
||||
* Add the bean to the collection.
|
||||
* <p>
|
||||
* This is disallowed for BeanMap.
|
||||
* </p>
|
||||
* Add the bean to the collection. This is disallowed for BeanMap.
|
||||
*/
|
||||
void internalAdd(Object bean);
|
||||
|
||||
@@ -168,7 +161,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
* Map.Entry.
|
||||
* <p>
|
||||
* For maps this returns the entrySet as we need the keys of the map.
|
||||
* </p>
|
||||
*/
|
||||
Collection<?> getActualEntries();
|
||||
|
||||
@@ -185,6 +177,11 @@ public interface BeanCollection<E> extends Serializable {
|
||||
*/
|
||||
boolean isReference();
|
||||
|
||||
/**
|
||||
* Return true if the collection is modify listening and has modifications.
|
||||
*/
|
||||
boolean hasModifications();
|
||||
|
||||
/**
|
||||
* Set modify listening on or off. This is used to keep track of objects that
|
||||
* have been added to or removed from the list set or map.
|
||||
@@ -192,7 +189,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
* This is required only for ManyToMany collections. The additions and
|
||||
* deletions are used to insert or delete entries from the intersection table.
|
||||
* Otherwise modifyListening is false.
|
||||
* </p>
|
||||
*/
|
||||
void setModifyListening(ModifyListenMode modifyListenMode);
|
||||
|
||||
@@ -206,7 +202,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
* <p>
|
||||
* This will potentially end up as an insert into a intersection table for a
|
||||
* ManyToMany.
|
||||
* </p>
|
||||
*/
|
||||
void modifyAddition(E bean);
|
||||
|
||||
@@ -215,7 +210,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
* <p>
|
||||
* This will potentially end up as an delete from an intersection table for a
|
||||
* ManyToMany.
|
||||
* </p>
|
||||
*/
|
||||
void modifyRemoval(Object bean);
|
||||
|
||||
|
||||
@@ -32,39 +32,47 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
private static final int STATE_REFERENCE = 1;
|
||||
private static final int STATE_LOADED = 2;
|
||||
|
||||
/**
|
||||
* Used when a bean is partially filled.
|
||||
*/
|
||||
private static final byte FLAG_LOADED_PROP = 1;
|
||||
private static final byte FLAG_CHANGED_PROP = 2;
|
||||
private static final byte FLAG_CHANGEDLOADED_PROP = 3;
|
||||
/**
|
||||
* Flags indicating if a property is a dirty embedded bean. Used to distinguish
|
||||
* between an embedded bean being completely overwritten and one of its
|
||||
* embedded properties being made dirty.
|
||||
*/
|
||||
private static final byte FLAG_EMBEDDED_DIRTY = 4;
|
||||
/**
|
||||
* Flags indicating if a property is a dirty embedded bean. Used to distinguish
|
||||
* between an embedded bean being completely overwritten and one of its
|
||||
* embedded properties being made dirty.
|
||||
*/
|
||||
private static final byte FLAG_ORIG_VALUE_SET = 8;
|
||||
|
||||
private transient final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
private transient NodeUsageCollector nodeUsageCollector;
|
||||
|
||||
private transient PersistenceContext persistenceContext;
|
||||
|
||||
private transient BeanLoader beanLoader;
|
||||
|
||||
private transient PreGetterCallback preGetterCallback;
|
||||
|
||||
private String ebeanServerName;
|
||||
|
||||
private boolean deletedFromCollection;
|
||||
|
||||
/**
|
||||
* The actual entity bean that 'owns' this intercept.
|
||||
*/
|
||||
private final EntityBean owner;
|
||||
|
||||
private EntityBean embeddedOwner;
|
||||
private int embeddedOwnerIndex;
|
||||
|
||||
/**
|
||||
* One of NEW, REF, UPD.
|
||||
*/
|
||||
private int state;
|
||||
|
||||
private boolean forceUpdate;
|
||||
|
||||
private boolean readOnly;
|
||||
|
||||
private boolean dirty;
|
||||
|
||||
/**
|
||||
* Flag set to disable lazy loading - typically for SQL "report" type entity beans.
|
||||
*/
|
||||
@@ -74,37 +82,9 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Flag set when lazy loading failed due to the underlying bean being deleted in the DB.
|
||||
*/
|
||||
private boolean lazyLoadFailure;
|
||||
|
||||
/**
|
||||
* Used when a bean is partially filled.
|
||||
*/
|
||||
private static final byte FLAG_LOADED_PROP = 1;
|
||||
|
||||
/**
|
||||
* Set of changed properties.
|
||||
*/
|
||||
private static final byte FLAG_CHANGED_PROP = 2;
|
||||
|
||||
private static final byte FLAG_CHANGEDLOADED_PROP = 3;
|
||||
|
||||
/**
|
||||
* Flags indicating if a property is a dirty embedded bean. Used to distinguish
|
||||
* between an embedded bean being completely overwritten and one of its
|
||||
* embedded properties being made dirty.
|
||||
*/
|
||||
private static final byte FLAG_EMBEDDED_DIRTY = 4;
|
||||
|
||||
/**
|
||||
* Flags indicating if a property is a dirty embedded bean. Used to distinguish
|
||||
* between an embedded bean being completely overwritten and one of its
|
||||
* embedded properties being made dirty.
|
||||
*/
|
||||
private static final byte FLAG_ORIG_VALUE_SET = 8;
|
||||
|
||||
private final byte[] flags;
|
||||
|
||||
private boolean fullyLoadedBean;
|
||||
private boolean loadedFromCache;
|
||||
private final byte[] flags;
|
||||
private Object[] origValues;
|
||||
private Exception[] loadErrors;
|
||||
private int lazyLoadProperty = -1;
|
||||
@@ -983,7 +963,6 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check for primitive boolean.
|
||||
*/
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
package io.ebean.bean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Holds entity beans by there type and id.
|
||||
* <p>
|
||||
* This is used to ensure only one instance for a given entity type and id is
|
||||
* used to build object graphs from queries and lazy loading.
|
||||
* </p>
|
||||
*/
|
||||
public interface PersistenceContext {
|
||||
|
||||
@@ -22,7 +19,6 @@ public interface PersistenceContext {
|
||||
* <p>
|
||||
* Returns an existing entity bean (if one is already there) and otherwise
|
||||
* returns null.
|
||||
* </p>
|
||||
*/
|
||||
Object putIfAbsent(Class<?> rootType, Object id, Object bean);
|
||||
|
||||
@@ -79,17 +75,11 @@ public interface PersistenceContext {
|
||||
*/
|
||||
boolean resetLimit();
|
||||
|
||||
/**
|
||||
* Return the list of dirty beans held by this persistence context.
|
||||
*/
|
||||
List<Object> dirtyBeans();
|
||||
|
||||
/**
|
||||
* Wrapper on a bean to also indicate if a bean has been deleted.
|
||||
* <p>
|
||||
* If a bean has been deleted then for the same persistence context is should
|
||||
* not be able to be fetched from persistence context or L2 cache.
|
||||
* </p>
|
||||
*/
|
||||
class WithOption {
|
||||
|
||||
|
||||
@@ -135,6 +135,11 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
// Support for modify additions deletions etc - ManyToMany
|
||||
// ---------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean hasModifications() {
|
||||
return modifyHolder != null && modifyHolder.hasModifications();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModifyListenMode getModifyListening() {
|
||||
return modifyListenMode;
|
||||
@@ -145,7 +150,6 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
*/
|
||||
@Override
|
||||
public void setModifyListening(ModifyListenMode mode) {
|
||||
|
||||
this.modifyListenMode = mode;
|
||||
this.modifyListening = mode != null && ModifyListenMode.NONE != mode;
|
||||
if (modifyListening) {
|
||||
|
||||
@@ -193,6 +193,12 @@ public class DatabaseConfig {
|
||||
*/
|
||||
private JsonConfig.Include jsonInclude = JsonConfig.Include.ALL;
|
||||
|
||||
/**
|
||||
* When true then by default DbJson beans are assumed to be dirty.
|
||||
* I believe we want to change this default to false in the future.
|
||||
*/
|
||||
private boolean jsonDirtyByDefault = true;
|
||||
|
||||
/**
|
||||
* The database platform name. Used to imply a DatabasePlatform to use.
|
||||
*/
|
||||
@@ -737,6 +743,26 @@ public class DatabaseConfig {
|
||||
this.jsonInclude = jsonInclude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if DbJson beans are assumed dirty by default.
|
||||
* <p>
|
||||
* That is, when true beans that do not implement ModifyAwareType are by
|
||||
* default assumed to be dirty and included in updates.
|
||||
*/
|
||||
public boolean isJsonDirtyByDefault() {
|
||||
return jsonDirtyByDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to false if we want DbJson beans to not be assumed to be dirty.
|
||||
* <p>
|
||||
* That is, when true beans that do not implement ModifyAwareType are by
|
||||
* default assumed to be dirty and included in updates.
|
||||
*/
|
||||
public void setJsonDirtyByDefault(boolean jsonDirtyByDefault) {
|
||||
this.jsonDirtyByDefault = jsonDirtyByDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the Database.
|
||||
*/
|
||||
@@ -2909,6 +2935,7 @@ public class DatabaseConfig {
|
||||
jsonInclude = p.getEnum(JsonConfig.Include.class, "jsonInclude", jsonInclude);
|
||||
jsonDateTime = p.getEnum(JsonConfig.DateTime.class, "jsonDateTime", jsonDateTime);
|
||||
jsonDate = p.getEnum(JsonConfig.Date.class, "jsonDate", jsonDate);
|
||||
jsonDirtyByDefault = p.getBoolean("jsonDirtyByDefault", jsonDirtyByDefault);
|
||||
|
||||
runMigration = p.getBoolean("migration.run", runMigration);
|
||||
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
|
||||
@@ -3369,7 +3396,7 @@ public class DatabaseConfig {
|
||||
this.loadModuleInfo = loadModuleInfo;
|
||||
}
|
||||
|
||||
public enum UuidVersion {
|
||||
public enum UuidVersion {
|
||||
VERSION4,
|
||||
VERSION1,
|
||||
VERSION1RND
|
||||
|
||||
@@ -9,9 +9,6 @@ import java.sql.Types;
|
||||
* functions for varchar, date and timestamp. If they are left null then that is
|
||||
* treated as though that data type can not be encrypted in the DB and will
|
||||
* instead use java client encryption.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public abstract class AbstractDbEncrypt implements DbEncrypt {
|
||||
|
||||
@@ -47,13 +44,10 @@ public abstract class AbstractDbEncrypt implements DbEncrypt {
|
||||
case Types.CHAR:
|
||||
case Types.LONGVARCHAR:
|
||||
return varcharEncryptFunction;
|
||||
|
||||
case Types.DATE:
|
||||
return dateEncryptFunction;
|
||||
|
||||
case Types.TIMESTAMP:
|
||||
return timestampEncryptFunction;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ import io.ebean.config.dbplatform.DbEncryptFunction;
|
||||
|
||||
/**
|
||||
* H2 encryption support via encrypt decrypt function.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class H2DbEncrypt extends AbstractDbEncrypt {
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ import io.ebean.config.dbplatform.DbEncryptFunction;
|
||||
|
||||
/**
|
||||
* MySql aes_encrypt aes_decrypt based encryption support.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class MySqlDbEncrypt extends AbstractDbEncrypt {
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ public class Oracle11Platform extends OraclePlatform {
|
||||
public Oracle11Platform() {
|
||||
super();
|
||||
this.platform = Platform.ORACLE11;
|
||||
this.columnAliasPrefix = "c";
|
||||
this.sqlLimiter = new OracleRownumSqlLimiter();
|
||||
this.basicSqlLimiter = new OracleRownumBasicLimiter();
|
||||
dbIdentity.setIdType(IdType.SEQUENCE);
|
||||
|
||||
@@ -60,7 +60,6 @@ public class OracleDbEncrypt extends AbstractDbEncrypt {
|
||||
* @param decryptFunction the decrypt stored procedure
|
||||
*/
|
||||
public OracleDbEncrypt(String encryptFunction, String decryptFunction) {
|
||||
|
||||
this.varcharEncryptFunction = new OraVarcharFunction(encryptFunction, decryptFunction);
|
||||
this.dateEncryptFunction = new OraDateFunction(encryptFunction, decryptFunction);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public class OraclePlatform extends DatabasePlatform {
|
||||
public OraclePlatform() {
|
||||
super();
|
||||
this.platform = Platform.ORACLE;
|
||||
this.columnAliasPrefix = "c";
|
||||
this.supportsDeleteTableAlias = true;
|
||||
this.maxTableNameLength = 30;
|
||||
this.maxConstraintNameLength = 30;
|
||||
|
||||
+1
@@ -25,6 +25,7 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
|
||||
this.platform = Platform.SQLSERVER;
|
||||
// disable persistBatchOnCascade mode for
|
||||
// SQL Server unless we are using sequences
|
||||
this.dbEncrypt = new SqlServerDbEncrypt();
|
||||
this.persistBatchOnCascade = PersistBatch.NONE;
|
||||
this.idInExpandedForm = true;
|
||||
this.selectCountWithAlias = true;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package io.ebean.config.dbplatform.sqlserver;
|
||||
|
||||
import io.ebean.config.dbplatform.AbstractDbEncrypt;
|
||||
import io.ebean.config.dbplatform.DbEncryptFunction;
|
||||
|
||||
/**
|
||||
* SQL Server EncryptByPassPhrase DecryptByPassPhrase based encryption support.
|
||||
*/
|
||||
public class SqlServerDbEncrypt extends AbstractDbEncrypt {
|
||||
|
||||
public SqlServerDbEncrypt() {
|
||||
this.varcharEncryptFunction = new VarcharFunction();
|
||||
this.dateEncryptFunction = new DateFunction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBindEncryptDataFirst() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class VarcharFunction implements DbEncryptFunction {
|
||||
|
||||
@Override
|
||||
public String getDecryptSql(String columnWithTableAlias) {
|
||||
return "convert(nvarchar,DecryptByPassPhrase(?," + columnWithTableAlias + "))";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEncryptBindSql() {
|
||||
return "EncryptByPassPhrase(?,?)";
|
||||
}
|
||||
}
|
||||
|
||||
private static class DateFunction implements DbEncryptFunction {
|
||||
|
||||
@Override
|
||||
public String getDecryptSql(String columnWithTableAlias) {
|
||||
return "cast(convert(nvarchar,DecryptByPassPhrase(?," + columnWithTableAlias + ")) as date)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEncryptBindSql() {
|
||||
return "EncryptByPassPhrase(?,format(?,'yyyy-MM-dd'))";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,4 +66,17 @@ public class JdbcClose {
|
||||
logger.warn("Error on connection rollback", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the statement
|
||||
*/
|
||||
public static void cancel(Statement stmt) {
|
||||
try {
|
||||
if (stmt != null) {
|
||||
stmt.cancel();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.warn("Error on cancelling statement", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</parent>
|
||||
<!-- <parent>-->
|
||||
<!-- <groupId>org.avaje</groupId>-->
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-parent-12.9.1</tag>
|
||||
<tag>ebean-parent-12.10.0</tag>
|
||||
</scm>
|
||||
|
||||
<name>ebean autotune</name>
|
||||
@@ -26,7 +26,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
+2
-2
@@ -113,7 +113,7 @@ public class AutoTuneDiffCollection {
|
||||
|
||||
diffCount++;
|
||||
|
||||
Origin origin = createOrigin(entry, point, tuneDetail.toString());
|
||||
Origin origin = createOrigin(entry, point, tuneDetail.asString());
|
||||
ProfileDiff diff = document.getProfileDiff();
|
||||
if (diff == null) {
|
||||
diff = new ProfileDiff();
|
||||
@@ -147,7 +147,7 @@ public class AutoTuneDiffCollection {
|
||||
Origin origin = new Origin();
|
||||
origin.setKey(point.getKey());
|
||||
origin.setBeanType(point.getBeanType());
|
||||
origin.setDetail(entry.getDetail().toString());
|
||||
origin.setDetail(entry.getDetail().asString());
|
||||
origin.setCallStack(point.getCallOrigin().getFullDescription());
|
||||
origin.setOriginal(query);
|
||||
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ public class TunedQueryInfo implements Serializable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return tunedDetail.toString();
|
||||
return tunedDetail.asString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+17
-17
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean bom</name>
|
||||
@@ -18,8 +18,8 @@
|
||||
<ebean-migration.version>12.4.0</ebean-migration.version>
|
||||
<ebean-test-docker.version>4.1</ebean-test-docker.version>
|
||||
<ebean-datasource.version>7.0</ebean-datasource.version>
|
||||
<ebean-agent.version>12.9.0</ebean-agent.version>
|
||||
<ebean-maven-plugin.version>12.9.1</ebean-maven-plugin.version>
|
||||
<ebean-agent.version>12.10.0</ebean-agent.version>
|
||||
<ebean-maven-plugin.version>12.10.0</ebean-maven-plugin.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -81,88 +81,88 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-api</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-xml</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-autotune</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>querybean-generator</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>kotlin-querybean-generator</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-redis</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
@@ -16,7 +16,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -11,8 +11,6 @@ public interface DataReader {
|
||||
|
||||
boolean next() throws SQLException;
|
||||
|
||||
void resetColumnPosition();
|
||||
|
||||
void incrementPos(int increment);
|
||||
|
||||
byte[] getBinaryBytes() throws SQLException;
|
||||
|
||||
+8
-8
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core</artifactId>
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-parent-12.9.1</tag>
|
||||
<tag>ebean-parent-12.10.0</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -72,7 +72,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>12.9.0</version>
|
||||
<version>12.9.4-RC1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -87,19 +87,19 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-api</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -182,7 +182,7 @@
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>42.2.10</version>
|
||||
<version>42.2.20</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@@ -302,7 +302,7 @@
|
||||
<plugin>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-maven-plugin</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
|
||||
@@ -155,7 +155,6 @@ public class BindParams implements Serializable {
|
||||
* Set an In Out parameter using position.
|
||||
*/
|
||||
public void setParameter(int position, Object value, int outType) {
|
||||
|
||||
Param p = getParam(position);
|
||||
p.setInValue(value);
|
||||
p.setOutType(outType);
|
||||
@@ -178,8 +177,8 @@ public class BindParams implements Serializable {
|
||||
* Using position set the In value of a parameter. Note that for nulls you
|
||||
* must use setNullParameter.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void setParameter(int position, Object value) {
|
||||
|
||||
Param p = getParam(position);
|
||||
if (value instanceof Collection) {
|
||||
// use of postgres ANY with positioned parameter
|
||||
@@ -214,7 +213,6 @@ public class BindParams implements Serializable {
|
||||
* Set a named In Out parameter.
|
||||
*/
|
||||
public void setParameter(String name, Object value, int outType) {
|
||||
|
||||
Param p = getParam(name);
|
||||
p.setInValue(value);
|
||||
p.setOutType(outType);
|
||||
@@ -232,7 +230,6 @@ public class BindParams implements Serializable {
|
||||
* Set a named In parameter that is not null.
|
||||
*/
|
||||
public Param setParameter(String name, Object value) {
|
||||
|
||||
Param p = getParam(name);
|
||||
p.setInValue(value);
|
||||
return p;
|
||||
@@ -299,7 +296,6 @@ public class BindParams implements Serializable {
|
||||
* Return true if the bind hash and count has not changed.
|
||||
*/
|
||||
public boolean isSameBindHash() {
|
||||
|
||||
if (bindHash == null) {
|
||||
bindHash = calcQueryPlanHash();
|
||||
return false;
|
||||
@@ -330,10 +326,6 @@ public class BindParams implements Serializable {
|
||||
|
||||
private final StringBuilder preparedSql;
|
||||
|
||||
public OrderedList() {
|
||||
this(new ArrayList<>());
|
||||
}
|
||||
|
||||
public OrderedList(List<Param> paramList) {
|
||||
this.paramList = paramList;
|
||||
this.preparedSql = new StringBuilder();
|
||||
|
||||
@@ -119,7 +119,7 @@ public class LoadManyRequest extends LoadRequest {
|
||||
if (extraWhere != null) {
|
||||
// replace special ${ta} placeholder with the base table alias
|
||||
// which is always t0 and add the extra where clause
|
||||
query.where().raw(extraWhere.replace("${ta}", "t0"));
|
||||
query.where().raw(extraWhere.replace("${ta}", "t0").replace("${mta}", "int_"));
|
||||
}
|
||||
|
||||
query.setLazyLoadForParents(many);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
|
||||
/**
|
||||
* SPI interface for underlying BeanDescriptor.
|
||||
*/
|
||||
public interface SpiBeanType {
|
||||
|
||||
/**
|
||||
* Return true if the bean contains a many property that has modifications.
|
||||
* <p>
|
||||
* That is a ManyToMany or a OneToMany with orphan removal with additions
|
||||
* or removals from the collection.
|
||||
*/
|
||||
boolean isToManyDirty(EntityBean bean);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* Manager of SpiBeanTypes.
|
||||
*/
|
||||
public interface SpiBeanTypeManager {
|
||||
|
||||
/**
|
||||
* Return the bean type for the given entity class.
|
||||
*/
|
||||
SpiBeanType getBeanType(Class<?> entityType);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import io.ebean.CancelableQuery;
|
||||
|
||||
/**
|
||||
* Cancellable query, that has a delegate.
|
||||
*
|
||||
* @author Roland Praml, FOCONIS AG
|
||||
*
|
||||
*/
|
||||
public interface SpiCancelableQuery extends CancelableQuery {
|
||||
|
||||
/**
|
||||
* Checks if the query was cancelled.
|
||||
* @throws PersistenceException if query was cancelled.
|
||||
*/
|
||||
void checkCancelled();
|
||||
|
||||
/**
|
||||
* Set the underlying cancelable query (with the PreparedStatement).
|
||||
*/
|
||||
void setCancelableQuery(CancelableQuery cancelableQuery);
|
||||
|
||||
}
|
||||
@@ -1,14 +1,6 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.DtoQuery;
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.ExtendedServer;
|
||||
import io.ebean.PersistenceContextScope;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.RowConsumer;
|
||||
import io.ebean.RowMapper;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.TxScope;
|
||||
import io.ebean.*;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.CallOrigin;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
@@ -25,6 +17,7 @@ import io.ebeaninternal.server.transaction.RemoteTransactionEvent;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Service Provider extension to EbeanServer.
|
||||
@@ -234,6 +227,11 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect
|
||||
*/
|
||||
<T> List<T> findSingleAttributeList(SpiSqlQuery query, Class<T> cls);
|
||||
|
||||
/**
|
||||
* SqlQuery find single attribute streaming the result to a consumer.
|
||||
*/
|
||||
<T> void findSingleAttributeEach(SpiSqlQuery query, Class<T> cls, Consumer<T> consumer);
|
||||
|
||||
/**
|
||||
* SqlQuery find one with mapper.
|
||||
*/
|
||||
@@ -249,6 +247,16 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect
|
||||
*/
|
||||
void findEachRow(SpiSqlQuery query, RowConsumer consumer);
|
||||
|
||||
/**
|
||||
* DTO findIterate query.
|
||||
*/
|
||||
<T> QueryIterator<T> findDtoIterate(SpiDtoQuery<T> query);
|
||||
|
||||
/**
|
||||
* DTO findStream query.
|
||||
*/
|
||||
<T> Stream<T> findDtoStream(SpiDtoQuery<T> query);
|
||||
|
||||
/**
|
||||
* DTO findList query.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* SPI extension to PersistenceContext.
|
||||
*/
|
||||
public interface SpiPersistenceContext extends PersistenceContext {
|
||||
|
||||
/**
|
||||
* Return the list of dirty beans held by this persistence context.
|
||||
*/
|
||||
List<Object> dirtyBeans(SpiBeanTypeManager manager);
|
||||
|
||||
}
|
||||
@@ -17,7 +17,6 @@ import io.ebeaninternal.server.core.SpiOrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.query.CancelableQuery;
|
||||
import io.ebeaninternal.server.querydefn.NaturalKeyBindParam;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryProperties;
|
||||
@@ -31,7 +30,7 @@ import java.util.Set;
|
||||
/**
|
||||
* Object Relational query - Internal extension to Query object.
|
||||
*/
|
||||
public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCodes {
|
||||
public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCodes, SpiCancelableQuery {
|
||||
|
||||
enum Mode {
|
||||
NORMAL(false), LAZYLOAD_MANY(false), LAZYLOAD_BEAN(true), REFRESH_BEAN(true);
|
||||
@@ -847,16 +846,6 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
|
||||
*/
|
||||
ReadEvent getFutureFetchAudit();
|
||||
|
||||
/**
|
||||
* Set the underlying cancelable query (with the PreparedStatement).
|
||||
*/
|
||||
void setCancelableQuery(CancelableQuery cancelableQuery);
|
||||
|
||||
/**
|
||||
* Return true if this query has been cancelled.
|
||||
*/
|
||||
boolean isCancelled();
|
||||
|
||||
/**
|
||||
* Return the base table to use if user defined on the query.
|
||||
*/
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.ebeaninternal.api;
|
||||
/**
|
||||
* SQL query binding (for SqlQuery and DtoQuery).
|
||||
*/
|
||||
public interface SpiSqlBinding {
|
||||
public interface SpiSqlBinding extends SpiCancelableQuery {
|
||||
|
||||
/**
|
||||
* Return the named or positioned parameters.
|
||||
|
||||
@@ -3,7 +3,6 @@ package io.ebeaninternal.api;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
@@ -196,7 +195,7 @@ public interface SpiTransaction extends Transaction {
|
||||
* later. This is along the lines of 'extended persistence context'
|
||||
* behaviour.
|
||||
*/
|
||||
PersistenceContext getPersistenceContext();
|
||||
SpiPersistenceContext getPersistenceContext();
|
||||
|
||||
/**
|
||||
* Set the persistence context to this transaction.
|
||||
@@ -208,7 +207,7 @@ public interface SpiTransaction extends Transaction {
|
||||
* and setPersistenceContext() enable a developer to reuse a single
|
||||
* PersistenceContext with multiple transactions.
|
||||
*/
|
||||
void setPersistenceContext(PersistenceContext context);
|
||||
void setPersistenceContext(SpiPersistenceContext context);
|
||||
|
||||
/**
|
||||
* Return the underlying Connection for internal use.
|
||||
|
||||
@@ -3,7 +3,6 @@ package io.ebeaninternal.api;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.TransactionCallback;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
@@ -379,12 +378,12 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistenceContext getPersistenceContext() {
|
||||
public SpiPersistenceContext getPersistenceContext() {
|
||||
return transaction.getPersistenceContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPersistenceContext(PersistenceContext context) {
|
||||
public void setPersistenceContext(SpiPersistenceContext context) {
|
||||
transaction.setPersistenceContext(context);
|
||||
}
|
||||
|
||||
|
||||
+47
-33
@@ -1,14 +1,10 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.CancelableQuery;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.util.JdbcClose;
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiSqlBinding;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.lib.Str;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.server.util.Str;
|
||||
import io.ebeaninternal.server.persist.Binder;
|
||||
import io.ebeaninternal.server.persist.TrimLogSql;
|
||||
import io.ebeaninternal.server.util.BindParamsParser;
|
||||
@@ -17,11 +13,14 @@ import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
/**
|
||||
* Wraps the objects involved in executing a SQL / Relational Query.
|
||||
*/
|
||||
public abstract class AbstractSqlQueryRequest {
|
||||
public abstract class AbstractSqlQueryRequest implements CancelableQuery {
|
||||
|
||||
protected final SpiSqlBinding query;
|
||||
|
||||
@@ -41,6 +40,8 @@ public abstract class AbstractSqlQueryRequest {
|
||||
|
||||
protected long startNano;
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
/**
|
||||
* Create the BeanFindRequest.
|
||||
*/
|
||||
@@ -48,6 +49,7 @@ public abstract class AbstractSqlQueryRequest {
|
||||
this.server = server;
|
||||
this.query = query;
|
||||
this.transaction = (SpiTransaction) t;
|
||||
this.query.setCancelableQuery(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,11 +85,6 @@ public abstract class AbstractSqlQueryRequest {
|
||||
return transaction.isLogSql();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resultSet and associated query plan if known.
|
||||
*/
|
||||
abstract void setResultSet(ResultSet resultSet, Object queryPlanKey) throws SQLException;
|
||||
|
||||
/**
|
||||
* Return the bindLog for this request.
|
||||
*/
|
||||
@@ -95,12 +92,15 @@ public abstract class AbstractSqlQueryRequest {
|
||||
return bindLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resultSet and associated query plan if known.
|
||||
*/
|
||||
abstract void setResultSet(ResultSet resultSet, Object queryPlanKey) throws SQLException;
|
||||
|
||||
/**
|
||||
* Return true if we can navigate to the next row.
|
||||
*/
|
||||
public boolean next() throws SQLException {
|
||||
return resultSet.next();
|
||||
}
|
||||
public abstract boolean next() throws SQLException;
|
||||
|
||||
protected abstract void requestComplete();
|
||||
|
||||
@@ -113,7 +113,6 @@ public abstract class AbstractSqlQueryRequest {
|
||||
JdbcClose.close(pstmt);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepare the SQL taking into account named bind parameters.
|
||||
*/
|
||||
@@ -145,24 +144,30 @@ public abstract class AbstractSqlQueryRequest {
|
||||
}
|
||||
|
||||
protected void executeAsSql(Binder binder) throws SQLException {
|
||||
prepareSql();
|
||||
Connection conn = transaction.getInternalConnection();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
lock.lock();
|
||||
try {
|
||||
query.checkCancelled();
|
||||
prepareSql();
|
||||
Connection conn = transaction.getInternalConnection();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
}
|
||||
if (query.getBufferFetchSizeHint() > 0) {
|
||||
pstmt.setFetchSize(query.getBufferFetchSizeHint());
|
||||
}
|
||||
BindParams bindParams = query.getBindParams();
|
||||
if (!bindParams.isEmpty()) {
|
||||
this.bindLog = binder.bind(bindParams, pstmt, conn);
|
||||
}
|
||||
if (isLogSql()) {
|
||||
transaction.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ")"));
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
if (query.getBufferFetchSizeHint() > 0) {
|
||||
pstmt.setFetchSize(query.getBufferFetchSizeHint());
|
||||
}
|
||||
BindParams bindParams = query.getBindParams();
|
||||
if (!bindParams.isEmpty()) {
|
||||
this.bindLog = binder.bind(bindParams, pstmt, conn);
|
||||
}
|
||||
if (isLogSql()) {
|
||||
transaction.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ")"));
|
||||
}
|
||||
|
||||
setResultSet(pstmt.executeQuery(), null);
|
||||
query.checkCancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,4 +177,13 @@ public abstract class AbstractSqlQueryRequest {
|
||||
return sql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
lock.lock();
|
||||
try {
|
||||
JdbcClose.cancel(pstmt);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1382,7 +1382,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> FutureList<T> findFutureList(Query<T> query, Transaction t) {
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query.copy();
|
||||
spiQuery.setFutureFetch(true);
|
||||
// FutureList query always run in it's own persistence content
|
||||
spiQuery.setPersistenceContext(new DefaultPersistenceContext());
|
||||
@@ -1433,32 +1433,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> Stream<T> findStream(Query<T> query, Transaction transaction) {
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, transaction);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return toStream(request.findIterate());
|
||||
} catch (RuntimeException ex) {
|
||||
request.endTransIfRequired();
|
||||
throw ex;
|
||||
}
|
||||
return toStream(findIterate(query, transaction));
|
||||
}
|
||||
|
||||
private <T> Stream<T> toStream(QueryIterator<T> queryIterator) {
|
||||
return stream(spliteratorUnknownSize(queryIterator, Spliterator.ORDERED), false)
|
||||
.onClose(new QueryIteratorClose(queryIterator));
|
||||
}
|
||||
|
||||
private static class QueryIteratorClose implements Runnable {
|
||||
private final QueryIterator<?> iterator;
|
||||
|
||||
private QueryIteratorClose(QueryIterator<?> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
iterator.close();
|
||||
}
|
||||
return stream(spliteratorUnknownSize(queryIterator, Spliterator.ORDERED), false).onClose(queryIterator::close);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1606,6 +1585,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return executeSqlQuery((req) -> req.findOneMapper(mapper), query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void findSingleAttributeEach(SpiSqlQuery query, Class<T> cls, Consumer<T> consumer) {
|
||||
executeSqlQuery((req) -> req.findSingleAttributeEach(cls, consumer), query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> findSingleAttributeList(SpiSqlQuery query, Class<T> cls) {
|
||||
return executeSqlQuery((req) -> req.findSingleAttributeList(cls), query);
|
||||
@@ -1649,6 +1633,23 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> QueryIterator<T> findDtoIterate(SpiDtoQuery<T> query) {
|
||||
DtoQueryRequest<T> request = new DtoQueryRequest<>(this, dtoQueryEngine, query);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return request.findIterate();
|
||||
} catch (RuntimeException ex) {
|
||||
request.endTransIfRequired();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Stream<T> findDtoStream(SpiDtoQuery<T> query) {
|
||||
return toStream(findDtoIterate(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> findDtoList(SpiDtoQuery<T> query) {
|
||||
DtoQueryRequest<T> request = new DtoQueryRequest<>(this, dtoQueryEngine, query);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.QueryIterator;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebeaninternal.api.SpiDtoQuery;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
@@ -52,6 +53,7 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
ormQuery.setType(type);
|
||||
ormQuery.setManualId();
|
||||
|
||||
query.setCancelableQuery(ormQuery);
|
||||
// execute the underlying ORM query returning the ResultSet
|
||||
SpiResultSet result = server.findResultSet(ormQuery, transaction);
|
||||
this.pstmt = result.getStatement();
|
||||
@@ -90,6 +92,11 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
}
|
||||
}
|
||||
|
||||
public QueryIterator<T> findIterate() {
|
||||
flushJdbcBatchOnQuery();
|
||||
return queryEngine.findIterate(this);
|
||||
}
|
||||
|
||||
public void findEach(Consumer<T> consumer) {
|
||||
flushJdbcBatchOnQuery();
|
||||
queryEngine.findEach(this, consumer);
|
||||
@@ -110,9 +117,13 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
return queryEngine.findList(this);
|
||||
}
|
||||
|
||||
public boolean next() throws SQLException {
|
||||
query.checkCancelled();
|
||||
return dataReader.next();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T readNextBean() throws SQLException {
|
||||
dataReader.resetColumnPosition();
|
||||
return (T) plan.readRow(dataReader);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.CacheMode;
|
||||
import io.ebean.CancelableQuery;
|
||||
import io.ebean.OrderBy;
|
||||
import io.ebean.PersistenceContextScope;
|
||||
import io.ebean.QueryIterator;
|
||||
@@ -35,7 +36,6 @@ import io.ebeaninternal.server.deploy.DeployPropertyParserMap;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.loadcontext.DLoadContext;
|
||||
import io.ebeaninternal.server.query.CQueryPlan;
|
||||
import io.ebeaninternal.server.query.CancelableQuery;
|
||||
import io.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -379,9 +379,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
public void setBatched() {
|
||||
batched = true;
|
||||
if (type == Type.INSERT || type == Type.UPDATE) {
|
||||
// used to trigger automatic jdbc batch flush
|
||||
intercept.registerGetterCallback(this);
|
||||
getterCallback = true;
|
||||
if (beanDescriptor.hasSingleIdProperty()) {
|
||||
// used to trigger automatic jdbc batch flush
|
||||
intercept.registerGetterCallback(this);
|
||||
getterCallback = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.ebeaninternal.server.core;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.lib.Str;
|
||||
import io.ebeaninternal.server.util.Str;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeaninternal.server.persist.PersistExecute;
|
||||
import io.ebeaninternal.server.persist.TrimLogSql;
|
||||
|
||||
@@ -20,17 +20,22 @@ public interface RelationalQueryEngine {
|
||||
/**
|
||||
* Find a list of beans using relational query.
|
||||
*/
|
||||
List<SqlRow> findList(RelationalQueryRequest request);
|
||||
|
||||
/**
|
||||
* Find each query using relational query.
|
||||
*/
|
||||
void findEach(RelationalQueryRequest request, Consumer<SqlRow> consumer);
|
||||
<T> List<T> findList(RelationalQueryRequest request, RowReader<T> reader);
|
||||
|
||||
/**
|
||||
* Find each while query using relational query.
|
||||
*/
|
||||
void findEach(RelationalQueryRequest request, Predicate<SqlRow> consumer);
|
||||
<T> void findEach(RelationalQueryRequest request, RowReader<T> reader, Predicate<T> consumer);
|
||||
|
||||
/**
|
||||
* Find each via raw consumer.
|
||||
*/
|
||||
void findEach(RelationalQueryRequest request, RowConsumer mapper);
|
||||
|
||||
/**
|
||||
* Find one via mapper.
|
||||
*/
|
||||
<T> T findOne(RelationalQueryRequest request, RowMapper<T> mapper);
|
||||
|
||||
/**
|
||||
* Find single attribute.
|
||||
@@ -43,19 +48,9 @@ public interface RelationalQueryEngine {
|
||||
<T> List<T> findSingleAttributeList(RelationalQueryRequest request, Class<T> cls);
|
||||
|
||||
/**
|
||||
* Find one via mapper.
|
||||
* Find single attribute streaming the result to a consumer.
|
||||
*/
|
||||
<T> T findOneMapper(RelationalQueryRequest request, RowMapper<T> mapper);
|
||||
|
||||
/**
|
||||
* Find list via mapper.
|
||||
*/
|
||||
<T> List<T> findListMapper(RelationalQueryRequest request, RowMapper<T> mapper);
|
||||
|
||||
/**
|
||||
* Find each via raw consumer.
|
||||
*/
|
||||
void findEachRow(RelationalQueryRequest request, RowConsumer mapper);
|
||||
<T> void findSingleAttributeEach(RelationalQueryRequest request, Class<T> cls, Consumer<T> consumer);
|
||||
|
||||
/**
|
||||
* Collect SQL query execution statistics.
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.RowConsumer;
|
||||
import io.ebean.RowMapper;
|
||||
import io.ebean.SqlQuery;
|
||||
import io.ebean.SqlRow;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.*;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiSqlBinding;
|
||||
|
||||
@@ -57,18 +53,24 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
|
||||
|
||||
boolean findEachRow(RowConsumer mapper) {
|
||||
flushJdbcBatchOnQuery();
|
||||
queryEngine.findEachRow(this, mapper);
|
||||
queryEngine.findEach(this, mapper);
|
||||
return true;
|
||||
}
|
||||
|
||||
<T> List<T> findListMapper(RowMapper<T> mapper) {
|
||||
flushJdbcBatchOnQuery();
|
||||
return queryEngine.findListMapper(this, mapper);
|
||||
return queryEngine.findList(this, () -> mapper.map(resultSet, rows++));
|
||||
}
|
||||
|
||||
<T> T findOneMapper(RowMapper<T> mapper) {
|
||||
flushJdbcBatchOnQuery();
|
||||
return queryEngine.findOneMapper(this, mapper);
|
||||
return queryEngine.findOne(this, mapper);
|
||||
}
|
||||
|
||||
public <T> boolean findSingleAttributeEach(Class<T> cls, Consumer<T> consumer) {
|
||||
flushJdbcBatchOnQuery();
|
||||
queryEngine.findSingleAttributeEach(this, cls, consumer);
|
||||
return true;
|
||||
}
|
||||
|
||||
public <T> List<T> findSingleAttributeList(Class<T> cls) {
|
||||
@@ -83,17 +85,17 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
|
||||
|
||||
public void findEach(Consumer<SqlRow> consumer) {
|
||||
flushJdbcBatchOnQuery();
|
||||
queryEngine.findEach(this, consumer);
|
||||
queryEngine.findEach(this, (resultSet, rowNum) -> consumer.accept(createNewRow()));
|
||||
}
|
||||
|
||||
public void findEachWhile(Predicate<SqlRow> consumer) {
|
||||
flushJdbcBatchOnQuery();
|
||||
queryEngine.findEach(this, consumer);
|
||||
queryEngine.findEach(this, this::createNewRow, consumer);
|
||||
}
|
||||
|
||||
public List<SqlRow> findList() {
|
||||
flushJdbcBatchOnQuery();
|
||||
return queryEngine.findList(this);
|
||||
return queryEngine.findList(this, this::createNewRow);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,16 +137,15 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
public void incrementRows() {
|
||||
rows++;
|
||||
}
|
||||
|
||||
public <T> List<T> mapList(RowMapper<T> mapper) throws SQLException {
|
||||
List<T> list = new ArrayList<>();
|
||||
while (next()) {
|
||||
list.add(mapper.map(resultSet, rows++));
|
||||
@Override
|
||||
public boolean next() throws SQLException {
|
||||
query.checkCancelled();
|
||||
if (!resultSet.next()) {
|
||||
return false;
|
||||
} else {
|
||||
rows++;
|
||||
return true;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public <T> T mapOne(RowMapper<T> mapper) throws SQLException {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Read a row building a result for that row.
|
||||
*/
|
||||
public interface RowReader<T> {
|
||||
|
||||
/**
|
||||
* Build and return a result for a row.
|
||||
*/
|
||||
T read() throws SQLException;
|
||||
}
|
||||
@@ -38,15 +38,7 @@ import io.ebean.plugin.BeanType;
|
||||
import io.ebean.plugin.ExpressionPath;
|
||||
import io.ebean.plugin.Property;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.BeanCacheResult;
|
||||
import io.ebeaninternal.api.CQueryPlanKey;
|
||||
import io.ebeaninternal.api.ConcurrencyMode;
|
||||
import io.ebeaninternal.api.LoadBeanContext;
|
||||
import io.ebeaninternal.api.LoadContext;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiUpdatePlan;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.api.TransactionEventTable.TableIUD;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
@@ -119,7 +111,7 @@ import static io.ebeaninternal.server.persist.DmlUtil.isNullOrZero;
|
||||
/**
|
||||
* Describes Beans including their deployment information.
|
||||
*/
|
||||
public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanDescriptor.class);
|
||||
|
||||
@@ -1755,6 +1747,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return idProperty != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return false for IdClass case with multiple @Id properties.
|
||||
*/
|
||||
public boolean hasSingleIdProperty() {
|
||||
return idPropertyIndex != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this type has a simple Id and the platform supports mutli-value binding.
|
||||
*/
|
||||
@@ -1880,7 +1879,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
if (refBean == null) {
|
||||
refBean = createReference(readOnly, false, id, pc);
|
||||
}
|
||||
return (EntityBean)refBean;
|
||||
return (EntityBean) refBean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2017,6 +2016,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return owner.getBeanDescriptor(otherType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if the table is managed (i.e. an existing m2m relation).
|
||||
*/
|
||||
public boolean isTableManaged(String tableName) {
|
||||
return owner.isTableManaged(tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the order column property.
|
||||
*/
|
||||
@@ -2937,6 +2943,20 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isToManyDirty(EntityBean bean) {
|
||||
final EntityBeanIntercept ebi = bean._ebean_getIntercept();
|
||||
for (BeanPropertyAssocMany<?> many : propertiesManySave) {
|
||||
if (ebi.isLoadedProperty(many.getPropertyIndex())) {
|
||||
final BeanCollection<?> value = (BeanCollection<?>) many.getValue(bean);
|
||||
if (value != null && value.hasModifications()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the bean is draftable and considered a 'live' instance.
|
||||
*/
|
||||
@@ -3139,7 +3159,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
|
||||
public boolean isIdLoaded(EntityBeanIntercept ebi) {
|
||||
return ebi.isLoadedProperty(idPropertyIndex);
|
||||
// assume id loaded for IdClass case with idPropertyIndex == -1
|
||||
return idPropertyIndex == -1 ? true : ebi.isLoadedProperty(idPropertyIndex);
|
||||
}
|
||||
|
||||
boolean hasIdValue(EntityBean bean) {
|
||||
@@ -3179,7 +3200,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
int propertyIndex = beanProperty.getPropertyIndex();
|
||||
if (!ebi.isDirtyProperty(propertyIndex) && ebi.isLoadedProperty(propertyIndex)) {
|
||||
Object value = beanProperty.getValue(ebi.getOwner());
|
||||
if (value == null || beanProperty.isDirtyValue(value)) {
|
||||
if (value != null && beanProperty.isDirtyValue(value)) {
|
||||
// mutable scalar value which is considered dirty so mark
|
||||
// it as such so that it is included in an update
|
||||
ebi.markPropertyAsChanged(propertyIndex);
|
||||
|
||||
@@ -26,9 +26,7 @@ import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.meta.QueryPlanInit;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.util.AnnotationUtil;
|
||||
import io.ebeaninternal.api.ConcurrencyMode;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.TransactionEventTable;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.server.cache.CacheChangeSet;
|
||||
import io.ebeaninternal.server.cache.SpiCacheManager;
|
||||
import io.ebeaninternal.server.core.InternString;
|
||||
@@ -88,7 +86,7 @@ import java.util.concurrent.TimeUnit;
|
||||
/**
|
||||
* Creates BeanDescriptors.
|
||||
*/
|
||||
public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
public class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTypeManager {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanDescriptorManager.class);
|
||||
|
||||
@@ -263,6 +261,11 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
return descQueueMap.get(queueId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiBeanType getBeanType(Class<?> entityType) {
|
||||
return getBeanDescriptor(entityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> BeanDescriptor<T> getBeanDescriptor(Class<T> entityType) {
|
||||
@@ -442,6 +445,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
return tableToDescMap.get(tableName.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTableManaged(String tableName) {
|
||||
return tableToDescMap.get(tableName.toLowerCase()) != null
|
||||
|| tableToViewDescMap.get(tableName.toLowerCase()) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate entity beans based on views via their dependent tables.
|
||||
*/
|
||||
|
||||
@@ -76,4 +76,10 @@ public interface BeanDescriptorMap {
|
||||
* Return true if Jackson core is present on the classpath.
|
||||
*/
|
||||
boolean isJacksonCorePresent();
|
||||
|
||||
/**
|
||||
* Returns true, if the given table (or view) is managed by ebean
|
||||
* (= an entity exists)
|
||||
*/
|
||||
boolean isTableManaged(String tableName);
|
||||
}
|
||||
|
||||
@@ -439,6 +439,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
@Override
|
||||
public String getAssocIsEmpty(SpiExpressionRequest request, String path) {
|
||||
boolean softDelete = targetDescriptor.isSoftDelete();
|
||||
boolean needsX2Table = softDelete || getExtraWhere() != null;
|
||||
StringBuilder sb = new StringBuilder(50);
|
||||
SpiQuery<?> query = request.getQueryRequest().getQuery();
|
||||
if (hasJoinTable()) {
|
||||
@@ -446,7 +447,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
} else {
|
||||
sb.append(targetDescriptor.getBaseTable(query.getTemporalMode()));
|
||||
}
|
||||
if (softDelete && hasJoinTable()) {
|
||||
if (needsX2Table && hasJoinTable()) {
|
||||
sb.append(" x join ");
|
||||
sb.append(targetDescriptor.getBaseTable(query.getTemporalMode()));
|
||||
sb.append(" x2 on ");
|
||||
@@ -461,6 +462,16 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
}
|
||||
exportedProperties[i].appendWhere(sb, "x.", path);
|
||||
}
|
||||
if (getExtraWhere() != null) {
|
||||
sb.append(" and ");
|
||||
if (hasJoinTable()) {
|
||||
sb.append(getExtraWhere().replace("${ta}", "x2").replace("${mta}", "x"));
|
||||
} else {
|
||||
sb.append(getExtraWhere().replace("${ta}", "x"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (softDelete) {
|
||||
String alias = hasJoinTable() ? "x2" : "x";
|
||||
sb.append(" and ").append(targetDescriptor.getSoftDeletePredicate(alias));
|
||||
@@ -1061,4 +1072,16 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
public void bindElementValue(SqlUpdate insert, Object value) {
|
||||
targetDescriptor.bindElementValue(insert, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if we must create a m2m join table.
|
||||
*/
|
||||
public boolean createJoinTable() {
|
||||
if (hasJoinTable() && getMappedBy() == null) {
|
||||
// only create on other 'owning' side
|
||||
return !descriptor.isTableManaged(intersectionJoin.getTable());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ public interface DbSqlContext {
|
||||
/**
|
||||
* Add a join to the sql query.
|
||||
*/
|
||||
void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2);
|
||||
void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2, String extraWhere);
|
||||
|
||||
/**
|
||||
* Push the current table alias onto the stack.
|
||||
|
||||
@@ -34,6 +34,8 @@ public final class TableJoin {
|
||||
private final int queryHash;
|
||||
|
||||
private final PropertyForeignKey foreignKey;
|
||||
|
||||
private final String extraWhere;
|
||||
|
||||
public TableJoin(DeployTableJoin deploy) {
|
||||
this(deploy, null);
|
||||
@@ -44,6 +46,7 @@ public final class TableJoin {
|
||||
*/
|
||||
public TableJoin(DeployTableJoin deploy, PropertyForeignKey foreignKey) {
|
||||
this.foreignKey = foreignKey;
|
||||
this.extraWhere = deploy.getExtraWhere();
|
||||
this.table = InternString.intern(deploy.getTable());
|
||||
this.type = deploy.getType();
|
||||
this.inheritInfo = deploy.getInheritInfo();
|
||||
@@ -57,6 +60,7 @@ public final class TableJoin {
|
||||
|
||||
private TableJoin(TableJoin source, String overrideColumn) {
|
||||
this.foreignKey = null;
|
||||
this.extraWhere = source.extraWhere;
|
||||
this.table = source.table;
|
||||
this.type = source.type;
|
||||
this.inheritInfo = source.inheritInfo;
|
||||
@@ -146,7 +150,7 @@ public final class TableJoin {
|
||||
|
||||
public SqlJoinType addJoin(SqlJoinType joinType, String a1, String a2, DbSqlContext ctx) {
|
||||
String joinLiteral = joinType.getLiteral(type);
|
||||
ctx.addJoin(joinLiteral, table, columns(), a1, a2);
|
||||
ctx.addJoin(joinLiteral, table, columns(), a1, a2, extraWhere);
|
||||
return joinType.autoToOuter(type);
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -86,6 +86,7 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
* collection.
|
||||
*/
|
||||
public void setExtraWhere(String extraWhere) {
|
||||
this.tableJoin.setExtraWhere(extraWhere);
|
||||
this.extraWhere = extraWhere;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@ public class DeployTableJoin {
|
||||
private ArrayList<DeployTableJoinColumn> columns = new ArrayList<>(4);
|
||||
|
||||
private InheritInfo inheritInfo;
|
||||
|
||||
private String extraWhere;
|
||||
|
||||
/**
|
||||
* Create a DeployTableJoin.
|
||||
@@ -137,6 +139,18 @@ public class DeployTableJoin {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the clause of an extra @Where annotation.
|
||||
* @return
|
||||
*/
|
||||
public String getExtraWhere() {
|
||||
return extraWhere;
|
||||
}
|
||||
|
||||
public void setExtraWhere(String extraWhere) {
|
||||
this.extraWhere = extraWhere;
|
||||
}
|
||||
|
||||
public DeployTableJoin createInverse(String tableName) {
|
||||
|
||||
DeployTableJoin inverse = new DeployTableJoin();
|
||||
|
||||
+1
-1
@@ -118,7 +118,7 @@ class AnnotationAssocManys extends AnnotationAssoc {
|
||||
|
||||
Where where = prop.getMetaAnnotationWhere(platform);
|
||||
if (where != null) {
|
||||
prop.setExtraWhere(where.clause());
|
||||
prop.setExtraWhere(processFormula(where.clause()));
|
||||
}
|
||||
|
||||
FetchPreference fetchPreference = get(prop, FetchPreference.class);
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
|
||||
Where where = prop.getMetaAnnotationWhere(platform);
|
||||
if (where != null) {
|
||||
// not expecting this to be used on assoc one properties
|
||||
prop.setExtraWhere(where.clause());
|
||||
prop.setExtraWhere(processFormula(where.clause()));
|
||||
}
|
||||
|
||||
PrimaryKeyJoinColumn primaryKeyJoin = get(prop, PrimaryKeyJoinColumn.class);
|
||||
|
||||
@@ -153,7 +153,7 @@ public class AnnotationFields extends AnnotationParser {
|
||||
|
||||
Formula formula = prop.getMetaAnnotationFormula(platform);
|
||||
if (formula != null) {
|
||||
prop.setSqlFormula(formula.select(), formula.join());
|
||||
prop.setSqlFormula(processFormula(formula.select()), processFormula(formula.join()));
|
||||
}
|
||||
|
||||
initWhoProperties(prop);
|
||||
@@ -334,7 +334,7 @@ public class AnnotationFields extends AnnotationParser {
|
||||
}
|
||||
Formula formula = prop.getMetaAnnotationFormula(platform);
|
||||
if (formula != null) {
|
||||
prop.setSqlFormula(formula.select(), formula.join());
|
||||
prop.setSqlFormula(processFormula(formula.select()), processFormula(formula.join()));
|
||||
}
|
||||
|
||||
final Aggregation aggregation = prop.getMetaAnnotation(Aggregation.class);
|
||||
|
||||
@@ -129,4 +129,11 @@ public abstract class AnnotationParser extends AnnotationBase {
|
||||
}
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process any formula from @Formula or @Where.
|
||||
*/
|
||||
protected String processFormula(String source) {
|
||||
return source == null ? null : source.replace("${dbTableName}", descriptor.getBaseTable());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
|
||||
<TITLE>AvajeLib</TITLE>
|
||||
</HEAD>
|
||||
<Body BGCOLOR="#ffffff">
|
||||
Core services for an application.
|
||||
<P>
|
||||
Core services including Logging, Deployment properties and Background thread for
|
||||
running frequent tasks.
|
||||
</P>
|
||||
</Body>
|
||||
</HTML>
|
||||
@@ -3,7 +3,7 @@ package io.ebeaninternal.server.persist.dml;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.lib.Str;
|
||||
import io.ebeaninternal.server.util.Str;
|
||||
import io.ebeaninternal.server.persist.BatchedPstmt;
|
||||
import io.ebeaninternal.server.persist.BatchedPstmtHolder;
|
||||
import io.ebeaninternal.server.persist.dmlbind.BindableRequest;
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.CancelableQuery;
|
||||
import io.ebean.QueryIterator;
|
||||
import io.ebean.Version;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebean.bean.NodeUsageCollector;
|
||||
import io.ebean.bean.NodeUsageListener;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.bean.*;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.event.readaudit.ReadEvent;
|
||||
import io.ebean.util.JdbcClose;
|
||||
@@ -19,12 +14,7 @@ import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.autotune.ProfilingListener;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.core.SpiOrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanCollectionHelpFactory;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
import io.ebeaninternal.server.deploy.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -34,11 +24,7 @@ import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
@@ -47,11 +33,9 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
* The SqlSelect is based on a tree (Object Graph). The tree is traversed to see
|
||||
* what parts are included in the tree according to the value of
|
||||
* find.getInclude();
|
||||
* </p>
|
||||
* <p>
|
||||
* The tree structure is flattened into a SqlSelectChain. The SqlSelectChain is
|
||||
* the key object used in reading the flat resultSet back into Objects.
|
||||
* </p>
|
||||
*/
|
||||
public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTransactionEvent {
|
||||
|
||||
@@ -156,8 +140,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
*/
|
||||
private PreparedStatement pstmt;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
private String bindLog;
|
||||
|
||||
private final CQueryPlan queryPlan;
|
||||
@@ -205,19 +187,15 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
this.query = request.getQuery();
|
||||
this.queryMode = query.getMode();
|
||||
this.lazyLoadManyProperty = query.getLazyLoadMany();
|
||||
|
||||
this.readOnly = request.isReadOnly();
|
||||
this.disableLazyLoading = query.isDisableLazyLoading();
|
||||
|
||||
this.objectGraphNode = query.getParentNode();
|
||||
this.profilingListener = query.getProfilingListener();
|
||||
this.autoTuneProfiling = profilingListener != null;
|
||||
this.profilingListenerRef = autoTuneProfiling ? new WeakReference<>(profilingListener) : null;
|
||||
|
||||
// set the generated sql back to the query
|
||||
// so its available to the user...
|
||||
query.setGeneratedSql(queryPlan.getSql());
|
||||
|
||||
SqlTree sqlTree = queryPlan.getSqlTree();
|
||||
this.rootNode = sqlTree.getRootNode();
|
||||
this.manyProperty = sqlTree.getManyProperty();
|
||||
@@ -293,15 +271,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
public void cancel() {
|
||||
lock.lock();
|
||||
try {
|
||||
this.cancelled = true;
|
||||
if (pstmt != null) {
|
||||
try {
|
||||
logger.debug("Cancelling query");
|
||||
pstmt.cancel();
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException("Error cancelling query", e);
|
||||
}
|
||||
}
|
||||
JdbcClose.cancel(pstmt);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
@@ -333,9 +303,8 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
ResultSet prepareResultSet(boolean forwardOnlyHint) throws SQLException {
|
||||
lock.lock();
|
||||
try {
|
||||
if (cancelled) {
|
||||
throw new SQLException("Query cancelled");
|
||||
}
|
||||
// cancelled before we started
|
||||
query.checkCancelled();
|
||||
startNano = System.nanoTime();
|
||||
SpiTransaction t = request.getTransaction();
|
||||
profileOffset = t.profileOffset();
|
||||
@@ -363,17 +332,18 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
pstmt.setFetchSize(query.getBufferFetchSizeHint());
|
||||
}
|
||||
bindLog = predicates.bind(queryPlan.bindEncryptedProperties(pstmt, conn));
|
||||
return pstmt.executeQuery();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
ResultSet ret = pstmt.executeQuery();
|
||||
query.checkCancelled();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the resources.
|
||||
* <p>
|
||||
* The JDBC resultSet and statement need to be closed. Its important that this method is called.
|
||||
* </p>
|
||||
*/
|
||||
public void close() {
|
||||
try {
|
||||
@@ -405,14 +375,12 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
|
||||
@Override
|
||||
public void setLazyLoadedChildBean(EntityBean bean, Object lazyLoadParentId) {
|
||||
|
||||
if (lazyLoadParentId != null) {
|
||||
if (!lazyLoadParentId.equals(this.lazyLoadParentId)) {
|
||||
// get the appropriate parent bean from the persistence context
|
||||
this.lazyLoadParentBean = (EntityBean) lazyLoadManyProperty.getBeanDescriptor().contextGet(getPersistenceContext(), lazyLoadParentId);
|
||||
this.lazyLoadParentId = lazyLoadParentId;
|
||||
}
|
||||
|
||||
// add the loadedBean to the appropriate collection of lazyLoadParentBean
|
||||
lazyLoadManyProperty.addBeanToCollectionWithCreate(lazyLoadParentBean, bean, true);
|
||||
}
|
||||
@@ -423,10 +391,8 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
* <p>
|
||||
* If the query includes a many then the first object in the returned array is
|
||||
* the one/master and the second the many/detail.
|
||||
* </p>
|
||||
*/
|
||||
private boolean readNextBean() throws SQLException {
|
||||
|
||||
if (!moveToNextRow()) {
|
||||
if (currentBean == null) {
|
||||
nextBean = null;
|
||||
@@ -440,7 +406,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
}
|
||||
|
||||
loadedBeanCount++;
|
||||
|
||||
if (manyProperty == null) {
|
||||
// only single resultSet row required to build object so we are done
|
||||
// read a single resultSet row into single bean
|
||||
@@ -486,14 +451,11 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
* Return true if we can move to the next resultSet row.
|
||||
*/
|
||||
private boolean moveToNextRow() throws SQLException {
|
||||
|
||||
if (!dataReader.next()) {
|
||||
noMoreRows = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
rowCount++;
|
||||
dataReader.resetColumnPosition();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -502,7 +464,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
}
|
||||
|
||||
boolean readBean() throws SQLException {
|
||||
|
||||
boolean result = hasNext();
|
||||
updateExecutionStatistics();
|
||||
return result;
|
||||
@@ -522,7 +483,8 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
boolean hasNext() throws SQLException {
|
||||
lock.lock();
|
||||
try {
|
||||
if (noMoreRows || cancelled) {
|
||||
query.checkCancelled();
|
||||
if (noMoreRows) {
|
||||
return false;
|
||||
}
|
||||
if (hasNextCache) {
|
||||
@@ -539,20 +501,16 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
* Read version beans and their effective dates.
|
||||
*/
|
||||
List<Version<T>> readVersions() throws SQLException {
|
||||
|
||||
List<Version<T>> versionList = new ArrayList<>();
|
||||
|
||||
Version<T> version;
|
||||
while ((version = readNextVersion()) != null) {
|
||||
versionList.add(version);
|
||||
}
|
||||
|
||||
updateExecutionStatistics();
|
||||
return versionList;
|
||||
}
|
||||
|
||||
private Version<T> readNextVersion() throws SQLException {
|
||||
|
||||
if (moveToNextRow()) {
|
||||
return rootNode.loadVersion(this);
|
||||
}
|
||||
@@ -696,7 +654,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
* Should we create profileNodes for beans created in this query.
|
||||
* <p>
|
||||
* This is true for all queries except lazy load bean queries.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public boolean isAutoTuneProfiling() {
|
||||
@@ -707,13 +664,11 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
}
|
||||
|
||||
private String getPath(String propertyName) {
|
||||
|
||||
if (currentPrefix == null) {
|
||||
return propertyName;
|
||||
} else if (propertyName == null) {
|
||||
return currentPrefix;
|
||||
}
|
||||
|
||||
String path = currentPathMap.get(propertyName);
|
||||
if (path != null) {
|
||||
return path;
|
||||
@@ -724,7 +679,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
|
||||
@Override
|
||||
public void profileBean(EntityBeanIntercept ebi, String prefix) {
|
||||
|
||||
ObjectGraphNode node = request.getGraphContext().getObjectGraphNode(prefix);
|
||||
ebi.setNodeUsageCollector(new NodeUsageCollector(node, profilingListenerRef));
|
||||
}
|
||||
@@ -749,7 +703,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
* a find many query with read auditing so build the ReadEvent and log it.
|
||||
*/
|
||||
void auditFindMany() {
|
||||
|
||||
if (auditIds != null && !auditIds.isEmpty()) {
|
||||
// get the id values of the underlying collection
|
||||
ReadEvent futureReadEvent = query.getFutureFetchAudit();
|
||||
@@ -787,7 +740,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
* Add the id to the audit id buffer and flush if needed in batches of 100.
|
||||
*/
|
||||
private void auditNextBean() {
|
||||
|
||||
if (auditIds == null) {
|
||||
auditIds = new ArrayList<>(100);
|
||||
}
|
||||
|
||||
@@ -189,6 +189,12 @@ class CQueryBuilder {
|
||||
|
||||
SpiQuery<?> query = request.getQuery();
|
||||
query.setSingleAttribute();
|
||||
if (!query.isIncludeSoftDeletes()) {
|
||||
BeanDescriptor<?> desc = request.getBeanDescriptor();
|
||||
if (desc.isSoftDelete()) {
|
||||
query.addSoftDeletePredicate(desc.getSoftDeletePredicate(alias(query.getAlias())));
|
||||
}
|
||||
}
|
||||
|
||||
CQueryPredicates predicates = new CQueryPredicates(binder, request);
|
||||
CQueryPlan queryPlan = request.getQueryPlan();
|
||||
@@ -616,11 +622,19 @@ class CQueryBuilder {
|
||||
if (request.isInlineCountDistinct()) {
|
||||
sb.append(")");
|
||||
}
|
||||
if (distinct && dbOrderBy != null && !query.isSingleAttribute()) {
|
||||
if (distinct && dbOrderBy != null) {
|
||||
// add the orderBy columns to the select clause (due to distinct)
|
||||
final OrderBy<?> orderBy = query.getOrderBy();
|
||||
if (orderBy != null && orderBy.supportsSelect()) {
|
||||
sb.append(", ").append(DbOrderByTrim.trim(dbOrderBy));
|
||||
String trimmed = DbOrderByTrim.trim(dbOrderBy);
|
||||
if (query.isSingleAttribute() && trimmed.equals(select.getSelectSql())) {
|
||||
// NOP, already in SQL
|
||||
// TODO: what to do if we select("id").orderBy("prop,id")?
|
||||
// Can we live with a query like "select t0.id, t0.prop, t0.id from"
|
||||
// or should we elliminate the second "t0.id" from select
|
||||
} else {
|
||||
sb.append(", ").append(trimmed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import io.ebeaninternal.server.core.DiffHelp;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.core.SpiResultSet;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.lib.Str;
|
||||
import io.ebeaninternal.server.util.Str;
|
||||
import io.ebeaninternal.server.persist.Binder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -66,11 +66,13 @@ public class CQueryEngine {
|
||||
|
||||
public <T> int delete(OrmQueryRequest<T> request) {
|
||||
CQueryUpdate query = queryBuilder.buildUpdateQuery(true, request);
|
||||
request.setCancelableQuery(query);
|
||||
return executeUpdate(request, query);
|
||||
}
|
||||
|
||||
public <T> int update(OrmQueryRequest<T> request) {
|
||||
CQueryUpdate query = queryBuilder.buildUpdateQuery(false, request);
|
||||
request.setCancelableQuery(query);
|
||||
return executeUpdate(request, query);
|
||||
}
|
||||
|
||||
@@ -97,6 +99,7 @@ public class CQueryEngine {
|
||||
public <A> List<A> findSingleAttributeList(OrmQueryRequest<?> request) {
|
||||
|
||||
CQueryFetchSingleAttribute rcQuery = queryBuilder.buildFetchAttributeQuery(request);
|
||||
request.setCancelableQuery(rcQuery);
|
||||
return findAttributeList(request, rcQuery);
|
||||
}
|
||||
|
||||
@@ -151,6 +154,7 @@ public class CQueryEngine {
|
||||
public <A> List<A> findIds(OrmQueryRequest<?> request) {
|
||||
|
||||
CQueryFetchSingleAttribute rcQuery = queryBuilder.buildFetchIdsQuery(request);
|
||||
request.setCancelableQuery(rcQuery);
|
||||
return findAttributeList(request, rcQuery);
|
||||
}
|
||||
|
||||
@@ -164,6 +168,7 @@ public class CQueryEngine {
|
||||
public <T> int findCount(OrmQueryRequest<T> request) {
|
||||
|
||||
CQueryRowCount rcQuery = queryBuilder.buildRowCountQuery(request);
|
||||
request.setCancelableQuery(rcQuery);
|
||||
try {
|
||||
|
||||
int count = rcQuery.findCount();
|
||||
@@ -235,8 +240,10 @@ public class CQueryEngine {
|
||||
|
||||
} catch (SQLException e) {
|
||||
try {
|
||||
PersistenceException pex = cquery.createPersistenceException(e);
|
||||
// create exception before closing connection
|
||||
cquery.close();
|
||||
throw cquery.createPersistenceException(e);
|
||||
throw pex;
|
||||
} finally {
|
||||
request.rollbackTransIfRequired();
|
||||
}
|
||||
@@ -259,6 +266,7 @@ public class CQueryEngine {
|
||||
// order by lower sys period desc
|
||||
query.order().desc(sysPeriodLower);
|
||||
CQuery<T> cquery = queryBuilder.buildQuery(request);
|
||||
request.setCancelableQuery(cquery);
|
||||
try {
|
||||
cquery.prepareBindExecuteQuery();
|
||||
if (request.isLogSql()) {
|
||||
@@ -327,6 +335,7 @@ public class CQueryEngine {
|
||||
*/
|
||||
public <T> SpiResultSet findResultSet(OrmQueryRequest<T> request) {
|
||||
CQuery<T> cquery = queryBuilder.buildQuery(request);
|
||||
request.setCancelableQuery(cquery);
|
||||
try {
|
||||
boolean fwdOnly;
|
||||
if (request.isFindIterate()) {
|
||||
@@ -411,6 +420,7 @@ public class CQueryEngine {
|
||||
EntityBean bean = null;
|
||||
|
||||
CQuery<T> cquery = queryBuilder.buildQuery(request);
|
||||
request.setCancelableQuery(cquery);
|
||||
|
||||
try {
|
||||
cquery.prepareBindExecuteQuery();
|
||||
|
||||
+34
-15
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.CancelableQuery;
|
||||
import io.ebean.CountedValue;
|
||||
import io.ebean.core.type.ScalarDataReader;
|
||||
import io.ebean.util.JdbcClose;
|
||||
@@ -18,11 +19,12 @@ import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Base compiled query request for single attribute queries.
|
||||
*/
|
||||
class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent {
|
||||
class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent, CancelableQuery {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CQueryFetchSingleAttribute.class);
|
||||
|
||||
@@ -65,6 +67,8 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent {
|
||||
private final boolean containsCounts;
|
||||
|
||||
private long profileOffset;
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
/**
|
||||
* Create the Sql select based on the request.
|
||||
@@ -111,7 +115,6 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent {
|
||||
value = new CountedValue<>(value, dataReader.getLong());
|
||||
}
|
||||
result.add(value);
|
||||
dataReader.resetColumnPosition();
|
||||
rowCount++;
|
||||
}
|
||||
|
||||
@@ -148,21 +151,27 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent {
|
||||
}
|
||||
|
||||
private void prepareExecute() throws SQLException {
|
||||
|
||||
SpiTransaction t = getTransaction();
|
||||
profileOffset = t.profileOffset();
|
||||
Connection conn = t.getInternalConnection();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
|
||||
if (query.getBufferFetchSizeHint() > 0) {
|
||||
pstmt.setFetchSize(query.getBufferFetchSizeHint());
|
||||
lock.lock();
|
||||
try {
|
||||
query.checkCancelled();
|
||||
SpiTransaction t = getTransaction();
|
||||
profileOffset = t.profileOffset();
|
||||
Connection conn = t.getInternalConnection();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
|
||||
if (query.getBufferFetchSizeHint() > 0) {
|
||||
pstmt.setFetchSize(query.getBufferFetchSizeHint());
|
||||
}
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
}
|
||||
|
||||
bindLog = predicates.bind(pstmt, conn);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
}
|
||||
|
||||
bindLog = predicates.bind(pstmt, conn);
|
||||
dataReader = new RsetDataReader(request.getDataTimeZone(), pstmt.executeQuery());
|
||||
query.checkCancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,4 +204,14 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent {
|
||||
Set<String> getDependentTables() {
|
||||
return queryPlan.getDependentTables();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
lock.lock();
|
||||
try {
|
||||
JdbcClose.cancel(pstmt);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class CQueryIteratorSimple<T> implements QueryIterator<T> {
|
||||
private final CQuery<T> cquery;
|
||||
|
||||
private final OrmQueryRequest<T> request;
|
||||
|
||||
|
||||
private boolean closed;
|
||||
|
||||
CQueryIteratorSimple(CQuery<T> cquery, OrmQueryRequest<T> request) {
|
||||
@@ -54,8 +54,4 @@ class CQueryIteratorSimple<T> implements QueryIterator<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new PersistenceException("Remove not allowed");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +76,4 @@ class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new PersistenceException("Remove not allowed");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import io.ebeaninternal.api.SpiQueryBindCapture;
|
||||
import io.ebeaninternal.api.SpiQueryPlan;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import io.ebeaninternal.server.lib.Str;
|
||||
import io.ebeaninternal.server.util.Str;
|
||||
import io.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
import io.ebeaninternal.server.type.DataBindCapture;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.CancelableQuery;
|
||||
import io.ebean.util.JdbcClose;
|
||||
import io.ebeaninternal.api.SpiProfileTransactionEvent;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
@@ -13,11 +14,12 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Executes the select row count query.
|
||||
*/
|
||||
class CQueryRowCount implements SpiProfileTransactionEvent {
|
||||
class CQueryRowCount implements SpiProfileTransactionEvent, CancelableQuery {
|
||||
|
||||
private final CQueryPlan queryPlan;
|
||||
|
||||
@@ -57,6 +59,8 @@ class CQueryRowCount implements SpiProfileTransactionEvent {
|
||||
private int rowCount;
|
||||
|
||||
private long profileOffset;
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
/**
|
||||
* Create the Sql select based on the request.
|
||||
@@ -110,14 +114,22 @@ class CQueryRowCount implements SpiProfileTransactionEvent {
|
||||
SpiTransaction t = getTransaction();
|
||||
profileOffset = t.profileOffset();
|
||||
Connection conn = t.getInternalConnection();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
lock.lock();
|
||||
try {
|
||||
query.checkCancelled();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
}
|
||||
|
||||
bindLog = predicates.bind(pstmt, conn);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
bindLog = predicates.bind(pstmt, conn);
|
||||
rset = pstmt.executeQuery();
|
||||
query.checkCancelled();
|
||||
|
||||
if (!rset.next()) {
|
||||
throw new PersistenceException("Expecting 1 row but got none?");
|
||||
}
|
||||
@@ -161,4 +173,14 @@ class CQueryRowCount implements SpiProfileTransactionEvent {
|
||||
Set<String> getDependentTables() {
|
||||
return queryPlan.getDependentTables();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
lock.lock();
|
||||
try {
|
||||
JdbcClose.cancel(pstmt);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.CancelableQuery;
|
||||
import io.ebean.util.JdbcClose;
|
||||
import io.ebeaninternal.api.SpiProfileTransactionEvent;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
@@ -10,11 +11,12 @@ import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Executes the delete query.
|
||||
* Executes the update query.
|
||||
*/
|
||||
class CQueryUpdate implements SpiProfileTransactionEvent {
|
||||
class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery {
|
||||
|
||||
private final CQueryPlan queryPlan;
|
||||
|
||||
@@ -45,6 +47,8 @@ class CQueryUpdate implements SpiProfileTransactionEvent {
|
||||
|
||||
private long profileOffset;
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
/**
|
||||
* Create the Sql select based on the request.
|
||||
*/
|
||||
@@ -82,15 +86,22 @@ class CQueryUpdate implements SpiProfileTransactionEvent {
|
||||
SpiTransaction t = getTransaction();
|
||||
profileOffset = t.profileOffset();
|
||||
Connection conn = t.getInternalConnection();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
lock.lock();
|
||||
try {
|
||||
query.checkCancelled();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
}
|
||||
|
||||
bindLog = predicates.bind(pstmt, conn);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
bindLog = predicates.bind(pstmt, conn);
|
||||
rowCount = pstmt.executeUpdate();
|
||||
|
||||
query.checkCancelled();
|
||||
|
||||
long executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
|
||||
request.slowQueryCheck(executionTimeMicros, rowCount);
|
||||
if (queryPlan.executionTime(executionTimeMicros)) {
|
||||
@@ -122,4 +133,14 @@ class CQueryUpdate implements SpiProfileTransactionEvent {
|
||||
.profileStream()
|
||||
.addQueryEvent(query.profileEventId(), profileOffset, desc.getName(), rowCount, query.getProfileId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
lock.lock();
|
||||
try {
|
||||
JdbcClose.cancel(pstmt);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,10 @@ import java.util.HashSet;
|
||||
class DefaultDbSqlContext implements DbSqlContext {
|
||||
|
||||
private static final String COMMA = ", ";
|
||||
|
||||
private static final String PERIOD = ".";
|
||||
private static final int STRING_BUILDER_INITIAL_CAPACITY = 140;
|
||||
|
||||
private static final String tableAliasPlaceHolder = "${ta}";
|
||||
private static final String tableAliasManyPlaceHolder = "${mta}";
|
||||
|
||||
private final String columnAliasPrefix;
|
||||
|
||||
@@ -111,27 +110,22 @@ class DefaultDbSqlContext implements DbSqlContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2) {
|
||||
|
||||
public void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2, String extraWhere) {
|
||||
if (tableJoins == null) {
|
||||
tableJoins = new HashSet<>();
|
||||
}
|
||||
|
||||
String joinKey = table + "-" + a1 + "-" + a2;
|
||||
if (tableJoins.contains(joinKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
tableJoins.add(joinKey);
|
||||
|
||||
sb.append(" ").append(type);
|
||||
boolean addAsOfOnClause = false;
|
||||
if (draftSupport != null) {
|
||||
appendTable(table, draftSupport.getDraftTable(table));
|
||||
|
||||
} else if (!historyQuery) {
|
||||
sb.append(" ").append(table).append(" ");
|
||||
|
||||
} else {
|
||||
// check if there is an associated history table and if so
|
||||
// use the unionAll view - we expect an additional predicate to match
|
||||
@@ -164,13 +158,17 @@ class DefaultDbSqlContext implements DbSqlContext {
|
||||
if (addAsOfOnClause) {
|
||||
sb.append(" and ").append(historySupport.getAsOfPredicate(a2));
|
||||
}
|
||||
if (extraWhere != null && !extraWhere.isEmpty()) {
|
||||
sb.append(" and ");
|
||||
// we will also need a many-table alias here
|
||||
sb.append(extraWhere.replace(tableAliasPlaceHolder, a2).replace(tableAliasManyPlaceHolder, a1));
|
||||
}
|
||||
}
|
||||
|
||||
private void appendTable(String table, String draftTable) {
|
||||
if (draftTable != null) {
|
||||
// there is an associated history table and view so use that
|
||||
sb.append(" ").append(draftTable).append(" ");
|
||||
|
||||
} else {
|
||||
sb.append(" ").append(table).append(" ");
|
||||
}
|
||||
@@ -193,7 +191,6 @@ class DefaultDbSqlContext implements DbSqlContext {
|
||||
|
||||
@Override
|
||||
public String getRelativePrefix(String propName) {
|
||||
|
||||
return currentPrefix == null ? propName : currentPrefix + "." + propName;
|
||||
}
|
||||
|
||||
@@ -228,7 +225,6 @@ class DefaultDbSqlContext implements DbSqlContext {
|
||||
// the same join has already been added.
|
||||
return;
|
||||
}
|
||||
|
||||
// we only want to add this join once
|
||||
formulaJoins.add(converted);
|
||||
sb.append(" ");
|
||||
@@ -263,13 +259,10 @@ class DefaultDbSqlContext implements DbSqlContext {
|
||||
|
||||
@Override
|
||||
public void appendHistorySysPeriod() {
|
||||
|
||||
String tableAlias = tableAliasStack.peek();
|
||||
|
||||
sb.append(COMMA);
|
||||
sb.append(historySupport.getSysPeriodLower(tableAlias));
|
||||
appendColumnAlias();
|
||||
|
||||
sb.append(COMMA);
|
||||
sb.append(historySupport.getSysPeriodUpper(tableAlias));
|
||||
appendColumnAlias();
|
||||
|
||||
+57
-94
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.query;
|
||||
import io.ebean.RowConsumer;
|
||||
import io.ebean.RowMapper;
|
||||
import io.ebean.SqlRow;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.metric.MetricFactory;
|
||||
@@ -10,10 +11,10 @@ import io.ebean.metric.TimedMetricMap;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.core.RelationalQueryEngine;
|
||||
import io.ebeaninternal.server.core.RelationalQueryRequest;
|
||||
import io.ebeaninternal.server.core.RowReader;
|
||||
import io.ebeaninternal.server.persist.Binder;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
@@ -59,12 +60,26 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findEach(RelationalQueryRequest request, Predicate<SqlRow> consumer) {
|
||||
public void findEach(RelationalQueryRequest request, RowConsumer consumer) {
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.ITERATE);
|
||||
request.mapEach(consumer);
|
||||
request.logSummary();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
|
||||
|
||||
} finally {
|
||||
request.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void findEach(RelationalQueryRequest request, RowReader<T> reader, Predicate<T> consumer) {
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.ITERATE);
|
||||
while (request.next()) {
|
||||
if (!consumer.test(readRow(request))) {
|
||||
if (!consumer.test(reader.read())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -79,25 +94,7 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findEach(RelationalQueryRequest request, Consumer<SqlRow> consumer) {
|
||||
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.ITERATE);
|
||||
while (request.next()) {
|
||||
consumer.accept(readRow(request));
|
||||
}
|
||||
request.logSummary();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
|
||||
|
||||
} finally {
|
||||
request.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T findOneMapper(RelationalQueryRequest request, RowMapper<T> mapper) {
|
||||
public <T> T findOne(RelationalQueryRequest request, RowMapper<T> mapper) {
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.BEAN);
|
||||
T value = request.mapOne(mapper);
|
||||
@@ -113,13 +110,15 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> findListMapper(RelationalQueryRequest request, RowMapper<T> mapper) {
|
||||
public <T> List<T> findList(RelationalQueryRequest request, RowReader<T> reader) {
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.LIST);
|
||||
List<T> list = request.mapList(mapper);
|
||||
List<T> rows = new ArrayList<>();
|
||||
while (request.next()) {
|
||||
rows.add(reader.read());
|
||||
}
|
||||
request.logSummary();
|
||||
return list;
|
||||
|
||||
return rows;
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
|
||||
|
||||
@@ -128,12 +127,19 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void findEachRow(RelationalQueryRequest request, RowConsumer consumer) {
|
||||
public <T> T findSingleAttribute(RelationalQueryRequest request, Class<T> cls) {
|
||||
ScalarType<T> scalarType = (ScalarType<T>) binder.getScalarType(cls);
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.LIST);
|
||||
request.mapEach(consumer);
|
||||
request.executeSql(binder, SpiQuery.Type.ATTRIBUTE);
|
||||
final DataReader dataReader = binder.createDataReader(request.getResultSet());
|
||||
T value = null;
|
||||
if (dataReader.next()) {
|
||||
value = scalarType.read(dataReader);
|
||||
}
|
||||
request.logSummary();
|
||||
return value;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
|
||||
@@ -147,68 +153,13 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
|
||||
@Override
|
||||
public <T> List<T> findSingleAttributeList(RelationalQueryRequest request, Class<T> cls) {
|
||||
ScalarType<T> scalarType = (ScalarType<T>) binder.getScalarType(cls);
|
||||
return findScalarList(request, scalarType);
|
||||
}
|
||||
|
||||
private <T> List<T> findScalarList(RelationalQueryRequest request, ScalarType<T> scalarType) {
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.ATTRIBUTE);
|
||||
List<T> list = new ArrayList<>();
|
||||
while (request.next()) {
|
||||
request.incrementRows();
|
||||
list.add(scalarType.read(binder.createDataReader(request.getResultSet())));
|
||||
final DataReader dataReader = binder.createDataReader(request.getResultSet());
|
||||
List<T> rows = new ArrayList<>();
|
||||
while (dataReader.next()) {
|
||||
rows.add(scalarType.read(dataReader));
|
||||
}
|
||||
|
||||
request.logSummary();
|
||||
return list;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
|
||||
|
||||
} finally {
|
||||
request.close();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T findSingleAttribute(RelationalQueryRequest request, Class<T> cls) {
|
||||
|
||||
ScalarType<T> scalarType = (ScalarType<T>) binder.getScalarType(cls);
|
||||
return findScalar(request, scalarType);
|
||||
}
|
||||
|
||||
private <T> T findScalar(RelationalQueryRequest request, ScalarType<T> scalarType) {
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.ATTRIBUTE);
|
||||
|
||||
T value = null;
|
||||
if (request.next()) {
|
||||
request.incrementRows();
|
||||
value = scalarType.read(binder.createDataReader(request.getResultSet()));
|
||||
}
|
||||
|
||||
request.logSummary();
|
||||
return value;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
|
||||
|
||||
} finally {
|
||||
request.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SqlRow> findList(RelationalQueryRequest request) {
|
||||
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.LIST);
|
||||
List<SqlRow> rows = new ArrayList<>();
|
||||
while (request.next()) {
|
||||
rows.add(readRow(request));
|
||||
}
|
||||
|
||||
request.logSummary();
|
||||
return rows;
|
||||
|
||||
@@ -220,11 +171,23 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the row from the ResultSet and return as a MapBean.
|
||||
*/
|
||||
private SqlRow readRow(RelationalQueryRequest request) throws SQLException {
|
||||
return request.createNewRow();
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> void findSingleAttributeEach(RelationalQueryRequest request, Class<T> cls, Consumer<T> consumer) {
|
||||
ScalarType<T> scalarType = (ScalarType<T>) binder.getScalarType(cls);
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.ATTRIBUTE);
|
||||
final DataReader dataReader = binder.createDataReader(request.getResultSet());
|
||||
while (dataReader.next()) {
|
||||
consumer.accept(scalarType.read(dataReader));
|
||||
}
|
||||
request.logSummary();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
|
||||
|
||||
} finally {
|
||||
request.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.QueryIterator;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.core.DtoQueryRequest;
|
||||
import io.ebeaninternal.server.persist.Binder;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
@@ -27,20 +29,29 @@ public class DtoQueryEngine {
|
||||
}
|
||||
return rows;
|
||||
|
||||
} catch (Throwable e) {
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
|
||||
} finally {
|
||||
request.close();
|
||||
}
|
||||
}
|
||||
|
||||
public <T> QueryIterator<T> findIterate(DtoQueryRequest<T> request) {
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.ITERATE);
|
||||
return new DtoQueryIterator<>(request);
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> void findEach(DtoQueryRequest<T> request, Consumer<T> consumer) {
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.ITERATE);
|
||||
while (request.next()) {
|
||||
consumer.accept(request.readNextBean());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
|
||||
} finally {
|
||||
request.close();
|
||||
@@ -77,9 +88,8 @@ public class DtoQueryEngine {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
|
||||
|
||||
} finally {
|
||||
request.close();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.QueryIterator;
|
||||
import io.ebeaninternal.server.core.DtoQueryRequest;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
class DtoQueryIterator<T> implements QueryIterator<T> {
|
||||
|
||||
private final DtoQueryRequest<T> request;
|
||||
private boolean closed;
|
||||
|
||||
DtoQueryIterator(DtoQueryRequest<T> request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
boolean result = false;
|
||||
try {
|
||||
result = request.next();
|
||||
return result;
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException(e);
|
||||
} finally {
|
||||
if (!result) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
try {
|
||||
return request.readNextBean();
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
request.close();
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -47,16 +47,6 @@ final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
|
||||
ctx.popTableAlias();
|
||||
}
|
||||
|
||||
/**
|
||||
* append extraWhere to the join.
|
||||
*/
|
||||
@Override
|
||||
protected SqlJoinType appendFromAsJoin(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
SqlJoinType join = super.appendFromAsJoin(ctx, joinType);
|
||||
super.appendExtraWhere(ctx);
|
||||
return join;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendExtraWhere(DbSqlContext ctx) {
|
||||
// extraWhere is already appended to the tableJoin
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package io.ebeaninternal.server.querydefn;
|
||||
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import io.ebean.CancelableQuery;
|
||||
import io.ebeaninternal.api.SpiCancelableQuery;
|
||||
|
||||
/**
|
||||
* Common code for Dto/Orm/RelationalQuery
|
||||
*
|
||||
* @author Roland Praml, FOCONIS AG
|
||||
*
|
||||
*/
|
||||
public class AbstractQuery implements SpiCancelableQuery {
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
private CancelableQuery cancelableQuery;
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
lock.lock();
|
||||
try {
|
||||
if (!cancelled) {
|
||||
cancelled = true;
|
||||
if (cancelableQuery != null) {
|
||||
cancelableQuery.cancel();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCancelled() {
|
||||
if (cancelled) {
|
||||
throw new PersistenceException("Query was cancelled");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelableQuery(CancelableQuery cancelableQuery) {
|
||||
lock.lock();
|
||||
try {
|
||||
checkCancelled();
|
||||
this.cancelableQuery = cancelableQuery;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.querydefn;
|
||||
|
||||
import io.ebean.DtoQuery;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.QueryIterator;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.SpiDtoQuery;
|
||||
@@ -15,11 +16,12 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Default implementation of DtoQuery.
|
||||
*/
|
||||
public class DefaultDtoQuery<T> implements SpiDtoQuery<T> {
|
||||
public class DefaultDtoQuery<T> extends AbstractQuery implements SpiDtoQuery<T> {
|
||||
|
||||
private final SpiEbeanServer server;
|
||||
|
||||
@@ -112,6 +114,16 @@ public class DefaultDtoQuery<T> implements SpiDtoQuery<T> {
|
||||
server.findDtoEachWhile(this, consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryIterator<T> findIterate() {
|
||||
return server.findDtoIterate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<T> findStream() {
|
||||
return server.findDtoStream(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> findList() {
|
||||
return server.findDtoList(this);
|
||||
|
||||
@@ -55,10 +55,10 @@ import io.ebeaninternal.server.deploy.BeanNaturalKey;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.InheritInfo;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import io.ebeaninternal.server.expression.DefaultExpressionList;
|
||||
import io.ebeaninternal.server.expression.IdInExpression;
|
||||
import io.ebeaninternal.server.expression.SimpleExpression;
|
||||
import io.ebeaninternal.server.query.CancelableQuery;
|
||||
import io.ebeaninternal.server.query.NativeSqlQueryPlanKey;
|
||||
import io.ebeaninternal.server.rawsql.SpiRawSql;
|
||||
import io.ebeaninternal.server.transaction.ExternalJdbcTransaction;
|
||||
@@ -81,7 +81,7 @@ import java.util.stream.Stream;
|
||||
/**
|
||||
* Default implementation of an Object Relational query.
|
||||
*/
|
||||
public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
|
||||
private static final String DEFAULT_QUERY_NAME = "default";
|
||||
|
||||
@@ -113,10 +113,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
private ProfilingListener profilingListener;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
private CancelableQuery cancelableQuery;
|
||||
|
||||
private Type type;
|
||||
|
||||
private String label;
|
||||
@@ -539,6 +535,14 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
if (havingExpressions != null) {
|
||||
havingExpressions.containsMany(beanDescriptor, manyWhereJoins);
|
||||
}
|
||||
if (orderBy != null) {
|
||||
for (Property orderProperty : orderBy.getProperties()) {
|
||||
ElPropertyDeploy elProp = beanDescriptor.getElPropertyDeploy(orderProperty.getProperty());
|
||||
if (elProp != null && elProp.containsFormulaWithJoin()) {
|
||||
manyWhereJoins.addFormulaWithJoin(orderProperty.getProperty());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -856,6 +860,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
copy.parentNode = parentNode;
|
||||
copy.forUpdate = forUpdate;
|
||||
copy.rawSql = rawSql;
|
||||
setCancelableQuery(copy); // required to cancel findId query
|
||||
return copy;
|
||||
}
|
||||
|
||||
@@ -2046,16 +2051,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return futureFetchAudit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelableQuery(CancelableQuery cancelableQuery) {
|
||||
lock.lock();
|
||||
try {
|
||||
this.cancelableQuery = cancelableQuery;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> setBaseTable(String baseTable) {
|
||||
this.baseTable = baseTable;
|
||||
@@ -2083,28 +2078,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return rootTableAlias != null ? rootTableAlias : defaultAlias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
lock.lock();
|
||||
try {
|
||||
if (!cancelled && cancelableQuery != null) {
|
||||
cancelled = true;
|
||||
cancelableQuery.cancel();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
lock.lock();
|
||||
try {
|
||||
return cancelled;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> validate() {
|
||||
|
||||
+25
-5
@@ -17,15 +17,15 @@ import java.util.function.Predicate;
|
||||
/**
|
||||
* Default implementation of SQuery - SQL Query.
|
||||
*/
|
||||
public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
public class DefaultRelationalQuery extends AbstractQuery implements SpiSqlQuery {
|
||||
|
||||
private static final long serialVersionUID = -1098305779779591068L;
|
||||
|
||||
private final transient SpiEbeanServer server;
|
||||
|
||||
private String label;
|
||||
private final String query;
|
||||
|
||||
private String query;
|
||||
private String label;
|
||||
|
||||
private int firstRow;
|
||||
|
||||
@@ -208,19 +208,25 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
return query;
|
||||
}
|
||||
|
||||
<T> T mapperFindOne(RowMapper<T> mapper) {
|
||||
private <T> T mapperFindOne(RowMapper<T> mapper) {
|
||||
return server.findOneMapper(this, mapper);
|
||||
}
|
||||
|
||||
<T> List<T> mapperFindList(RowMapper<T> mapper) {
|
||||
private <T> List<T> mapperFindList(RowMapper<T> mapper) {
|
||||
return server.findListMapper(this, mapper);
|
||||
}
|
||||
|
||||
private <T> void mapperFindEach(RowMapper<T> mapper, Consumer<T> consumer) {
|
||||
server.findEachRow(this, (resultSet, rowNum) -> consumer.accept(mapper.map(resultSet, rowNum)));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Override
|
||||
public <T> TypeQuery<T> mapToScalar(Class<T> attributeType) {
|
||||
return new Scalar(attributeType);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Override
|
||||
public <T> TypeQuery<T> mapTo(RowMapper<T> mapper) {
|
||||
return new Mapper(mapper);
|
||||
@@ -248,6 +254,15 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
public List<T> findList() {
|
||||
return findSingleAttributeList(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findEach(Consumer<T> consumer) {
|
||||
scalarFindEach(type, consumer);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> void scalarFindEach(Class<T> type, Consumer<T> consumer) {
|
||||
server.findSingleAttributeEach(this, type, consumer);
|
||||
}
|
||||
|
||||
private class Mapper<T> implements SqlQuery.TypeQuery<T> {
|
||||
@@ -272,5 +287,10 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
public List<T> findList() {
|
||||
return mapperFindList(mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findEach(Consumer<T> consumer) {
|
||||
mapperFindEach(mapper, consumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import io.ebeaninternal.server.query.DefaultSqlRow;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
public class DRawSqlService implements SpiRawSqlService {
|
||||
|
||||
@@ -48,7 +49,26 @@ public class DRawSqlService implements SpiRawSqlService {
|
||||
if (ret.containsKey(name)) {
|
||||
name = combine(meta.getSchemaName(i), meta.getTableName(i), name);
|
||||
}
|
||||
ret.put(name, resultSet.getObject(i));
|
||||
|
||||
// convert (C/B)LOBs to java objects.
|
||||
// A java.sql.Clob depends on an open connection, so storing this object in a map
|
||||
// that is accessed later, when the connection is closed, will result in a "connection is closed" exception.
|
||||
// From the java.sql.Clob documentation: "... which means that a Clob object contains a logical pointer to the SQL CLOB
|
||||
// data rather than the data itself."
|
||||
switch (meta.getColumnType(i)) {
|
||||
case Types.CLOB:
|
||||
case Types.NCLOB:
|
||||
ret.put(name, resultSet.getString(i));
|
||||
break;
|
||||
|
||||
case Types.BLOB:
|
||||
ret.put(name, resultSet.getBytes(i));
|
||||
break;
|
||||
|
||||
default:
|
||||
ret.put(name, resultSet.getObject(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
+15
-13
@@ -2,6 +2,9 @@ package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebeaninternal.api.SpiBeanType;
|
||||
import io.ebeaninternal.api.SpiBeanTypeManager;
|
||||
import io.ebeaninternal.api.SpiPersistenceContext;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@@ -11,19 +14,16 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
* <p>
|
||||
* Ensures only one instance of a bean is used according to its type and unique
|
||||
* id.
|
||||
* </p>
|
||||
* <p>
|
||||
* PersistenceContext lives on a Transaction and as such is expected to only
|
||||
* have a single thread accessing it at a time. This is not expected to be used
|
||||
* concurrently.
|
||||
* </p>
|
||||
* <p>
|
||||
* Duplicate beans are ones having the same type and unique id value. These are
|
||||
* considered duplicates and replaced by the bean instance that was already
|
||||
* loaded into the PersistenceContext.
|
||||
* </p>
|
||||
*/
|
||||
public final class DefaultPersistenceContext implements PersistenceContext {
|
||||
public final class DefaultPersistenceContext implements SpiPersistenceContext {
|
||||
|
||||
/**
|
||||
* Map used hold caches. One cache per bean type.
|
||||
@@ -199,12 +199,12 @@ public final class DefaultPersistenceContext implements PersistenceContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> dirtyBeans() {
|
||||
public List<Object> dirtyBeans(SpiBeanTypeManager manager) {
|
||||
lock.lock();
|
||||
try {
|
||||
List<Object> list = new ArrayList<>();
|
||||
for (ClassContext classContext : typeCache.values()) {
|
||||
classContext.dirtyBeans(list);
|
||||
classContext.dirtyBeans(manager, list);
|
||||
}
|
||||
return list;
|
||||
} finally {
|
||||
@@ -223,26 +223,26 @@ public final class DefaultPersistenceContext implements PersistenceContext {
|
||||
}
|
||||
|
||||
private ClassContext getClassContext(Class<?> rootType) {
|
||||
return typeCache.computeIfAbsent(rootType, k -> new ClassContext());
|
||||
return typeCache.computeIfAbsent(rootType, k -> new ClassContext(rootType));
|
||||
}
|
||||
|
||||
private static class ClassContext {
|
||||
|
||||
private final Map<Object, Object> map = new HashMap<>();
|
||||
|
||||
private final Class<?> rootType;
|
||||
private Set<Object> deleteSet;
|
||||
|
||||
private int initialSize;
|
||||
|
||||
private ClassContext parent;
|
||||
|
||||
private ClassContext() {
|
||||
private ClassContext(Class<?> rootType) {
|
||||
this.rootType = rootType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create as a shallow copy.
|
||||
*/
|
||||
private ClassContext(ClassContext source, boolean initial) {
|
||||
this.rootType = source.rootType;
|
||||
if (initial || source.isTransfer()) {
|
||||
parent = source.transferParent();
|
||||
initialSize = parent.size();
|
||||
@@ -334,9 +334,11 @@ public final class DefaultPersistenceContext implements PersistenceContext {
|
||||
/**
|
||||
* Add the dirty beans to the list.
|
||||
*/
|
||||
void dirtyBeans(List<Object> list) {
|
||||
void dirtyBeans(SpiBeanTypeManager manager, List<Object> list) {
|
||||
final SpiBeanType beanType = manager.getBeanType(rootType);
|
||||
for (Object value : map.values()) {
|
||||
if (((EntityBean) value)._ebean_getIntercept().isDirty()) {
|
||||
EntityBean bean = (EntityBean) value;
|
||||
if (bean._ebean_getIntercept().isDirty() || beanType.isToManyDirty(bean)) {
|
||||
list.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-8
@@ -3,13 +3,9 @@ package io.ebeaninternal.server.transaction;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.TransactionCallback;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebeaninternal.api.SpiProfileTransactionEvent;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.TransactionEvent;
|
||||
import io.ebeaninternal.api.TxnProfileEventCodes;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
@@ -58,7 +54,7 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
/**
|
||||
* Holder of the objects fetched to ensure unique objects are used.
|
||||
*/
|
||||
private PersistenceContext persistenceContext;
|
||||
private SpiPersistenceContext persistenceContext;
|
||||
|
||||
private Object tenantId;
|
||||
|
||||
@@ -421,7 +417,7 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
* Return the persistence context associated with this transaction.
|
||||
*/
|
||||
@Override
|
||||
public PersistenceContext getPersistenceContext() {
|
||||
public SpiPersistenceContext getPersistenceContext() {
|
||||
return persistenceContext;
|
||||
}
|
||||
|
||||
@@ -434,7 +430,7 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public void setPersistenceContext(PersistenceContext context) {
|
||||
public void setPersistenceContext(SpiPersistenceContext context) {
|
||||
if (!isActive()) {
|
||||
throw new IllegalStateException(illegalStateMessage);
|
||||
}
|
||||
|
||||
@@ -3,20 +3,16 @@ package io.ebeaninternal.server.transaction;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.TransactionCallback;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform.OnQueryOnly;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebeaninternal.api.SpiProfileTransactionEvent;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.TransactionEvent;
|
||||
import io.ebeaninternal.api.TxnProfileEventCodes;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.lib.Str;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeaninternal.server.persist.BatchedSqlException;
|
||||
import io.ebeaninternal.server.util.Str;
|
||||
import io.ebeanservice.docstore.api.DocStoreTransaction;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -25,12 +21,7 @@ import javax.persistence.PersistenceException;
|
||||
import javax.persistence.RollbackException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
@@ -99,7 +90,7 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
/**
|
||||
* Holder of the objects fetched to ensure unique objects are used.
|
||||
*/
|
||||
private PersistenceContext persistenceContext;
|
||||
private SpiPersistenceContext persistenceContext;
|
||||
|
||||
/**
|
||||
* Used to give developers more control over the insert update and delete
|
||||
@@ -806,7 +797,7 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
* Return the persistence context associated with this transaction.
|
||||
*/
|
||||
@Override
|
||||
public PersistenceContext getPersistenceContext() {
|
||||
public SpiPersistenceContext getPersistenceContext() {
|
||||
return persistenceContext;
|
||||
}
|
||||
|
||||
@@ -816,10 +807,9 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
* This could be considered similar to EJB3 Extended PersistanceContext. In
|
||||
* that you get the PersistanceContext from a transaction, hold onto it, and
|
||||
* then set it back later to a second transaction.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public void setPersistenceContext(PersistenceContext context) {
|
||||
public void setPersistenceContext(SpiPersistenceContext context) {
|
||||
if (!isActive()) {
|
||||
throw new IllegalStateException(illegalStateMessage);
|
||||
}
|
||||
@@ -951,11 +941,13 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
*/
|
||||
private void connectionEndForQueryOnly() {
|
||||
try {
|
||||
withEachCallback(TransactionCallback::preCommit);
|
||||
if (onQueryOnly == OnQueryOnly.COMMIT) {
|
||||
performCommit();
|
||||
} else {
|
||||
performRollback();
|
||||
}
|
||||
withEachCallback(TransactionCallback::postCommit);
|
||||
} catch (SQLException e) {
|
||||
logger.error("Error when ending a query only transaction via " + onQueryOnly, e);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ package io.ebeaninternal.server.transaction;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.TransactionCallback;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebeaninternal.api.SpiPersistenceContext;
|
||||
import io.ebeaninternal.api.SpiProfileTransactionEvent;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.TransactionEvent;
|
||||
@@ -349,12 +349,12 @@ class NoTransaction implements SpiTransaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistenceContext getPersistenceContext() {
|
||||
public SpiPersistenceContext getPersistenceContext() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPersistenceContext(PersistenceContext context) {
|
||||
public void setPersistenceContext(SpiPersistenceContext context) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiTransactionProxy;
|
||||
import io.ebeaninternal.server.lib.Str;
|
||||
import io.ebeaninternal.server.util.Str;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.Connection;
|
||||
|
||||
+3
-12
@@ -5,7 +5,6 @@ import io.ebean.ProfileLocation;
|
||||
import io.ebean.TxScope;
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
import io.ebean.annotation.TxType;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.cache.ServerCacheNotification;
|
||||
import io.ebean.cache.ServerCacheNotify;
|
||||
import io.ebean.config.CurrentTenantProvider;
|
||||
@@ -19,15 +18,7 @@ import io.ebean.metric.MetricFactory;
|
||||
import io.ebean.metric.TimedMetric;
|
||||
import io.ebean.metric.TimedMetricMap;
|
||||
import io.ebean.plugin.SpiServer;
|
||||
import io.ebeaninternal.api.ScopeTrans;
|
||||
import io.ebeaninternal.api.ScopedTransaction;
|
||||
import io.ebeaninternal.api.SpiLogManager;
|
||||
import io.ebeaninternal.api.SpiLogger;
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiTransactionManager;
|
||||
import io.ebeaninternal.api.TransactionEvent;
|
||||
import io.ebeaninternal.api.TransactionEventTable;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.api.TransactionEventTable.TableIUD;
|
||||
import io.ebeaninternal.server.cache.CacheChangeSet;
|
||||
import io.ebeaninternal.server.cluster.ClusterManager;
|
||||
@@ -770,8 +761,8 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
/**
|
||||
* Experimental - find dirty beans in the persistence context and persist them.
|
||||
*/
|
||||
public void flushTransparent(PersistenceContext persistenceContext, SpiTransaction transaction) {
|
||||
List<Object> dirtyBeans = persistenceContext.dirtyBeans();
|
||||
public void flushTransparent(SpiPersistenceContext persistenceContext, SpiTransaction transaction) {
|
||||
List<Object> dirtyBeans = persistenceContext.dirtyBeans(beanDescriptorManager);
|
||||
if (!dirtyBeans.isEmpty()) {
|
||||
server.updateAll(dirtyBeans, transaction);
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.ModifyAwareType;
|
||||
|
||||
/**
|
||||
* Check dirty state of json value which might be modify aware.
|
||||
*/
|
||||
class CheckMarkedDirty {
|
||||
|
||||
/**
|
||||
* Return true if the value should be considered dirty (and included in an update).
|
||||
*/
|
||||
static boolean isDirty(Object value) {
|
||||
if (value instanceof ModifyAwareType) {
|
||||
ModifyAwareType modifyAware = (ModifyAwareType) value;
|
||||
if (modifyAware.isMarkedDirty()) {
|
||||
// reset the dirty state (consider not dirty after update)
|
||||
modifyAware.setMarkedDirty(false);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,7 @@ package io.ebeaninternal.server.type;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
|
||||
import io.ebean.annotation.DbArray;
|
||||
import io.ebean.annotation.DbEnumType;
|
||||
import io.ebean.annotation.DbEnumValue;
|
||||
import io.ebean.annotation.EnumValue;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.annotation.*;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.config.JsonConfig;
|
||||
import io.ebean.config.PlatformConfig;
|
||||
@@ -34,125 +30,70 @@ import org.slf4j.LoggerFactory;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.EnumType;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.*;
|
||||
import java.sql.Date;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
import java.sql.Types;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.Month;
|
||||
import java.time.MonthDay;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.OffsetTime;
|
||||
import java.time.Year;
|
||||
import java.time.YearMonth;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Currency;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
import java.time.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Default implementation of TypeManager.
|
||||
* <p>
|
||||
* Manages the list of ScalarType that is available.
|
||||
* </p>
|
||||
*/
|
||||
public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultTypeManager.class);
|
||||
|
||||
private final ConcurrentHashMap<Class<?>, ScalarType<?>> typeMap;
|
||||
|
||||
private final ConcurrentHashMap<Integer, ScalarType<?>> nativeMap;
|
||||
|
||||
private final ConcurrentHashMap<String, ScalarType<?>> logicalMap;
|
||||
|
||||
private final DefaultTypeFactory extraTypeFactory;
|
||||
|
||||
private final ScalarType<?> hstoreType = new ScalarTypePostgresHstore();
|
||||
|
||||
private final ScalarTypeFile fileType = new ScalarTypeFile();
|
||||
|
||||
private final ScalarType<?> charType = new ScalarTypeChar();
|
||||
|
||||
private final ScalarType<?> charArrayType = new ScalarTypeCharArray();
|
||||
|
||||
private final ScalarType<?> longVarcharType = new ScalarTypeLongVarchar();
|
||||
|
||||
private final ScalarType<?> clobType = new ScalarTypeClob();
|
||||
|
||||
private final ScalarType<?> byteType = new ScalarTypeByte();
|
||||
|
||||
private final ScalarType<?> binaryType = new ScalarTypeBytesBinary();
|
||||
|
||||
private final ScalarType<?> blobType = new ScalarTypeBytesBlob();
|
||||
|
||||
private final ScalarType<?> varbinaryType = new ScalarTypeBytesVarbinary();
|
||||
|
||||
private final ScalarType<?> longVarbinaryType = new ScalarTypeBytesLongVarbinary();
|
||||
|
||||
private final ScalarType<?> shortType = new ScalarTypeShort();
|
||||
|
||||
private final ScalarType<?> integerType = ScalarTypeInteger.INSTANCE;
|
||||
|
||||
private final ScalarType<?> longType = new ScalarTypeLong();
|
||||
|
||||
private final ScalarType<?> doubleType = new ScalarTypeDouble();
|
||||
|
||||
private final ScalarType<?> floatType = new ScalarTypeFloat();
|
||||
|
||||
private final ScalarType<?> bigDecimalType = new ScalarTypeBigDecimal();
|
||||
|
||||
private final ScalarType<?> timeType = new ScalarTypeTime();
|
||||
|
||||
private final ScalarType<?> urlType = new ScalarTypeURL();
|
||||
private final ScalarType<?> uriType = new ScalarTypeURI();
|
||||
private final ScalarType<?> localeType = new ScalarTypeLocale();
|
||||
private final ScalarType<?> currencyType = new ScalarTypeCurrency();
|
||||
private final ScalarType<?> timeZoneType = new ScalarTypeTimeZone();
|
||||
|
||||
private final ScalarType<?> stringType = ScalarTypeString.INSTANCE;
|
||||
|
||||
private final ScalarType<?> classType = new ScalarTypeClass();
|
||||
|
||||
private final JsonConfig.DateTime jsonDateTime;
|
||||
private final JsonConfig.Date jsonDate;
|
||||
|
||||
private final Object objectMapper;
|
||||
|
||||
private final boolean objectMapperPresent;
|
||||
|
||||
private final boolean postgres;
|
||||
|
||||
private final TypeJsonManager jsonManager;
|
||||
private final boolean offlineMigrationGeneration;
|
||||
|
||||
private final EnumType defaultEnumType;
|
||||
|
||||
// OPTIONAL ScalarTypes registered if Jackson/JsonNode is in the classpath
|
||||
@@ -191,24 +132,20 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
this.typeMap = new ConcurrentHashMap<>();
|
||||
this.nativeMap = new ConcurrentHashMap<>();
|
||||
this.logicalMap = new ConcurrentHashMap<>();
|
||||
|
||||
this.postgres = isPostgres(config.getDatabasePlatform());
|
||||
this.objectMapperPresent = config.getClassLoadConfig().isJacksonObjectMapperPresent();
|
||||
this.objectMapper = (objectMapperPresent) ? initObjectMapper(config) : null;
|
||||
|
||||
this.jsonManager = (objectMapperPresent) ? new TypeJsonManager(postgres, objectMapper, config.isJsonDirtyByDefault()) : null;
|
||||
this.extraTypeFactory = new DefaultTypeFactory(config);
|
||||
this.postgres = isPostgres(config.getDatabasePlatform());
|
||||
this.arrayTypeListFactory = arrayTypeListFactory(config.getDatabasePlatform());
|
||||
this.arrayTypeSetFactory = arrayTypeSetFactory(config.getDatabasePlatform());
|
||||
|
||||
this.offlineMigrationGeneration = DbOffline.isGenerateMigration();
|
||||
|
||||
this.defaultEnumType = config.getDefaultEnumType();
|
||||
|
||||
initialiseStandard(config);
|
||||
initialiseJavaTimeTypes(config);
|
||||
initialiseJodaTypes(config);
|
||||
initialiseJacksonTypes(config);
|
||||
|
||||
loadTypesFromProviders(config, objectMapper);
|
||||
loadGeoTypeBinder(config);
|
||||
|
||||
@@ -489,7 +426,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
if (objectMapper == null) {
|
||||
throw new IllegalArgumentException("Type [" + type + "] unsupported for @DbJson mapping - Jackson ObjectMapper not present");
|
||||
}
|
||||
return ScalarTypeJsonObjectMapper.createTypeFor(postgres, (AnnotatedField) prop.getJacksonField(), (ObjectMapper) objectMapper, dbType, docType);
|
||||
return ScalarTypeJsonObjectMapper.createTypeFor(jsonManager, (AnnotatedField) prop.getJacksonField(), dbType, docType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -588,8 +525,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
private ScalarTypeEnum<?> createEnumScalarType2(Class<?> enumType) {
|
||||
boolean integerType = true;
|
||||
Map<String, String> nameValueMap = new LinkedHashMap<>();
|
||||
Field[] fields = enumType.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
for (Field field : enumType.getDeclaredFields()) {
|
||||
EnumValue enumValue = AnnotationUtil.get(field, EnumValue.class);
|
||||
if (enumValue != null) {
|
||||
nameValueMap.put(field.getName(), enumValue.value());
|
||||
@@ -652,8 +588,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
}
|
||||
|
||||
private ScalarTypeEnum<?> createEnumScalarTypePerExtentions(Class<? extends Enum<?>> enumType) {
|
||||
Method[] methods = enumType.getMethods();
|
||||
for (Method method : methods) {
|
||||
for (Method method : enumType.getMethods()) {
|
||||
DbEnumValue dbValue = AnnotationUtil.get(method, DbEnumValue.class);
|
||||
if (dbValue != null) {
|
||||
boolean integerValues = DbEnumType.INTEGER == dbValue.storage();
|
||||
@@ -668,7 +603,6 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
* Create the Mapping of Enum fields to DB values using EnumValue annotations.
|
||||
* <p>
|
||||
* Return null if the EnumValue annotations are not present/used.
|
||||
* </p>
|
||||
*/
|
||||
private ScalarTypeEnum<?> createEnumScalarTypeDbValue(Class<? extends Enum<?>> enumType, Method method, boolean integerType, int length, boolean withConstraint) {
|
||||
Map<String, String> nameValueMap = new LinkedHashMap<>();
|
||||
@@ -697,11 +631,11 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
EnumToDbValueMap<?> beanDbMap = EnumToDbValueMap.create(integerType);
|
||||
int maxValueLen = 0;
|
||||
for (Map.Entry<String, String> entry : nameValueMap.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
String name = entry.getKey().trim();
|
||||
String value = entry.getValue();
|
||||
maxValueLen = Math.max(maxValueLen, value.length());
|
||||
Object enumValue = Enum.valueOf(enumType, name.trim());
|
||||
beanDbMap.add(enumValue, value, name.trim());
|
||||
Object enumValue = Enum.valueOf(enumType, name);
|
||||
beanDbMap.add(enumValue, value, name);
|
||||
}
|
||||
if (dbColumnLength == 0 && !integerType) {
|
||||
dbColumnLength = maxValueLen;
|
||||
@@ -711,31 +645,24 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
/**
|
||||
* Automatically find any ScalarTypes by searching through the class path.
|
||||
* <p>
|
||||
* In avaje.properties define a list of packages in which ScalarTypes are
|
||||
* found. This will search for any class that implements the ScalarType
|
||||
* interface and register it with this TypeManager.
|
||||
* </p>
|
||||
*/
|
||||
private void initialiseCustomScalarTypes(BootupClasses bootupClasses) {
|
||||
for (Class<? extends ScalarType<?>> cls : bootupClasses.getScalarTypes()) {
|
||||
try {
|
||||
ScalarType<?> scalarType;
|
||||
if (objectMapper == null) {
|
||||
scalarType = cls.newInstance();
|
||||
scalarType = cls.getDeclaredConstructor().newInstance();
|
||||
} else {
|
||||
try {
|
||||
// first try objectMapper constructor
|
||||
Constructor<? extends ScalarType<?>> constructor = cls.getConstructor(ObjectMapper.class);
|
||||
scalarType = constructor.newInstance((ObjectMapper) objectMapper);
|
||||
scalarType = cls.getDeclaredConstructor(ObjectMapper.class).newInstance(objectMapper);
|
||||
} catch (NoSuchMethodException e) {
|
||||
scalarType = cls.newInstance();
|
||||
scalarType = cls.getDeclaredConstructor().newInstance();
|
||||
}
|
||||
}
|
||||
addCustomType(scalarType);
|
||||
} catch (Exception e) {
|
||||
String msg = "Error loading ScalarType [" + cls.getName() + "]";
|
||||
logger.error(msg, e);
|
||||
logger.error("Error loading ScalarType [" + cls.getName() + "]", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -755,8 +682,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void initialiseScalarConverters(BootupClasses bootupClasses) {
|
||||
List<Class<? extends ScalarTypeConverter<?, ?>>> foundTypes = bootupClasses.getScalarConverters();
|
||||
for (Class<? extends ScalarTypeConverter<?, ?>> foundType : foundTypes) {
|
||||
for (Class<? extends ScalarTypeConverter<?, ?>> foundType : bootupClasses.getScalarConverters()) {
|
||||
try {
|
||||
Class<?>[] paramTypes = TypeReflectHelper.getParams(foundType, ScalarTypeConverter.class);
|
||||
if (paramTypes.length != 2) {
|
||||
@@ -768,7 +694,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
if (wrappedType == null) {
|
||||
throw new IllegalStateException("Could not find ScalarType for: " + paramTypes[1]);
|
||||
}
|
||||
ScalarTypeConverter converter = foundType.newInstance();
|
||||
ScalarTypeConverter converter = foundType.getDeclaredConstructor().newInstance();
|
||||
ScalarTypeWrapper stw = new ScalarTypeWrapper(logicalType, wrappedType, converter);
|
||||
logger.debug("Register ScalarTypeWrapper from {} -> {} using:{}", logicalType, persistType, foundType);
|
||||
add(stw);
|
||||
@@ -780,8 +706,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void initialiseAttributeConverters(BootupClasses bootupClasses) {
|
||||
List<Class<? extends AttributeConverter<?, ?>>> foundTypes = bootupClasses.getAttributeConverters();
|
||||
for (Class<? extends AttributeConverter<?, ?>> foundType : foundTypes) {
|
||||
for (Class<? extends AttributeConverter<?, ?>> foundType : bootupClasses.getAttributeConverters()) {
|
||||
try {
|
||||
Class<?>[] paramTypes = TypeReflectHelper.getParams(foundType, AttributeConverter.class);
|
||||
if (paramTypes.length != 2) {
|
||||
@@ -793,7 +718,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
if (wrappedType == null) {
|
||||
throw new IllegalStateException("Could not find ScalarType for: " + paramTypes[1]);
|
||||
}
|
||||
AttributeConverter converter = foundType.newInstance();
|
||||
AttributeConverter converter = foundType.getDeclaredConstructor().newInstance();
|
||||
ScalarTypeWrapper stw = new ScalarTypeWrapper(logicalType, wrappedType, new AttributeConverterAdapter(converter));
|
||||
logger.debug("Register ScalarTypeWrapper from {} -> {} using:{}", logicalType, persistType, foundType);
|
||||
add(stw);
|
||||
@@ -825,12 +750,15 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
}
|
||||
|
||||
private void initialiseJavaTimeTypes(DatabaseConfig config) {
|
||||
|
||||
ZoneId zoneId = getZoneId(config);
|
||||
|
||||
typeMap.put(java.nio.file.Path.class, new ScalarTypePath());
|
||||
addType(java.time.Period.class, new ScalarTypePeriod());
|
||||
addType(java.time.LocalDate.class, new ScalarTypeLocalDate(jsonDate));
|
||||
addType(java.time.LocalDateTime.class, new ScalarTypeLocalDateTime(jsonDateTime));
|
||||
addType(OffsetDateTime.class, new ScalarTypeOffsetDateTime(jsonDateTime));
|
||||
addType(ZonedDateTime.class, new ScalarTypeZonedDateTime(jsonDateTime));
|
||||
addType(OffsetDateTime.class, new ScalarTypeOffsetDateTime(jsonDateTime, zoneId));
|
||||
addType(ZonedDateTime.class, new ScalarTypeZonedDateTime(jsonDateTime, zoneId));
|
||||
addType(Instant.class, new ScalarTypeInstant(jsonDateTime));
|
||||
addType(DayOfWeek.class, new ScalarTypeDayOfWeek());
|
||||
addType(Month.class, new ScalarTypeMonth());
|
||||
@@ -846,6 +774,11 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
addType(Duration.class, (durationNanos) ? new ScalarTypeDurationWithNanos() : new ScalarTypeDuration());
|
||||
}
|
||||
|
||||
private ZoneId getZoneId(DatabaseConfig config) {
|
||||
final String dataTimeZone = config.getDataTimeZone();
|
||||
return (dataTimeZone == null) ? ZoneOffset.systemDefault() : TimeZone.getTimeZone(dataTimeZone).toZoneId();
|
||||
}
|
||||
|
||||
private void addType(Class<?> clazz, ScalarType<?> scalarType) {
|
||||
typeMap.put(clazz, scalarType);
|
||||
logicalMap.putIfAbsent(clazz.getSimpleName(), scalarType);
|
||||
@@ -890,23 +823,16 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
nativeMap.put(DbPlatformType.HSTORE, hstoreType);
|
||||
|
||||
ScalarType<?> utilDateType = extraTypeFactory.createUtilDate(jsonDateTime, jsonDate);
|
||||
addType(java.util.Date.class, utilDateType);
|
||||
|
||||
ScalarType<?> calType = extraTypeFactory.createCalendar(jsonDateTime);
|
||||
addType(Calendar.class, calType);
|
||||
|
||||
ScalarType<?> mathBigIntType = extraTypeFactory.createMathBigInteger();
|
||||
addType(BigInteger.class, mathBigIntType);
|
||||
addType(java.util.Date.class, extraTypeFactory.createUtilDate(jsonDateTime, jsonDate));
|
||||
addType(Calendar.class, extraTypeFactory.createCalendar(jsonDateTime));
|
||||
addType(BigInteger.class, extraTypeFactory.createMathBigInteger());
|
||||
|
||||
ScalarTypeBool booleanType = extraTypeFactory.createBoolean();
|
||||
addType(Boolean.class, booleanType);
|
||||
addType(boolean.class, booleanType);
|
||||
|
||||
// register the boolean literals to the platform for DDL default values
|
||||
databasePlatform.setDbTrueLiteral(booleanType.getDbTrueLiteral());
|
||||
databasePlatform.setDbFalseLiteral(booleanType.getDbFalseLiteral());
|
||||
|
||||
// always register Types.BOOLEAN to our boolean type
|
||||
nativeMap.put(Types.BOOLEAN, booleanType);
|
||||
if (booleanType.getJdbcType() == Types.BIT) {
|
||||
@@ -915,7 +841,6 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
}
|
||||
|
||||
PlatformConfig.DbUuid dbUuid = config.getPlatformConfig().getDbUuid();
|
||||
|
||||
if (offlineMigrationGeneration || (databasePlatform.isNativeUuidType() && dbUuid.useNativeType())) {
|
||||
addType(UUID.class, new ScalarTypeUUIDNative());
|
||||
} else {
|
||||
@@ -1027,6 +952,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
nativeMap.put(Types.TIMESTAMP, timestampType);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private void addInetAddressType(ScalarType scalarType) {
|
||||
addType(InetAddress.class, scalarType);
|
||||
addType(Inet4Address.class, scalarType);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.databind.*;
|
||||
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
|
||||
|
||||
class JacksonTypeHelper {
|
||||
|
||||
private final AnnotatedField field;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final JavaType javaType;
|
||||
private final DeserializationConfig deserConfig;
|
||||
private final AnnotationIntrospector ai;
|
||||
|
||||
JacksonTypeHelper(AnnotatedField field, ObjectMapper objectMapper) {
|
||||
this.field = field;
|
||||
this.objectMapper = objectMapper;
|
||||
this.javaType = field.getType();
|
||||
this.deserConfig = objectMapper.getDeserializationConfig();
|
||||
this.ai = deserConfig.getAnnotationIntrospector();
|
||||
}
|
||||
|
||||
JavaType type() {
|
||||
if (ai == null || javaType == null || javaType.hasRawClass(Object.class)) {
|
||||
return javaType;
|
||||
} else {
|
||||
try {
|
||||
return ai.refineDeserializationType(deserConfig, field, javaType);
|
||||
} catch (JsonMappingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ObjectWriter objectWriter() {
|
||||
if (ai == null || javaType == null || javaType.hasRawClass(Object.class)) {
|
||||
return objectMapper.writerFor(javaType);
|
||||
} else {
|
||||
try {
|
||||
JavaType serType = ai.refineSerializationType(objectMapper.getSerializationConfig(), field, javaType);
|
||||
return objectMapper.writerFor(serType);
|
||||
} catch (JsonMappingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,12 +43,8 @@ public class RsetDataReader implements DataReader {
|
||||
|
||||
@Override
|
||||
public boolean next() throws SQLException {
|
||||
return rset.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetColumnPosition() {
|
||||
pos = 0;
|
||||
return rset.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,7 +71,6 @@ public class RsetDataReader implements DataReader {
|
||||
return rset.getBigDecimal(pos());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public InputStream getBinaryStream() throws SQLException {
|
||||
return rset.getBinaryStream(pos());
|
||||
@@ -84,19 +79,13 @@ public class RsetDataReader implements DataReader {
|
||||
@Override
|
||||
public Boolean getBoolean() throws SQLException {
|
||||
boolean v = rset.getBoolean(pos());
|
||||
if (rset.wasNull()) {
|
||||
return null;
|
||||
}
|
||||
return v;
|
||||
return rset.wasNull() ? null : v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte getByte() throws SQLException {
|
||||
byte v = rset.getByte(pos());
|
||||
if (rset.wasNull()) {
|
||||
return null;
|
||||
}
|
||||
return v;
|
||||
return rset.wasNull() ? null : v;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,62 +106,42 @@ public class RsetDataReader implements DataReader {
|
||||
@Override
|
||||
public Double getDouble() throws SQLException {
|
||||
double v = rset.getDouble(pos());
|
||||
if (rset.wasNull()) {
|
||||
return null;
|
||||
}
|
||||
return v;
|
||||
return rset.wasNull() ? null : v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float getFloat() throws SQLException {
|
||||
float v = rset.getFloat(pos());
|
||||
if (rset.wasNull()) {
|
||||
return null;
|
||||
}
|
||||
return v;
|
||||
return rset.wasNull() ? null : v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getInt() throws SQLException {
|
||||
int v = rset.getInt(pos());
|
||||
if (rset.wasNull()) {
|
||||
return null;
|
||||
}
|
||||
return v;
|
||||
return rset.wasNull() ? null : v;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Long getLong() throws SQLException {
|
||||
long v = rset.getLong(pos());
|
||||
if (rset.wasNull()) {
|
||||
return null;
|
||||
}
|
||||
return v;
|
||||
return rset.wasNull() ? null : v;
|
||||
}
|
||||
|
||||
|
||||
public Ref getRef() throws SQLException {
|
||||
return rset.getRef(pos());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Short getShort() throws SQLException {
|
||||
short s = rset.getShort(pos());
|
||||
if (rset.wasNull()) {
|
||||
return null;
|
||||
}
|
||||
return s;
|
||||
return rset.wasNull() ? null : s;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getString() throws SQLException {
|
||||
return rset.getString(pos());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Time getTime() throws SQLException {
|
||||
Calendar cal = dataTimeZone.getTimeZone();
|
||||
@@ -196,14 +165,10 @@ public class RsetDataReader implements DataReader {
|
||||
@Override
|
||||
public String getStringFromStream() throws SQLException {
|
||||
Reader reader = rset.getCharacterStream(pos());
|
||||
if (reader == null) {
|
||||
return null;
|
||||
}
|
||||
return readStringLob(reader);
|
||||
return reader == null ? null : readStringLob(reader);
|
||||
}
|
||||
|
||||
protected String readStringLob(Reader reader) throws SQLException {
|
||||
|
||||
char[] buffer = new char[clobBufferSize];
|
||||
int readLength;
|
||||
StringBuilder out = new StringBuilder(stringInitialSize);
|
||||
@@ -215,7 +180,6 @@ public class RsetDataReader implements DataReader {
|
||||
} catch (IOException e) {
|
||||
throw new SQLException("IOException reading Clob " + e.getMessage());
|
||||
}
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
@@ -226,19 +190,16 @@ public class RsetDataReader implements DataReader {
|
||||
}
|
||||
|
||||
protected byte[] getBinaryLob(InputStream in) throws SQLException {
|
||||
|
||||
if (in == null) {
|
||||
return null;
|
||||
}
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
|
||||
byte[] buf = new byte[bufferSize];
|
||||
int len;
|
||||
while ((len = in.read(buf, 0, buf.length)) != -1) {
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
byte[] data = out.toByteArray();
|
||||
|
||||
if (data.length == 0) {
|
||||
data = null;
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ abstract class ScalarTypeJsonCollection<T> extends ScalarTypeBase<T> implements
|
||||
*/
|
||||
@Override
|
||||
public boolean isDirty(Object value) {
|
||||
return CheckMarkedDirty.isDirty(value);
|
||||
return TypeJsonManager.checkIsDirty(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -123,7 +123,7 @@ public abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
|
||||
*/
|
||||
@Override
|
||||
public boolean isDirty(Object value) {
|
||||
return CheckMarkedDirty.isDirty(value);
|
||||
return TypeJsonManager.checkIsDirty(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+29
-80
@@ -3,15 +3,10 @@ package io.ebeaninternal.server.type;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.AnnotationIntrospector;
|
||||
import com.fasterxml.jackson.databind.DeserializationConfig;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||
import com.fasterxml.jackson.databind.SerializationConfig;
|
||||
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
@@ -34,38 +29,23 @@ import java.util.Set;
|
||||
/**
|
||||
* Supports @DbJson properties using Jackson ObjectMapper.
|
||||
*/
|
||||
public class ScalarTypeJsonObjectMapper {
|
||||
class ScalarTypeJsonObjectMapper {
|
||||
|
||||
/**
|
||||
* Create and return the appropriate ScalarType.
|
||||
*/
|
||||
public static ScalarType<?> createTypeFor(boolean postgres, AnnotatedField field, ObjectMapper objectMapper,
|
||||
int dbType, DocPropertyType docType) {
|
||||
|
||||
static ScalarType<?> createTypeFor(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
|
||||
Class<?> type = field.getRawType();
|
||||
String pgType = getPostgresType(postgres, dbType);
|
||||
if (Set.class.equals(type)) {
|
||||
return new OmSet(objectMapper, field, dbType, pgType, docType);
|
||||
return new OmSet(jsonManager, field, dbType, docType);
|
||||
}
|
||||
if (List.class.equals(type)) {
|
||||
return new OmList(objectMapper, field, dbType, pgType, docType);
|
||||
return new OmList(jsonManager, field, dbType, docType);
|
||||
}
|
||||
if (Map.class.equals(type)) {
|
||||
return new OmMap(objectMapper, field, dbType, pgType);
|
||||
return new OmMap(jsonManager, field, dbType);
|
||||
}
|
||||
return new GenericObject(objectMapper, field, dbType, pgType);
|
||||
}
|
||||
|
||||
private static String getPostgresType(boolean postgres, int dbType) {
|
||||
if (postgres) {
|
||||
switch (dbType) {
|
||||
case DbPlatformType.JSON:
|
||||
return PostgresHelper.JSON_TYPE;
|
||||
case DbPlatformType.JSONB:
|
||||
return PostgresHelper.JSONB_TYPE;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return new GenericObject(jsonManager, field, dbType, type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,8 +53,8 @@ public class ScalarTypeJsonObjectMapper {
|
||||
*/
|
||||
private static class GenericObject extends Base<Object> {
|
||||
|
||||
public GenericObject(ObjectMapper objectMapper, AnnotatedField field, int dbType, String pgType) {
|
||||
super(Object.class, objectMapper, field, dbType, pgType, DocPropertyType.OBJECT);
|
||||
GenericObject(TypeJsonManager jsonManager, AnnotatedField field, int dbType, Class<?> rawType) {
|
||||
super(Object.class, jsonManager, field, dbType, DocPropertyType.OBJECT, rawType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,8 +64,8 @@ public class ScalarTypeJsonObjectMapper {
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static class OmSet extends Base<Set> {
|
||||
|
||||
public OmSet(ObjectMapper objectMapper, AnnotatedField field, int dbType, String pgType, DocPropertyType docType) {
|
||||
super(Set.class, objectMapper, field, dbType, pgType, docType);
|
||||
OmSet(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
|
||||
super(Set.class, jsonManager, field, dbType, docType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,8 +82,8 @@ public class ScalarTypeJsonObjectMapper {
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static class OmList extends Base<List> {
|
||||
|
||||
public OmList(ObjectMapper objectMapper, AnnotatedField field, int dbType, String pgType, DocPropertyType docType) {
|
||||
super(List.class, objectMapper, field, dbType, pgType, docType);
|
||||
OmList(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
|
||||
super(List.class, jsonManager, field, dbType, docType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -120,8 +100,8 @@ public class ScalarTypeJsonObjectMapper {
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static class OmMap extends Base<Map> {
|
||||
|
||||
public OmMap(ObjectMapper objectMapper, AnnotatedField field, int dbType, String pgType) {
|
||||
super(Map.class, objectMapper, field, dbType, pgType, DocPropertyType.OBJECT);
|
||||
OmMap(TypeJsonManager jsonManager, AnnotatedField field, int dbType) {
|
||||
super(Map.class, jsonManager, field, dbType, DocPropertyType.OBJECT);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -139,51 +119,25 @@ public class ScalarTypeJsonObjectMapper {
|
||||
private static abstract class Base<T> extends ScalarTypeBase<T> {
|
||||
|
||||
private final ObjectWriter objectWriter;
|
||||
|
||||
private final ObjectMapper objectReader;
|
||||
|
||||
private JavaType deserType;
|
||||
|
||||
private final JavaType deserType;
|
||||
private final String pgType;
|
||||
|
||||
private final DocPropertyType docType;
|
||||
private final TypeJsonManager.DirtyHandler dirtyHandler;
|
||||
|
||||
/**
|
||||
* Construct given the object mapper, property type and DB type for storage.
|
||||
*/
|
||||
public Base(Class<T> cls, ObjectMapper objectMapper, AnnotatedField field, int dbType, String pgType, DocPropertyType docType) {
|
||||
Base(Class<T> cls, TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
|
||||
this(cls, jsonManager, field, dbType, docType, cls);
|
||||
}
|
||||
|
||||
Base(Class<T> cls, TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType, Class<?> rawType) {
|
||||
super(cls, false, dbType);
|
||||
this.pgType = pgType;
|
||||
this.objectReader = jsonManager.objectMapper();
|
||||
this.pgType = jsonManager.postgresType(dbType);
|
||||
this.docType = docType;
|
||||
this.objectReader = objectMapper;
|
||||
|
||||
JavaType javaType = field.getType();
|
||||
DeserializationConfig deserConfig = objectMapper.getDeserializationConfig();
|
||||
AnnotationIntrospector ai = deserConfig.getAnnotationIntrospector();
|
||||
|
||||
if (ai != null && javaType != null && !javaType.hasRawClass(Object.class)) {
|
||||
try {
|
||||
this.deserType = ai.refineDeserializationType(deserConfig, field, javaType);
|
||||
} catch (JsonMappingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
this.deserType = javaType;
|
||||
}
|
||||
|
||||
SerializationConfig serConfig = objectMapper.getSerializationConfig();
|
||||
ai = deserConfig.getAnnotationIntrospector();
|
||||
|
||||
if (ai != null && javaType != null && !javaType.hasRawClass(Object.class)) {
|
||||
try {
|
||||
JavaType serType = ai.refineSerializationType(serConfig, field, javaType);
|
||||
this.objectWriter = objectMapper.writerFor(serType);
|
||||
} catch (JsonMappingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
this.objectWriter = objectMapper.writerFor(javaType);
|
||||
}
|
||||
this.dirtyHandler = jsonManager.dirtyHandler(cls, rawType);
|
||||
final JacksonTypeHelper helper = new JacksonTypeHelper(field, objectReader);
|
||||
this.deserType = helper.type();
|
||||
this.objectWriter = helper.objectWriter();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,7 +153,7 @@ public class ScalarTypeJsonObjectMapper {
|
||||
*/
|
||||
@Override
|
||||
public boolean isDirty(Object value) {
|
||||
return CheckMarkedDirty.isDirty(value);
|
||||
return dirtyHandler.isDirty(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -224,12 +178,7 @@ public class ScalarTypeJsonObjectMapper {
|
||||
if (value == null) {
|
||||
binder.setNull(Types.VARCHAR); // use varchar, otherwise SqlServer/db2 will fail with 'Invalid JDBC data type 5.001.'
|
||||
} else {
|
||||
try {
|
||||
String json = objectWriter.writeValueAsString(value);
|
||||
binder.setString(json);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new SQLException("Unable to create JSON", e);
|
||||
}
|
||||
binder.setString(formatValue(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.ebean.config.JsonConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Timestamp;
|
||||
import java.sql.Types;
|
||||
import java.time.Instant;
|
||||
@@ -29,12 +32,37 @@ public class ScalarTypeLocalDateTime extends ScalarTypeBaseDateTime<LocalDateTim
|
||||
|
||||
@Override
|
||||
protected String toJsonNanos(LocalDateTime value) {
|
||||
return String.valueOf(convertToMillis(value));
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toJsonISO8601(LocalDateTime dateTime) {
|
||||
return dateTime.atZone(ZoneId.systemDefault()).toInstant().toString();
|
||||
protected String toJsonISO8601(LocalDateTime value) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LocalDateTime fromJsonISO8601(String value) {
|
||||
return LocalDateTime.parse(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime jsonRead(JsonParser parser) throws IOException {
|
||||
return LocalDateTime.parse(parser.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, LocalDateTime value) throws IOException {
|
||||
writer.writeString(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(LocalDateTime value) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime parse(String value) {
|
||||
return LocalDateTime.parse(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,8 +15,11 @@ import static io.ebeaninternal.server.type.IsoJsonDateTimeParser.formatIso;
|
||||
*/
|
||||
public class ScalarTypeOffsetDateTime extends ScalarTypeBaseDateTime<OffsetDateTime> {
|
||||
|
||||
public ScalarTypeOffsetDateTime(JsonConfig.DateTime mode) {
|
||||
private final ZoneId zoneId;
|
||||
|
||||
public ScalarTypeOffsetDateTime(JsonConfig.DateTime mode, ZoneId zoneId) {
|
||||
super(mode, OffsetDateTime.class, false, Types.TIMESTAMP);
|
||||
this.zoneId = zoneId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,7 +49,7 @@ public class ScalarTypeOffsetDateTime extends ScalarTypeBaseDateTime<OffsetDateT
|
||||
|
||||
@Override
|
||||
public OffsetDateTime convertFromInstant(Instant ts) {
|
||||
return OffsetDateTime.ofInstant(ts, ZoneId.systemDefault());
|
||||
return OffsetDateTime.ofInstant(ts, zoneId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,7 +33,7 @@ public class ScalarTypePostgresHstore extends ScalarTypeBase<Map> {
|
||||
|
||||
@Override
|
||||
public boolean isDirty(Object value) {
|
||||
return CheckMarkedDirty.isDirty(value);
|
||||
return TypeJsonManager.checkIsDirty(value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -13,8 +13,11 @@ import java.time.ZonedDateTime;
|
||||
*/
|
||||
public class ScalarTypeZonedDateTime extends ScalarTypeBaseDateTime<ZonedDateTime> {
|
||||
|
||||
public ScalarTypeZonedDateTime(JsonConfig.DateTime mode) {
|
||||
private final ZoneId zoneId;
|
||||
|
||||
public ScalarTypeZonedDateTime(JsonConfig.DateTime mode, ZoneId zoneId) {
|
||||
super(mode, ZonedDateTime.class, false, Types.TIMESTAMP);
|
||||
this.zoneId = zoneId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,7 +47,7 @@ public class ScalarTypeZonedDateTime extends ScalarTypeBaseDateTime<ZonedDateTim
|
||||
|
||||
@Override
|
||||
public ZonedDateTime convertFromInstant(Instant ts) {
|
||||
return ZonedDateTime.ofInstant(ts, ZoneId.systemDefault());
|
||||
return ZonedDateTime.ofInstant(ts, zoneId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.ebean.ModifyAwareType;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
|
||||
class TypeJsonManager {
|
||||
|
||||
interface DirtyHandler {
|
||||
boolean isDirty(Object value);
|
||||
}
|
||||
|
||||
private final boolean postgres;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final DirtyHandler defaultHandler;
|
||||
private final DirtyHandler modifyAwareHandler;
|
||||
|
||||
TypeJsonManager(boolean postgres, Object objectMapper, boolean defaultDirty) {
|
||||
this.postgres = postgres;
|
||||
this.objectMapper = (ObjectMapper) objectMapper;
|
||||
this.defaultHandler = new DefaultHandler(defaultDirty);
|
||||
this.modifyAwareHandler = new ModifyAwareHandler();
|
||||
}
|
||||
|
||||
ObjectMapper objectMapper() {
|
||||
return objectMapper;
|
||||
}
|
||||
|
||||
String postgresType(int dbType) {
|
||||
if (postgres) {
|
||||
switch (dbType) {
|
||||
case DbPlatformType.JSON:
|
||||
return PostgresHelper.JSON_TYPE;
|
||||
case DbPlatformType.JSONB:
|
||||
return PostgresHelper.JSONB_TYPE;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DirtyHandler to use.
|
||||
*/
|
||||
DirtyHandler dirtyHandler(Class<?> cls, Class<?> rawType) {
|
||||
if (!Object.class.equals(cls) || ModifyAwareType.class.isAssignableFrom(rawType)) {
|
||||
// Set, List and Map are modify aware
|
||||
return modifyAwareHandler;
|
||||
}
|
||||
return defaultHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the value should be considered dirty (and included in an update).
|
||||
*/
|
||||
static boolean checkIsDirty(Object value) {
|
||||
if (value instanceof ModifyAwareType) {
|
||||
return checkModifyAware(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean checkModifyAware(Object value) {
|
||||
ModifyAwareType modifyAware = (ModifyAwareType) value;
|
||||
if (modifyAware.isMarkedDirty()) {
|
||||
// reset the dirty state (consider not dirty after update)
|
||||
modifyAware.setMarkedDirty(false);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static final class ModifyAwareHandler implements DirtyHandler {
|
||||
@Override
|
||||
public boolean isDirty(Object value) {
|
||||
return checkModifyAware(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Effectively constant based on {@link DatabaseConfig#isJsonDirtyByDefault()}
|
||||
*/
|
||||
static final class DefaultHandler implements DirtyHandler {
|
||||
|
||||
private final boolean dirty;
|
||||
|
||||
DefaultHandler(boolean dirty) {
|
||||
this.dirty = dirty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty(Object value) {
|
||||
return dirty;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1
-2
@@ -1,4 +1,4 @@
|
||||
package io.ebeaninternal.server.lib;
|
||||
package io.ebeaninternal.server.util;
|
||||
|
||||
/**
|
||||
* String utility for adding strings together.
|
||||
@@ -11,7 +11,6 @@ public class Str {
|
||||
* Append strings together.
|
||||
*/
|
||||
public static String add(String s0, String s1, String... args) {
|
||||
|
||||
// determine a decent buffer size
|
||||
int len = 16 + s0.length();
|
||||
if (s1 != null) {
|
||||
@@ -6,10 +6,13 @@ import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tests.model.basic.Customer;
|
||||
import org.tests.model.basic.ResetBasicData;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -30,6 +33,73 @@ public class DtoQuery2Test extends BaseTestCase {
|
||||
assertThat(list).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dto_findIterator_closeWithResources() {
|
||||
ResetBasicData.reset();
|
||||
|
||||
int counter = 0;
|
||||
try (QueryIterator<DCust> iterator = server()
|
||||
.findDto(DCust.class, "select id, name from o_customer where id > ?")
|
||||
.setParameter(0)
|
||||
.findIterate()) {
|
||||
|
||||
if (iterator.hasNext()) {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(counter).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dto_findIterator() {
|
||||
ResetBasicData.reset();
|
||||
final int expectedCount = server().find(Customer.class).findCount();
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
int counter = 0;
|
||||
try (final QueryIterator<DCust> iterator = server().findDto(DCust.class, "select id, name from o_customer where id > :id")
|
||||
.setParameter("id", 0)
|
||||
.findIterate()) {
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
final DCust cust = iterator.next();
|
||||
counter++;
|
||||
assertThat(cust).isNotNull();
|
||||
assertThat(cust.getName()).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(counter).isEqualTo(expectedCount);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertSql(sql.get(0)).contains("select id, name from o_customer where id > ?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dto_findStream() {
|
||||
ResetBasicData.reset();
|
||||
final int expectedCount = server().find(Customer.class).findCount();
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
try (final Stream<DCust> stream =
|
||||
server()
|
||||
.findDto(DCust.class, "select id, name from o_customer where id > ?")
|
||||
.setParameter(0)
|
||||
.findStream()) {
|
||||
|
||||
final List<String> names = stream
|
||||
.map(DCust::getName)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertThat(names.size()).isEqualTo(expectedCount);
|
||||
}
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertSql(sql.get(0)).contains("select id, name from o_customer where id > ?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dto_findEach_constructorMatch() {
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user