Compare commits

...
Author SHA1 Message Date
rbygrave 9eb54da2dc [maven-release-plugin] prepare release ebean-parent-12.8.3 2021-04-19 21:40:07 +12:00
rbygrave bb6ff866c7 #2220 - DDL generation of nullable columns expected with nullable Embedded containing non null properties 2021-04-19 21:37:05 +12:00
Robin Bygrave 2d719c5b37 #2218 - @Encrypted columns on Oracle. The identifier alias starts with an underscore (ORA-00911)
Change to use zx__ for the encryption column alias prefix rather than _e_
2021-04-19 20:08:38 +12:00
Robin Bygrave 9d54b264ad #2219 - Compile error with ZonedDateTime and query beans - type argument java.time.ZonedDateTime is not within bounds of type-variable D 2021-04-19 17:04:57 +12:00
rbygrave 65b282a056 [maven-release-plugin] prepare for next development iteration 2021-04-09 17:49:16 +12:00
rbygrave fc174c8988 [maven-release-plugin] prepare release ebean-parent-12.8.2 2021-04-09 17:49:07 +12:00
rbygrave 06802896ef #2213 Bump agent for @Transactional(autoPersistUpdates = TxOption.ON) 2021-04-09 16:17:58 +12:00
Robin Bygrave 7f13ac9e5e #2213 - Support autoPersistUpdates via TxScope 2021-04-08 23:15:49 +12:00
Rob BygraveandGitHub 4a1374d22d Merge pull request #2213 from ebean-orm/feature/transparentPersistence
Initial experimental support for autoPersistUpdates / "transparent persistence"
2021-04-08 23:03:05 +12:00
rbygrave 4ee8d647b7 No effective change - tidy test TestCustomerFinder 2021-04-07 23:28:53 +12:00
rbygrave 59793f3c01 Improve javadoc for Transaction#setGetGeneratedKeys
More clearly document the limitation that we can't update beans that don't have id values
2021-04-07 15:50:04 +12:00
rbygrave f7f383d8f3 No effective change - tidy tests TestRawSqlPositionedParams, TestRawSqlUnparsedQuery 2021-04-07 13:38:34 +12:00
rbygrave 562a3959b6 Merge branch 'master' of github.com:ebean-orm/ebean 2021-04-06 21:14:21 +12:00
rbygrave bb1275f4f9 #2217 - Inserting an entity bean is skipped with a String @Id property and no other properties set 2021-04-06 21:13:59 +12:00
Rob BygraveandGitHub 7fde6b9331 Merge pull request #2216 from ebean-orm/feature/2177-take2
#2177 - Fix where schema not provided by JDBC driver
2021-04-06 20:13:24 +12:00
rbygrave 3e97a4005a Change TestCustomerFinder drop query plan threshold for tests only 2021-04-06 20:09:09 +12:00
Rob BygraveandGitHub c271760230 Merge pull request #2215 from PromanSEW/patch-1
Fix copy/paste typo in javadoc for endOr()
2021-04-06 20:00:02 +12:00
rbygrave f7c1845f0c #2177 - Fix where schema not provided by JDBC driver
For @Table with schema, using findNative with a JDBC driver that does not provide the schema this fix matches by just using the table name.
2021-04-06 19:58:02 +12:00
Roman ParshikovandGitHub 9b04f20ed3 Fix copy/paste typo 2021-04-06 13:32:57 +08:00
Rob BygraveandGitHub 62b6b6293b Merge pull request #2214 from cit-consulting/fix_with_lock
Explicit locking method withLock() is unaccessible on query beans
2021-04-06 08:27:38 +12:00
Vladimir Konkov 6d8a1c9398 Explicit locking method withLock() is unaccessible on query beans 2021-04-05 21:45:48 +03:00
Robin Bygrave 303039735f Refactor rename transparentPersistence to autoPersistUpdates 2021-04-01 23:04:22 +13:00
Robin Bygrave 6bcf5c6881 Add DatabaseConfig.autoPersistUpdates for experimental "transparent persistence" 2021-04-01 22:57:34 +13:00
Robin Bygrave 0527f45f5a For experimental "transparent persistence" - Inserts added to PC
When inserting bean with transparent persistence the beans are registered with the persistence context. This is done to support the cases where the beans are inserted, a flush() occurs, the inserted bean is mutated and now dirty - we want that to be detected and get an update
2021-04-01 16:59:03 +13:00
Robin Bygrave ba397bbb0c For experimental "transparent persistence" - Delete removes from PC early
When delete bean executed with transparent persistence, this marks the bean as removed from the persistence context early.

This avoids a "dirty" deleted bean from being seen as a "dirty" bean in the persistence context at flush() time.
2021-04-01 16:04:18 +13:00
Robin Bygrave a617d58902 Initial experimental support for "transparent persistence"
In short, when this is turned on at flush() get any dirty beans held by the PersistenceContext and persist them (always update for these beans)
2021-04-01 14:47:54 +13:00
Robin Bygrave 7fc4a229ae Merge branch 'master' of https://github.com/ebean-orm/ebean 2021-04-01 12:23:00 +13:00
Robin Bygrave f53d31aa2e No functional change - final field for BatchControl 2021-04-01 12:22:45 +13:00
Rob BygraveandGitHub b889b6a6b0 Merge pull request #2212 from tobias-/bitwise_querybean_operators
Add bitwise operators to query beans
2021-04-01 09:38:00 +13:00
Tobias f9b55a06b8 Add bitwise operators to query beans 2021-03-31 12:53:06 +02:00
rob bygrave 09d772a5e5 [maven-release-plugin] prepare for next development iteration 2021-03-31 00:06:13 +13:00
65 changed files with 1099 additions and 212 deletions
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.8.3</version>
</parent>
<name>ebean api</name>
@@ -50,7 +50,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>6.15</version>
<version>7.0</version>
</dependency>
<dependency>
@@ -1671,7 +1671,7 @@ public interface ExpressionList<T> {
ExpressionList<T> endAnd();
/**
* End a AND junction - synonym for endJunction().
* End a OR junction - synonym for endJunction().
*/
ExpressionList<T> endOr();
@@ -59,6 +59,14 @@ public interface Transaction extends AutoCloseable {
*/
void register(TransactionCallback callback);
/**
* EXPERIMENTAL - turn on automatic persistence of dirty beans and batchMode true.
* <p>
* With this turned on beans that are dirty in the persistence context
* are automatically persisted on flush() and commit().
*/
void setAutoPersistUpdates(boolean autoPersistUpdates);
/**
* Set a label on the transaction.
* <p>
@@ -339,15 +347,12 @@ public interface Transaction extends AutoCloseable {
* The batch is automatically flushed when it hits the batch size and also when we
* execute queries or when we mix UpdateSql and CallableSql with save and delete of
* beans.
* </p>
* <p>
* We use {@link #flush()} to explicitly flush the batch and we can use
* {@link #setFlushOnQuery(boolean)} and {@link #setFlushOnMixed(boolean)}
* to control the automatic flushing behaviour.
* </p>
* <p>
* Example: batch processing of CallableSql executing every 10 rows
* </p>
*
* <pre>{@code
*
@@ -392,14 +397,11 @@ public interface Transaction extends AutoCloseable {
* <p>
* This only takes effect when batch mode on the transaction has not already meant that
* JDBC batch mode is being used.
* </p>
* <p>
* This is useful when the single save() or delete() cascades. For example, inserting a 'master' cascades
* and inserts a collection of 'detail' beans. The detail beans can be inserted using JDBC batch.
* </p>
* <p>
* This is effectively already turned on for all platforms apart from older Sql Server.
* </p>
*
* @param batchMode the batch mode to use per save(), insert(), update() or delete()
* @see io.ebean.config.DatabaseConfig#setPersistBatchOnCascade(PersistBatch)
@@ -422,15 +424,19 @@ public interface Transaction extends AutoCloseable {
int getBatchSize();
/**
* Specify if you want batched inserts to use getGeneratedKeys.
* Specify if we want batched inserts to use getGeneratedKeys.
* <p>
* By default batched inserts will try to use getGeneratedKeys if it is
* supported by the underlying jdbc driver and database.
* </p>
* <p>
* You may want to turn getGeneratedKeys off when you are inserting a large
* number of objects and you don't care about getting back the ids.
* </p>
* We want to turn off getGeneratedKeys when we are inserting a large
* number of objects and we don't care about getting back the ids. In this
* way we avoid the extra cost of getting back the generated id values
* from the database.
* <p>
* Note that when we do turn off getGeneratedKeys then we have the limitation
* that after a bean has been inserted we are unable to then mutate the bean
* and update it in the same transaction as we have not obtained it's id value.
*/
void setGetGeneratedKeys(boolean getGeneratedKeys);
@@ -449,13 +455,11 @@ public interface Transaction extends AutoCloseable {
* <p>
* If you want to execute both WITHOUT having the batch automatically flush
* you need to call this with batchFlushOnMixed = false.
* </p>
* <p>
* Note that UpdateSql and CallableSql are ALWAYS executed first (before the
* beans are executed). This is because the UpdateSql and CallableSql have
* already been bound to their PreparedStatements. The beans on the other hand
* have a 2 step process (delayed binding).
* </p>
*/
void setFlushOnMixed(boolean batchFlushOnMixed);
@@ -473,7 +477,6 @@ public interface Transaction extends AutoCloseable {
* <p>
* Calling this method with batchFlushOnQuery = false means that you can
* execute a query and the batch will not be automatically flushed.
* </p>
*/
void setFlushOnQuery(boolean batchFlushOnQuery);
@@ -490,7 +493,6 @@ public interface Transaction extends AutoCloseable {
* should be flushed prior to executing a query.
* <p>
* The default is for this to be true.
* </p>
*/
boolean isFlushOnQuery();
@@ -507,7 +509,6 @@ public interface Transaction extends AutoCloseable {
* flush the batch if you like.
* <p>
* Flushing occurs automatically when:
* </p>
* <ul>
* <li>the batch size is reached</li>
* <li>A query is executed on the same transaction</li>
@@ -533,11 +534,9 @@ public interface Transaction extends AutoCloseable {
* commit() rollback() and end() methods on the Transaction should still be
* used. Calling these methods on the Connection would be a big no no unless
* you know what you are doing.
* </p>
* <p>
* Examples of when a developer may wish to use the connection directly are:
* Savepoints, advanced CLOB BLOB use and advanced stored procedure calls.
* </p>
*/
Connection getConnection();
@@ -545,17 +544,14 @@ public interface Transaction extends AutoCloseable {
* Add table modification information to the TransactionEvent.
* <p>
* Use this in conjunction with getConnection() and raw JDBC.
* </p>
* <p>
* This effectively informs Ebean of the data that has been changed by the
* transaction and this information is normally automatically handled by Ebean
* when you save entity beans or use UpdateSql etc.
* </p>
* <p>
* If you use raw JDBC then you can use this method to inform Ebean for the
* tables that have been modified. Ebean uses this information to keep its
* caches in synch and maintain text indexes.
* </p>
*/
void addModification(String tableName, boolean inserts, boolean updates, boolean deletes);
@@ -2,6 +2,7 @@ package io.ebean;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.TxIsolation;
import io.ebean.annotation.TxOption;
import io.ebean.annotation.TxType;
import java.util.ArrayList;
@@ -33,6 +34,8 @@ public final class TxScope {
private TxIsolation isolation;
private TxOption autoPersistUpdates;
private PersistBatch batch;
private PersistBatch batchOnCascade;
@@ -123,6 +126,13 @@ public final class TxScope {
+ "] serverName[" + serverName + "] rollbackFor[" + rollbackFor + "] noRollbackFor[" + noRollbackFor + "]";
}
/**
* Return the AutoPersistUpdates mode as a nullable Boolean.
*/
public Boolean getAutoPersistUpdates() {
return autoPersistUpdates == null ? null : autoPersistUpdates.asBoolean();
}
/**
* Return true if PersistBatch has been set.
*/
@@ -176,6 +186,14 @@ public final class TxScope {
return this;
}
/**
* Set the autoPersistUpdates mode.
*/
public TxScope setAutoPersistUpdates(TxOption autoPersistUpdates) {
this.autoPersistUpdates = autoPersistUpdates;
return this;
}
/**
* Return the transaction profile id.
*/
@@ -1,5 +1,7 @@
package io.ebean.bean;
import java.util.List;
/**
* Holds entity beans by there type and id.
* <p>
@@ -77,6 +79,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>
@@ -232,6 +232,12 @@ public class DatabaseConfig {
*/
private String historyTableSuffix = "_history";
/**
* When true explicit transactions beans that have been made dirty will be
* automatically persisted via update on flush.
*/
private boolean autoPersistUpdates;
/**
* Use for transaction scoped batch mode.
*/
@@ -905,6 +911,20 @@ public class DatabaseConfig {
this.tenantCatalogProvider = tenantCatalogProvider;
}
/**
* Return true if dirty beans are automatically persisted.
*/
public boolean isAutoPersistUpdates() {
return autoPersistUpdates;
}
/**
* Set to true if dirty beans are automatically persisted.
*/
public void setAutoPersistUpdates(boolean autoPersistUpdates) {
this.autoPersistUpdates = autoPersistUpdates;
}
/**
* Return the PersistBatch mode to use by default at the transaction level.
* <p>
@@ -2807,6 +2827,7 @@ public class DatabaseConfig {
loadDocStoreSettings(p);
defaultServer = p.getBoolean("defaultServer", defaultServer);
autoPersistUpdates = p.getBoolean("autoPersistUpdates", autoPersistUpdates);
loadModuleInfo = p.getBoolean("loadModuleInfo", loadModuleInfo);
maxCallStack = p.getInt("maxCallStack", maxCallStack);
dumpMetricsOnShutdown = p.getBoolean("dumpMetricsOnShutdown", dumpMetricsOnShutdown);
@@ -3348,7 +3369,7 @@ public class DatabaseConfig {
this.loadModuleInfo = loadModuleInfo;
}
public enum UuidVersion {
public enum UuidVersion {
VERSION4,
VERSION1,
VERSION1RND
+3 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.8.3</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.8.1</tag>
<tag>ebean-parent-12.8.3</tag>
</scm>
<name>ebean autotune</name>
@@ -26,7 +26,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>provided</scope>
</dependency>
+17 -17
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.8.3</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.8.1</ebean-agent.version>
<ebean-maven-plugin.version>12.8.1</ebean-maven-plugin.version>
<ebean-agent.version>12.8.2</ebean-agent.version>
<ebean-maven-plugin.version>12.8.2</ebean-maven-plugin.version>
</properties>
<dependencyManagement>
@@ -81,88 +81,88 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-xml</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-autotune</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-redis</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.8.3</version>
</parent>
<artifactId>ebean-core-type</artifactId>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<dependency>
+7 -7
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.8.3</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.8.1</tag>
<tag>ebean-parent-12.8.3</tag>
</scm>
<profiles>
@@ -72,7 +72,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.6.1</version>
<version>12.8.4a</version>
<scope>test</scope>
</dependency>
@@ -87,19 +87,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<dependency>
@@ -302,7 +302,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>12.8.1</version>
<version>12.8.2</version>
<executions>
<execution>
<id>test</id>
@@ -73,6 +73,10 @@ public class ScopeTrans {
restoreBatchGeneratedKeys = transaction.getBatchGetGeneratedKeys();
restoreBatchFlushOnQuery = transaction.isFlushOnQuery();
}
Boolean autoPersistUpdates = txScope.getAutoPersistUpdates();
if (autoPersistUpdates != null) {
transaction.setAutoPersistUpdates(autoPersistUpdates);
}
if (txScope.isBatchSet()) {
transaction.setBatchMode(txScope.isBatchMode());
}
@@ -147,6 +147,11 @@ public interface SpiTransaction extends Transaction {
*/
int depth();
/**
* Return true if dirty beans are automatically persisted.
*/
boolean isAutoPersistUpdates();
/**
* Return true if this transaction was created explicitly via
* <code>Ebean.beginTransaction()</code>.
@@ -43,6 +43,16 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
return transaction.getLabel();
}
@Override
public void setAutoPersistUpdates(boolean autoPersistUpdates) {
transaction.setAutoPersistUpdates(autoPersistUpdates);
}
@Override
public boolean isAutoPersistUpdates() {
return transaction.isAutoPersistUpdates();
}
@Override
public void commitAndContinue() {
transaction.commitAndContinue();
@@ -236,7 +236,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
this.clockService = config.getClockService();
DocStoreIntegration docStoreComponents = config.createDocStoreIntegration(this);
this.transactionManager = config.createTransactionManager(docStoreComponents.updateProcessor());
this.transactionManager = config.createTransactionManager(this, docStoreComponents.updateProcessor());
this.documentStore = docStoreComponents.documentStore();
this.queryPlanManager = config.initQueryPlanManager(transactionManager);
this.metaInfoManager = new DefaultMetaInfoManager(this);
@@ -23,6 +23,9 @@ import java.util.function.Predicate;
*/
public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
private static final String ENC_PREFIX = EncryptAlias.PREFIX;
private static final String ENC_PREFIX_UPPER = EncryptAlias.PREFIX.toUpperCase();
private final SpiDtoQuery<T> query;
private final DtoQueryEngine queryEngine;
@@ -133,9 +136,9 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
}
static String parseColumn(String columnLabel) {
if (columnLabel.startsWith("_e_") || columnLabel.startsWith("_E_")) {
if (columnLabel.startsWith(ENC_PREFIX) || columnLabel.startsWith(ENC_PREFIX_UPPER)) {
// encrypted column alias in the form _e_<tableAlias>_<column>
final int pos = columnLabel.indexOf("_", 3);
final int pos = columnLabel.indexOf("_", 4);
if (pos > -1) {
return columnLabel.substring(pos + 1);
}
@@ -0,0 +1,8 @@
package io.ebeaninternal.server.core;
/**
* Used to create column alias for encrypted columns.
*/
public interface EncryptAlias {
String PREFIX = "zx__";
}
@@ -423,13 +423,13 @@ public class InternalConfiguration {
/**
* Create the TransactionManager taking into account autoCommit mode.
*/
TransactionManager createTransactionManager(DocStoreUpdateProcessor indexUpdateProcessor) {
TransactionManager createTransactionManager(SpiServer server, DocStoreUpdateProcessor indexUpdateProcessor) {
TransactionScopeManager scopeManager = createTransactionScopeManager();
boolean notifyL2CacheInForeground = cacheManager.isLocalL2Caching() || config.isNotifyL2CacheInForeground();
TransactionManagerOptions options =
new TransactionManagerOptions(notifyL2CacheInForeground, config, scopeManager, clusterManager, backgroundExecutor,
new TransactionManagerOptions(server, notifyL2CacheInForeground, config, scopeManager, clusterManager, backgroundExecutor,
indexUpdateProcessor, beanDescriptorManager, dataSource(), profileHandler(), logManager,
tableModState, cacheNotify, clockService);
@@ -887,6 +887,14 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
}
/**
* Remove deleted beans from the persistence context early.
*/
public void removeFromPersistenceContext() {
idValue = beanDescriptor.getId(entityBean);
beanDescriptor.contextDeleted(transaction.getPersistenceContext(), idValue);
}
/**
* Aggressive L1 and L2 cache cleanup for deletes.
*/
@@ -1030,6 +1038,17 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
if (!publish) {
beanDescriptor.setDraft(entityBean);
}
if (transaction.isAutoPersistUpdates() && idValue != null) {
// with getGeneratedKeys off we will not have a idValue
beanDescriptor.contextPut(transaction.getPersistenceContext(), idValue, entityBean);
}
}
/**
* Return if persist can be skipped on the reference only bean.
*/
public boolean isSkipReference() {
return intercept.isReference() || (Flags.isRecurse(flags) && beanDescriptor.referenceIdPropertyOnly(intercept));
}
public boolean isReference() {
@@ -2473,7 +2473,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Return the property path given the db table and column.
*/
public String findBeanPath(String schemaName, String tableName, String columnName) {
if (matchBaseTable(schemaName, tableName)) {
if (matchBaseTable(tableName)) {
return columnPath.get(columnName);
}
BeanPropertyAssoc<?> assocProperty = tablePath.get(tableName);
@@ -2489,10 +2489,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return null;
}
private boolean matchBaseTable(String schemaName, String tableName) {
boolean matchBaseTable(String tableName) {
return tableName.isEmpty()
|| baseTable.equalsIgnoreCase(tableName)
|| baseTable.equalsIgnoreCase(schemaName + "." + tableName);
|| baseTable.endsWith("." + tableName);
}
/**
@@ -3134,7 +3134,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return ebi.isReference() || referenceIdPropertyOnly(ebi);
}
boolean referenceIdPropertyOnly(EntityBeanIntercept ebi) {
public boolean referenceIdPropertyOnly(EntityBeanIntercept ebi) {
return idOnlyReference && ebi.hasIdOnly(idPropertyIndex);
}
@@ -18,6 +18,7 @@ import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.json.SpiJsonReader;
import io.ebeaninternal.api.json.SpiJsonWriter;
import io.ebeaninternal.server.core.EncryptAlias;
import io.ebeaninternal.server.core.InternString;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedWhenCreated;
@@ -65,6 +66,8 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
private static final Logger logger = LoggerFactory.getLogger(BeanProperty.class);
private static final String ENC_PREFIX = " " + EncryptAlias.PREFIX;
/**
* Flag to mark this is the id property.
*/
@@ -531,7 +534,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
* Return the SQL for the column including decryption function and column alias.
*/
private String getDecryptSqlWithColumnAlias(String tableAlias) {
return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()) + " _e_" + tableAlias + "_" + this.getDbColumn();
return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()) + ENC_PREFIX + tableAlias + "_" + this.getDbColumn();
}
@Override
@@ -81,18 +81,18 @@ class BeanPropertyAssocManySqlHelp<T> {
@Override
public void visitOneImported(BeanPropertyAssocOne<?> p) {
// do nothing
}
@Override
public void visitScalar(BeanProperty p) {
public void visitScalar(BeanProperty p, boolean allowNonNull) {
sb.append(",").append(p.getDbColumn());
colCount++;
}
@Override
public void visitEnd() {
// do nothing
}
}
@@ -44,9 +44,11 @@ public abstract class BaseTablePropertyVisitor implements BeanPropertyVisitor {
public abstract void visitOneImported(BeanPropertyAssocOne<?> p);
/**
* Override this method for normal scalar property.
* Override this method for normal scalar property. With allowNonNull
* used with properties of Embedded beans that can only be Nonnull when
* the Embedded bean is Nonnull.
*/
@Override
public abstract void visitScalar(BeanProperty p);
public abstract void visitScalar(BeanProperty p, boolean allowNonNull);
}
@@ -40,8 +40,8 @@ public interface BeanPropertyVisitor {
void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded);
/**
* Visit a scalar property.
* Visit a scalar property specify allowing non-null (e.g. embedded bean scalar properties).
*/
void visitScalar(BeanProperty p);
void visitScalar(BeanProperty p, boolean allowNonNull);
}
@@ -72,7 +72,7 @@ public class VisitProperties {
} else {
// simple scalar type
pv.visitScalar(p);
pv.visitScalar(p, true);
}
}
@@ -81,7 +81,7 @@ public final class BatchControl {
*/
private int bufferMax;
private Queue[] queues = new Queue[3];
private final Queue[] queues = new Queue[3];
/**
* Create for a given transaction, PersistExecute, default size and getGeneratedKeys.
@@ -447,7 +447,7 @@ public final class DefaultPersister implements Persister {
public void insert(EntityBean bean, Transaction t) {
PersistRequestBean<?> req = createRequest(bean, t, PersistRequest.Type.INSERT);
if (req.isReference()) {
if (req.isSkipReference()) {
// skip insert on reference bean
return;
}
@@ -881,6 +881,7 @@ public final class DefaultPersister implements Persister {
}
int count = request.executeOrQueue();
request.removeFromPersistenceContext();
if (request.isPersistCascade()) {
deleteAssocOne(request);
@@ -1,11 +1,9 @@
package io.ebeaninternal.server.transaction;
import io.ebean.bean.EntityBean;
import io.ebean.bean.PersistenceContext;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
/**
@@ -200,6 +198,20 @@ public final class DefaultPersistenceContext implements PersistenceContext {
}
}
@Override
public List<Object> dirtyBeans() {
lock.lock();
try {
List<Object> list = new ArrayList<>();
for (ClassContext classContext : typeCache.values()) {
classContext.dirtyBeans(list);
}
return list;
} finally {
lock.unlock();
}
}
@Override
public String toString() {
lock.lock();
@@ -318,6 +330,17 @@ public final class DefaultPersistenceContext implements PersistenceContext {
deleteSet.add(id);
map.remove(id);
}
/**
* Add the dirty beans to the list.
*/
void dirtyBeans(List<Object> list) {
for (Object value : map.values()) {
if (((EntityBean) value)._ebean_getIntercept().isDirty()) {
list.add(value);
}
}
}
}
}
@@ -95,6 +95,16 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
return startNanos;
}
@Override
public void setAutoPersistUpdates(boolean autoPersistUpdates) {
// do nothing
}
@Override
public boolean isAutoPersistUpdates() {
return false;
}
@Override
public void setLabel(String label) {
// do nothing
@@ -187,6 +187,8 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
private final long startNanos;
private boolean autoPersistUpdates;
/**
* Create a new JdbcTransaction.
*/
@@ -209,11 +211,12 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
this.batchOnCascadeMode = false;
this.onQueryOnly = OnQueryOnly.ROLLBACK;
} else {
this.autoPersistUpdates = explicit && manager.isAutoPersistUpdates();
this.logSql = manager.isLogSql();
this.logSummary = manager.isLogSummary();
this.skipCacheAfterWrite = manager.isSkipCacheAfterWrite();
this.batchMode = manager.getPersistBatch();
this.batchOnCascadeMode = manager.getPersistBatchOnCascade();
this.batchMode = manager.isPersistBatch();
this.batchOnCascadeMode = manager.isPersistBatchOnCascade();
this.onQueryOnly = manager.getOnQueryOnly();
}
@@ -294,6 +297,17 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
return sb.toString();
}
@Override
public void setAutoPersistUpdates(boolean autoPersistUpdates) {
this.autoPersistUpdates = autoPersistUpdates;
this.batchMode = true;
}
@Override
public boolean isAutoPersistUpdates() {
return autoPersistUpdates;
}
@Override
public boolean isSkipCacheExplicit() {
return (skipCache != null && !skipCache);
@@ -774,6 +788,10 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
* Flush the JDBC batch and execute derived relationship statements if necessary.
*/
private void internalBatchFlush() {
if (autoPersistUpdates) {
// Experimental - flush dirty beans held by the persistence context
manager.flushTransparent(persistenceContext, this);
}
batchFlush();
if (deferredList != null) {
for (PersistDeferredRelationship deferred : deferredList) {
@@ -1042,7 +1060,7 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
throw new IllegalStateException(illegalStateMessage);
}
try {
if (queryOnly) {
if (queryOnly && !autoPersistUpdates) {
connectionEndForQueryOnly();
} else {
flushCommitAndNotify();
@@ -25,6 +25,16 @@ class NoTransaction implements SpiTransaction {
static final NoTransaction INSTANCE = new NoTransaction();
@Override
public void setAutoPersistUpdates(boolean autoPersistUpdates) {
// do nothing
}
@Override
public boolean isAutoPersistUpdates() {
return false;
}
@Override
public void setLabel(String label) {
// do nothing
@@ -5,6 +5,7 @@ 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;
@@ -17,6 +18,7 @@ import io.ebean.meta.MetricVisitor;
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;
@@ -59,6 +61,8 @@ public class TransactionManager implements SpiTransactionManager {
private static final Logger clusterLogger = LoggerFactory.getLogger("io.ebean.Cluster");
private final SpiServer server;
private final BeanDescriptorManager beanDescriptorManager;
/**
@@ -100,6 +104,8 @@ public class TransactionManager implements SpiTransactionManager {
*/
final DocStoreUpdateProcessor docStoreUpdateProcessor;
private final boolean autoPersistUpdates;
private final boolean persistBatch;
private final boolean persistBatchOnCascade;
@@ -153,7 +159,7 @@ public class TransactionManager implements SpiTransactionManager {
* Create the TransactionManager
*/
public TransactionManager(TransactionManagerOptions options) {
this.server = options.server;
this.logManager = options.logManager;
this.txnLogger = logManager.txn();
this.txnDebug = txnLogger.isDebug();
@@ -161,6 +167,7 @@ public class TransactionManager implements SpiTransactionManager {
this.supportsSavepointId = databasePlatform.isSupportsSavepointId();
this.skipCacheAfterWrite = options.config.isSkipCacheAfterWrite();
this.notifyL2CacheInForeground = options.notifyL2CacheInForeground;
this.autoPersistUpdates = options.config.isAutoPersistUpdates();
this.persistBatch = PersistBatch.ALL == options.config.getPersistBatch();
this.persistBatchOnCascade = PersistBatch.ALL == options.config.appliedPersistBatchOnCascade();
this.rollbackOnChecked = options.config.isTransactionRollbackOnChecked();
@@ -279,11 +286,15 @@ public class TransactionManager implements SpiTransactionManager {
return bulkEventListenerMap;
}
boolean getPersistBatch() {
boolean isAutoPersistUpdates() {
return autoPersistUpdates;
}
boolean isPersistBatch() {
return persistBatch;
}
public boolean getPersistBatchOnCascade() {
boolean isPersistBatchOnCascade() {
return persistBatchOnCascade;
}
@@ -786,4 +797,14 @@ public class TransactionManager implements SpiTransactionManager {
public boolean isLogSummary() {
return logManager.sum().isDebug();
}
/**
* Experimental - find dirty beans in the persistence context and persist them.
*/
public void flushTransparent(PersistenceContext persistenceContext, SpiTransaction transaction) {
List<Object> dirtyBeans = persistenceContext.dirtyBeans();
if (!dirtyBeans.isEmpty()) {
server.updateAll(dirtyBeans, transaction);
}
}
}
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.transaction;
import io.ebean.BackgroundExecutor;
import io.ebean.cache.ServerCacheNotify;
import io.ebean.config.DatabaseConfig;
import io.ebean.plugin.SpiServer;
import io.ebeaninternal.api.SpiLogManager;
import io.ebeaninternal.api.SpiProfileHandler;
import io.ebeaninternal.server.cluster.ClusterManager;
@@ -15,6 +16,7 @@ import io.ebeanservice.docstore.api.DocStoreUpdateProcessor;
*/
public class TransactionManagerOptions {
final SpiServer server;
final boolean notifyL2CacheInForeground;
final DatabaseConfig config;
final ClusterManager clusterManager;
@@ -31,11 +33,11 @@ public class TransactionManagerOptions {
final ClockService clockService;
public TransactionManagerOptions(boolean notifyL2CacheInForeground, DatabaseConfig config, TransactionScopeManager scopeManager, ClusterManager clusterManager,
public TransactionManagerOptions(SpiServer server, boolean notifyL2CacheInForeground, DatabaseConfig config, TransactionScopeManager scopeManager, ClusterManager clusterManager,
BackgroundExecutor backgroundExecutor, DocStoreUpdateProcessor docStoreUpdateProcessor,
BeanDescriptorManager descMgr, DataSourceSupplier dataSourceSupplier, SpiProfileHandler profileHandler,
SpiLogManager logManager, TableModState tableModState, ServerCacheNotify cacheNotify, ClockService clockService) {
this.server = server;
this.notifyL2CacheInForeground = notifyL2CacheInForeground;
this.config = config;
this.scopeManager = scopeManager;
@@ -149,6 +149,7 @@ public class ServerConfigTest {
ServerConfig serverConfig = new ServerConfig();
assertTrue(serverConfig.isIdGeneratorAutomatic());
assertTrue(serverConfig.isDefaultServer());
assertFalse(serverConfig.isAutoPersistUpdates());
serverConfig.setIdGeneratorAutomatic(false);
assertFalse(serverConfig.isIdGeneratorAutomatic());
@@ -166,6 +167,8 @@ public class ServerConfigTest {
serverConfig.setLoadModuleInfo(false);
assertFalse(serverConfig.isAutoLoadModuleInfo());
serverConfig.setAutoPersistUpdates(true);
assertTrue(serverConfig.isAutoPersistUpdates());
}
@Test
@@ -11,10 +11,10 @@ public class DtoQueryRequestTest {
public void testParse() {
assertEquals("foo", DtoQueryRequest.parseColumn("foo"));
assertEquals("bar", DtoQueryRequest.parseColumn("_e_t0_bar"));
assertEquals("BAR", DtoQueryRequest.parseColumn("_E_T0_BAR"));
assertEquals("baz", DtoQueryRequest.parseColumn("_e_t42_baz"));
assertEquals("BAZ", DtoQueryRequest.parseColumn("_E_T42_BAZ"));
assertEquals("bar", DtoQueryRequest.parseColumn("zx__t0_bar"));
assertEquals("BAR", DtoQueryRequest.parseColumn("ZX__T0_BAR"));
assertEquals("baz", DtoQueryRequest.parseColumn("zx__t42_baz"));
assertEquals("BAZ", DtoQueryRequest.parseColumn("ZX__T42_BAZ"));
assertEquals("e_t42_nope", DtoQueryRequest.parseColumn("e_t42_nope"));
assertEquals("_f_t42_nope", DtoQueryRequest.parseColumn("_f_t42_nope"));
@@ -4,21 +4,22 @@ import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.bean.EntityBean;
import io.ebean.plugin.Property;
import io.ebeaninternal.server.core.CacheOptions;
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import io.ebeaninternal.server.deploy.meta.DeployIdentityMode;
import io.ebeanservice.docstore.api.DocStoreBeanAdapter;
import org.junit.Test;
import org.tests.model.basic.Animal;
import org.tests.model.basic.AnimalShelter;
import org.tests.model.basic.Cat;
import org.tests.model.basic.Contact;
import org.tests.model.basic.Country;
import org.tests.model.basic.Customer;
import org.tests.model.basic.Dog;
import org.tests.model.basic.Order;
import org.tests.model.basic.*;
import org.tests.model.bridge.BSite;
import org.tests.model.bridge.BUser;
import java.util.Collection;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class BeanDescriptorTest extends BaseTestCase {
@@ -88,6 +89,42 @@ public class BeanDescriptorTest extends BaseTestCase {
assertThat(props).extracting("name").contains("id", "status", "orderDate", "shipDate");
}
@Test
public void matchBaseTable() {
BeanDescriptor<Customer> desc = getBeanDescriptor(Customer.class);
assertTrue(desc.matchBaseTable("o_customer"));
}
@Test
public void matchBaseTable_whenTableHasSchema_expect_matchRegardlessOfSchema() {
DeployBeanDescriptor<Customer> deploy = mockDeployCustomer();
when(deploy.getBaseTable()).thenReturn("foo.o_customer");
BeanDescriptor<?> desc1 = new BeanDescriptor<>(mockOwner(), deploy);
assertTrue(desc1.matchBaseTable("o_customer"));
when(deploy.getBaseTable()).thenReturn("bar.o_customer");
BeanDescriptor<?> desc2 = new BeanDescriptor<>(mockOwner(), deploy);
assertTrue(desc2.matchBaseTable("o_customer"));
}
@SuppressWarnings("unchecked")
private DeployBeanDescriptor<Customer> mockDeployCustomer() {
DeployBeanDescriptor<Customer> deploy = mock(DeployBeanDescriptor.class);
when(deploy.getBeanType()).thenReturn(Customer.class);
when(deploy.getIdentityMode()).thenReturn(DeployIdentityMode.auto());
when(deploy.buildIdentityMode()).thenReturn(IdentityMode.NONE);
when(deploy.getCacheOptions()).thenReturn(CacheOptions.NO_CACHING);
return deploy;
}
private BeanDescriptorMap mockOwner() {
BeanDescriptorMap owner = mock(BeanDescriptorMap.class);
when(owner.createDocStoreBeanAdapter(any(), any())).thenReturn(mock(DocStoreBeanAdapter.class));
return owner;
}
@Test
public void merge_when_empty() {
@@ -25,6 +25,10 @@ public class PersonCacheEmail {
this.email = email;
}
public PersonCacheEmail(String id) {
this.id = id;
}
public String getId() {
return id;
}
@@ -0,0 +1,73 @@
package org.tests.cache.personinfo;
import io.ebean.annotation.WhenCreated;
import io.ebean.annotation.WhenModified;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Version;
import javax.validation.constraints.Size;
import java.time.Instant;
@Entity
public class PersonOther {
@Id
@Size(max=128)
private String id;
private String email;
@WhenCreated
private Instant whenCreated;
@WhenModified
private Instant whenModified;
@Version
private long version;
public PersonOther(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Instant getWhenCreated() {
return whenCreated;
}
public void setWhenCreated(Instant whenCreated) {
this.whenCreated = whenCreated;
}
public Instant getWhenModified() {
return whenModified;
}
public void setWhenModified(Instant whenModified) {
this.whenModified = whenModified;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
@@ -0,0 +1,30 @@
package org.tests.cache.personinfo;
import io.ebean.BaseTestCase;
import io.ebean.DB;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class TestStringIdOnly extends BaseTestCase {
@Test
public void insert() {
PersonCacheEmail b0 = new PersonCacheEmail("IdOnly");
DB.save(b0);
PersonCacheEmail found = DB.find(PersonCacheEmail.class, b0.getId());
assertThat(found).isNotNull();
}
@Test
public void insert_whenIdOnly() {
PersonOther b0 = new PersonOther("IdOnly");
DB.save(b0);
PersonOther found = DB.find(PersonOther.class, b0.getId());
assertThat(found).isNotNull();
}
}
@@ -1,12 +1,16 @@
package org.tests.model.embedded;
import io.ebean.annotation.NotNull;
import javax.persistence.Embeddable;
import java.util.Date;
@Embeddable
public class EEmbDatePeriod {
@NotNull
Date date1;
@NotNull
Date date2;
public Date getDate1() {
@@ -31,7 +31,7 @@ public class TestCustomerFinder extends BaseTestCase {
runQueries();
StringBuilder buffer0 = new StringBuilder();
server().getMetaInfoManager()
DB.getDefault().getMetaInfoManager()
.collectMetricsAsJson()
.withHeader(false)
.write(buffer0);
@@ -45,7 +45,7 @@ public class TestCustomerFinder extends BaseTestCase {
runQueries();
StringBuilder buffer1 = new StringBuilder();
server().getMetaInfoManager()
DB.getDefault().getMetaInfoManager()
.collectMetricsAsJson()
.withHeader(false)
.write(buffer1);
@@ -55,7 +55,6 @@ public class TestCustomerFinder extends BaseTestCase {
assertThat(json1).contains("\"name\":\"txn.main\"");
assertThat(json1).contains("\"name\":\"orm.Customer.findList\"");
assertThat(json1).doesNotContain("\"sql\":\"select t0.id, t0.status, t0.name");
}
@Test
@@ -85,7 +84,6 @@ public class TestCustomerFinder extends BaseTestCase {
assertThat(customer.getName()).isEqualTo(customer1.getName());
assertThat(Customer.find.db().getName()).isEqualTo(DB.getDefault().getName());
}
@Test
@@ -178,7 +176,7 @@ public class TestCustomerFinder extends BaseTestCase {
// change default collect query plan threshold to 200 micros
QueryPlanInit init0 = new QueryPlanInit();
init0.setAll(true);
init0.setThresholdMicros(200);
init0.setThresholdMicros(2);
final List<MetaQueryPlan> plans = server().getMetaInfoManager().queryPlanInit(init0);
assertThat(plans.size()).isGreaterThan(1);
@@ -188,14 +186,14 @@ public class TestCustomerFinder extends BaseTestCase {
// change query plan threshold to 100 micros
QueryPlanInit init = new QueryPlanInit();
init.setAll(true);
init.setThresholdMicros(100);
init.setThresholdMicros(1);
final List<MetaQueryPlan> appliedToPlans = server().getMetaInfoManager().queryPlanInit(init);
assertThat(appliedToPlans.size()).isGreaterThan(4);
// run queries again
runQueries();
ServerMetrics metrics = server().getMetaInfoManager().collectMetrics();
ServerMetrics metrics = DB.getDefault().getMetaInfoManager().collectMetrics();
List<MetaQueryMetric> planStats = metrics.getQueryMetrics();
assertThat(planStats.size()).isGreaterThan(4);
@@ -1,38 +1,42 @@
package org.tests.rawsql;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.Query;
import io.ebean.DB;
import io.ebean.RawSql;
import io.ebean.RawSqlBuilder;
import org.junit.Test;
import org.tests.model.basic.Customer;
import org.tests.model.basic.ResetBasicData;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.assertNotNull;
import static org.assertj.core.api.Assertions.assertThat;
public class TestRawSqlPositionedParams extends BaseTestCase {
private static final RawSql RAWSQL_1 = RawSqlBuilder
.parse("select r.id, r.name from o_customer r where r.id >= ? and r.name like ?")
.create();
private static final RawSql RAW_SQL_2 = RawSqlBuilder
.unparsed("select r.id, r.name from o_customer r where r.id >= ? and r.name like ?")
.columnMapping("r.id", "id")
.columnMapping("r.name", "name")
.create();
@Test
public void test() {
ResetBasicData.reset();
RawSql rawSql = RawSqlBuilder
.parse("select r.id, r.name from o_customer r where r.id >= ? and r.name like ?")
.create();
List<Customer> list = DB.find(Customer.class)
.setRawSql(RAWSQL_1)
.setParameter(1)
.setParameter("R%")
.where().lt("id", 2001)
.findList();
Query<Customer> query = Ebean.find(Customer.class);
query.setRawSql(rawSql);
query.setParameter(1, 1);
query.setParameter(2, "R%");
query.where().lt("id", 2001);
List<Customer> list = query.findList();
assertNotNull(list);
assertThat(list).isNotNull();
}
@Test
@@ -40,18 +44,12 @@ public class TestRawSqlPositionedParams extends BaseTestCase {
ResetBasicData.reset();
RawSql rawSql = RawSqlBuilder
.unparsed("select r.id, r.name from o_customer r where r.id >= ? and r.name like ?")
.columnMapping("r.id", "id")
.columnMapping("r.name", "name").create();
List<Customer> list = DB.find(Customer.class)
.setRawSql(RAW_SQL_2)
.setParameter(1)
.setParameter("R%")
.findList();
Query<Customer> query = Ebean.find(Customer.class);
query.setRawSql(rawSql);
query.setParameter(1, 1);
query.setParameter(2, "R%");
List<Customer> list = query.findList();
assertNotNull(list);
assertThat(list).isNotNull();
}
}
@@ -1,19 +1,25 @@
package org.tests.rawsql;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.Query;
import io.ebean.DB;
import io.ebean.RawSql;
import io.ebean.RawSqlBuilder;
import org.junit.Test;
import org.tests.model.basic.Customer;
import org.tests.model.basic.ResetBasicData;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class TestRawSqlUnparsedQuery extends BaseTestCase {
private static final RawSql rawSql = RawSqlBuilder
.unparsed("select r.id, r.name from o_customer r where r.id >= :a and r.name like :b")
.columnMapping("r.id", "id")
.columnMapping("r.name", "name")
.create();
@Test
public void testDoubleUnparsedQuery() {
@@ -25,17 +31,14 @@ public class TestRawSqlUnparsedQuery extends BaseTestCase {
}
private static void test() {
RawSql rawSql = RawSqlBuilder
.unparsed("select r.id, r.name from o_customer r where r.id >= :a and r.name like :b")
.columnMapping("r.id", "id").columnMapping("r.name", "name").create();
Query<Customer> query = Ebean.find(Customer.class);
query.setRawSql(rawSql);
query.setParameter("a", 1);
query.setParameter("b", "R%");
List<Customer> list = DB.find(Customer.class)
.setRawSql(rawSql)
.setParameter("a", 1)
.setParameter("b", "R%")
.findList();
List<Customer> list = query.findList();
Assert.assertNotNull(list);
assertThat(list).isNotNull();
}
}
@@ -0,0 +1,298 @@
package org.tests.transparentpersist;
import io.ebean.*;
import io.ebean.annotation.*;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeantest.LoggedSql;
import org.junit.Test;
import org.tests.model.basic.Customer;
import org.tests.model.basic.EBasicVer;
import org.tests.model.basic.Order;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class TestTransparentPersist extends BaseTestCase {
@Test
public void insertFlush_mutateFlush_expect_update() {
LoggedSql.start();
EBasicVer newBean;
try (Transaction transaction = DB.beginTransaction()) {
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
newBean = new EBasicVer("insertMe");
DB.save(newBean);
// flush - new bean needs to get registered into persistence context
transaction.flush();
// make it dirty, we expect it to execute an update on flush()
newBean.setName("make it dirty - auto save");
// flush again, auto persist dirty bean in persistence context
transaction.commit();
}
List<String> sql = LoggedSql.stop();
EBasicVer found = DB.find(EBasicVer.class, newBean.getId());
assertThat(found.getName()).isEqualTo("make it dirty - auto save");
assertThat(sql).hasSize(4);
assertThat(sql.get(0)).contains("insert into e_basicver");
assertThat(sql.get(1)).contains(" -- bind(");
assertThat(sql.get(2)).contains("update e_basicver set name=?, last_update=? where id=? and last_update=?");
assertThat(sql.get(3)).contains(" -- bind(");
DB.delete(found);
}
@Test
public void delete_expect_beanRemovedFromPersistenceContext() {
EBasicVer b0 = new EBasicVer("simpleDelete");
DB.save(b0);
try (Transaction transaction = DB.beginTransaction()) {
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
EBasicVer found = DB.find(EBasicVer.class, b0.getId());
// make it dirty
found.setName("make it dirty");
// delete it, should remove it from the "live" part of persistence context
// with the expectation that no update is executed (no dirty in PC update)
DB.delete(found);
transaction.commit();
}
EBasicVer after = DB.find(EBasicVer.class, b0.getId());
assertThat(after).isNull();
}
@Test
public void simpleInsertUpdateDelete_experimental() {
EBasicVer b0 = new EBasicVer("simpleIUD_0");
b0.save();
EBasicVer b1 = new EBasicVer("simpleIUD_1");
b1.save();
EBasicVer b2 = new EBasicVer("simpleIUD_2");
b2.save();
EBasicVer newBean;
try (Transaction transaction = DB.beginTransaction()) {
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
EBasicVer found = DB.find(EBasicVer.class, b0.getId());
found.setName("auto dirty");
// delete by id
DB.delete(EBasicVer.class, b1.getId());
// find and delete, note the delete is batched up to execute later
DB.delete(DB.find(EBasicVer.class, b2.getId()));
// insert is batched up to execute later
newBean = new EBasicVer("simpleIUD_New1");
DB.save(newBean);
// can still mutate newBean before flush (but not after flush yet as new bean isn't put into Persistence context)
newBean.setName("simpleIUD_New2");
transaction.commit();
}
EBasicVer after = DB.find(EBasicVer.class, b0.getId());
assertThat(after.getName()).isEqualTo("auto dirty");
EBasicVer wasInserted = DB.find(EBasicVer.class, newBean.getId());
assertThat(wasInserted.getName()).isEqualTo("simpleIUD_New2");
assertThat(DB.find(EBasicVer.class, b1.getId())).isNull();
assertThat(DB.find(EBasicVer.class, b2.getId())).isNull();
DB.delete(after);
DB.delete(wasInserted);
}
@Test
public void simpleUpdate_experimental() {
EBasicVer transPersist = new EBasicVer("simulate_simpleUpdate");
transPersist.save();
try (Transaction transaction = DB.beginTransaction()) {
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
EBasicVer found = DB.find(EBasicVer.class, transPersist.getId());
found.setName("Persisted automatically as dirty");
transaction.commit();
}
EBasicVer after = DB.find(EBasicVer.class,transPersist.getId());
assertThat(after.getName()).isEqualTo("Persisted automatically as dirty");
DB.delete(after);
}
@Test
public void updateWithPersistCascadeInsert() {
// setup data
Customer c0 = new Customer();
c0.setName("firstCust");
Order order = new Order();
order.setStatus(Order.Status.NEW);
order.setCustomer(c0);
DB.save(order);
try (Transaction transaction = DB.beginTransaction()) {
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
Order foundOrder = DB.find(Order.class, order.getId());
foundOrder.setStatus(Order.Status.APPROVED);
// cascade persist will insert this customer (even though it isn't in the persistence context)
Customer c1 = new Customer();
c1.setName("newCust CascadePersist");
foundOrder.setCustomer(c1);
transaction.commit();
}
Order checkOrder = DB.find(Order.class, order.getId());
assertThat(checkOrder.getStatus()).isEqualTo(Order.Status.APPROVED);
assertThat(checkOrder.getCustomer().getName()).isEqualTo("newCust CascadePersist");
DB.delete(checkOrder);
DB.delete(Customer.class, checkOrder.getCustomer().getId());
DB.delete(Customer.class, c0.getId());
}
@Test
public void updateReferenceOnlyWithPersistCascade_Insert_andUpdateForeignKey() {
// setup data
Customer c0 = new Customer();
c0.setName("firstCust");
Order order = new Order();
order.setStatus(Order.Status.NEW);
order.setCustomer(c0);
DB.save(order);
LoggedSql.start();
try (Transaction transaction = DB.beginTransaction()) {
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
Order foundOrder = DB.find(Order.class, order.getId());
// we ONLY mutate the foreign key
// cascade persist will insert this customer (even though it isn't in the persistence context)
Customer c1 = new Customer();
c1.setName("newCust CascadePersist");
foundOrder.setCustomer(c1);
transaction.commit();
}
List<String> sql = LoggedSql.stop();
Order checkOrder = DB.find(Order.class, order.getId());
assertThat(checkOrder.getStatus()).isEqualTo(Order.Status.NEW);
assertThat(checkOrder.getCustomer().getName()).isEqualTo("newCust CascadePersist");
assertThat(sql).hasSize(5);
assertThat(sql.get(0)).contains("select t0.id, t0.status, t0.order_date");
assertThat(sql.get(1)).contains("insert into o_customer");
assertThat(sql.get(2)).contains(" -- bind(");
assertThat(sql.get(3)).contains("update o_order set updtime=?, kcustomer_id=? where id=? and updtime=?");
assertThat(sql.get(4)).contains(" -- bind(");
DB.delete(checkOrder);
DB.delete(Customer.class, checkOrder.getCustomer().getId());
DB.delete(Customer.class, c0.getId());
}
@Test
public void txScope_setAutoPersistUpdates() {
EBasicVer b0 = new EBasicVer("txScopeUpdate");
b0.save();
DB.execute(TxScope.required().setAutoPersistUpdates(TxOption.ON), () -> {
EBasicVer found = DB.find(EBasicVer.class, b0.getId());
found.setName("mutable txScope bean");
});
EBasicVer after = DB.find(EBasicVer.class, b0.getId());
assertThat(after.getName()).isEqualTo("mutable txScope bean");
DB.delete(after);
}
@Test
public void txn_setAutoPersistUpdates() {
EBasicVer b0 = new EBasicVer("txn autoPersistUpdates ON");
b0.save();
performUpdate(b0);
EBasicVer after = DB.find(EBasicVer.class, b0.getId());
assertThat(after.getName()).isEqualTo("mutated with autoPersistUpdates ON");
DB.delete(after);
}
@Transactional(autoPersistUpdates = TxOption.ON)
private void performUpdate(EBasicVer b0) {
EBasicVer found = DB.find(EBasicVer.class, b0.getId());
found.setName("mutated with autoPersistUpdates ON");
}
@Test
public void simulate_transparentPersistence_forSimpleUpdate() {
EBasicVer transPersist = new EBasicVer("simulate_simpleUpdate");
transPersist.save();
try (Transaction transaction = DB.beginTransaction()) {
transaction.setBatchMode(true);
transaction.setBatchSize(10);
EBasicVer found = DB.find(EBasicVer.class, transPersist.getId());
found.setName("Changed");
// simulate transparent persistence
List<Object> dirtyBeans = simulateTransparentPersist(transaction);
assertThat(dirtyBeans).hasSize(1);
assertThat(dirtyBeans).contains(found);
// would occur as first part of flush
transaction.flush();
transaction.commit();
}
EBasicVer after = DB.find(EBasicVer.class,transPersist.getId());
assertThat(after.getName()).isEqualTo("Changed");
DB.delete(after);
}
private List<Object> simulateTransparentPersist(Transaction transaction) {
Database db = DB.getDefault();
List<Object> dirtyBeans = getDirtyBeansFromPersistenceContext(transaction);
for (Object dirtyBean : dirtyBeans) {
db.update(dirtyBean, transaction);
}
return dirtyBeans;
}
private List<Object> getDirtyBeansFromPersistenceContext(Transaction transaction) {
return ((SpiTransaction)transaction).getPersistenceContext().dirtyBeans();
}
}
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.8.3</version>
</parent>
<name>ebean ddl generation</name>
@@ -28,14 +28,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>provided</scope>
</dependency>
@@ -76,7 +76,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>12.8.1</version>
<version>12.8.2</version>
<executions>
<execution>
<id>test</id>
@@ -175,7 +175,8 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
if (p instanceof BeanPropertyAssocOne) {
visitOneImported((BeanPropertyAssocOne<?>)p);
} else {
visitScalar(p);
// only allow Nonnull if embedded is Nonnull
visitScalar(p, !embedded.isNullable());
}
if (embedded.isId()) {
// compound primary key
@@ -259,8 +260,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
}
@Override
public void visitScalar(BeanProperty p) {
public void visitScalar(BeanProperty p, boolean allowNonNull) {
if (p.isSecondaryTable()) {
lastColumn = null;
return;
@@ -292,7 +292,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
}
} else {
col.setDefaultValue(p.getDbColumnDefault());
if (!p.isNullable() || p.isDDLNotNull()) {
if (allowNonNull && (!p.isNullable() || p.isDDLNotNull())) {
col.setNotnull(true);
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.8.3</version>
</parent>
<name>ebean external mapping api</name>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.8.3</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.8.1</tag>
<tag>ebean-parent-12.8.3</tag>
</scm>
<name>ebean external mapping xml</name>
@@ -33,7 +33,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<dependency>
@@ -59,14 +59,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>test</scope>
</dependency>
+3 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.8.3</version>
</parent>
<name>ebean postgis</name>
@@ -23,7 +23,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>provided</scope>
</dependency>
@@ -74,7 +74,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>test</scope>
</dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.8.3</version>
</parent>
<name>ebean querybean</name>
@@ -17,7 +17,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>provided</scope>
</dependency>
@@ -57,21 +57,21 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>test</scope>
</dependency>
@@ -24,4 +24,70 @@ public class PInteger<R> extends PBaseNumber<R,Integer> {
super(name, root, prefix);
}
/**
* Add bitwise AND expression of the given bit flags to compare with the match/mask.
* <p>
* <pre>{@code
*
* // Flags Bulk + Size = Size
* // ... meaning Bulk is not set and Size is set
*
* int selectedFlags = BwFlags.HAS_BULK + BwFlags.HAS_SIZE;
* int mask = BwFlags.HAS_SIZE; // Only Size flag set
*
* bitwiseAnd(selectedFlags, mask)
*
* }</pre>
*
* @param flags The flags we are looking for
*/
public R bitwiseAnd(int flags, int mask) {
expr().bitwiseAnd(_name, flags, mask);
return _root;
}
/**
* Add expression for ALL of the given bit flags to be set.
* <pre>{@code
*
* bitwiseAll(BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
*
* }</pre>
*
* @param flags The flags we are looking for
*/
public R bitwiseAll(int flags) {
expr().bitwiseAll(_name, flags);
return _root;
}
/**
* Add expression for ANY of the given bit flags to be set.
* <pre>{@code
*
* bitwiseAny(BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
*
* }</pre>
*
* @param flags The flags we are looking for
*/
public R bitwiseAny(int flags) {
expr().bitwiseAny(_name, flags);
return _root;
}
/**
* Add expression for the given bit flags to be NOT set.
* <pre>{@code
*
* bitwiseNot(BwFlags.HAS_COLOUR)
*
* }</pre>
*
* @param flags The flags we are looking for
*/
public R bitwiseNot(int flags) {
expr().bitwiseNot(_name, flags);
return _root;
}
}
@@ -5,7 +5,7 @@ package io.ebean.typequery;
*
* @param <R> the root query bean type
*/
public class PLong<R> extends PBaseNumber<R,Long> {
public class PLong<R> extends PBaseNumber<R, Long> {
/**
* Construct with a property name and root instance.
@@ -14,7 +14,7 @@ public class PLong<R> extends PBaseNumber<R,Long> {
* @param root the root query bean instance
*/
public PLong(String name, R root) {
super(name , root);
super(name, root);
}
/**
@@ -24,4 +24,70 @@ public class PLong<R> extends PBaseNumber<R,Long> {
super(name, root, prefix);
}
/**
* Add bitwise AND expression of the given bit flags to compare with the match/mask.
* <p>
* <pre>{@code
*
* // Flags Bulk + Size = Size
* // ... meaning Bulk is not set and Size is set
*
* long selectedFlags = BwFlags.HAS_BULK + BwFlags.HAS_SIZE;
* long mask = BwFlags.HAS_SIZE; // Only Size flag set
*
* bitwiseAnd(selectedFlags, mask)
*
* }</pre>
*
* @param flags The flags we are looking for
*/
public R bitwiseAnd(long flags, long mask) {
expr().bitwiseAnd(_name, flags, mask);
return _root;
}
/**
* Add expression for ALL of the given bit flags to be set.
* <pre>{@code
*
* bitwiseAll(BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
*
* }</pre>
*
* @param flags The flags we are looking for
*/
public R bitwiseAll(long flags) {
expr().bitwiseAll(_name, flags);
return _root;
}
/**
* Add expression for ANY of the given bit flags to be set.
* <pre>{@code
*
* bitwiseAny(BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
*
* }</pre>
*
* @param flags The flags we are looking for
*/
public R bitwiseAny(long flags) {
expr().bitwiseAny(_name, flags);
return _root;
}
/**
* Add expression for the given bit flags to be NOT set.
* <pre>{@code
*
* bitwiseNot(BwFlags.HAS_COLOUR)
*
* }</pre>
*
* @param flags The flags we are looking for
*/
public R bitwiseNot(long flags) {
expr().bitwiseNot(_name, flags);
return _root;
}
}
@@ -5,7 +5,7 @@ package io.ebean.typequery;
*
* @param <R> the root query bean type
*/
public class PShort<R> extends PBaseNumber<R,Short> {
public class PShort<R> extends PBaseNumber<R, Short> {
/**
* Construct with a property name and root instance.
@@ -24,4 +24,70 @@ public class PShort<R> extends PBaseNumber<R,Short> {
super(name, root, prefix);
}
/**
* Add bitwise AND expression of the given bit flags to compare with the match/mask.
* <p>
* <pre>{@code
*
* // Flags Bulk + Size = Size
* // ... meaning Bulk is not set and Size is set
*
* short selectedFlags = BwFlags.HAS_BULK + BwFlags.HAS_SIZE;
* short mask = BwFlags.HAS_SIZE; // Only Size flag set
*
* bitwiseAnd(selectedFlags, mask)
*
* }</pre>
*
* @param flags The flags we are looking for
*/
public R bitwiseAnd(short flags, short mask) {
expr().bitwiseAnd(_name, flags, mask);
return _root;
}
/**
* Add expression for ALL of the given bit flags to be set.
* <pre>{@code
*
* bitwiseAll(BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
*
* }</pre>
*
* @param flags The flags we are looking for
*/
public R bitwiseAll(short flags) {
expr().bitwiseAll(_name, flags);
return _root;
}
/**
* Add expression for ANY of the given bit flags to be set.
* <pre>{@code
*
* bitwiseAny(BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
*
* }</pre>
*
* @param flags The flags we are looking for
*/
public R bitwiseAny(short flags) {
expr().bitwiseAny(_name, flags);
return _root;
}
/**
* Add expression for the given bit flags to be NOT set.
* <pre>{@code
*
* bitwiseNot(BwFlags.HAS_COLOUR)
*
* }</pre>
*
* @param flags The flags we are looking for
*/
public R bitwiseNot(short flags) {
expr().bitwiseNot(_name, flags);
return _root;
}
}
@@ -0,0 +1,50 @@
package io.ebean.typequery;
import java.time.ZonedDateTime;
/**
* ZonedDateTime property.
*
* @param <R> the root query bean type
*/
public class PZonedDateTime<R> extends PBaseCompareable<R, ZonedDateTime> {
/**
* Construct with a property name and root instance.
*
* @param name property name
* @param root the root query bean instance
*/
public PZonedDateTime(String name, R root) {
super(name, root);
}
/**
* Construct with additional path prefix.
*/
public PZonedDateTime(String name, R root, String prefix) {
super(name, root, prefix);
}
/**
* Same as greater than.
*
* @param value the equal to bind value
* @return the root query bean instance
*/
public R after(ZonedDateTime value) {
expr().gt(_name, value);
return _root;
}
/**
* Same as less than.
*
* @param value the equal to bind value
* @return the root query bean instance
*/
public R before(ZonedDateTime value) {
expr().lt(_name, value);
return _root;
}
}
@@ -679,7 +679,7 @@ public abstract class TQRootBean<T, R> {
* Provides us with the ability to explicitly use Postgres
* SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks.
*/
R withLock(Query.LockType lockType) {
public R withLock(Query.LockType lockType) {
query.withLock(lockType);
return root;
}
@@ -693,7 +693,7 @@ public abstract class TQRootBean<T, R> {
* Provides us with the ability to explicitly use Postgres
* SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks.
*/
R withLock(Query.LockType lockType, Query.LockWait lockWait) {
public R withLock(Query.LockType lockType, Query.LockWait lockWait) {
query.withLock(lockType, lockWait);
return root;
}
@@ -3,6 +3,7 @@ package org.example.domain;
import io.ebean.annotation.DbArray;
import javax.persistence.*;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -11,33 +12,35 @@ import java.util.Map;
* Contact entity bean.
*/
@Entity
@Table(name="be_contact")
@Table(name = "be_contact")
public class Contact extends BaseModel {
@DbArray
List<String> phoneNumbers = new ArrayList();
List<String> phoneNumbers = new ArrayList<>();
@Column(length=50)
@Column(length = 50)
String firstName;
@Column(length=50)
@Column(length = 50)
String lastName;
@Column(length=200)
@Column(length = 200)
String email;
@Column(length=20)
@Column(length = 20)
String phone;
@ManyToOne(optional=false)
ZonedDateTime zoneDateTime;
@ManyToOne(optional = false)
Customer customer;
@OneToMany(mappedBy = "contact")
List<ContactNote> notes;
@OneToMany(cascade = CascadeType.PERSIST)
@MapKey(name="key")
Map<String,ContactOther> others;
@MapKey(name = "key")
Map<String, ContactOther> others;
/**
* Default constructor.
@@ -85,6 +88,14 @@ public class Contact extends BaseModel {
this.phone = phone;
}
public ZonedDateTime getZoneDateTime() {
return zoneDateTime;
}
public void setZoneDateTime(ZonedDateTime zoneDateTime) {
this.zoneDateTime = zoneDateTime;
}
public Customer getCustomer() {
return customer;
}
@@ -3,13 +3,15 @@ package org.querytest;
import org.example.domain.query.QContact;
import org.junit.Test;
import java.time.ZonedDateTime;
public class QContactTest {
@Test
public void test_oneToManyMap() {
new QContact()
.others.fetch()
.zoneDateTime.before(ZonedDateTime.now())
.findList();
}
}
@@ -71,6 +71,7 @@ public class QCustomerTest {
// not found using other transaction
final Customer foundNot = new QCustomer()
.name.eq("explicitTransaction")
.withLock(Query.LockType.SHARE)
.findOne();
assertThat(foundNot).isNull();
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.8.3</version>
</parent>
<artifactId>ebean-redis</artifactId>
@@ -22,35 +22,35 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>test</scope>
</dependency>
+3 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.8.3</version>
</parent>
<name>ebean test</name>
@@ -29,14 +29,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<dependency>
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.8.3</version>
</parent>
<name>ebean composite</name>
@@ -22,20 +22,20 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
<!-- Technically optional but most expected to use query beans -->
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.8.3</version>
</parent>
<name>kotlin querybean generator</name>
@@ -29,7 +29,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>test</scope>
</dependency>
@@ -43,7 +43,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>test</scope>
</dependency>
@@ -64,7 +64,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<scope>test</scope>
</dependency>
@@ -146,7 +146,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>12.8.1</version>
<version>12.8.2</version>
<executions>
<execution>
<id>test</id>
@@ -25,10 +25,9 @@ class PropertyTypeMap {
*/
private final PropertyType dbJsonType = new PropertyType("PJson");
private Map<String,PropertyType> map = new HashMap<>();
private final Map<String,PropertyType> map = new HashMap<>();
PropertyTypeMap() {
map.put("boolean", new PropertyType("PBoolean"));
map.put("short", new PropertyType("PShort"));
map.put("int", new PropertyType("PInteger"));
@@ -57,7 +56,6 @@ class PropertyTypeMap {
addType(Locale.class);
addType(File.class);
addType(InetAddress.class);
map.put(URI.class.getName(), new PropertyType("PUri"));
map.put(URL.class.getName(), new PropertyType("PUrl"));
map.put(UUID.class.getName(), new PropertyType("PUuid"));
@@ -74,7 +72,6 @@ class PropertyTypeMap {
}
private void addJava8Types() {
try {
Class.forName("java.time.Instant");
} catch (ClassNotFoundException e) {
@@ -94,6 +91,7 @@ class PropertyTypeMap {
addType(java.time.YearMonth.class);
addType(java.time.ZoneId.class);
addType(java.time.ZoneOffset.class);
addType(java.time.ZonedDateTime.class);
}
private void addJodaTypes() {
+2 -2
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean-parent</artifactId>
<version>12.8.1</version>
<version>12.8.3</version>
<packaging>pom</packaging>
<name>ebean parent</name>
@@ -18,7 +18,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-parent-12.8.1</tag>
<tag>ebean-parent-12.8.3</tag>
</scm>
<licenses>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.8.3</version>
</parent>
<name>querybean generator</name>
@@ -25,10 +25,9 @@ class PropertyTypeMap {
*/
private final PropertyType dbJsonType = new PropertyType("PJson");
private Map<String,PropertyType> map = new HashMap<>();
private final Map<String,PropertyType> map = new HashMap<>();
PropertyTypeMap() {
map.put("boolean", new PropertyType("PBoolean"));
map.put("short", new PropertyType("PShort"));
map.put("int", new PropertyType("PInteger"));
@@ -57,7 +56,6 @@ class PropertyTypeMap {
map.put("java.lang.Class<?>", new PropertyType("PClass"));
addType(File.class);
addType(InetAddress.class);
map.put(URI.class.getName(), new PropertyType("PUri"));
map.put(URL.class.getName(), new PropertyType("PUrl"));
map.put(UUID.class.getName(), new PropertyType("PUuid"));
@@ -73,7 +71,6 @@ class PropertyTypeMap {
}
private void addJava8Types() {
try {
Class.forName("java.time.Instant");
} catch (ClassNotFoundException e) {
@@ -93,6 +90,7 @@ class PropertyTypeMap {
addType(java.time.YearMonth.class);
addType(java.time.ZoneId.class);
addType(java.time.ZoneOffset.class);
addType(java.time.ZonedDateTime.class);
}
private void addJodaTypes() {