Compare commits

..
Author SHA1 Message Date
rob bygrave 08d241d946 #1966 Remove sequence batching - postgres opps 2020-03-05 00:34:05 +13:00
rob bygrave 1c24ebf6dc Remove Sequence batching in favour of JPA increment 50
Also remove h2sqldb in favour of always using H2
2020-03-05 00:22:20 +13:00
347 changed files with 5176 additions and 16779 deletions
+20 -27
View File
@@ -4,12 +4,12 @@
<parent>
<groupId>org.avaje</groupId>
<artifactId>java8-oss</artifactId>
<version>2.2</version>
<version>2.1</version>
</parent>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>12.3.9</version>
<version>12.1.14-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -22,7 +22,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-12.3.9</tag>
<tag>HEAD</tag>
</scm>
<profiles>
@@ -63,7 +63,7 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-config</artifactId>
<artifactId>config</artifactId>
<version>1.0</version>
</dependency>
@@ -72,22 +72,22 @@
to assist with IDE auto-completion with Ebean API
-->
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-jsr305</artifactId>
<version>1.0</version>
<groupId>io.ebean</groupId>
<artifactId>ebean-jsr305</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>persistence-api</artifactId>
<version>2.2.4</version>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>6.13</version>
<version>6.9</version>
</dependency>
<dependency>
@@ -99,7 +99,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-datasource</artifactId>
<version>5.1</version>
<version>4.7.3</version>
</dependency>
<dependency>
@@ -126,14 +126,14 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-databind.version}</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<!-- provided scope to read validation annotations Size etc -->
@@ -141,21 +141,21 @@
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.7</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<!-- Provided scope to compile with java 11 -->
@@ -170,7 +170,7 @@
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<!-- Provided scope for Postgres JSON/JSONB support -->
@@ -178,7 +178,7 @@
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.10</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<!-- Test scope -->
@@ -200,7 +200,7 @@
<dependency>
<groupId>io.ebean.test</groupId>
<artifactId>ebean-test-docker</artifactId>
<version>3.1.5</version>
<version>3.1.3</version>
<scope>test</scope>
</dependency>
@@ -248,13 +248,6 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>2.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sap.cloud.db.jdbc</groupId>
<artifactId>ngdbc</artifactId>
@@ -299,7 +292,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>12.3.7</version>
<version>12.1.12</version>
<executions>
<execution>
<id>test</id>
@@ -317,7 +310,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M4</version>
<version>2.22.1</version>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
<trimStackTrace>false</trimStackTrace>
@@ -31,18 +31,15 @@ public interface BackgroundExecutor {
* Execute a task periodically with a fixed delay between each execution.
* <p>
* For example, execute a runnable every minute.
* </p>
* <p>
* The delay is the time between executions no matter how long the task took.
* That is, this method has the same behaviour characteristics as
* {@link ScheduledExecutorService#scheduleWithFixedDelay(Runnable, long, long, TimeUnit)}
* </p>
*/
void executePeriodically(Runnable r, long delay, TimeUnit unit);
/**
* Execute a task periodically additionally with an initial delay different from delay.
*/
void executePeriodically(Runnable r, long initialDelay, long delay, TimeUnit unit);
/**
* Schedules a Runnable for one-shot action that becomes enabled after the given delay.
*
+1 -1
View File
@@ -14,7 +14,7 @@ import java.util.Optional;
*
* public class CustomerFinder extends BeanFinder<Long,Customer> {
*
* Inject
* @Inject
* public CustomerFinder(Database database) {
* super(Customer.class, database);
* }
+2 -2
View File
@@ -9,10 +9,10 @@ import java.util.Collection;
* <p>
* <pre>{@code
*
* Repository
* @Repository
* public class CustomerRepository extends BeanRepository<Long,Customer> {
*
* Inject
* @Inject
* public CustomerRepository(Database server) {
* super(Customer.class, server);
* }
+19 -20
View File
@@ -60,9 +60,7 @@ import java.util.concurrent.Callable;
*
* }</pre>
*/
public final class DB {
private static final DbContext context = DbContext.getInstance();
public class DB {
/**
* Hide constructor.
@@ -74,7 +72,7 @@ public final class DB {
* Return the default database.
*/
public static Database getDefault() {
return context.getDefault();
return Ebean.getDefaultServer();
}
/**
@@ -83,24 +81,25 @@ public final class DB {
* @param name The name of the database
*/
public static Database byName(String name) {
return context.get(name);
return Ebean.getServer(name);
}
/**
* Return the ScriptRunner for the default database.
* <p>
* Useful to run SQL scripts that are resources. For example a test script
* for inserting seed data for a particular test.
*
* <pre>{@code
*
* DB.script().run("/scripts/test-script.sql")
*
* }</pre>
*/
public static ScriptRunner script() {
return getDefault().script();
}
// /**
// * Register the server with this Ebean singleton. Specify if the registered
// * server is the primary/default database.
// */
// public static void register(EbeanServer server, boolean defaultServer) {
// serverMgr.register(server, defaultServer);
// }
//
// /**
// * Backdoor for registering a mock implementation of EbeanServer as the default database.
// */
// protected static EbeanServer mock(String name, EbeanServer server, boolean defaultServer) {
// EbeanServer originalPrimaryServer = serverMgr.defaultServer;
// serverMgr.registerWithName(name, server, defaultServer);
// return originalPrimaryServer;
// }
/**
* Return the ExpressionFactory from the default database.
+1 -21
View File
@@ -1,6 +1,5 @@
package io.ebean;
import io.ebean.annotation.Platform;
import io.ebean.annotation.TxIsolation;
import io.ebean.cache.ServerCacheManager;
import io.ebean.config.DatabaseConfig;
@@ -133,25 +132,6 @@ public interface Database {
*/
MetaInfoManager getMetaInfoManager();
/**
* Return the platform used for this database instance.
* <p>
* Note many platforms have multiple specific platform types so often we want to
* get the base platform via {@link Platform#base()}.
* </p>
* <pre>{@code
*
* Platform platform = database.getPlatform().base();
* if (platform == Platform.MYSQL) {
* // do MySql specific function
* }
*
* }</pre>
*
* @return platform for this database instance
*/
Platform getPlatform();
/**
* Return the extended API intended for use by plugins.
*/
@@ -881,7 +861,7 @@ public interface Database {
* <pre>{@code
* public class Order { ...
*
* OneToMany(cascade=CascadeType.ALL, mappedBy="order")
* @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
* List<OrderDetail> details;
* ...
* }
-140
View File
@@ -1,140 +0,0 @@
package io.ebean;
import io.ebean.config.BeanNotEnhancedException;
import io.ebean.datasource.DataSourceConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
/**
* Holds Database instances.
*/
final class DbContext {
private static final Logger logger = LoggerFactory.getLogger(DbContext.class);
static {
EbeanVersion.getVersion();
}
private static final DbContext INSTANCE = new DbContext();
/**
* Cache for fast concurrent read access.
*/
private final ConcurrentHashMap<String, Database> concMap = new ConcurrentHashMap<>();
/**
* Cache for synchronized read, creation and put. Protected by the monitor object.
*/
private final HashMap<String, Database> syncMap = new HashMap<>();
private final Object monitor = new Object();
/**
* The 'default' Database.
*/
private Database defaultDatabase;
private DbContext() {
try {
if (!DbPrimary.isSkip()) {
// look to see if there is a default server defined
String defaultName = DbPrimary.getDefaultServerName();
logger.debug("defaultName:{}", defaultName);
if (defaultName != null && !defaultName.trim().isEmpty()) {
defaultDatabase = getWithCreate(defaultName.trim());
}
}
} catch (BeanNotEnhancedException e) {
throw e;
} catch (DataSourceConfigurationException e) {
String msg = "Configuration error creating DataSource for the default Database." +
" This typically means a missing application-test.yaml or missing ebean-test dependency." +
" See https://ebean.io/docs/trouble-shooting#datasource";
throw new DataSourceConfigurationException(msg, e);
} catch (Throwable e) {
logger.error("Error trying to create the default Database", e);
throw new RuntimeException(e);
}
}
/**
* Return the shared singleton instance.
*/
static DbContext getInstance() {
return INSTANCE;
}
/**
* Return the default database.
*/
Database getDefault() {
if (defaultDatabase == null) {
String msg = "The default Database has not been defined?";
msg += " This is normally set via the ebean.datasource.default property.";
msg += " Otherwise it should be registered programmatically via registerServer()";
throw new PersistenceException(msg);
}
return defaultDatabase;
}
/**
* Return the database by name.
*/
Database get(String name) {
if (name == null || name.isEmpty()) {
return defaultDatabase;
}
// non-synchronized read
Database server = concMap.get(name);
if (server != null) {
return server;
}
// synchronized read, create and put
return getWithCreate(name);
}
/**
* Synchronized read, create and put of Databases.
*/
private Database getWithCreate(String name) {
synchronized (monitor) {
Database server = syncMap.get(name);
if (server == null) {
// register when creating server this way
server = EbeanServerFactory.create(name);
register(server, false);
}
return server;
}
}
/**
* Register a server so we can get it by its name.
*/
void register(Database server, boolean isDefault) {
registerWithName(server.getName(), server, isDefault);
}
private void registerWithName(String name, Database server, boolean isDefault) {
synchronized (monitor) {
concMap.put(name, server);
syncMap.put(name, server);
if (isDefault) {
defaultDatabase = server;
}
}
}
Database mock(String name, Database server, boolean defaultServer) {
Database originalPrimaryServer = this.defaultDatabase;
registerWithName(name, server, defaultServer);
return originalPrimaryServer;
}
}
+1 -1
View File
@@ -149,7 +149,7 @@ public interface DocumentStore {
* .setUseDocStore(true)
* .where()... // perhaps add predicates
* .findEachWhile(new Predicate<Order>() {
* Override
* @Override
* public void accept(Order bean) {
* // process the bean
*
+220 -86
View File
@@ -2,22 +2,29 @@ package io.ebean;
import io.ebean.annotation.TxIsolation;
import io.ebean.cache.ServerCacheManager;
import io.ebean.config.BeanNotEnhancedException;
import io.ebean.config.ServerConfig;
import io.ebean.datasource.DataSourceConfigurationException;
import io.ebean.plugin.Property;
import io.ebean.text.csv.CsvReader;
import io.ebean.text.json.JsonContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
/**
* Deprecated - please migrate to use <code>io.ebean.DB</code>.
* Ebean will be Deprecated - please migrate to use DB.
* <p>
* Ebean is a registry of {@link Database} by name. Ebean has now been renamed to {@link DB}.
* <p>
@@ -25,10 +32,124 @@ import java.util.concurrent.Callable;
* <p>
* The preference is to use DB and Database rather than Ebean and EbeanServer.
*/
@Deprecated
public final class Ebean {
private static final Logger logger = LoggerFactory.getLogger(Ebean.class);
private static final DbContext context = DbContext.getInstance();
static {
EbeanVersion.getVersion(); // initialises the version class and logs the version.
}
/**
* Manages creation and cache of Databases.
*/
private static final Ebean.ServerManager serverMgr = new Ebean.ServerManager();
/**
* Helper class for managing fast and safe access and creation of Databases.
*/
private static final class ServerManager {
/**
* Cache for fast concurrent read access.
*/
private final ConcurrentHashMap<String, EbeanServer> concMap = new ConcurrentHashMap<>();
/**
* Cache for synchronized read, creation and put. Protected by the monitor object.
*/
private final HashMap<String, EbeanServer> syncMap = new HashMap<>();
private final Object monitor = new Object();
/**
* The 'default' Database.
*/
private EbeanServer defaultServer;
private ServerManager() {
try {
if (!PrimaryServer.isSkip()) {
// look to see if there is a default server defined
String defaultName = PrimaryServer.getDefaultServerName();
logger.debug("defaultName:{}", defaultName);
if (defaultName != null && !defaultName.trim().isEmpty()) {
defaultServer = getWithCreate(defaultName.trim());
}
}
} catch (BeanNotEnhancedException e) {
throw e;
} catch (DataSourceConfigurationException e) {
String msg = "Configuration error creating DataSource for the default Database." +
" This typically means a missing application-test.yaml or missing ebean-test dependency." +
" See https://ebean.io/docs/trouble-shooting#datasource";
throw new DataSourceConfigurationException(msg, e);
} catch (Throwable e) {
logger.error("Error trying to create the default Database", e);
throw new RuntimeException(e);
}
}
private EbeanServer getDefaultServer() {
if (defaultServer == null) {
String msg = "The default Database has not been defined?";
msg += " This is normally set via the ebean.datasource.default property.";
msg += " Otherwise it should be registered programmatically via registerServer()";
throw new PersistenceException(msg);
}
return defaultServer;
}
private EbeanServer get(String name) {
if (name == null || name.isEmpty()) {
return defaultServer;
}
// non-synchronized read
EbeanServer server = concMap.get(name);
if (server != null) {
return server;
}
// synchronized read, create and put
return getWithCreate(name);
}
/**
* Synchronized read, create and put of Databases.
*/
private EbeanServer getWithCreate(String name) {
synchronized (monitor) {
EbeanServer server = syncMap.get(name);
if (server == null) {
// register when creating server this way
server = EbeanServerFactory.create(name);
register(server, false);
}
return server;
}
}
/**
* Register a server so we can get it by its name.
*/
private void register(EbeanServer server, boolean isDefaultServer) {
registerWithName(server.getName(), server, isDefaultServer);
}
private void registerWithName(String name, EbeanServer server, boolean isDefaultServer) {
synchronized (monitor) {
concMap.put(name, server);
syncMap.put(name, server);
if (isDefaultServer) {
defaultServer = server;
}
}
}
}
private Ebean() {
}
@@ -52,7 +173,7 @@ public final class Ebean {
* @param name the name of the server, can use null for the 'default server'
*/
public static EbeanServer getServer(String name) {
return (EbeanServer)context.get(name);
return serverMgr.get(name);
}
/**
@@ -62,27 +183,7 @@ public final class Ebean {
* </p>
*/
public static EbeanServer getDefaultServer() {
return (EbeanServer)context.getDefault();
}
/**
* Register the server with this Ebean singleton. Specify if the registered
* server is the primary/default database.
*/
@Deprecated
public static void register(EbeanServer server, boolean defaultServer) {
context.register(server, defaultServer);
}
/**
* Backdoor for registering a mock implementation of EbeanServer as the default database.
*/
protected static EbeanServer mock(String name, EbeanServer server, boolean defaultServer) {
return (EbeanServer)context.mock(name, server, defaultServer);
}
private static Database getDefault() {
return context.getDefault();
return serverMgr.getDefaultServer();
}
/**
@@ -103,7 +204,24 @@ public final class Ebean {
* </p>
*/
public static ExpressionFactory getExpressionFactory() {
return getDefault().getExpressionFactory();
return serverMgr.getDefaultServer().getExpressionFactory();
}
/**
* Register the server with this Ebean singleton. Specify if the registered
* server is the primary/default database.
*/
public static void register(EbeanServer server, boolean defaultServer) {
serverMgr.register(server, defaultServer);
}
/**
* Backdoor for registering a mock implementation of EbeanServer as the default database.
*/
protected static EbeanServer mock(String name, EbeanServer server, boolean defaultServer) {
EbeanServer originalPrimaryServer = serverMgr.defaultServer;
serverMgr.registerWithName(name, server, defaultServer);
return originalPrimaryServer;
}
/**
@@ -119,7 +237,7 @@ public final class Ebean {
* </p>
*/
public static Object nextId(Class<?> beanType) {
return getDefault().nextId(beanType);
return serverMgr.getDefaultServer().nextId(beanType);
}
/**
@@ -165,7 +283,7 @@ public final class Ebean {
* </p>
*/
public static Transaction beginTransaction() {
return getDefault().beginTransaction();
return serverMgr.getDefaultServer().beginTransaction();
}
/**
@@ -174,7 +292,7 @@ public final class Ebean {
* @param isolation the Transaction isolation level
*/
public static Transaction beginTransaction(TxIsolation isolation) {
return getDefault().beginTransaction(isolation);
return serverMgr.getDefaultServer().beginTransaction(isolation);
}
/**
@@ -227,7 +345,7 @@ public final class Ebean {
* }</pre>
*/
public static Transaction beginTransaction(TxScope scope) {
return getDefault().beginTransaction(scope);
return serverMgr.getDefaultServer().beginTransaction(scope);
}
/**
@@ -235,7 +353,7 @@ public final class Ebean {
* in scope.
*/
public static Transaction currentTransaction() {
return getDefault().currentTransaction();
return serverMgr.getDefaultServer().currentTransaction();
}
/**
@@ -265,21 +383,21 @@ public final class Ebean {
* @throws PersistenceException if there is no currently active transaction
*/
public static void register(TransactionCallback transactionCallback) throws PersistenceException {
getDefault().register(transactionCallback);
serverMgr.getDefaultServer().register(transactionCallback);
}
/**
* Commit the current transaction.
*/
public static void commitTransaction() {
getDefault().commitTransaction();
serverMgr.getDefaultServer().commitTransaction();
}
/**
* Rollback the current transaction.
*/
public static void rollbackTransaction() {
getDefault().rollbackTransaction();
serverMgr.getDefaultServer().rollbackTransaction();
}
/**
@@ -307,14 +425,14 @@ public final class Ebean {
* }</pre>
*/
public static void endTransaction() {
getDefault().endTransaction();
serverMgr.getDefaultServer().endTransaction();
}
/**
* Mark the current transaction as rollback only.
*/
public static void setRollbackOnly() {
getDefault().currentTransaction().setRollbackOnly();
serverMgr.getDefaultServer().currentTransaction().setRollbackOnly();
}
/**
@@ -325,7 +443,7 @@ public final class Ebean {
* </p>
*/
public static Map<String, ValuePair> diff(Object a, Object b) {
return getDefault().diff(a, b);
return serverMgr.getDefaultServer().diff(a, b);
}
/**
@@ -346,7 +464,7 @@ public final class Ebean {
* <pre>{@code
* public class Order { ...
*
* OneToMany(cascade=CascadeType.ALL, mappedBy="order")
* @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
* List<OrderDetail> details;
* ...
* }
@@ -359,7 +477,7 @@ public final class Ebean {
* </p>
*/
public static void save(Object bean) throws OptimisticLockException {
getDefault().save(bean);
serverMgr.getDefaultServer().save(bean);
}
/**
@@ -367,14 +485,14 @@ public final class Ebean {
* want to explicitly insert it.
*/
public static void insert(Object bean) {
getDefault().insert(bean);
serverMgr.getDefaultServer().insert(bean);
}
/**
* Insert a collection of beans.
*/
public static void insertAll(Collection<?> beans) {
getDefault().insertAll(beans);
serverMgr.getDefaultServer().insertAll(beans);
}
/**
@@ -397,7 +515,7 @@ public final class Ebean {
* }</pre>
*/
public static void markAsDirty(Object bean) throws OptimisticLockException {
getDefault().markAsDirty(bean);
serverMgr.getDefaultServer().markAsDirty(bean);
}
/**
@@ -412,6 +530,16 @@ public final class Ebean {
* <b>Optimistic Locking: </b> Note that if the version property is not set when update() is
* called then no optimistic locking is performed (internally ConcurrencyMode.NONE is used).
* </p>
* <p>
* <b>{@link ServerConfig#setUpdatesDeleteMissingChildren(boolean)}: </b> When cascade saving to a
* OneToMany or ManyToMany the updatesDeleteMissingChildren setting controls if any other children
* that are in the database but are not in the collection are deleted.
* </p>
* <p>
* <b>{@link ServerConfig#setUpdateChangesOnly(boolean)}: </b> The updateChangesOnly setting
* controls if only the changed properties are included in the update or if all the loaded
* properties are included instead.
* </p>
* <pre>{@code
*
* // A 'stateless update' example
@@ -424,14 +552,14 @@ public final class Ebean {
* }</pre>
*/
public static void update(Object bean) throws OptimisticLockException {
getDefault().update(bean);
serverMgr.getDefaultServer().update(bean);
}
/**
* Update the beans in the collection.
*/
public static void updateAll(Collection<?> beans) throws OptimisticLockException {
getDefault().updateAll(beans);
serverMgr.getDefaultServer().updateAll(beans);
}
/**
@@ -440,7 +568,7 @@ public final class Ebean {
* @param bean The bean to merge
*/
public static void merge(Object bean) {
getDefault().merge(bean);
serverMgr.getDefaultServer().merge(bean);
}
/**
@@ -450,14 +578,14 @@ public final class Ebean {
* @param options The options to control the merge
*/
public static void merge(Object bean, MergeOptions options) {
getDefault().merge(bean, options);
serverMgr.getDefaultServer().merge(bean, options);
}
/**
* Save all the beans from a Collection.
*/
public static int saveAll(Collection<?> beans) throws OptimisticLockException {
return getDefault().saveAll(beans);
return serverMgr.getDefaultServer().saveAll(beans);
}
/**
@@ -508,7 +636,7 @@ public final class Ebean {
*/
@Nonnull
public static Set<Property> checkUniqueness(Object bean) {
return getDefault().checkUniqueness(bean);
return serverMgr.getDefaultServer().checkUniqueness(bean);
}
/**
@@ -516,7 +644,7 @@ public final class Ebean {
*/
@Nonnull
public static Set<Property> checkUniqueness(Object bean, Transaction transaction) {
return getDefault().checkUniqueness(bean, transaction);
return serverMgr.getDefaultServer().checkUniqueness(bean, transaction);
}
/**
@@ -539,56 +667,56 @@ public final class Ebean {
* </p>
*/
public static boolean delete(Object bean) throws OptimisticLockException {
return getDefault().delete(bean);
return serverMgr.getDefaultServer().delete(bean);
}
/**
* Delete the bean in permanent fashion (will not use soft delete).
*/
public static boolean deletePermanent(Object bean) throws OptimisticLockException {
return getDefault().deletePermanent(bean);
return serverMgr.getDefaultServer().deletePermanent(bean);
}
/**
* Delete the bean given its type and id.
*/
public static int delete(Class<?> beanType, Object id) {
return getDefault().delete(beanType, id);
return serverMgr.getDefaultServer().delete(beanType, id);
}
/**
* Delete permanent the bean given its type and id.
*/
public static int deletePermanent(Class<?> beanType, Object id) {
return getDefault().deletePermanent(beanType, id);
return serverMgr.getDefaultServer().deletePermanent(beanType, id);
}
/**
* Delete several beans given their type and id values.
*/
public static int deleteAll(Class<?> beanType, Collection<?> ids) {
return getDefault().deleteAll(beanType, ids);
return serverMgr.getDefaultServer().deleteAll(beanType, ids);
}
/**
* Delete permanent several beans given their type and id values.
*/
public static int deleteAllPermanent(Class<?> beanType, Collection<?> ids) {
return getDefault().deleteAllPermanent(beanType, ids);
return serverMgr.getDefaultServer().deleteAllPermanent(beanType, ids);
}
/**
* Delete all the beans in the Collection.
*/
public static int deleteAll(Collection<?> beans) throws OptimisticLockException {
return getDefault().deleteAll(beans);
return serverMgr.getDefaultServer().deleteAll(beans);
}
/**
* Delete permanent all the beans in the Collection (will not use soft delete).
*/
public static int deleteAllPermanent(Collection<?> beans) throws OptimisticLockException {
return getDefault().deleteAllPermanent(beans);
return serverMgr.getDefaultServer().deleteAllPermanent(beans);
}
/**
@@ -599,7 +727,7 @@ public final class Ebean {
* </p>
*/
public static void refresh(Object bean) {
getDefault().refresh(bean);
serverMgr.getDefaultServer().refresh(bean);
}
/**
@@ -617,7 +745,7 @@ public final class Ebean {
* @param manyPropertyName the property name of the List Set or Map to refresh.
*/
public static void refreshMany(Object bean, String manyPropertyName) {
getDefault().refreshMany(bean, manyPropertyName);
serverMgr.getDefaultServer().refreshMany(bean, manyPropertyName);
}
/**
@@ -642,7 +770,7 @@ public final class Ebean {
* @param id the id value
*/
public static <T> T getReference(Class<T> beanType, Object id) {
return getDefault().getReference(beanType, id);
return serverMgr.getDefaultServer().getReference(beanType, id);
}
/**
@@ -685,7 +813,7 @@ public final class Ebean {
* @param sortByClause the properties to sort the list by
*/
public static <T> void sort(List<T> list, String sortByClause) {
getDefault().sort(list, sortByClause);
serverMgr.getDefaultServer().sort(list, sortByClause);
}
/**
@@ -735,7 +863,7 @@ public final class Ebean {
*/
@Nullable
public static <T> T find(Class<T> beanType, Object id) {
return getDefault().find(beanType, id);
return serverMgr.getDefaultServer().find(beanType, id);
}
/**
@@ -750,7 +878,7 @@ public final class Ebean {
*/
@Deprecated
public static SqlQuery createSqlQuery(String sql) {
return getDefault().sqlQuery(sql);
return serverMgr.getDefaultServer().sqlQuery(sql);
}
/**
@@ -765,7 +893,7 @@ public final class Ebean {
*/
@Deprecated
public static SqlUpdate createSqlUpdate(String sql) {
return getDefault().sqlUpdate(sql);
return serverMgr.getDefaultServer().sqlUpdate(sql);
}
/**
@@ -774,7 +902,7 @@ public final class Ebean {
* @see CallableSql
*/
public static CallableSql createCallableSql(String sql) {
return getDefault().createCallableSql(sql);
return serverMgr.getDefaultServer().createCallableSql(sql);
}
/**
@@ -806,14 +934,16 @@ public final class Ebean {
* }</pre>
*/
public static <T> Update<T> createUpdate(Class<T> beanType, String ormUpdate) {
return getDefault().createUpdate(beanType, ormUpdate);
return serverMgr.getDefaultServer().createUpdate(beanType, ormUpdate);
}
/**
* Create a CsvReader for a given beanType.
*/
public static <T> CsvReader<T> createCsvReader(Class<T> beanType) {
return getDefault().createCsvReader(beanType);
return serverMgr.getDefaultServer().createCsvReader(beanType);
}
/**
@@ -828,7 +958,7 @@ public final class Ebean {
* @return The query
*/
public static <T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery) {
return getDefault().createNamedQuery(beanType, namedQuery);
return serverMgr.getDefaultServer().createNamedQuery(beanType, namedQuery);
}
/**
@@ -851,7 +981,8 @@ public final class Ebean {
* @return A ORM Query object for this beanType
*/
public static <T> Query<T> createQuery(Class<T> beanType) {
return getDefault().createQuery(beanType);
return serverMgr.getDefaultServer().createQuery(beanType);
}
/**
@@ -888,7 +1019,8 @@ public final class Ebean {
* @return The query with expressions defined as per the parsed query statement
*/
public static <T> Query<T> createQuery(Class<T> beanType, String eql) {
return getDefault().createQuery(beanType, eql);
return serverMgr.getDefaultServer().createQuery(beanType, eql);
}
/**
@@ -903,7 +1035,8 @@ public final class Ebean {
* @return A ORM Query object for this beanType
*/
public static <T> Query<T> find(Class<T> beanType) {
return getDefault().find(beanType);
return serverMgr.getDefaultServer().find(beanType);
}
/**
@@ -926,7 +1059,7 @@ public final class Ebean {
* @return The query to set parameters and execute
*/
public static <T> Query<T> findNative(Class<T> beanType, String nativeSql) {
return getDefault().findNative(beanType, nativeSql);
return serverMgr.getDefaultServer().findNative(beanType, nativeSql);
}
/**
@@ -941,7 +1074,7 @@ public final class Ebean {
* @param <T> The type of the DTO bean.
*/
public static <T> DtoQuery<T> findDto(Class<T> dtoType, String sql) {
return getDefault().findDto(dtoType, sql);
return serverMgr.getDefaultServer().findDto(dtoType, sql);
}
/**
@@ -963,7 +1096,7 @@ public final class Ebean {
* @return The update query to use
*/
public static <T> UpdateQuery<T> update(Class<T> beanType) {
return getDefault().update(beanType);
return serverMgr.getDefaultServer().update(beanType);
}
/**
@@ -977,7 +1110,7 @@ public final class Ebean {
* </p>
*/
public static <T> Filter<T> filter(Class<T> beanType) {
return getDefault().filter(beanType);
return serverMgr.getDefaultServer().filter(beanType);
}
/**
@@ -1020,7 +1153,7 @@ public final class Ebean {
* @see Ebean#execute(CallableSql)
*/
public static int execute(SqlUpdate sqlUpdate) {
return getDefault().execute(sqlUpdate);
return serverMgr.getDefaultServer().execute(sqlUpdate);
}
/**
@@ -1048,7 +1181,7 @@ public final class Ebean {
* @see Ebean#execute(SqlUpdate)
*/
public static int execute(CallableSql callableSql) {
return getDefault().execute(callableSql);
return serverMgr.getDefaultServer().execute(callableSql);
}
/**
@@ -1072,7 +1205,7 @@ public final class Ebean {
* }</pre>
*/
public static void execute(TxScope scope, Runnable r) {
getDefault().execute(scope, r);
serverMgr.getDefaultServer().execute(scope, r);
}
/**
@@ -1099,7 +1232,7 @@ public final class Ebean {
* }</pre>
*/
public static void execute(Runnable r) {
getDefault().execute(r);
serverMgr.getDefaultServer().execute(r);
}
/**
@@ -1124,7 +1257,7 @@ public final class Ebean {
* }</pre>
*/
public static <T> T executeCall(TxScope scope, Callable<T> c) {
return getDefault().executeCall(scope, c);
return serverMgr.getDefaultServer().executeCall(scope, c);
}
/**
@@ -1157,7 +1290,7 @@ public final class Ebean {
* }</pre>
*/
public static <T> T executeCall(Callable<T> c) {
return getDefault().executeCall(c);
return serverMgr.getDefaultServer().executeCall(c);
}
/**
@@ -1191,7 +1324,8 @@ public final class Ebean {
* @param deletes true if rows on the table where deleted
*/
public static void externalModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
getDefault().externalModification(tableName, inserts, updates, deletes);
serverMgr.getDefaultServer().externalModification(tableName, inserts, updates, deletes);
}
/**
@@ -1201,14 +1335,14 @@ public final class Ebean {
* </p>
*/
public static BeanState getBeanState(Object bean) {
return getDefault().getBeanState(bean);
return serverMgr.getDefaultServer().getBeanState(bean);
}
/**
* Return the manager of the server cache ("L2" cache).
*/
public static ServerCacheManager getServerCacheManager() {
return getDefault().getServerCacheManager();
return serverMgr.getDefaultServer().getServerCacheManager();
}
/**
@@ -1216,14 +1350,14 @@ public final class Ebean {
* queries.
*/
public static BackgroundExecutor getBackgroundExecutor() {
return getDefault().getBackgroundExecutor();
return serverMgr.getDefaultServer().getBackgroundExecutor();
}
/**
* Return the JsonContext for reading/writing JSON.
*/
public static JsonContext json() {
return getDefault().json();
return serverMgr.getDefaultServer().json();
}
}
-2
View File
@@ -1,7 +1,6 @@
package io.ebean;
/**
* Deprecated - please migrate to <code>io.ebean.Database</code>.
* Provides the API for fetching and saving beans to a particular Database.
* <p>
* Effectively this is an alias for {@link Database} which is now the new
@@ -9,7 +8,6 @@ package io.ebean;
* <p>
* The preference is to use DB and Database rather than Ebean and EbeanServer.
*/
@Deprecated
public interface EbeanServer extends Database {
}
@@ -11,8 +11,6 @@ import java.util.Properties;
import java.util.ServiceLoader;
/**
* Deprecated - please migrate to DatabaseFactory.
* <p>
* Creates EbeanServer instances.
* <p>
* This uses either a ServerConfig or properties in the ebean.properties file to
@@ -30,7 +28,6 @@ import java.util.ServiceLoader;
* convenient way of using the 'default/primary' EbeanServer.
* </p>
*/
@Deprecated
public class EbeanServerFactory {
@@ -73,7 +70,7 @@ public class EbeanServerFactory {
EbeanServer server = createInternal(config);
if (config.isRegister()) {
DbPrimary.setSkip(true);
PrimaryServer.setSkip(true);
Ebean.register(server, config.isDefaultServer());
}
@@ -126,7 +123,7 @@ public class EbeanServerFactory {
if (containerConfig == null) {
// effectively load configuration from ebean.properties
Properties properties = DbPrimary.getProperties();
Properties properties = PrimaryServer.getProperties();
containerConfig = new ContainerConfig();
containerConfig.loadFromProperties(properties);
}
+4 -8
View File
@@ -6,18 +6,18 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* Class to determine the ebean version.
*
* Class to determine the ebean version. (
* @author Roland Praml, FOCONIS AG
*
*/
public class EbeanVersion {
private EbeanVersion() {
}
private static final Logger logger = LoggerFactory.getLogger(EbeanVersion.class);
private static String version = "unknown";
static {
try {
Properties prop = new Properties();
@@ -34,10 +34,6 @@ public class EbeanVersion {
}
}
private EbeanVersion() {
// hide
}
/**
* Returns the ebean version (read from /META-INF/maven/io.ebean/ebean/pom.properties)
*/
+1 -1
View File
@@ -53,7 +53,7 @@ public class Expr {
}
/**
* In Range - {@code property >= value1 and property < value2}.
* In Range - property >= value1 and property < value2.
* <p>
* Unlike Between inRange is "half open" and usually more useful for use with dates or timestamps.
* </p>
@@ -155,7 +155,7 @@ public interface ExpressionFactory {
Expression ineObject(String propertyName, Object value);
/**
* In Range - {@code property >= value1 and property < value2}.
* In Range - property >= value1 and property < value2.
* <p>
* Unlike Between inRange is "half open" and usually more useful for use with dates or timestamps.
* </p>
+20 -50
View File
@@ -70,40 +70,30 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> order(String orderByClause);
/**
* Set the order by clause replacing the existing order by clause if there is
* one.
* <p>
* This follows SQL syntax using commas between each property with the
* optional asc and desc keywords representing ascending and descending order
* respectively.
*/
ExpressionList<T> orderBy(String orderBy);
/**
* Return the OrderBy so that you can append an ascending or descending
* property to the order by clause.
* <p>
* This will never return a null. If no order by clause exists then an 'empty'
* OrderBy object is returned.
* <p>
* This is the same as <code>orderBy()</code>
* </p>
*/
OrderBy<T> order();
/**
* Return the OrderBy so that you can append an ascending or descending
* property to the order by clause.
* <p>
* This will never return a null. If no order by clause exists then an 'empty'
* OrderBy object is returned.
* <p>
* This is the same as <code>order()</code>
* Deprecated migrate to order().
*/
@Deprecated
OrderBy<T> orderBy();
/**
* Deprecated migrate to {@link #orderBy(String)}
* Deprecated migrate to {@link #order(String)}
*/
@Deprecated
Query<T> orderBy(String orderBy);
/**
* Deprecated migrate to {@link #order(String)}
*/
@Deprecated
Query<T> setOrderBy(String orderBy);
@@ -859,7 +849,7 @@ public interface ExpressionList<T> {
ExpressionList<T> inRangeWith(String lowProperty, String highProperty, Object value);
/**
* In Range - {@code property >= value1 and property < value2}.
* In Range - property >= value1 and property < value2.
* <p>
* Unlike Between inRange is "half open" and usually more useful for use with dates or timestamps.
* </p>
@@ -1251,35 +1241,26 @@ public interface ExpressionList<T> {
/**
* Add raw expression with a single parameter.
* <p>
* The raw expression should contain a single ? or ?1
* at the location of the parameter. We use ?1 when binding a
* collection for an IN expression.
* The raw expression should contain a single ? at the location of the
* parameter.
* </p>
* <p>
* When properties in the clause are fully qualified as table-column names
* then they are not translated. logical property name names (not fully
* qualified) will still be translated to their physical name.
* </p>
* <p>
* <h4>Examples:</h4>
* <h4>Example:</h4>
* <pre>{@code
*
* // use a database function
* raw("add_days(orderDate, 10) < ?", someDate)
*
* raw("name like ?", "Rob%")
*
* raw("name in (?1)", asList("Rob", "Fiona", "Jack"))
*
* raw("name = any(?)", asList("Rob", "Fiona", "Jack"))
*
* }</pre>
*
* <h4>Subquery examples:</h4>
* <h4>Subquery example:</h4>
* <pre>{@code
*
* // Bind collection using ?1
* .raw("id in (select c.id from o_customer c where c.name in (?1))", asList("Rob", "Fiona", "Jack"))
*
* // Using Postgres ANY expression
* .raw("t0.customer_id in (select customer_id from customer_group where group_id = any(?::uuid[]))", groupIds)
*
* }</pre>
@@ -1289,25 +1270,14 @@ public interface ExpressionList<T> {
/**
* Add raw expression with an array of parameters.
* <p>
* The raw expression should contain the same number of ? or ?1, ?2 ... bind parameters
* as there are values. We use ?1, ?2 etc when binding a collection for an IN expression.
* The raw expression should contain the same number of ? as there are
* parameters.
* </p>
* <p>
* When properties in the clause are fully qualified as table-column names
* then they are not translated. logical property name names (not fully
* qualified) will still be translated to their physical name.
* </p>
*
* <h4>Examples:</h4>
* <pre>{@code
*
* raw("unitPrice > ? and product.id > ?", 2, 3)
*
* raw("(status = ? or (orderDate < ? and shipDate is null) or customer.name like ?)",
* Order.Status.APPROVED,
* new Timestamp(System.currentTimeMillis()),
* "Rob")
*
* }</pre></pre>
*/
ExpressionList<T> raw(String raw, Object... values);
+5 -11
View File
@@ -92,9 +92,11 @@ public interface ExtendedServer {
* {@link #findEachWhile(Query, Predicate, Transaction)} is preferred
* to findIterate(). The reason is that those methods automatically take care of
* closing the queryIterator (and the underlying jdbc statement and resultSet).
* </p>
* <p>
* This is similar to findEach in that not all the result beans need to be held
* in memory at the same time and as such is good for processing large queries.
* </p>
*
* @see Query#findIterate()
* @see Query#findEach(Consumer)
@@ -104,10 +106,7 @@ public interface ExtendedServer {
<T> QueryIterator<T> findIterate(Query<T> query, Transaction transaction);
/**
* Execute the query returning the result as a Stream.
* <p>
* Note that this can support very large queries iterating any number of results.
* To do so internally it can use multiple persistence contexts.
* Return the query result as a Stream using a single persistence context.
* <p>
* Note that the stream needs to be closed so use with try with resources.
* </p>
@@ -116,17 +115,12 @@ public interface ExtendedServer {
<T> Stream<T> findStream(Query<T> query, Transaction transaction);
/**
* Deprecated - migrate to findStream().
* <p>
* Execute the query returning the result as a Stream.
* <p>
* Note that this can support very large queries iterating any number of results.
* To do so internally it can use multiple persistence contexts.
* Return the query result as a Stream (with multiple persistence contexts).
* <p>
* Note that the stream needs to be closed so use with try with resources.
* </p>
*/
@Nonnull
@Deprecated
<T> Stream<T> findLargeStream(Query<T> query, Transaction transaction);
/**
+2 -4
View File
@@ -37,13 +37,12 @@ import java.util.List;
* }
* }
*
* Entity
* @Entity
* public class Customer extends BaseModel {
*
* public static final CustomerFinder find = new CustomerFinder();
* ...
*
* }
* }</pre>
* <p>
* When the Finder is registered as a field on Customer it can then be used like:
@@ -80,13 +79,12 @@ public class Finder<I, T> {
* // ... add extra customer specific finder methods
* }
*
* Entity
* @Entity
* public class Customer extends BaseModel {
*
* public static final CustomerFinder find = new CustomerFinder();
* ...
*
* }
* }</pre>
*/
public Finder(Class<T> type) {
+9 -9
View File
@@ -32,19 +32,19 @@ import io.ebean.bean.EntityBean;
* // Typically there is a common base model that has some
* // common properties like the ones below
*
* MappedSuperclass
* public class BaseModel extends Model {
* @MappedSuperclass
* public class BaseModel extends Model {
*
* Id Long id;
* @Id Long id;
*
* Version Long version;
* @Version Long version;
*
* WhenCreated Timestamp whenCreated;
* @WhenCreated Timestamp whenCreated;
*
* WhenUpdated Timestamp whenUpdated;
* @WhenUpdated Timestamp whenUpdated;
*
* ...
*
* ...
* }
* }</pre>
* <p>
* <h3>Extend the Model</h3>
@@ -52,7 +52,7 @@ import io.ebean.bean.EntityBean;
*
* // Extend the mappedSuperclass
*
* Entity Table(name="o_account")
* @Entity @Table(name="o_account")
* public class Customer extends BaseModel {
*
* String name;
+3 -1
View File
@@ -1,5 +1,7 @@
package io.ebean;
import io.ebean.util.StringHelper;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@@ -339,7 +341,7 @@ public final class OrderBy<T> implements Serializable {
if (collation != null) {
if (collation.contains("${}")) {
// this is a complex collation, e.g. DB2 - we must replace the property
sb.append(collation.replace("${}", property));
sb.append(StringHelper.replaceString(collation, "${}", property));
} else {
sb.append(property);
sb.append(" collate ").append(collation);
+2 -2
View File
@@ -94,8 +94,8 @@ public interface PagedList<T> {
* Return the Future row count. You might get this if you wish to cancel the total row count query
* or specify a timeout for the row count query.
* <p>
* The loadRowCount() and getTotalRowCount() methods internally make use of this getFutureRowCount() method.
* Generally I expect people to prefer loadRowCount() and getTotalRowCount() over getFutureRowCount().
* The loadRowCount() & getTotalRowCount() methods internally make use of this getFutureRowCount() method.
* Generally I expect people to prefer loadRowCount() & getTotalRowCount() over getFutureRowCount().
* </p>
* <pre>{@code
*
+1 -1
View File
@@ -18,7 +18,7 @@ import java.util.List;
*
* // where a bean is annotated with a complex
* // natural key made of several properties
* Cache(naturalKey = {"store","code","sku"})
* @Cache(naturalKey = {"store","code","sku"})
*
*
* Pairs pairs = new Pairs("sku", "code");
@@ -9,7 +9,7 @@ import java.util.Properties;
* <p/>
* Intended for internal use as part of bootup, construction, registration of the default database.
*/
class DbPrimary {
class PrimaryServer {
private static String defaultServerName;
@@ -19,7 +19,7 @@ class DbPrimary {
* Set whether to skip automatically creating the primary database.
*/
static synchronized void setSkip(boolean skip) {
DbPrimary.skip = skip;
PrimaryServer.skip = skip;
}
/**
+9 -22
View File
@@ -711,9 +711,9 @@ public interface Query<T> {
/**
* Execute the query returning the result as a Stream.
* <p>
* Note that this can support very large queries iterating
* any number of results. To do so internally it can use
* multiple persistence contexts.
* Note that this will hold all resulting beans in memory using a single
* persistence context. Use findLargeStream() for queries that expect to
* return a large number of results.
* </p>
* <pre>{@code
*
@@ -731,8 +731,6 @@ public interface Query<T> {
Stream<T> findStream();
/**
* Deprecated - migrate to findStream.
* <p>
* Execute the query returning the result as a Stream.
* <p>
* Note that this uses multiple persistence contexts such that we can use
@@ -751,7 +749,6 @@ public interface Query<T> {
* }</pre>
*/
@Nonnull
@Deprecated
Stream<T> findLargeStream();
/**
@@ -1355,13 +1352,9 @@ public interface Query<T> {
Query<T> having(Expression addExpressionToHaving);
/**
* Set the order by clause replacing the existing order by clause if there is
* one.
* <p>
* This follows SQL syntax using commas between each property with the
* optional asc and desc keywords representing ascending and descending order
* respectively.
* Deprecated migrate to {@link #order(String)}
*/
@Deprecated
Query<T> orderBy(String orderByClause);
/**
@@ -1380,20 +1373,13 @@ public interface Query<T> {
* <p>
* This will never return a null. If no order by clause exists then an 'empty'
* OrderBy object is returned.
* <p>
* This is the same as <code>orderBy()</code>
*/
OrderBy<T> order();
/**
* Return the OrderBy so that you can append an ascending or descending
* property to the order by clause.
* <p>
* This will never return a null. If no order by clause exists then an 'empty'
* OrderBy object is returned.
* <p>
* This is the same as <code>order()</code>
* Deprecated migrate to order().
*/
@Deprecated
OrderBy<T> orderBy();
/**
@@ -1402,8 +1388,9 @@ public interface Query<T> {
Query<T> setOrder(OrderBy<T> orderBy);
/**
* Set an OrderBy object to replace any existing OrderBy clause.
* Deprecated migrate to {@link #setOrder(OrderBy)}
*/
@Deprecated
Query<T> setOrderBy(OrderBy<T> orderBy);
/**
+42 -29
View File
@@ -6,13 +6,17 @@ package io.ebean;
* <p>
* If you don't want to build object graphs you can use {@link SqlQuery} instead
* which returns {@link SqlRow} objects rather than entity beans.
* </p>
* <p>
* <b>Unparsed RawSql:</b>
* </p>
* <p>
* When RawSql is created via {@link RawSqlBuilder#unparsed(String)} then Ebean can not
* modify the SQL at all. It can't add any extra expressions into the SQL.
* </p>
* <p>
* <b>Parsed RawSql:</b>
* </p>
* <p>
* When RawSql is created via {@link RawSqlBuilder#parse(String)} then Ebean will parse the
* SQL and find places in the SQL where it can add extra where expressions, add
@@ -20,13 +24,16 @@ package io.ebean;
* explicitly tell Ebean where these insertion points are you can place special
* strings into your SQL ({@code ${where}} or {@code ${andWhere}} and {@code ${having}} or
* {@code ${andHaving})}.
* </p>
* <p>
* If the SQL already includes a WHERE clause put in {@code ${andWhere}} in the location
* you want Ebean to add any extra where expressions. If the SQL doesn't have a
* WHERE clause put {@code ${where}} in instead. Similarly you can put in {@code ${having}} or
* {@code ${andHaving}} where you want Ebean put add extra having expressions.
* </p>
* <p>
* <b>Aggregates:</b>
* </p>
* <p>
* Often RawSql will be used with Aggregate functions (sum, avg, max etc). The
* follow example shows an example based on Total Order Amount -
@@ -37,37 +44,38 @@ package io.ebean;
* on RawSql and not based on a real DB Table or DB View. It has some properties
* to hold the values for the aggregate functions (sum etc) and a &#064;OneToOne
* to Order.
* </p>
* <p>
* <h3>Example OrderAggregate</h3>
* <pre>{@code
* ...
* // Sql indicates to that this bean
* // is based on RawSql rather than a table
* // @Sql indicates to that this bean
* // is based on RawSql rather than a table
*
* Entity
* Sql
* public class OrderAggregate {
* @Entity
* @Sql
* public class OrderAggregate {
*
* OneToOne
* Order order;
* @OneToOne
* Order order;
*
* Double totalAmount;
* Double totalAmount;
*
* Double totalItems;
* Double totalItems;
*
* // getters and setters
* ...
*
* // getters and setters
* ...
* }
* }</pre>
*
* <p>
* <h3>Example 1:</h3>
*
* <p>
* <pre>{@code
*
* String sql = " select order_id, o.status, c.id, c.name, sum(l.order_qty*l.unit_price) as totalAmount"
* + " from order o"
* + " join customer c on c.id = o.customer_id "
* + " join order_line l on l.order_id = o.id " + " group by order_id, o.status ";
* String sql = " select order_id, o.status, c.id, c.name, sum(d.order_qty*d.unit_price) as totalAmount"
* + " from o_order o"
* + " join o_customer c on c.id = o.kcustomer_id "
* + " join o_order_detail d on d.order_id = o.id " + " group by order_id, o.status ";
*
* RawSql rawSql = RawSqlBuilder.parse(sql)
* // map the sql result columns to bean properties
@@ -87,17 +95,18 @@ package io.ebean;
*
*
* }</pre>
*
* <p>
* <h3>Example 2:</h3>
* <p>
* The following example uses a FetchConfig().query() so that after the initial
* RawSql query is executed Ebean executes a secondary query to fetch the
* associated order status, orderDate along with the customer name.
*
* </p>
* <p>
* <pre>{@code
*
* String sql = " select order_id, 'ignoreMe', sum(l.order_qty*l.unit_price) as totalAmount "
* + " from order_line l"
* String sql = " select order_id, 'ignoreMe', sum(d.order_qty*d.unit_price) as totalAmount "
* + " from o_order_detail d"
* + " group by order_id ";
*
* RawSql rawSql = RawSqlBuilder.parse(sql)
@@ -116,23 +125,25 @@ package io.ebean;
* .findList();
*
* }</pre>
* <p>
* <h3>Example 3: tableAliasMapping</h3>
* <p>
* Instead of mapping each column you can map each table alias to a path using tableAliasMapping().
* </p>
* <pre>{@code
*
* String rs = "select o.id, o.status, c.id, c.name, "+
* " l.id, l.order_qty, p.id, p.name " +
* "from orders o join o_customer c on c.id = o.customer_id " +
* "join order_line l on l.order_id = o.id " +
* "join product p on p.id = l.product_id " +
* " d.id, d.order_qty, p.id, p.name " +
* "from o_order o join o_customer c on c.id = o.kcustomer_id " +
* "join o_order_detail d on d.order_id = o.id " +
* "join o_product p on p.id = d.product_id " +
* "where o.id <= :maxOrderId and p.id = :productId "+
* "order by o.id, l.id asc";
* "order by o.id, d.id asc";
*
* RawSql rawSql = RawSqlBuilder.parse(rs)
* .tableAliasMapping("c", "customer")
* .tableAliasMapping("l", "lines")
* .tableAliasMapping("p", "lines.product")
* .tableAliasMapping("d", "details")
* .tableAliasMapping("p", "details.product")
* .create();
*
* List<Order> ordersFromRaw = DB.find(Order.class)
@@ -143,7 +154,9 @@ package io.ebean;
*
* }</pre>
* <p>
* <p>
* Note that lazy loading also works with object graphs built with RawSql.
* </p>
*/
public interface RawSql {
+1 -1
View File
@@ -22,7 +22,7 @@ import java.sql.SQLException;
* //
* class CustomerMapper implements RowMapper<CustomerDto> {
*
* Override
* @Override
* public CustomerDto map(ResultSet rset, int rowNum) throws SQLException {
*
* long id = rset.getLong(1);
-37
View File
@@ -224,49 +224,12 @@ public interface SqlQuery extends Serializable {
*
* }</pre>
*
* <p>
* When binding a collection of values into a IN expression we should use
* indexed parameters like ?1, ?2, ?3 etc rather than just ?.
* </p>
*
* <pre>{@code
*
* String sql = "select c.id, c.name from customer c where c.name in (?1)";
*
* List<SqlRow> rows = DB.sqlQuery(sql)
* .setParameter(asList("Rob", "Fiona", "Jack"))
* .findList();
*
*
* List<SqlRow> rows = DB.sqlQuery(sql)
* .setParameter(1, asList("Rob", "Fiona", "Jack"))
* .findList();
* }</pre>
*
* @param value The value to bind
*/
SqlQuery setParameter(Object value);
/**
* Bind the parameter by its index position (1 based like JDBC).
* <p>
* When binding a collection of values into a IN expression we should use
* indexed parameters like ?1, ?2, ?3 etc rather than just ?.
* </p>
*
* <pre>{@code
*
* String sql = "select c.id, c.name from customer c where c.name in (?1)";
*
* List<SqlRow> rows = DB.sqlQuery(sql)
* .setParameter(asList("Rob", "Fiona", "Jack"))
* .findList();
*
*
* List<SqlRow> rows = DB.sqlQuery(sql)
* .setParameter(1, asList("Rob", "Fiona", "Jack"))
* .findList();
* }</pre>
*/
SqlQuery setParameter(int position, Object value);
+22 -44
View File
@@ -5,16 +5,18 @@ package io.ebean;
* <p>
* Provides a simple way to execute raw SQL insert update or delete statements
* without having to resort to JDBC.
* </p>
* <p>
* Supports the use of positioned or named parameters and can automatically
* notify Ebean of the table modified so that Ebean can maintain its cache.
* </p>
* <p>
* Note that {@link #setAutoTableMod(boolean)} and
* Ebean#externalModification(String, boolean, boolean, boolean)} can be to
* notify Ebean of external changes and enable Ebean to maintain it's "L2"
* server cache.
* </p>
*
* <h2>Positioned parameter example</h2>
* <pre>{@code
*
* // example using 'positioned' parameters
@@ -28,7 +30,6 @@ package io.ebean;
*
* }</pre>
*
* <h2>Named parameter example</h2>
* <pre>{@code
*
* // example using 'named' parameters
@@ -44,31 +45,8 @@ package io.ebean;
* String msg = "There were " + rows + " rows updated";
*
* }</pre>
*
* <h2>Index parameter examples (e.g. ?1, ?2, ?3 ...)</h2>
* <p>
* We can use index parameters like ?1, ?2, ?3 etc when binding arrays/collections
* of values into an IN expression.
* </p>
* <pre>{@code
*
* // Binding a list of 3 values (9991, 9992, 9993) into an IN expression
*
* DB.sqlUpdate("delete from o_customer where name = ? and id in (?2)")
* .setParameter(1, "Foo")
* .setParameter(2, asList(9991, 9992, 9993))
* .execute();
*
* // note this effectively is the same as
*
* DB.sqlUpdate("delete from o_customer where name = ? and id in (?2)")
* .setParameter("Foo")
* .setParameter(asList(9991, 9992, 9993))
* .execute();
*
* }</pre>
*
* <h3>Example: Using setParameter()</h3>
* <h3>Example: Using setNextParameter()</h3>
* <pre>{@code
*
* String sql = "insert into audit_log (id, description, modified_description) values (?,?,?)";
@@ -78,19 +56,19 @@ package io.ebean;
* try (Transaction txn = DB.beginTransaction()) {
* txn.setBatchMode(true);
*
* insert.setParameter(10000);
* insert.setParameter("hello");
* insert.setParameter("rob");
* insert.setNextParameter(10000);
* insert.setNextParameter("hello");
* insert.setNextParameter("rob");
* insert.execute();
*
* insert.setParameter(10001);
* insert.setParameter("goodbye");
* insert.setParameter("rob");
* insert.setNextParameter(10001);
* insert.setNextParameter("goodbye");
* insert.setNextParameter("rob");
* insert.execute();
*
* insert.setParameter(10002);
* insert.setParameter("chow");
* insert.setParameter("bob");
* insert.setNextParameter(10002);
* insert.setNextParameter("chow");
* insert.setNextParameter("bob");
* insert.execute();
*
* txn.commit();
@@ -103,19 +81,19 @@ package io.ebean;
*
* try (Transaction txn = DB.beginTransaction()) {
*
* insert.setParameter(10000);
* insert.setParameter("hello");
* insert.setParameter("rob");
* insert.setNextParameter(10000);
* insert.setNextParameter("hello");
* insert.setNextParameter("rob");
* insert.addBatch();
*
* insert.setParameter(10001);
* insert.setParameter("goodbye");
* insert.setParameter("rob");
* insert.setNextParameter(10001);
* insert.setNextParameter("goodbye");
* insert.setNextParameter("rob");
* insert.addBatch();
*
* insert.setParameter(10002);
* insert.setParameter("chow");
* insert.setParameter("bob");
* insert.setNextParameter(10002);
* insert.setNextParameter("chow");
* insert.setNextParameter("bob");
* insert.addBatch();
*
* int[] rows = insert.executeBatch();
+6 -38
View File
@@ -313,7 +313,7 @@ public interface Transaction extends AutoCloseable {
* <h3>getGeneratedKeys</h3>
* <p>
* Often with large batch inserts we want to turn off getGeneratedKeys. We do
* this via {@link #setGetGeneratedKeys(boolean)}.
* this via {@link #setBatchGetGeneratedKeys(boolean)}.
* Also note that some JDBC drivers do not support getGeneratedKeys in JDBC batch mode.
* </p>
* <pre>{@code
@@ -342,7 +342,7 @@ public interface Transaction extends AutoCloseable {
* </p>
* <p>
* We use {@link #flush()} to explicitly flush the batch and we can use
* {@link #setFlushOnQuery(boolean)} and {@link #setFlushOnMixed(boolean)}
* {@link #setBatchFlushOnQuery(boolean)} and {@link #setBatchFlushOnMixed(boolean)}
* to control the automatic flushing behaviour.
* </p>
* <p>
@@ -432,15 +432,7 @@ public interface Transaction extends AutoCloseable {
* number of objects and you don't care about getting back the ids.
* </p>
*/
void setGetGeneratedKeys(boolean getGeneratedKeys);
/**
* Deprecated renamed to setGetGeneratedKeys().
*/
@Deprecated
default void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
setGetGeneratedKeys(getGeneratedKeys);
}
void setBatchGetGeneratedKeys(boolean getGeneratedKeys);
/**
* By default when mixing UpdateSql (or CallableSql) with Beans the batch is
@@ -457,15 +449,7 @@ public interface Transaction extends AutoCloseable {
* have a 2 step process (delayed binding).
* </p>
*/
void setFlushOnMixed(boolean batchFlushOnMixed);
/**
* Deprecated renamed to setFlushOnMixed().
*/
@Deprecated
default void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
setFlushOnMixed(batchFlushOnMixed);
}
void setBatchFlushOnMixed(boolean batchFlushOnMixed);
/**
* By default executing a query will automatically flush any batched
@@ -475,15 +459,7 @@ public interface Transaction extends AutoCloseable {
* execute a query and the batch will not be automatically flushed.
* </p>
*/
void setFlushOnQuery(boolean batchFlushOnQuery);
/**
* Deprecated renamed to setFlushOnQuery().
*/
@Deprecated
default void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
setFlushOnQuery(batchFlushOnQuery);
}
void setBatchFlushOnQuery(boolean batchFlushOnQuery);
/**
* Return true if the batch (of persisted beans or executed UpdateSql etc)
@@ -492,15 +468,7 @@ public interface Transaction extends AutoCloseable {
* The default is for this to be true.
* </p>
*/
boolean isFlushOnQuery();
/**
* Deprecated renamed to isFlushOnQuery().
*/
@Deprecated
default boolean isBatchFlushOnQuery() {
return isFlushOnQuery();
}
boolean isBatchFlushOnQuery();
/**
* The batch will be flushing automatically but you can use this to explicitly
+20 -21
View File
@@ -12,27 +12,26 @@ package io.ebean;
* The following is an example of named updates on an entity bean.
* </p>
* <pre>{@code
* ...
* NamedUpdates(value = {
* NamedUpdate(
* name = "setTitle",
* notifyCache = false,
* update = "update topic set title = :title, postCount = :count where id = :id"),
* NamedUpdate(
* name = "setPostCount",
* notifyCache = false,
* update = "update f_topic set post_count = :postCount where id = :id"),
* NamedUpdate(
* name = "incrementPostCount",
* notifyCache = false,
* update = "update Topic set postCount = postCount + 1 where id = :id")
* //update = "update f_topic set post_count = post_count + 1 where id = :id")
* })
* Entity
* Table(name = "f_topic")
* public class Topic {
* ...
* }
* ...
* @NamedUpdates(value = {
* @NamedUpdate(
* name = "setTitle",
* notifyCache = false,
* update = "update topic set title = :title, postCount = :count where id = :id"),
* @NamedUpdate(
* name = "setPostCount",
* notifyCache = false,
* update = "update f_topic set post_count = :postCount where id = :id"),
* @NamedUpdate(
* name = "incrementPostCount",
* notifyCache = false,
* update = "update Topic set postCount = postCount + 1 where id = :id")
* //update = "update f_topic set post_count = post_count + 1 where id = :id")
* })
* @Entity
* @Table(name = "f_topic")
* public class Topic {
* ...
* }</pre>
*
* <p>
@@ -40,8 +40,6 @@ public final class EntityBeanIntercept implements Serializable {
private String ebeanServerName;
private boolean deletedFromCollection;
/**
* The actual entity bean that 'owns' this intercept.
*/
@@ -82,14 +80,14 @@ public final class EntityBeanIntercept implements Serializable {
private static final byte FLAG_CHANGED_PROP = 2;
/**
* Flags indicating if a property is a dirty embedded bean. Used to distinguish
* Flags indicating if a property is a dirty embedded bean. Used to distingush
* 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
* Flags indicating if a property is a dirty embedded bean. Used to distingush
* between an embedded bean being completely overwritten and one of its
* embedded properties being made dirty.
*/
@@ -371,11 +369,13 @@ public final class EntityBeanIntercept implements Serializable {
/**
* Set the loaded state to true.
* <p>
* Calls to setter methods after the bean is loaded can result in
* 'Old Values' being created.
* Calls to setter methods after the bean is loaded can result in 'Old Values'
* being created to support ConcurrencyMode.ALL
* </p>
* <p>
* Worth noting that this is also set after a insert/update. By doing so it
* 'resets' the bean for making further changes and saving again.
* </p>
*/
public void setLoaded() {
this.state = STATE_LOADED;
@@ -389,7 +389,8 @@ public final class EntityBeanIntercept implements Serializable {
}
/**
* When finished loading for lazy or refresh on an already partially populated bean.
* When finished loading for lazy or refresh on an already partially populated
* bean.
*/
public void setLoadedLazy() {
this.state = STATE_LOADED;
@@ -420,6 +421,9 @@ public final class EntityBeanIntercept implements Serializable {
/**
* Set true to turn off lazy loading.
* <p>
* Typically used to disable lazy loading on SQL based report beans.
* </p>
*/
public void setDisableLazyLoad(boolean disableLazyLoad) {
this.disableLazyLoad = disableLazyLoad;
@@ -439,6 +443,7 @@ public final class EntityBeanIntercept implements Serializable {
* Return true if the embedded bean is new or dirty and hence needs saving.
*/
public boolean isEmbeddedNewOrDirty(Object embeddedBean) {
if (embeddedBean == null) {
// if it was previously set then the owning bean would
// have oldValues containing the previous embedded bean
@@ -446,6 +451,7 @@ public final class EntityBeanIntercept implements Serializable {
}
if (embeddedBean instanceof EntityBean) {
return ((EntityBean) embeddedBean)._ebean_getIntercept().isNewOrDirty();
} else {
// non-enhanced so must assume it is new and needs to be saved
return true;
@@ -463,8 +469,8 @@ public final class EntityBeanIntercept implements Serializable {
}
/**
* Finds the index position of a given property. Returns -1 if the
* property can not be found.
* Finds the index position of a given property. Returns -1 if the property
* can not be found.
*/
public int findProperty(String propertyName) {
String[] names = owner._ebean_getPropertyNames();
@@ -487,7 +493,7 @@ public final class EntityBeanIntercept implements Serializable {
}
/**
* Return the number of properties.
* Return the number of properties.s
*/
public int getPropertyLength() {
return owner._ebean_getPropertyNames().length;
@@ -509,7 +515,8 @@ public final class EntityBeanIntercept implements Serializable {
}
/**
* Set the property to be treated as unloaded. Used for properties initialised in default constructor.
* Set the property to be treated as unloaded. Used for properties initialised in default
* constructor.
*/
public void setPropertyUnloaded(int propertyIndex) {
flags[propertyIndex] &= ~FLAG_LOADED_PROP;
@@ -596,6 +603,7 @@ public final class EntityBeanIntercept implements Serializable {
* For forced update on a 'New' bean set all the loaded properties to changed.
*/
public void setNewBeanForUpdate() {
for (int i = 0; i < flags.length; i++) {
if ((flags[i] & FLAG_LOADED_PROP) != 0) {
flags[i] |= FLAG_CHANGED_PROP;
@@ -650,7 +658,8 @@ public final class EntityBeanIntercept implements Serializable {
for (int i = 0; i < len; i++) {
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
// the property has been changed on this bean
props.add((prefix == null ? getProperty(i) : prefix + getProperty(i)));
String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i));
props.add(propName);
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
// an embedded property has been changed - recurse
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
@@ -663,10 +672,12 @@ public final class EntityBeanIntercept implements Serializable {
* Return true if any of the given property names are dirty.
*/
public boolean hasDirtyProperty(Set<String> propertyNames) {
String[] names = owner._ebean_getPropertyNames();
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
// the property has been changed on this bean
if (propertyNames.contains(names[i])) {
return true;
}
@@ -699,9 +710,10 @@ public final class EntityBeanIntercept implements Serializable {
String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i));
Object newVal = owner._ebean_getField(i);
Object oldVal = getOrigValue(i);
if (notEqual(oldVal, newVal)) {
if (!areEqual(oldVal, newVal)) {
dirtyValues.put(propName, new ValuePair(newVal, oldVal));
}
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
// an embedded property has been changed - recurse
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
@@ -720,9 +732,10 @@ public final class EntityBeanIntercept implements Serializable {
// the property has been changed on this bean
Object newVal = owner._ebean_getField(i);
Object oldVal = getOrigValue(i);
if (notEqual(oldVal, newVal)) {
if (!areEqual(oldVal, newVal)) {
visitor.visit(i, newVal, oldVal);
}
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
// an embedded property has been changed - recurse
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
@@ -743,7 +756,7 @@ public final class EntityBeanIntercept implements Serializable {
}
/**
* Add and return a dirty property hash.
* Add and return a dirty property hash recursing into embedded beans.
*/
private void addDirtyPropertyKey(StringBuilder sb) {
if (sortOrder > 0) {
@@ -803,18 +816,21 @@ public final class EntityBeanIntercept implements Serializable {
* Load the bean when it is a reference.
*/
protected void loadBean(int loadProperty) {
synchronized (this) {
if (beanLoader == null) {
final Database database = DB.byName(ebeanServerName);
if (database == null) {
throw new PersistenceException("Database [" + ebeanServerName + "] was not found?");
}
// For stand alone reference bean or after deserialisation lazy load
// using the ebeanServer. Synchronise only on the bean.
loadBeanInternal(loadProperty, database.getPluginApi());
return;
}
}
synchronized (beanLoader) {
// Lazy loading using LoadBeanContext which supports batch loading
// Synchronise on the beanLoader (a 'node' of the LoadBeanContext 'tree')
@@ -826,24 +842,32 @@ public final class EntityBeanIntercept implements Serializable {
* Invoke the lazy loading. This method is synchronised externally.
*/
private void loadBeanInternal(int loadProperty, BeanLoader loader) {
if ((flags[loadProperty] & FLAG_LOADED_PROP) != 0) {
// race condition where multiple threads calling preGetter concurrently
return;
}
if (lazyLoadFailure) {
// failed when batch lazy loaded by another bean in the batch
throw new EntityNotFoundException("(Lazy) loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted");
}
if (lazyLoadProperty == -1) {
lazyLoadProperty = loadProperty;
if (nodeUsageCollector != null) {
nodeUsageCollector.setLoadProperty(getProperty(lazyLoadProperty));
}
loader.loadBean(this);
if (lazyLoadFailure) {
// failed when lazy loading this bean
throw new EntityNotFoundException("Lazy loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted.");
}
// bean should be loaded and intercepting now. setLoaded() has
// been called by the lazy loading mechanism
}
@@ -853,31 +877,32 @@ public final class EntityBeanIntercept implements Serializable {
* Helper method to check if two objects are equal.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
protected static boolean notEqual(Object obj1, Object obj2) {
protected boolean areEqual(Object obj1, Object obj2) {
if (obj1 == null) {
return (obj2 != null);
return (obj2 == null);
}
if (obj2 == null) {
return true;
return false;
}
if (obj1 == obj2) {
return false;
return true;
}
if (obj1 instanceof BigDecimal) {
// Use comparable for BigDecimal as equals
// uses scale in comparison...
if (obj2 instanceof BigDecimal) {
Comparable com1 = (Comparable) obj1;
return (com1.compareTo(obj2) != 0);
return (com1.compareTo(obj2) == 0);
} else {
return true;
return false;
}
}
if (obj1 instanceof URL) {
// use the string format to determine if dirty
return !obj1.toString().equals(obj2.toString());
return obj1.toString().equals(obj2.toString());
}
return !obj1.equals(obj2);
return obj1.equals(obj2);
}
/**
@@ -888,7 +913,6 @@ public final class EntityBeanIntercept implements Serializable {
}
private void preGetterCallback(int propertyIndex) {
PreGetterCallback preGetterCallback = this.preGetterCallback;
if (preGetterCallback != null) {
preGetterCallback.preGetterTrigger(propertyIndex);
}
@@ -918,20 +942,25 @@ public final class EntityBeanIntercept implements Serializable {
}
/**
* OneToMany and ManyToMany only set loaded state.
* OneToMany and ManyToMany don't have any interception so just check for
* PropertyChangeSupport.
*/
public void preSetterMany(boolean interceptField, int propertyIndex, Object oldValue, Object newValue) {
if (readOnly) {
throw new IllegalStateException("This bean is readOnly");
}
setLoadedProperty(propertyIndex);
}
private void setChangedPropertyValue(int propertyIndex, boolean setDirtyState, Object origValue) {
if (readOnly) {
throw new IllegalStateException("This bean is readOnly");
}
setChangedProperty(propertyIndex);
if (setDirtyState) {
setOriginalValue(propertyIndex, origValue);
setDirtyStatus();
@@ -956,9 +985,10 @@ public final class EntityBeanIntercept implements Serializable {
* the old values for use with ConcurrencyMode.ALL.
*/
public void preSetter(boolean intercept, int propertyIndex, Object oldValue, Object newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (notEqual(oldValue, newValue)) {
} else if (!areEqual(oldValue, newValue)) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
}
}
@@ -968,6 +998,7 @@ public final class EntityBeanIntercept implements Serializable {
* Check for primitive boolean.
*/
public void preSetter(boolean intercept, int propertyIndex, boolean oldValue, boolean newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
@@ -979,6 +1010,7 @@ public final class EntityBeanIntercept implements Serializable {
* Check for primitive int.
*/
public void preSetter(boolean intercept, int propertyIndex, int oldValue, int newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
@@ -990,6 +1022,7 @@ public final class EntityBeanIntercept implements Serializable {
* long.
*/
public void preSetter(boolean intercept, int propertyIndex, long oldValue, long newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
@@ -1001,6 +1034,7 @@ public final class EntityBeanIntercept implements Serializable {
* double.
*/
public void preSetter(boolean intercept, int propertyIndex, double oldValue, double newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (Double.compare(oldValue, newValue) != 0) {
@@ -1012,6 +1046,7 @@ public final class EntityBeanIntercept implements Serializable {
* float.
*/
public void preSetter(boolean intercept, int propertyIndex, float oldValue, float newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (Float.compare(oldValue, newValue) != 0) {
@@ -1023,6 +1058,7 @@ public final class EntityBeanIntercept implements Serializable {
* short.
*/
public void preSetter(boolean intercept, int propertyIndex, short oldValue, short newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
@@ -1034,6 +1070,7 @@ public final class EntityBeanIntercept implements Serializable {
* char.
*/
public void preSetter(boolean intercept, int propertyIndex, char oldValue, char newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
@@ -1045,6 +1082,7 @@ public final class EntityBeanIntercept implements Serializable {
* byte.
*/
public void preSetter(boolean intercept, int propertyIndex, byte oldValue, byte newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
@@ -1056,6 +1094,7 @@ public final class EntityBeanIntercept implements Serializable {
* char[].
*/
public void preSetter(boolean intercept, int propertyIndex, char[] oldValue, char[] newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (!Arrays.equals(oldValue, newValue)) {
@@ -1067,6 +1106,7 @@ public final class EntityBeanIntercept implements Serializable {
* byte[].
*/
public void preSetter(boolean intercept, int propertyIndex, byte[] oldValue, byte[] newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (!Arrays.equals(oldValue, newValue)) {
@@ -1097,17 +1137,6 @@ public final class EntityBeanIntercept implements Serializable {
this.sortOrder = sortOrder;
}
/**
* Set if the entity was deleted from a BeanCollection.
*/
public void setDeletedFromCollection(final boolean deletedFromCollection) {
this.deletedFromCollection = deletedFromCollection;
}
public boolean isOrphanDelete() {
return deletedFromCollection && !isNew();
}
/**
* Set the load error that happened on this property.
*/
@@ -61,22 +61,6 @@ public interface PersistenceContext {
*/
int size(Class<?> rootType);
/**
* Return a copy of the Persistence context to use for large query iteration.
*/
PersistenceContext forIterate();
/**
* Return a new Persistence context during iteration of large query result.
*/
PersistenceContext forIterateReset();
/**
* Return true if the persistence context has grown and hit the 'reset limit'
* during large query iteration.
*/
boolean resetLimit();
/**
* Wrapper on a bean to also indicate if a bean has been deleted.
* <p>
@@ -1,7 +1,5 @@
package io.ebean.common;
import io.ebean.bean.EntityBean;
import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedHashSet;
@@ -56,11 +54,6 @@ class ModifyHolder<E> implements Serializable {
void modifyAddition(E bean) {
if (bean != null) {
touched = true;
if (bean instanceof EntityBean) {
((EntityBean) bean)._ebean_getIntercept().setDeletedFromCollection(false);
}
// If it is to delete then just remove the deletion
if (!undoDeletion(bean)) {
// Insert
@@ -77,11 +70,6 @@ class ModifyHolder<E> implements Serializable {
void modifyRemoval(Object bean) {
if (bean != null) {
touched = true;
if (bean instanceof EntityBean) {
((EntityBean) bean)._ebean_getIntercept().setDeletedFromCollection(true);
}
// If it is to be added then just remove the addition
if (!undoAddition(bean)) {
modifyDeletions.add((E) bean);
@@ -3,6 +3,8 @@ package io.ebean.config;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.util.AnnotationUtil;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Inheritance;
import javax.persistence.Table;
@@ -14,6 +16,10 @@ import javax.persistence.Table;
*/
public abstract class AbstractNamingConvention implements NamingConvention {
private static final Pattern TABLE_REPLACE = Pattern.compile("{table}", Pattern.LITERAL);
private static final Pattern COLUMN_REPLACE = Pattern.compile("{column}", Pattern.LITERAL);
/**
* The Constant DEFAULT_SEQ_FORMAT.
*/
@@ -79,9 +85,13 @@ public abstract class AbstractNamingConvention implements NamingConvention {
@Override
public String getSequenceName(String rawTableName, String pkColumn) {
final String tableNameUnquoted = databasePlatform.unQuote(rawTableName);
String seqName = sequenceFormat.replace("{table}", tableNameUnquoted);
pkColumn = (pkColumn == null) ? "" : databasePlatform.unQuote(pkColumn);
return seqName.replace("{column}", pkColumn);
String s = TABLE_REPLACE.matcher(sequenceFormat).replaceAll(Matcher.quoteReplacement(tableNameUnquoted));
if (pkColumn == null) {
pkColumn = "";
} else {
pkColumn = databasePlatform.unQuote(pkColumn);
}
return COLUMN_REPLACE.matcher(s).replaceAll(Matcher.quoteReplacement(pkColumn));
}
/**
@@ -212,8 +222,8 @@ public abstract class AbstractNamingConvention implements NamingConvention {
* Return true if this class is part of entity inheritance.
*/
protected boolean hasInheritance(Class<?> supCls) {
return AnnotationUtil.typeHas(supCls, Inheritance.class)
|| AnnotationUtil.has(supCls, DiscriminatorValue.class);
return AnnotationUtil.findAnnotationRecursive(supCls, Inheritance.class) != null
|| AnnotationUtil.findAnnotation(supCls, DiscriminatorValue.class) != null;
}
@@ -245,12 +255,16 @@ public abstract class AbstractNamingConvention implements NamingConvention {
* Gets the table name from annotation.
*/
protected TableName getTableNameFromAnnotation(Class<?> beanClass) {
final Table table = AnnotationUtil.typeGet(beanClass, Table.class);
if (table != null && !isEmpty(table.name())) {
final Table t = AnnotationUtil.findAnnotationRecursive(beanClass, Table.class);
// Take the annotation if defined
if (t != null && !isEmpty(t.name())) {
// Note: empty catalog and schema are converted to null
// Only need to convert quoted identifiers from annotations
return new TableName(quoteIdentifiers(table.catalog()), quoteIdentifiers(table.schema()), quoteIdentifiers(table.name()));
return new TableName(quoteIdentifiers(t.catalog()), quoteIdentifiers(t.schema()), quoteIdentifiers(t.name()));
}
// No annotation
return null;
}
@@ -13,8 +13,6 @@ import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.Properties;
import static io.ebean.util.StringHelper.replace;
/**
* Configuration for the DB migration processing.
*/
@@ -377,8 +375,8 @@ public class DbMigrationConfig {
*/
public String getDdlHeader() {
if (ddlHeader != null && !ddlHeader.isEmpty()) {
ddlHeader = replace(ddlHeader, "${version}", EbeanVersion.getVersion());
ddlHeader = replace(ddlHeader, "${timestamp}", ZonedDateTime.now().format( DateTimeFormatter.ISO_INSTANT ));
ddlHeader = StringHelper.replaceString(ddlHeader, "${version}", EbeanVersion.getVersion());
ddlHeader = StringHelper.replaceString(ddlHeader, "${timestamp}", ZonedDateTime.now().format( DateTimeFormatter.ISO_INSTANT ));
}
return ddlHeader;
}
@@ -18,9 +18,4 @@ public @interface ModuleInfo {
* The db name prefix is added to entity classes for non default databases.
*/
String[] entities() default {};
/**
* Other classes like Attribute Converters.
*/
String[] other() default {};
}
@@ -34,11 +34,6 @@ public class PlatformConfig {
*/
private String databaseBooleanFalse;
/**
* For DB's using sequences this is the number of sequence values prefetched.
*/
private int databaseSequenceBatchSize = 20;
/**
* Set for DB's that support both Sequence and Identity (and the default choice is not desired).
*/
@@ -79,7 +74,6 @@ public class PlatformConfig {
public PlatformConfig(PlatformConfig platformConfig) {
this.databaseBooleanFalse = platformConfig.databaseBooleanFalse;
this.databaseBooleanTrue = platformConfig.databaseBooleanTrue;
this.databaseSequenceBatchSize = platformConfig.databaseSequenceBatchSize;
this.idType = platformConfig.idType;
this.geometrySRID = platformConfig.geometrySRID;
this.dbUuid = platformConfig.dbUuid;
@@ -173,20 +167,6 @@ public class PlatformConfig {
this.databaseBooleanFalse = databaseBooleanFalse;
}
/**
* Return the number of DB sequence values that should be preallocated.
*/
public int getDatabaseSequenceBatchSize() {
return databaseSequenceBatchSize;
}
/**
* Set the number of DB sequence values that should be preallocated.
*/
public void setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
this.databaseSequenceBatchSize = databaseSequenceBatchSize;
}
/**
* Return the Geometry SRID.
*/
@@ -294,7 +274,6 @@ public class PlatformConfig {
public void loadSettings(PropertiesWrapper p) {
idType = p.getEnum(IdType.class, "idType", idType);
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize);
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
databaseInetAddressVarchar = p.getBoolean("databaseInetAddressVarchar", databaseInetAddressVarchar);
+145 -76
View File
@@ -44,8 +44,6 @@ import java.util.Properties;
import java.util.ServiceLoader;
/**
* Deprecated - please migrate to <code>io.ebean.DatabaseConfig</code>.
*
* The configuration used for creating a Database.
* <p>
* Used to programmatically construct a Database and optionally register it
@@ -82,7 +80,6 @@ import java.util.ServiceLoader;
* @author rbygrave
* @see DatabaseFactory
*/
@Deprecated
public class ServerConfig {
/**
@@ -94,7 +91,7 @@ public class ServerConfig {
* Typically configuration type objects that are passed by this ServerConfig
* to plugins. For example - IgniteConfiguration passed to Ignite plugin.
*/
private final Map<String, Object> serviceObject = new HashMap<>();
private Map<String, Object> serviceObject = new HashMap<>();
private ContainerConfig containerConfig;
@@ -142,7 +139,7 @@ public class ServerConfig {
* Having this on and registering entity classes means we don't need to manually
* write that code or use classpath scanning to find entity classes.
*/
private boolean loadModuleInfo = true;
private boolean loadModuleInfo;
/**
* List of interesting classes such as entities, embedded, ScalarTypes,
@@ -188,7 +185,7 @@ public class ServerConfig {
private JsonConfig.Date jsonDate = JsonConfig.Date.ISO8601;
/**
* For writing JSON specify if null values or empty collections should be excluded.
* For writing JSON specify if null values or empty collections should be exluded.
* By default all values are included.
*/
private JsonConfig.Include jsonInclude = JsonConfig.Include.ALL;
@@ -334,6 +331,18 @@ public class ServerConfig {
*/
private ClassLoadConfig classLoadConfig = new ClassLoadConfig();
/**
* Set to true if the DataSource uses autoCommit.
* <p>
* Indicates that Ebean should use autoCommit friendly Transactions and TransactionManager.
*/
private boolean autoCommitMode;
/**
* Set to true if transaction begin should be started with explicit statement.
*/
private boolean explicitTransactionBeginMode;
/**
* The data source JNDI name if using a JNDI DataSource.
*/
@@ -466,11 +475,6 @@ public class ServerConfig {
private String enabledL2Regions;
/**
* Set to true to effectively disable L2 cache plugins.
*/
private boolean localOnlyL2Cache;
/**
* Should the javax.validation.constraints.NotNull enforce a notNull column in DB.
* If set to false, use io.ebean.annotation.NotNull or Column(nullable=true).
@@ -528,6 +532,7 @@ public class ServerConfig {
* Construct a Database Configuration for programmatically creating an Database.
*/
public ServerConfig() {
}
/**
@@ -592,6 +597,7 @@ public class ServerConfig {
* Put a service object into configuration such that it can be passed to a plugin.
* <p>
* For example, put IgniteConfiguration in to be passed to the Ignite plugin.
* </p>
*/
public void putServiceObject(String key, Object configObject) {
serviceObject.put(key, configObject);
@@ -702,7 +708,8 @@ public class ServerConfig {
/**
* Set the JSON include mode used when writing JSON.
* <p>
* Set to NON_NULL or NON_EMPTY to suppress nulls or null and empty collections respectively.
* Set to NON_NULL or NON_EMPTY to suppress nulls or null & empty collections respectively.
* </p>
*/
public void setJsonInclude(JsonConfig.Include jsonInclude) {
this.jsonInclude = jsonInclude;
@@ -747,6 +754,7 @@ public class ServerConfig {
* when it is created.
* <p>
* By default this is set to true.
* </p>
*/
public boolean isRegister() {
return register;
@@ -757,6 +765,7 @@ public class ServerConfig {
* singleton when it is created.
* <p>
* By default this is set to true.
* </p>
*/
public void setRegister(boolean register) {
this.register = register;
@@ -767,6 +776,7 @@ public class ServerConfig {
* with the Ebean singleton.
* <p>
* This is only used when {@link #setRegister(boolean)} is also true.
* </p>
*/
public boolean isDefaultServer() {
return defaultServer;
@@ -777,6 +787,7 @@ public class ServerConfig {
* with the DB singleton.
* <p>
* This is only used when {@link #setRegister(boolean)} is also true.
* </p>
*/
public void setDefaultServer(boolean defaultServer) {
this.defaultServer = defaultServer;
@@ -888,6 +899,7 @@ public class ServerConfig {
* When INSERT or ALL is used then save(), delete() etc do not execute immediately but instead go into
* a JDBC batch execute buffer that is flushed. The buffer is flushed if a query is executed, transaction ends
* or the batch size is meet.
* </p>
*/
public PersistBatch getPersistBatch() {
return persistBatch;
@@ -899,6 +911,7 @@ public class ServerConfig {
* When INSERT or ALL is used then save(), delete() etc do not execute immediately but instead go into
* a JDBC batch execute buffer that is flushed. The buffer is flushed if a query is executed, transaction ends
* or the batch size is meet.
* </p>
*/
public void setPersistBatch(PersistBatch persistBatch) {
this.persistBatch = persistBatch;
@@ -909,8 +922,10 @@ public class ServerConfig {
* <p>
* This makes sense when a save() or delete() cascades and executes multiple child statements. The best case
* for this is when saving a master/parent bean this cascade inserts many detail/child beans.
* </p>
* <p>
* This only takes effect when the persistBatch mode at the transaction level does not take effect.
* </p>
*/
public PersistBatch getPersistBatchOnCascade() {
return persistBatchOnCascade;
@@ -921,8 +936,10 @@ public class ServerConfig {
* <p>
* This makes sense when a save() or delete() etc cascades and executes multiple child statements. The best caase
* for this is when saving a master/parent bean this cascade inserts many detail/child beans.
* </p>
* <p>
* This only takes effect when the persistBatch mode at the transaction level does not take effect.
* </p>
*/
public void setPersistBatchOnCascade(PersistBatch persistBatchOnCascade) {
this.persistBatchOnCascade = persistBatchOnCascade;
@@ -931,13 +948,17 @@ public class ServerConfig {
/**
* Deprecated, please migrate to using setPersistBatch().
* <p>
* Set to true if you what to use JDBC batching for persisting and deleting beans.
* Set to true if you what to use JDBC batching for persisting and deleting
* beans.
* </p>
* <p>
* With this Ebean will batch up persist requests and use the JDBC batch api.
* This is a performance optimisation designed to reduce the network chatter.
* </p>
* <p>
* When true this is equivalent to {@code setPersistBatch(PersistBatch.ALL)} or
* when false to {@code setPersistBatch(PersistBatch.NONE)}
* </p>
*/
public void setPersistBatching(boolean persistBatching) {
this.persistBatch = (persistBatching) ? PersistBatch.ALL : PersistBatch.NONE;
@@ -954,6 +975,7 @@ public class ServerConfig {
* Set the batch size used for JDBC batching. If unset this defaults to 20.
* <p>
* You can also set the batch size on the transaction.
* </p>
*
* @see Transaction#setBatchSize(int)
*/
@@ -1015,28 +1037,19 @@ public class ServerConfig {
* <p>
* This is the number of beans or collections loaded when lazy loading is
* invoked by default.
* </p>
* <p>
* The default value is for this is 10 (load 10 beans or collections).
* </p>
* <p>
* You can explicitly control the lazy loading batch size for a given join on
* a query using +lazy(batchSize) or JoinConfig.
* </p>
*/
public void setLazyLoadBatchSize(int lazyLoadBatchSize) {
this.lazyLoadBatchSize = lazyLoadBatchSize;
}
/**
* Set the number of sequences to fetch/preallocate when using DB sequences.
* <p>
* This is a performance optimisation to reduce the number times Ebean
* requests a sequence to be used as an Id for a bean (aka reduce network
* chatter).
*/
public void setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
}
/**
* Return the default JDBC fetchSize hint for findList queries.
*/
@@ -1070,6 +1083,7 @@ public class ServerConfig {
* <p>
* This is used to set user context information to the ChangeSet in the
* foreground thread prior to the logging occurring in a background thread.
* </p>
*/
public ChangeLogPrepare getChangeLogPrepare() {
return changeLogPrepare;
@@ -1080,6 +1094,7 @@ public class ServerConfig {
* <p>
* This is used to set user context information to the ChangeSet in the
* foreground thread prior to the logging occurring in a background thread.
* </p>
*/
public void setChangeLogPrepare(ChangeLogPrepare changeLogPrepare) {
this.changeLogPrepare = changeLogPrepare;
@@ -1176,6 +1191,7 @@ public class ServerConfig {
* It is expected that an implementation is used that read user context information
* (user id, user ip address etc) and sets it on the ReadEvent bean before it is sent
* to the ReadAuditLogger.
* </p>
*/
public void setReadAuditPrepare(ReadAuditPrepare readAuditPrepare) {
this.readAuditPrepare = readAuditPrepare;
@@ -1246,6 +1262,7 @@ public class ServerConfig {
* Return the time zone to use when reading/writing Timestamps via JDBC.
* <p>
* When set a Calendar object is used in JDBC calls when reading/writing Timestamp objects.
* </p>
*/
public String getDataTimeZone() {
return System.getProperty("ebean.dataTimeZone", dataTimeZone);
@@ -1508,6 +1525,7 @@ public class ServerConfig {
* Return the NamingConvention.
* <p>
* If none has been set the default UnderscoreNamingConvention is used.
* </p>
*/
public NamingConvention getNamingConvention() {
return namingConvention;
@@ -1517,6 +1535,7 @@ public class ServerConfig {
* Set the NamingConvention.
* <p>
* If none is set the default UnderscoreNamingConvention is used.
* </p>
*/
public void setNamingConvention(NamingConvention namingConvention) {
this.namingConvention = namingConvention;
@@ -1628,9 +1647,11 @@ public class ServerConfig {
* <p>
* Note that the DataSource is expected to use AutoCommit true mode avoiding the need
* for explicit commit (or rollback).
* </p>
* <p>
* This read only DataSource will be used for implicit query only transactions. It is not
* used if the transaction is created explicitly or if the query is an update or delete query.
* </p>
*/
public void setReadOnlyDataSource(DataSource readOnlyDataSource) {
this.readOnlyDataSource = readOnlyDataSource;
@@ -1670,10 +1691,12 @@ public class ServerConfig {
* Return the configuration for the read only DataSource.
* <p>
* This is only used if autoReadOnlyDataSource is true.
* </p>
* <p>
* The driver, url, username and password default to the configuration for the main DataSource if they are not
* set on this configuration. This means there is actually no need to set any configuration here and we only
* set configuration for url, username and password etc if it is different from the main DataSource.
* </p>
*/
public DataSourceConfig getReadOnlyDataSourceConfig() {
return readOnlyDataSourceConfig;
@@ -1699,17 +1722,56 @@ public class ServerConfig {
* By default a prefix of "java:comp/env/jdbc/" is used to lookup the
* DataSource. This prefix is not used if dataSourceJndiName starts with
* "java:".
* </p>
*/
public void setDataSourceJndiName(String dataSourceJndiName) {
this.dataSourceJndiName = dataSourceJndiName;
}
/**
* Return true if autoCommit mode is on. This indicates to Ebean to use autoCommit friendly Transactions and TransactionManager.
*/
public boolean isAutoCommitMode() {
return autoCommitMode;
}
/**
* Set to true if autoCommit mode is on and Ebean should use autoCommit friendly Transactions and TransactionManager.
*/
public void setAutoCommitMode(boolean autoCommitMode) {
this.autoCommitMode = autoCommitMode;
}
/**
* Return true if transaction begin should be started with explicit statement.
*/
public boolean isExplicitTransactionBeginMode() {
return explicitTransactionBeginMode;
}
/**
* Set to true if transaction begin should be started with explicit statement.
* <p>
* This works for H2 and Postgres but not for Oracle - only use this if you first name
* is Daryl or you have explicitly talked to Rob about this feature.
* </p>
* <p>
* This is generally not expected to be turned on but instead allow transactions to start
* implicitly which is generally the standard approach.
* </p>
*/
public void setExplicitTransactionBeginMode(boolean explicitTransactionBeginMode) {
this.explicitTransactionBeginMode = explicitTransactionBeginMode;
}
/**
* Return a value used to represent TRUE in the database.
* <p>
* This is used for databases that do not support boolean natively.
* </p>
* <p>
* The value returned is either a Integer or a String (e.g. "1", or "T").
* </p>
*/
public String getDatabaseBooleanTrue() {
return platformConfig.getDatabaseBooleanTrue();
@@ -1719,8 +1781,10 @@ public class ServerConfig {
* Set the value to represent TRUE in the database.
* <p>
* This is used for databases that do not support boolean natively.
* </p>
* <p>
* The value set is either a Integer or a String (e.g. "1", or "T").
* </p>
*/
public void setDatabaseBooleanTrue(String databaseTrue) {
platformConfig.setDatabaseBooleanTrue(databaseTrue);
@@ -1730,8 +1794,10 @@ public class ServerConfig {
* Return a value used to represent FALSE in the database.
* <p>
* This is used for databases that do not support boolean natively.
* </p>
* <p>
* The value returned is either a Integer or a String (e.g. "0", or "F").
* </p>
*/
public String getDatabaseBooleanFalse() {
return platformConfig.getDatabaseBooleanFalse();
@@ -1741,42 +1807,21 @@ public class ServerConfig {
* Set the value to represent FALSE in the database.
* <p>
* This is used for databases that do not support boolean natively.
* </p>
* <p>
* The value set is either a Integer or a String (e.g. "0", or "F").
* </p>
*/
public void setDatabaseBooleanFalse(String databaseFalse) {
this.platformConfig.setDatabaseBooleanFalse(databaseFalse);
}
/**
* Return the number of DB sequence values that should be preallocated.
*/
public int getDatabaseSequenceBatchSize() {
return platformConfig.getDatabaseSequenceBatchSize();
}
/**
* Set the number of DB sequence values that should be preallocated and cached
* by Ebean.
* <p>
* This is only used for DB's that use sequences and is a performance
* optimisation. This reduces the number of times Ebean needs to get a
* sequence value from the Database reducing network chatter.
* <p>
* By default this value is 10 so when we need another Id (and don't have one
* in our cache) Ebean will fetch 10 id's from the database. Note that when
* the cache drops to have full (which is 5 by default) Ebean will fetch
* another batch of Id's in a background thread.
*/
public void setDatabaseSequenceBatch(int databaseSequenceBatchSize) {
this.platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
}
/**
* Return the database platform name (can be null).
* <p>
* If null then the platform is determined automatically via the JDBC driver
* information.
* </p>
*/
public String getDatabasePlatformName() {
return databasePlatformName;
@@ -1787,13 +1832,16 @@ public class ServerConfig {
* <p>
* If none is set then the platform is determined automatically via the JDBC
* driver information.
* </p>
* <p>
* This can be used when the Database Platform can not be automatically
* detected from the JDBC driver (possibly 3rd party JDBC driver). It is also
* useful when you want to do offline DDL generation for a database platform
* that you don't have access to.
* </p>
* <p>
* Values are oracle, h2, postgres, mysql, sqlserver16, sqlserver17.
* </p>
*/
public void setDatabasePlatformName(String databasePlatformName) {
this.databasePlatformName = databasePlatformName;
@@ -1811,6 +1859,7 @@ public class ServerConfig {
* <p>
* If none is set then the platform is determined via the databasePlatformName
* or automatically via the JDBC driver information.
* </p>
*/
public void setDatabasePlatform(DatabasePlatform databasePlatform) {
this.databasePlatform = databasePlatform;
@@ -1841,8 +1890,10 @@ public class ServerConfig {
* Set the EncryptKeyManager.
* <p>
* This is required when you want to use encrypted properties.
* </p>
* <p>
* You can also set this in ebean.proprerties:
* </p>
* <p>
* <pre>{@code
* # set via ebean.properties
@@ -1858,6 +1909,7 @@ public class ServerConfig {
* <p>
* This is optionally used to programmatically define which columns are
* encrypted instead of using the {@link Encrypted} Annotation.
* </p>
*/
public EncryptDeployManager getEncryptDeployManager() {
return encryptDeployManager;
@@ -1868,6 +1920,7 @@ public class ServerConfig {
* <p>
* This is optionally used to programmatically define which columns are
* encrypted instead of using the {@link Encrypted} Annotation.
* </p>
*/
public void setEncryptDeployManager(EncryptDeployManager encryptDeployManager) {
this.encryptDeployManager = encryptDeployManager;
@@ -1887,6 +1940,7 @@ public class ServerConfig {
* <p>
* Ebean has a default implementation that it will use if you do not set your
* own Encryptor implementation.
* </p>
*/
public void setEncryptor(Encryptor encryptor) {
this.encryptor = encryptor;
@@ -1904,6 +1958,7 @@ public class ServerConfig {
* <p>
* Typically used to create an Database instance for DDL Migration generation
* without requiring a real DataSource / Database to connect to.
* </p>
*/
public void setDbOffline(boolean dbOffline) {
this.dbOffline = dbOffline;
@@ -1914,6 +1969,7 @@ public class ServerConfig {
* <p>
* Note that if this is not set then the DbPlatform may already have a
* DbEncrypt set and that will be used.
* </p>
*/
public DbEncrypt getDbEncrypt() {
return dbEncrypt;
@@ -1924,6 +1980,7 @@ public class ServerConfig {
* <p>
* Note that if this is not set then the DbPlatform may already have a
* DbEncrypt set (H2, MySql, Postgres and Oracle platforms have a DbEncrypt)
* </p>
*/
public void setDbEncrypt(DbEncrypt dbEncrypt) {
this.dbEncrypt = dbEncrypt;
@@ -1998,6 +2055,7 @@ public class ServerConfig {
* Set to true if LocalTime should be persisted with nanos precision.
* <p>
* Otherwise it is persisted using java.sql.Time which is seconds precision.
* </p>
*/
public void setLocalTimeWithNanos(boolean localTimeWithNanos) {
this.localTimeWithNanos = localTimeWithNanos;
@@ -2007,6 +2065,7 @@ public class ServerConfig {
* Return true if Duration should be persisted with nanos precision (SQL DECIMAL).
* <p>
* Otherwise it is persisted with second precision (SQL INTEGER).
* </p>
*/
public boolean isDurationWithNanos() {
return durationWithNanos;
@@ -2016,6 +2075,7 @@ public class ServerConfig {
* Set to true if Duration should be persisted with nanos precision (SQL DECIMAL).
* <p>
* Otherwise it is persisted with second precision (SQL INTEGER).
* </p>
*/
public void setDurationWithNanos(boolean durationWithNanos) {
this.durationWithNanos = durationWithNanos;
@@ -2086,6 +2146,7 @@ public class ServerConfig {
* <p>
* Typically this is a sql script that inserts test seed data when running tests.
* Place a sql script in src/test/resources that inserts test seed data.
* </p>
*/
public String getDdlSeedSql() {
return ddlSeedSql;
@@ -2096,6 +2157,7 @@ public class ServerConfig {
* <p>
* Typically this is a sql script that inserts test seed data when running tests.
* Place a sql script in src/test/resources that inserts test seed data.
* </p>
*/
public void setDdlSeedSql(String ddlSeedSql) {
this.ddlSeedSql = ddlSeedSql;
@@ -2172,11 +2234,14 @@ public class ServerConfig {
* <p>
* The class can be an Entity, Embedded type, ScalarType, BeanPersistListener,
* BeanFinder or BeanPersistController.
* </p>
* <p>
* If no classes are specified then the classes are found automatically via
* searching the class path.
* </p>
* <p>
* Alternatively the classes can be added via {@link #setClasses(List)}.
* </p>
*
* @param cls the entity type (or other type) that should be registered by this
* database.
@@ -2198,6 +2263,7 @@ public class ServerConfig {
* Add a package to search for entities via class path search.
* <p>
* This is only used if classes have not been explicitly specified.
* </p>
*/
public void addPackage(String packageName) {
packages.add(packageName);
@@ -2207,6 +2273,7 @@ public class ServerConfig {
* Return packages to search for entities via class path search.
* <p>
* This is only used if classes have not been explicitly specified.
* </p>
*/
public List<String> getPackages() {
return packages;
@@ -2216,6 +2283,7 @@ public class ServerConfig {
* Set packages to search for entities via class path search.
* <p>
* This is only used if classes have not been explicitly specified.
* </p>
*/
public void setPackages(List<String> packages) {
this.packages = packages;
@@ -2227,8 +2295,10 @@ public class ServerConfig {
* <p>
* If no classes are specified then the classes are found automatically via
* searching the class path.
* </p>
* <p>
* Alternatively the classes can contain added via {@link #addClass(Class)}.
* </p>
*/
public void setClasses(List<Class<?>> classes) {
this.classes = classes;
@@ -2248,6 +2318,7 @@ public class ServerConfig {
* This defaults to true and means that for "find by id" and "find by natural key"
* queries that normally hit L2 bean cache automatically will not do so after a write/persist
* on the transaction.
* </p>
* <p>
* <pre>{@code
*
@@ -2308,6 +2379,7 @@ public class ServerConfig {
* Set to false if by default updates in JDBC batch should not include all properties.
* <p>
* This mode can be explicitly set per transaction.
* </p>
*
* @see Transaction#setUpdateAllLoadedProperties(boolean)
*/
@@ -2375,6 +2447,7 @@ public class ServerConfig {
* <p>
* Note alternatively you can use {@link #setQueryAdapters(List)} to set all
* the BeanQueryAdapter instances.
* </p>
*/
public void add(BeanQueryAdapter beanQueryAdapter) {
queryAdapters.add(beanQueryAdapter);
@@ -2392,6 +2465,7 @@ public class ServerConfig {
* <p>
* Note alternatively you can use {@link #add(BeanQueryAdapter)} to add
* BeanQueryAdapter instances one at a time.
* </p>
*/
public void setQueryAdapters(List<BeanQueryAdapter> queryAdapters) {
this.queryAdapters = queryAdapters;
@@ -2423,6 +2497,7 @@ public class ServerConfig {
* <p>
* Note alternatively you can use {@link #setPersistControllers(List)} to set
* all the BeanPersistController instances.
* </p>
*/
public void add(BeanPersistController beanPersistController) {
persistControllers.add(beanPersistController);
@@ -2433,6 +2508,7 @@ public class ServerConfig {
* <p>
* Note alternatively you can use {@link #setPostLoaders(List)} to set
* all the BeanPostLoad instances.
* </p>
*/
public void add(BeanPostLoad postLoad) {
postLoaders.add(postLoad);
@@ -2443,6 +2519,7 @@ public class ServerConfig {
* <p>
* Note alternatively you can use {@link #setPostConstructListeners(List)} to set
* all the BeanPostConstructListener instances.
* </p>
*/
public void add(BeanPostConstructListener listener) {
postConstructListeners.add(listener);
@@ -2502,6 +2579,7 @@ public class ServerConfig {
* <p>
* Note alternatively you can use {@link #add(BeanPersistController)} to add
* BeanPersistController instances one at a time.
* </p>
*/
public void setPersistControllers(List<BeanPersistController> persistControllers) {
this.persistControllers = persistControllers;
@@ -2512,6 +2590,7 @@ public class ServerConfig {
* <p>
* Note alternatively you can use {@link #setPersistListeners(List)} to set
* all the BeanPersistListener instances.
* </p>
*/
public void add(BeanPersistListener beanPersistListener) {
persistListeners.add(beanPersistListener);
@@ -2557,6 +2636,7 @@ public class ServerConfig {
* <p>
* Note alternatively you can use {@link #add(BeanPersistListener)} to add
* BeanPersistListener instances one at a time.
* </p>
*/
public void setPersistListeners(List<BeanPersistListener> persistListeners) {
this.persistListeners = persistListeners;
@@ -2681,7 +2761,7 @@ public class ServerConfig {
*/
protected void loadDataSourceSettings(PropertiesWrapper p) {
dataSourceConfig.loadSettings(p.properties, name);
readOnlyDataSourceConfig.loadSettings(p.properties, name + "-ro");
readOnlyDataSourceConfig.loadSettings(p.properties, name);
}
/**
@@ -2742,9 +2822,10 @@ public class ServerConfig {
collectQueryPlanThresholdMicros = p.getLong("collectQueryPlanThresholdMicros", collectQueryPlanThresholdMicros);
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
localOnlyL2Cache = p.getBoolean("localOnlyL2Cache", localOnlyL2Cache);
enabledL2Regions = p.get("enabledL2Regions", enabledL2Regions);
notifyL2CacheInForeground = p.getBoolean("notifyL2CacheInForeground", notifyL2CacheInForeground);
explicitTransactionBeginMode = p.getBoolean("explicitTransactionBeginMode", explicitTransactionBeginMode);
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
useJavaxValidationNotNull = p.getBoolean("useJavaxValidationNotNull", useJavaxValidationNotNull);
autoReadOnlyDataSource = p.getBoolean("autoReadOnlyDataSource", autoReadOnlyDataSource);
@@ -2851,12 +2932,14 @@ public class ServerConfig {
* @return the classes
*/
private List<Class<?>> getClasses(PropertiesWrapper properties) {
String classNames = properties.get("classes", null);
if (classNames == null) {
return classes;
}
List<Class<?>> classList = new ArrayList<>();
String[] split = StringHelper.splitNames(classNames);
for (String cn : split) {
if (!"class".equalsIgnoreCase(cn)) {
@@ -2872,10 +2955,13 @@ public class ServerConfig {
}
private List<String> getSearchList(String searchNames, List<String> defaultValue) {
if (searchNames != null) {
String[] entries = StringHelper.splitNames(searchNames);
List<String> hitList = new ArrayList<>(entries.length);
Collections.addAll(hitList, entries);
return hitList;
} else {
return defaultValue;
@@ -2887,6 +2973,7 @@ public class ServerConfig {
* platform supports getGeneratedKeys in batch mode.
*/
public PersistBatch appliedPersistBatchOnCascade() {
if (persistBatchOnCascade == PersistBatch.INHERIT) {
// use the platform default (ALL except SQL Server which has NONE)
return databasePlatform.getPersistBatchOnCascade();
@@ -2898,6 +2985,7 @@ public class ServerConfig {
* Return the Jackson ObjectMapper.
* <p>
* Note that this is not strongly typed as Jackson ObjectMapper is an optional dependency.
* </p>
*/
public Object getObjectMapper() {
return objectMapper;
@@ -2907,6 +2995,7 @@ public class ServerConfig {
* Set the Jackson ObjectMapper.
* <p>
* Note that this is not strongly typed as Jackson ObjectMapper is an optional dependency.
* </p>
*/
public void setObjectMapper(Object objectMapper) {
this.objectMapper = objectMapper;
@@ -2925,6 +3014,7 @@ public class ServerConfig {
* Setting this to true has the effect that eq(propertyName, value), ieq(propertyName, value) and
* ne(propertyName, value) have no effect when the value is null. The expression factory adds a NoopExpression
* which will add "1=1" into the SQL rather than "is null".
* </p>
*/
public void setExpressionEqualsWithNullAsNoop(boolean expressionEqualsWithNullAsNoop) {
this.expressionEqualsWithNullAsNoop = expressionEqualsWithNullAsNoop;
@@ -2972,20 +3062,6 @@ public class ServerConfig {
this.disableL2Cache = disableL2Cache;
}
/**
* Return true to use local only L2 cache. Effectively ignore l2 cache plugin like ebean-redis etc.
*/
public boolean isLocalOnlyL2Cache() {
return localOnlyL2Cache;
}
/**
* Force the use of local only L2 cache. Effectively ignore l2 cache plugin like ebean-redis etc.
*/
public void setLocalOnlyL2Cache(boolean localOnlyL2Cache) {
this.localOnlyL2Cache = localOnlyL2Cache;
}
/**
* Returns if we use javax.validation.constraints.NotNull
*/
@@ -3018,6 +3094,7 @@ public class ServerConfig {
* In general we don't want to do that as when we use a distributed cache (like Ignite, Hazelcast etc)
* we are making network calls and we prefer to do this in background and not impact the response time
* of the executing transaction.
* </p>
*/
public void setNotifyL2CacheInForeground(boolean notifyL2CacheInForeground) {
this.notifyL2CacheInForeground = notifyL2CacheInForeground;
@@ -3054,6 +3131,7 @@ public class ServerConfig {
* <p>
* Typically used in Db Migration generation for many platform targets that might have different
* configuration for IdType, UUID, quoted identifiers etc.
* </p>
*
* @param propertiesPath The properties path used for loading and setting properties
* @param platformPrefix The prefix used for loading and setting properties
@@ -3090,6 +3168,7 @@ public class ServerConfig {
* Set mapping locations to search for xml mapping via class path search.
* <p>
* This is only used if classes have not been explicitly specified.
* </p>
*/
public void setMappingLocations(List<String> mappingLocations) {
this.mappingLocations = mappingLocations;
@@ -3175,22 +3254,12 @@ public class ServerConfig {
* <p>
* When false we either register entity classes via application code or use classpath
* scanning to find and register entity classes.
* </p>
*/
public boolean isAutoLoadModuleInfo() {
return loadModuleInfo && classes.isEmpty();
}
/**
* Set false to turn off automatic registration of entity beans.
* <p>
* When using query beans that also generates a module info class that
* can register the entity bean classes (to avoid classpath scanning).
* This is on by default and setting this to false turns it off.
*/
public void setLoadModuleInfo(boolean loadModuleInfo) {
this.loadModuleInfo = loadModuleInfo;
}
public enum UuidVersion {
VERSION4,
VERSION1,
@@ -15,7 +15,7 @@ package io.ebean.config;
*
* public class EbeanConfigProvider implements ServerConfigProvider {
*
* Override
* @Override
* public void apply(ServerConfig config) {
*
* // register the entity bean classes explicitly
@@ -43,8 +43,11 @@ public abstract class AbstractDbEncrypt implements DbEncrypt {
public DbEncryptFunction getDbEncryptFunction(int jdbcType) {
switch (jdbcType) {
case Types.VARCHAR:
return varcharEncryptFunction;
case Types.CLOB:
return varcharEncryptFunction;
case Types.CHAR:
return varcharEncryptFunction;
case Types.LONGVARCHAR:
return varcharEncryptFunction;
@@ -110,10 +110,6 @@ public class DatabasePlatform {
*/
protected DbIdentity dbIdentity = new DbIdentity();
protected boolean sequenceBatchMode = true;
protected int sequenceBatchSize = 20;
/**
* The history support for this database platform.
*/
@@ -146,7 +142,7 @@ public class DatabasePlatform {
protected String truncateTable = "truncate table %s";
protected String columnAliasPrefix;
protected String columnAliasPrefix = "c";
/**
* Use a BackTick ` at the beginning and end of table or column names that you
@@ -235,7 +231,6 @@ public class DatabasePlatform {
* Configure the platform given the server configuration.
*/
public void configure(PlatformConfig config) {
this.sequenceBatchSize = config.getDatabaseSequenceBatchSize();
this.caseSensitiveCollation = config.isCaseSensitiveCollation();
configureIdType(config.getIdType());
configure(config, config.isAllQuotedIdentifiers());
@@ -298,20 +293,6 @@ public class DatabasePlatform {
return platform.name().toLowerCase();
}
/**
* Return true if we are using Sequence batch mode rather than STEP.
*/
public boolean isSequenceBatchMode() {
return sequenceBatchMode;
}
/**
* Set to false to not use sequence batch mode but instead STEP mode.
*/
public void setSequenceBatchMode(boolean sequenceBatchMode) {
this.sequenceBatchMode = sequenceBatchMode;
}
/**
* Return true if this database platform supports native ILIKE expression.
*/
@@ -381,13 +362,13 @@ public class DatabasePlatform {
/**
* Return a DB Sequence based IdGenerator.
*
* @param be the BackgroundExecutor that can be used to load the sequence if
* desired
* @param ds the DataSource
* @param stepSize the sequence allocation size as defined by mapping (defaults to 50)
* @param seqName the name of the sequence
* @param be the BackgroundExecutor that can be used to load the sequence if
* desired
* @param ds the DataSource
* @param increment the sequence allocation size as defined by mapping (defaults to 50)
* @param seqName the name of the sequence
*/
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return null;
}
@@ -7,18 +7,30 @@ package io.ebean.config.dbplatform;
* As an alternative to using DB encryption you can encrypt/decrypt in java via
* a special ScalarType but this has the limitation that you can't include that
* property in query where clauses.
* </p>
*
* @author rbygrave
*/
public interface DbEncrypt {
/**
* Return the DB encrypt function for the given JDBC type.
*/
// /**
// * Return the SQL for decrypting a column returning a VARCHAR.
// */
// String getDecryptSql(String columnWithTableAlias);
//
// /**
// * Return the DB function with bind variables used to encrypt a VARCHAR
// * value.
// */
// String getEncryptBindSql();
DbEncryptFunction getDbEncryptFunction(int jdbcType);
/**
* Return the DB type that encrypted Strings are stored in.
* <p>
* This is VARCHAR for MySql and VARBINARY for most others.
* </p>
*/
int getEncryptDbType();
@@ -1,5 +1,8 @@
package io.ebean.config.dbplatform;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Defines the identity/sequence behaviour for the database.
*/
@@ -7,6 +10,8 @@ public class DbIdentity {
private static final String TABLE_PLACEHOLDER = "{table}";
private static final Pattern TABLE_REPLACE = Pattern.compile(TABLE_PLACEHOLDER, Pattern.LITERAL);
/**
* Set if this DB supports sequences. Note some DB's support both Sequences
* and Identity.
@@ -50,7 +55,10 @@ public class DbIdentity {
if (selectLastInsertedIdTemplate == null) {
return null;
}
return selectLastInsertedIdTemplate.replace(TABLE_PLACEHOLDER, table);
if (!selectLastInsertedIdTemplate.contains(TABLE_PLACEHOLDER)) {
return selectLastInsertedIdTemplate;
}
return TABLE_REPLACE.matcher(selectLastInsertedIdTemplate).replaceAll(Matcher.quoteReplacement(table));
}
/**
@@ -116,7 +116,7 @@ public class DbPlatformTypeMapping {
} else {
put(DbType.VARCHAR, new DbPlatformType("varchar", 255));
put(DbType.DECIMAL, new DbPlatformType("decimal", 16, 3));
put(DbType.DECIMAL, new DbPlatformType("decimal", 38));
put(DbType.VARBINARY, new DbPlatformType("varbinary", 255));
put(DbType.BINARY, new DbPlatformType("binary", 255));
put(DbType.CHAR, new DbPlatformType("char", 1));
@@ -1,57 +0,0 @@
package io.ebean.config.dbplatform;
import io.ebean.BackgroundExecutor;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* Database sequence based IdGenerator using Sequence Step 1 but batch fetch many sequence values.
*/
public abstract class SequenceBatchIdGenerator extends SequenceIdGenerator {
/**
* Construct where batchSize is the sequence step size.
*
*/
public SequenceBatchIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
super(be, ds, seqName, batchSize);
}
/**
* If allocateSize is large load some sequences in a background thread.
* <p>
* For example, when inserting a bean with a cascade on a OneToMany with many
* beans Ebean can call this to ensure .
* </p>
*/
@Override
public void preAllocateIds(int requestSize) {
if (allocationSize > 1 && requestSize > allocationSize) {
// only bother if allocateSize is bigger than
// the normal loading batchSize
if (requestSize > 100) {
// max out at 100 for now
requestSize = 100;
}
loadInBackground(requestSize);
}
}
/**
* Add the next set of Ids as the next value plus all the following numbers up to the step size.
*/
@Override
protected List<Long> readIds(ResultSet resultSet, int loadSize) throws SQLException {
List<Long> newIds = new ArrayList<>(loadSize);
while (resultSet.next()) {
newIds.add(resultSet.getLong(1));
}
return newIds;
}
}
@@ -59,7 +59,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
this.allocationSize = allocationSize;
}
public abstract String getSql(int batchSize);
public abstract String getSql();
/**
* Returns the sequence name.
@@ -102,7 +102,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
if (size > 0) {
maybeLoadMoreInBackground(size);
} else {
loadMore(allocationSize);
loadMore();
}
return idList.pollFirst();
}
@@ -116,8 +116,8 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
}
}
private void loadMore(int requestSize) {
List<Long> newIds = getMoreIds(requestSize);
private void loadMore() {
List<Long> newIds = getMoreIds();
synchronized (monitor) {
idList.addAll(newIds);
}
@@ -139,7 +139,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
currentlyBackgroundLoading = true;
backgroundExecutor.execute(() -> {
loadMore(requestSize);
loadMore();
synchronized (backgroundLoadMonitor) {
currentlyBackgroundLoading = false;
}
@@ -150,14 +150,14 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
/**
* Read the resultSet returning the list of Id values.
*/
protected abstract List<Long> readIds(ResultSet resultSet, int loadSize) throws SQLException;
protected abstract List<Long> readIds(ResultSet resultSet) throws SQLException;
/**
* Get more Id's by executing a query and reading the Id's returned.
*/
protected List<Long> getMoreIds(int requestSize) {
protected List<Long> getMoreIds() {
String sql = getSql(requestSize);
String sql = getSql();
Connection connection = null;
PreparedStatement statement = null;
@@ -168,7 +168,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
statement = connection.prepareStatement(sql);
resultSet = statement.executeQuery();
List<Long> newIds = readIds(resultSet, requestSize);
List<Long> newIds = readIds(resultSet);
if (logger.isTraceEnabled()) {
logger.trace("seq:{} loaded:{} sql:{}", seqName, newIds.size(), sql);
}
@@ -13,19 +13,25 @@ import java.util.List;
*/
public abstract class SequenceStepIdGenerator extends SequenceIdGenerator {
private final String nextSql;
/**
* Construct with stepSize (typically 50).
*/
protected SequenceStepIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
protected SequenceStepIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int stepSize, String nextSql) {
super(be, ds, seqName, stepSize);
this.nextSql = nextSql;
}
@Override
public String getSql() {
return nextSql;
}
/**
* Add the next set of Ids as the next value plus all the following numbers up to the step size.
*/
@Override
protected List<Long> readIds(ResultSet resultSet, int ignoreRequestSize) throws SQLException {
protected List<Long> readIds(ResultSet resultSet) throws SQLException {
List<Long> newIds = new ArrayList<>(allocationSize);
if (resultSet.next()) {
long start = resultSet.getLong(1);
@@ -16,8 +16,10 @@ public class ClickHousePlatform extends DatabasePlatform {
//this.dbEncrypt =
//this.historySupport =
//this.exceptionTranslator =
this.nativeUuidType = true;
this.dbDefaultValue.setNow("now()");
this.columnAliasPrefix = null;
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(false);
@@ -32,7 +34,7 @@ public class ClickHousePlatform extends DatabasePlatform {
dbTypeMap.put(DbType.SMALLINT, new DbPlatformType("UInt16", false));
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("UInt32", false));
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("UInt64", false));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("Decimal", 16, 3));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("Decimal", 16,4));
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("Float64", false));
dbTypeMap.put(DbType.DATE, new DbPlatformType("Date", false));
@@ -42,7 +42,8 @@ public class DB2Platform extends DatabasePlatform {
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("bigint", false));
dbTypeMap.put(DbType.REAL, new DbPlatformType("real"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("decimal", 16, 3));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("decimal", 15));
persistBatchOnCascade = PersistBatch.NONE;
}
@@ -51,9 +52,8 @@ public class DB2Platform extends DatabasePlatform {
* sequence values.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new DB2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new DB2Sequence(be, ds, seqName, increment);
}
}
@@ -0,0 +1,17 @@
package io.ebean.config.dbplatform.db2;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
/**
* DB2 specific sequence Id Generator.
*/
class DB2Sequence extends SequenceStepIdGenerator {
DB2Sequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "values nextval for " + seqName);
}
}
@@ -1,35 +0,0 @@
package io.ebean.config.dbplatform.db2;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* DB2 specific sequence Id Generator.
*/
public class DB2SequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
private final String unionBaseSql;
/**
* Construct given a dataSource and sql to return the next sequence value.
*/
public DB2SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
super(be, ds, seqName, batchSize);
this.baseSql = "values nextval for " + seqName;
this.unionBaseSql = " union " + baseSql;
}
@Override
public String getSql(int batchSize) {
StringBuilder sb = new StringBuilder();
sb.append(baseSql);
for (int i = 1; i < batchSize; i++) {
sb.append(unionBaseSql);
}
return sb.toString();
}
}
@@ -0,0 +1,14 @@
package io.ebean.config.dbplatform.h2;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
class H2DbSequence extends SequenceStepIdGenerator {
H2DbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "select " + seqName + ".nextval");
}
}
@@ -26,6 +26,7 @@ public class H2Platform extends DatabasePlatform {
this.supportsDeleteTableAlias = true;
this.inlineSqlUpdateLimit = true;
this.dbDefaultValue.setNow("now()");
this.columnAliasPrefix = null;
this.exceptionTranslator =
new SqlErrorCodes()
.addAcquireLock("50200","HYT00")
@@ -41,13 +42,9 @@ public class H2Platform extends DatabasePlatform {
dbTypeMap.put(DbType.UUID, new DbPlatformType("uuid", false));
}
/**
* Return a H2 specific sequence IdGenerator that supports batch fetching
* sequence values.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new H2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new H2DbSequence(be, ds, seqName, increment);
}
@Override
@@ -1,35 +0,0 @@
package io.ebean.config.dbplatform.h2;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* H2 specific sequence Id Generator.
*/
public class H2SequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
private final String unionBaseSql;
/**
* Construct given a dataSource and sql to return the next sequence value.
*/
public H2SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
super(be, ds, seqName, batchSize);
this.baseSql = "select " + seqName + ".nextval";
this.unionBaseSql = " union " + baseSql;
}
@Override
public String getSql(int batchSize) {
StringBuilder sb = new StringBuilder();
sb.append(baseSql);
for (int i = 1; i < batchSize; i++) {
sb.append(unionBaseSql);
}
return sb.toString();
}
}
@@ -17,6 +17,7 @@ public class HanaPlatform extends DatabasePlatform {
this.sqlLimiter = new HanaSqlLimiter();
this.persistBatchOnCascade = PersistBatch.NONE;
this.supportsResultSetConcurrencyModeUpdatable = false;
this.columnAliasPrefix = null;
this.historySupport = new HanaHistorySupport();
this.basicSqlLimiter = new HanaBasicSqlLimiter();
@@ -1,38 +0,0 @@
package io.ebean.config.dbplatform.hsqldb;
import io.ebean.BackgroundExecutor;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.config.dbplatform.h2.H2DbEncrypt;
import io.ebean.config.dbplatform.h2.H2SequenceIdGenerator;
import javax.sql.DataSource;
/**
* H2 specific platform.
*/
public class HsqldbPlatform extends DatabasePlatform {
public HsqldbPlatform() {
super();
this.platform = Platform.HSQLDB;
this.dbEncrypt = new H2DbEncrypt();
this.truncateTable = "delete from %s";
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
this.dbIdentity.setSupportsIdentity(true);
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
}
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new H2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
}
}
@@ -1,4 +0,0 @@
/**
* Hsqldb specific support.
*/
package io.ebean.config.dbplatform.hsqldb;
@@ -1,38 +0,0 @@
package io.ebean.config.dbplatform.mariadb;
import io.ebean.config.dbplatform.DbStandardHistorySupport;
/**
* History support for MariaDB.
*/
public class MariaDbHistorySupport extends DbStandardHistorySupport {
/**
* Return the ' as of timestamp ?' clause appended after the table name.
*/
@Override
public String getAsOfViewSuffix(String asOfViewSuffix) {
return " for system_time as of ?";
}
@Override
public String getVersionsBetweenSuffix(String asOfViewSuffix) {
return " for system_time between ? and ?";
}
/**
* Returns the SQL Server specific effective start column.
*/
@Override
public String getSysPeriodLower(String tableAlias, String sysPeriod) {
return tableAlias + ".row_start";
}
/**
* Returns the SQL Server specific effective end column.
*/
@Override
public String getSysPeriodUpper(String tableAlias, String sysPeriod) {
return tableAlias + ".row_end";
}
}
@@ -1,16 +0,0 @@
package io.ebean.config.dbplatform.mariadb;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.mysql.BaseMySqlPlatform;
/**
* MariaDB platform.
*/
public class MariaDbPlatform extends BaseMySqlPlatform {
public MariaDbPlatform() {
super();
this.platform = Platform.MARIADB;
this.historySupport = new MariaDbHistorySupport();
}
}
@@ -1,68 +0,0 @@
package io.ebean.config.dbplatform.mysql;
import io.ebean.Query;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.SqlErrorCodes;
import java.sql.Types;
/**
* Base platform for both MySql and MariaDB.
*/
public abstract class BaseMySqlPlatform extends DatabasePlatform {
public BaseMySqlPlatform() {
super();
this.useExtraTransactionOnIterateSecondaryQueries = true;
this.selectCountWithAlias = true;
this.supportsSavepointId = false;
this.inlineSqlUpdateLimit = true;
this.dbEncrypt = new MySqlDbEncrypt();
this.historySupport = new MySqlHistorySupport();
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsIdentity(true);
this.dbIdentity.setSupportsSequence(false);
this.dbDefaultValue.setNow("now(6)"); // must have same precision as TIMESTAMP
this.dbDefaultValue.setFalse("0");
this.dbDefaultValue.setTrue("1");
this.exceptionTranslator =
new SqlErrorCodes()
.addAcquireLock("1205")
.addDuplicateKey("1062", "1169")
.addDataIntegrity("630", "839", "840", "893", "1215", "1216", "1217", "1364", "1451", "1452", "1557")
.build();
this.openQuote = "`";
this.closeQuote = "`";
// use pipe for escaping as it depends if mysql runs in no_backslash_escapes or not.
this.likeClauseRaw = "like ? escape''";
this.likeClauseEscaped = "like ? escape'|'";
this.forwardOnlyHintOnFindIterate = true;
this.booleanDbType = Types.BIT;
dbTypeMap.put(DbType.BIT, new DbPlatformType("tinyint(1)"));
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("tinyint(1)"));
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime(6)"));
dbTypeMap.put(DbType.CLOB, new MySqlClob());
dbTypeMap.put(DbType.BLOB, new MySqlBlob());
dbTypeMap.put(DbType.BINARY, new DbPlatformType("binary", 255));
dbTypeMap.put(DbType.VARBINARY, new DbPlatformType("varbinary", 255));
dbTypeMap.put(DbType.JSON, new DbPlatformType("json", false));
dbTypeMap.put(DbType.JSONB, new DbPlatformType("json", false));
}
@Override
protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) {
// NOWAIT and SKIP LOCKED currently not supported with MySQL
return sql + " for update";
}
}
@@ -4,7 +4,7 @@ import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
public class MySql55Platform extends BaseMySqlPlatform {
public class MySql55Platform extends MySqlPlatform {
public MySql55Platform() {
super();
@@ -1,15 +1,78 @@
package io.ebean.config.dbplatform.mysql;
import io.ebean.Query;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.SqlErrorCodes;
import java.sql.Types;
/**
* MySQL specific platform.
* <p>
* <ul>
* <li>supportsGetGeneratedKeys = true</li>
* <li>Uses LIMIT OFFSET clause</li>
* <li>Uses ` for quoted identifiers</li>
* </ul>
* </p>
*/
public class MySqlPlatform extends BaseMySqlPlatform {
public class MySqlPlatform extends DatabasePlatform {
public MySqlPlatform() {
super();
this.platform = Platform.MYSQL;
this.useExtraTransactionOnIterateSecondaryQueries = true;
this.selectCountWithAlias = true;
this.supportsSavepointId = false;
this.inlineSqlUpdateLimit = true;
this.dbEncrypt = new MySqlDbEncrypt();
this.historySupport = new MySqlHistorySupport();
this.columnAliasPrefix = null;
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsIdentity(true);
this.dbIdentity.setSupportsSequence(false);
this.dbDefaultValue.setNow("now(6)"); // must have same precision as TIMESTAMP
this.dbDefaultValue.setFalse("0");
this.dbDefaultValue.setTrue("1");
this.exceptionTranslator =
new SqlErrorCodes()
.addAcquireLock("1205")
.addDuplicateKey("1062", "1169")
.addDataIntegrity("630", "839", "840", "893", "1215", "1216", "1217", "1364", "1451", "1452", "1557")
.build();
this.openQuote = "`";
this.closeQuote = "`";
// use pipe for escaping as it depends if mysql runs in no_backslash_escapes or not.
this.likeClauseRaw = "like ? escape''";
this.likeClauseEscaped = "like ? escape'|'";
this.forwardOnlyHintOnFindIterate = true;
this.booleanDbType = Types.BIT;
dbTypeMap.put(DbType.BIT, new DbPlatformType("tinyint(1)"));
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("tinyint(1)"));
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime(6)"));
dbTypeMap.put(DbType.CLOB, new MySqlClob());
dbTypeMap.put(DbType.BLOB, new MySqlBlob());
dbTypeMap.put(DbType.BINARY, new DbPlatformType("binary", 255));
dbTypeMap.put(DbType.VARBINARY, new DbPlatformType("varbinary", 255));
dbTypeMap.put(DbType.JSON, new DbPlatformType("json", false));
dbTypeMap.put(DbType.JSONB, new DbPlatformType("json", false));
}
@Override
protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) {
// NOWAIT and SKIP LOCKED currently not supported with MySQL
return sql + " for update";
}
}
@@ -23,6 +23,7 @@ public class NuoDbPlatform extends DatabasePlatform {
this.idInExpandedForm = true;
this.supportsResultSetConcurrencyModeUpdatable = false;
this.supportsDeleteTableAlias = true;
this.columnAliasPrefix = null;
this.likeClauseRaw = "like ?";
this.historySupport = new NuoDbHistorySupport();
//this.dbEncrypt = ...
@@ -43,8 +44,8 @@ public class NuoDbPlatform extends DatabasePlatform {
}
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new NuoDbSequence(be, ds, seqName, stepSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new NuoDbSequence(be, ds, seqName, increment);
}
@Override
@@ -5,20 +5,10 @@ import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
public class NuoDbSequence extends SequenceStepIdGenerator {
class NuoDbSequence extends SequenceStepIdGenerator {
private final String nextSql;
/**
* Construct where batchSize is the sequence step size.
*/
public NuoDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize);
this.nextSql = "select next value for " + seqName + " from dual";
NuoDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "select next value for " + seqName + " from dual");
}
@Override
public String getSql(int batchSize) {
return nextSql;
}
}
@@ -1,7 +1,6 @@
package io.ebean.config.dbplatform.oracle;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.IdType;
/**
* Oracle 11 platform using rownum sql limiting.
@@ -13,6 +12,5 @@ public class Oracle11Platform extends OraclePlatform {
this.platform = Platform.ORACLE11;
this.columnAliasPrefix = "c";
this.sqlLimiter = new OracleRownumSqlLimiter();
dbIdentity.setIdType(IdType.SEQUENCE);
}
}
@@ -0,0 +1,14 @@
package io.ebean.config.dbplatform.oracle;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
class OracleDbSequence extends SequenceStepIdGenerator {
OracleDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "select " + seqName + ".nextval from dual");
}
}
@@ -22,6 +22,7 @@ public class OraclePlatform extends DatabasePlatform {
public OraclePlatform() {
super();
this.platform = Platform.ORACLE;
this.columnAliasPrefix = null;
this.supportsDeleteTableAlias = true;
this.maxTableNameLength = 30;
this.maxConstraintNameLength = 30;
@@ -30,7 +31,7 @@ public class OraclePlatform extends DatabasePlatform {
this.basicSqlLimiter = new BasicSqlAnsiLimiter();
this.historySupport = new OracleDbHistorySupport();
this.truncateTable = "truncate table %s cascade";
dbIdentity.setIdType(IdType.IDENTITY);
dbIdentity.setIdType(IdType.SEQUENCE);
dbIdentity.setSupportsSequence(true);
dbIdentity.setSupportsIdentity(true);
dbIdentity.setSupportsGetGeneratedKeys(true);
@@ -62,7 +63,8 @@ public class OraclePlatform extends DatabasePlatform {
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("number", 19, 4));
dbTypeMap.put(DbType.SMALLINT, new DbPlatformType("number", 5));
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("number", 3));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("number", 16, 3));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("number", 38));
dbTypeMap.put(DbType.VARCHAR, new DbPlatformType("varchar2", 255));
dbTypeMap.put(DbType.LONGVARBINARY, new DbPlatformType("blob"));
@@ -74,8 +76,8 @@ public class OraclePlatform extends DatabasePlatform {
}
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new OracleSequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new OracleDbSequence(be, ds, seqName, increment);
}
@Override
@@ -1,27 +0,0 @@
package io.ebean.config.dbplatform.oracle;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* Oracle specific sequence Id Generator.
*/
public class OracleSequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
/**
* Construct given a dataSource and sql to return the next sequence value.
*/
public OracleSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
super(be, ds, seqName, batchSize);
this.baseSql = "select " + seqName + ".nextval, a from (select level as a FROM dual CONNECT BY level <= ";
}
@Override
public String getSql(int batchSize) {
return baseSql + batchSize + ")";
}
}
@@ -0,0 +1,14 @@
package io.ebean.config.dbplatform.postgres;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
class PostgresDbSequence extends SequenceStepIdGenerator {
PostgresDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "select nextval('" + seqName + "')");
}
}
@@ -34,6 +34,7 @@ public class PostgresPlatform extends DatabasePlatform {
this.blobDbType = Types.LONGVARBINARY;
this.clobDbType = Types.VARCHAR;
this.nativeUuidType = true;
this.columnAliasPrefix = null;
this.truncateTable = "truncate table %s cascade";
this.dbEncrypt = new PostgresDbEncrypt();
this.historySupport = new PostgresHistorySupport();
@@ -70,6 +71,7 @@ public class PostgresPlatform extends DatabasePlatform {
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("float"));
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("decimal", 38));
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("timestamptz"));
dbTypeMap.put(DbType.BINARY, dbBytea);
@@ -107,9 +109,8 @@ public class PostgresPlatform extends DatabasePlatform {
* Create a Postgres specific sequence IdGenerator.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new PostgresSequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new PostgresDbSequence(be, ds, seqName, increment);
}
@Override
@@ -1,27 +0,0 @@
package io.ebean.config.dbplatform.postgres;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* Postgres specific sequence Id Generator.
*/
public class PostgresSequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
/**
* Construct given a dataSource and sql to return the next sequence value.
*/
public PostgresSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
super(be, ds, seqName, batchSize);
this.baseSql = "select nextval('" + seqName + "'), s.generate_series from (select generate_series from generate_series(1,";
}
@Override
public String getSql(int batchSize) {
return baseSql + batchSize + ") ) as s";
}
}
@@ -32,7 +32,8 @@ public class SqlAnywherePlatform extends DatabasePlatform {
dbTypeMap.put(DbType.REAL, new DbPlatformType("float(16)"));
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("float(32)"));
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("numeric", 16, 3));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("numeric", 28));
dbTypeMap.put(DbType.BLOB, new DbPlatformType("binary(4500)"));
dbTypeMap.put(DbType.CLOB, new DbPlatformType("long varchar"));
dbTypeMap.put(DbType.LONGVARBINARY, new DbPlatformType("long binary"));
@@ -12,6 +12,7 @@ public class SqlServer17Platform extends SqlServerBasePlatform {
public SqlServer17Platform() {
super();
this.platform = Platform.SQLSERVER17;
this.columnAliasPrefix = null;
this.nativeUuidType = true;
this.dbTypeMap.put(DbType.UUID, new DbPlatformType("uniqueidentifier", false));
}
@@ -36,7 +36,6 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsIdentity(true);
this.dbIdentity.setSupportsSequence(true);
this.sequenceBatchMode = false;
this.exceptionTranslator =
new SqlErrorCodes()
@@ -62,7 +61,9 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
dbTypeMap.put(DbType.REAL, new DbPlatformType("float(16)"));
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("float(32)"));
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("numeric", 16, 3));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("numeric", 28));
dbTypeMap.put(DbType.DATE, new DbPlatformType("date"));
dbTypeMap.put(DbType.TIME, new DbPlatformType("time"));
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime2"));
@@ -93,12 +94,9 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
sb.append('[').append(ch).append(']');
}
/**
* Create a Postgres specific sequence IdGenerator.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new SqlServerStepSequence(be, ds, seqName, stepSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new SqlServerDbSequence(be, ds, seqName, increment);
}
/**
@@ -0,0 +1,13 @@
package io.ebean.config.dbplatform.sqlserver;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
class SqlServerDbSequence extends SequenceStepIdGenerator {
SqlServerDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "select next value for " + seqName);
}
}
@@ -5,7 +5,7 @@ import io.ebean.config.dbplatform.DbStandardHistorySupport;
/**
* History support only valid on SqlServer 2016 or later.
*
* @author Vilmos Nagy
* @author Vilmos Nagy <vilmos.nagy@outlook.com>
*/
public class SqlServerHistorySupport extends DbStandardHistorySupport {
@@ -1,24 +0,0 @@
package io.ebean.config.dbplatform.sqlserver;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
public class SqlServerStepSequence extends SequenceStepIdGenerator {
private final String nextSql;
/**
* Construct where batchSize is the sequence step size.
*/
public SqlServerStepSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize);
this.nextSql = "select next value for "+seqName;
}
@Override
public String getSql(int batchSize) {
return nextSql;
}
}
@@ -171,7 +171,7 @@ public class DefaultCsvCallback<T> implements CsvCallback<T> {
logger.info("Creating transaction, batchSize[" + persistBatchSize + "]");
transaction.setBatchMode(true);
transaction.setBatchSize(persistBatchSize);
transaction.setGetGeneratedKeys(false);
transaction.setBatchGetGeneratedKeys(false);
} else {
// explicitly turn off JDBC batching in case
+265 -59
View File
@@ -1,98 +1,304 @@
package io.ebean.util;
import io.ebean.annotation.Formula;
import io.ebean.annotation.Platform;
import io.ebean.annotation.Where;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Annotation utility methods to find annotations.
* Annotation utility methods to find annotations recursively - taken from the spring framework.
*/
public class AnnotationUtil {
/**
* Determine if the supplied {@link Annotation} is defined in the core JDK {@code java.lang.annotation} package.
*/
public static boolean notJavaLang(Annotation annotation) {
return !annotation.annotationType().getName().startsWith("java.lang.annotation");
public static boolean isInJavaLangAnnotationPackage(Annotation annotation) {
return annotation.annotationType().getName().startsWith("java.lang.annotation");
}
/**
* Simple get on field or method with no meta-annotations or platform filtering.
* Find a single {@link Annotation} of {@code annotationType} on the supplied {@link AnnotatedElement}.
* <p>
* Meta-annotations will be searched if the annotation is not <em>directly present</em> on the supplied element.
* <p>
* <strong>Warning</strong>: this method operates generically on annotated elements. In other words, this method
* does not execute specialized search algorithms for classes or methods. It only traverses through Annotations!
* It also does not filter out platform dependent annotations!
*/
public static <A extends Annotation> A get(AnnotatedElement element, Class<A> annotation) {
return element.getAnnotation(annotation);
}
@SuppressWarnings("unchecked")
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
if (annotationType == null) {
return null;
}
// check if directly present, if not, start search for meta-annotations.
Annotation[] anns = annotatedElement.getAnnotations();
if (anns.length == 0) {
return null; // no annotations present, so searching for meta annotations not required
}
/**
* Simple has with no meta-annotations or platform filtering.
*/
public static <A extends Annotation> boolean has(AnnotatedElement element, Class<A> annotation) {
return get(element, annotation) != null;
}
/**
* On class get the annotation - includes inheritance.
*/
public static <A extends Annotation> A typeGet(Class<?> clazz, Class<A> annotationType) {
while (clazz != null && clazz != Object.class) {
final A val = clazz.getAnnotation(annotationType);
if (val != null) {
return val;
// As we need the anns array anyway, we iterate over this instead
// of using annotatedElement.getAnnotation(...) which is synchronized internally
for (Annotation ann : anns) {
if (ann.annotationType() == annotationType) {
return (A) ann;
}
}
return findAnnotation(anns, annotationType, new HashSet<>());
}
/**
* Find a single {@link Annotation} of {@code annotationType} on the supplied class.
* <p>Meta-annotations will be searched if the annotation is not directly present on
* the supplied element.
* <p><strong>Note</strong>: this method searches for annotations at class & superClass(es)!
*/
@SuppressWarnings("unchecked")
public static <A extends Annotation> A findAnnotationRecursive(Class<?> clazz, Class<A> annotationType) {
if (annotationType == null) {
return null;
}
while (clazz != null && clazz != Object.class) {
// check if directly present, if not, start search for meta-annotations.
Annotation[] anns = clazz.getAnnotations();
if (anns.length != 0) {
for (Annotation ann : anns) {
if (ann.annotationType() == annotationType) {
return (A) ann;
}
}
A ann = findAnnotation(anns, annotationType, new HashSet<>());
if (ann != null) {
return ann;
}
}
// no meta-annotation present at this class - traverse to superclass
clazz = clazz.getSuperclass();
}
return null;
}
/**
* Finds the first annotation of a type for this platform. (if annotation is platform specific, otherwise first
* found annotation is returned)
*/
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType, Platform platform) {
if (annotationType == null) {
return null;
}
Set<A> anns = findAnnotations(annotatedElement, annotationType);
return getPlatformMatchingAnnotation(anns, platform);
}
/**
* Finds all annotations recusively for a class and its superclasses or interfaces.
*/
public static <A extends Annotation> Set<A> findAnnotationsRecursive(Class<?> clazz, Class<A> annotationType) {
Objects.requireNonNull(annotationType);
Set<A> ret = new LinkedHashSet<>();
Set<Annotation> visited = new HashSet<>();
Set<Class<?>> visitedInterfaces = new HashSet<>();
while (clazz != null && !clazz.getName().startsWith("java.lang.")) {
findMetaAnnotationsRecursive(clazz, annotationType, ret, visited, visitedInterfaces);
clazz = clazz.getSuperclass();
}
return ret;
}
/**
* Searches the interfaces for annotations.
*/
private static <A extends Annotation> void findMetaAnnotationsRecursive(Class<?> clazz,
Class<A> annotationType, Set<A> ret,
Set<Annotation> visited, Set<Class<?>> visitedInterfaces) {
findMetaAnnotations(clazz, annotationType, ret, visited);
for (Class<?> iface : clazz.getInterfaces()) {
if (!iface.getName().startsWith("java.lang.") && visitedInterfaces.add(iface)) {
findMetaAnnotationsRecursive(iface, annotationType, ret, visited, visitedInterfaces);
}
}
}
/**
* Perform the search algorithm avoiding endless recursion by tracking which
* annotations have already been visited.
*/
@SuppressWarnings("unchecked")
private static <A extends Annotation> A findAnnotation(Annotation[] anns, Class<A> annotationType, Set<Annotation> visited) {
for (Annotation ann : anns) {
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
Annotation[] metaAnns = ann.annotationType().getAnnotations();
for (Annotation metaAnn : metaAnns) {
if (metaAnn.annotationType() == annotationType) {
return (A) metaAnn;
}
}
if (metaAnns.length > 0) {
A annotation = findAnnotation(metaAnns, annotationType, visited);
if (annotation != null) {
return annotation;
}
}
}
}
return null;
}
/**
* On class get all the annotations - includes inheritance.
* Find all {@link Annotation}s of {@code annotationType} on the supplied {@link AnnotatedElement}.
* <p>
* Meta-annotations will be searched if the annotation is not <em>directly present</em> on the supplied element.
* <p>
* <strong>Warning</strong>: this method operates generically on annotated elements. In other words, this method
* does not execute specialized search algorithms for classes or methods. It only traverses through Annotations!
*/
public static <A extends Annotation> Set<A> typeGetAll(Class<?> clazz, Class<A> annotationType) {
Set<A> result = new LinkedHashSet<>();
typeGetAllCollect(clazz, annotationType, result);
return result;
}
private static <A extends Annotation> void typeGetAllCollect(Class<?> clazz, Class<A> annotationType, Set<A> result) {
while (clazz != null && clazz != Object.class) {
final A val = clazz.getAnnotation(annotationType);
if (val != null) {
result.add(val);
}
clazz = clazz.getSuperclass();
public static <A extends Annotation> Set<A> findAnnotations(AnnotatedElement annotatedElement, Class<A> annotationType) {
if (annotationType == null) {
return null;
}
Set<A> ret = new LinkedHashSet<>();
findMetaAnnotations(annotatedElement, annotationType, ret, new HashSet<>());
return ret;
}
/**
* On class simple check for annotation - includes inheritance.
* Perform the search algorithm avoiding endless recursion by tracking which
* annotations have already been visited.
*/
public static <A extends Annotation> boolean typeHas(Class<?> clazz, Class<A> annotation) {
return typeGet(clazz, annotation) != null;
}
@SuppressWarnings("unchecked")
private static <A extends Annotation> void findMetaAnnotations(AnnotatedElement annotatedElement, Class<A> annotationType, Set<A> ret, Set<Annotation> visited) {
/**
* Find all the annotations for the filter searching meta-annotations.
*/
public static Set<Annotation> metaFindAllFor(AnnotatedElement element, Set<Class<?>> filter) {
Set<Annotation> visited = new HashSet<>();
Set<Annotation> result = new LinkedHashSet<>();
for (Annotation ann : element.getAnnotations()) {
metaAdd(ann, filter, visited, result);
}
return result;
}
private static void metaAdd(Annotation ann, Set<Class<?>> filter, Set<Annotation> visited, Set<Annotation> result) {
if (notJavaLang(ann) && visited.add(ann)) {
if (filter.contains(ann.annotationType())) {
result.add(ann);
} else {
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
metaAdd(metaAnn, filter, visited, result);
Annotation[] anns = annotatedElement.getAnnotations();
for (Annotation ann : anns) {
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
if (ann.annotationType() == annotationType) {
ret.add((A) ann);
} else {
Method repeatableValueMethod = getRepeatableValueMethod(ann, annotationType);
if (repeatableValueMethod != null) {
try {
A[] repeatedAnns = (A[]) repeatableValueMethod.invoke(ann);
for (Annotation repeatedAnn : repeatedAnns) {
ret.add((A) repeatedAnn);
findMetaAnnotations(repeatedAnn.annotationType(), annotationType, ret, visited);
}
} catch (Exception e) { // catch all exceptions (thrown by invoke)
throw new RuntimeException(e);
}
} else {
findMetaAnnotations(ann.annotationType(), annotationType, ret, visited);
}
}
}
}
}
// caches for getRepeatableValueMethod
private static Method getNullMethod() {
try {
return AnnotationUtil.class.getDeclaredMethod("getNullMethod");
} catch (NoSuchMethodException e) {
return null;
}
}
private static final ConcurrentMap<Annotation, Method> valueMethods = new ConcurrentHashMap<>();
// only a non-null-marker the valueMethods - Cache
private static final Method nullMethod = getNullMethod();
/**
* Returns the <code>value()</code> method for a possible containerAnnotation.
* Method is retuned only, if its signature is <code>array of containingType</code>.
*/
private static <A extends Annotation> Method getRepeatableValueMethod(
Annotation containerAnnotation, Class<A> containingType) {
Method method = valueMethods.get(containerAnnotation);
if (method == null) {
try {
method = containerAnnotation.annotationType().getMethod("value");
} catch (NoSuchMethodException e) {
method = nullMethod;
} catch (Exception e) {
throw new RuntimeException(e);
}
Method prev = valueMethods.putIfAbsent(containerAnnotation, method);
method = prev == null ? method : prev;
}
if (method != nullMethod) {
Class<?> retType = method.getReturnType();
if (retType.isArray() && retType.getComponentType() == containingType) {
return method;
}
}
return null;
}
/**
* Finds a suitable annotation from <code>Set<T> anns</code> for this platform.
* To distinguish between platforms, annotation type <code>T</code> must define
* a method withthis signature:
* <p>
* <code>Class<? extends DatabasePlatform>[] platforms() default {};</code>
* </p>
* The finding rules are:
* <ol>
* <li>Check if T has method "platforms" if not, return <code>ann[0]</code></code>
* <li>find the annotation that is defined for <code>databasePlatform</code></li>
* <li>otherwise return the annotation for default platform (platforms = {})</li>
* <li>return null
* </ol>
* (This mechanism is currently used by {@link Where} and {@link Formula})
*/
public static <T extends Annotation> T getPlatformMatchingAnnotation(Set<T> anns, Platform matchPlatform) {
if (anns.isEmpty()) {
return null;
}
Method getPlatformsMethod = null;
T fallback = null;
for (T ann : anns) {
try {
if (getPlatformsMethod == null) {
getPlatformsMethod = ann.getClass().getMethod("platforms");
}
if (!Platform[].class.isAssignableFrom(getPlatformsMethod.getReturnType())) {
return ann;
}
Platform[] platforms = (Platform[]) getPlatformsMethod.invoke(ann);
if (platforms.length == 0) {
fallback = ann;
} else {
for (Platform platform : platforms) {
if (matchPlatform == platform) {
return ann;
}
}
}
} catch (NoSuchMethodException e) {
return ann; // not platform specific - return first one
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return fallback;
}
}
+388 -56
View File
@@ -1,5 +1,6 @@
package io.ebean.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
@@ -9,10 +10,152 @@ import java.util.regex.Pattern;
*/
public class StringHelper {
private static final char SINGLE_QUOTE = '\'';
private static final char DOUBLE_QUOTE = '"';
private static final Pattern SPLIT_NAMES = Pattern.compile("[\\s,;]+");
private static final String[] EMPTY_STRING_ARRAY = new String[0];
/**
* parses a String of the form name1='value1' name2='value2'. Note that you
* can use either single or double quotes for any particular name value pair
* and the end quote must match the begin quote.
*/
public static HashMap<String, String> parseNameQuotedValue(String tag) throws RuntimeException {
if (tag == null || tag.length() < 1) {
return null;
}
// make sure that the quotes are matched...
// int remainer = countOccurances(tag, ""+quote) % 2;
// if (remainer == 1) {
// dp("remainder = "+remainer);
// throw new StringParsingException("Unmatched quote in "+tag);
// }
// make sure that th last character is not an equals...
// (check now so I don't need to check this every time..)
if (tag.charAt(tag.length() - 1) == '=') {
throw new RuntimeException("missing quoted value at the end of " + tag);
}
HashMap<String, String> map = new HashMap<>();
// recursively parse out the name value pairs...
return parseNameQuotedValue(map, tag, 0);
}
/**
* recursively parse out name value pairs (where the value is quoted, with
* either single or double quotes).
*/
private static HashMap<String, String> parseNameQuotedValue(HashMap<String, String> map,
String tag, int pos) throws RuntimeException {
while (true) {
int equalsPos = tag.indexOf('=', pos);
if (equalsPos > -1) {
// check for begin quote...
char firstQuote = tag.charAt(equalsPos + 1);
if (firstQuote != SINGLE_QUOTE && firstQuote != DOUBLE_QUOTE) {
throw new RuntimeException("missing begin quote at " + (equalsPos) + "["
+ tag.charAt(equalsPos + 1) + "] in [" + tag + "]");
}
// check for end quote...
int endQuotePos = tag.indexOf(firstQuote, equalsPos + 2);
if (endQuotePos == -1) {
throw new RuntimeException("missing end quote [" + firstQuote + "] after " + pos + " in [" + tag + "]");
}
// we have a valid name and value...
// dp("pos="+pos+" equalsPos="+equalsPos+"
// endQuotePos="+endQuotePos);
String name = tag.substring(pos, equalsPos);
// dp("name="+name+"; value="+value+";");
// trim off any whitespace from the front of name...
name = trimFront(name, " ");
if ((name.indexOf(SINGLE_QUOTE) > -1) || (name.indexOf(DOUBLE_QUOTE) > -1)) {
throw new RuntimeException("attribute name contains a quote [" + name + "]");
}
String value = tag.substring(equalsPos + 2, endQuotePos);
map.put(name, value);
pos = endQuotePos + 1;
} else {
// no more equals... stop parsing...
return map;
}
}
}
/**
* Returns the number of times a particular String occurs in another String.
* e.g. count the number of single quotes.
*/
public static int countOccurances(String content, String occurs) {
return countOccurances(content, occurs, 0, 0);
}
private static int countOccurances(String content, String occurs, int pos, int countSoFar) {
while (true) {
int equalsPos = content.indexOf(occurs, pos);
if (equalsPos > -1) {
countSoFar += 1;
pos = equalsPos + occurs.length();
// dp("countSoFar="+countSoFar+" pos="+pos);
} else {
return countSoFar;
}
}
}
/**
* Parses out a list of Name Value pairs that are delimited together. Will
* always return a StringMap. If allNameValuePairs is null, or no name values
* can be parsed out an empty StringMap is returned.
*
* @param allNameValuePairs the entire string to be parsed.
* @param listDelimiter (typically ';') the delimited between the list
* @param nameValueSeparator (typically '=') the separator between the name and value
*/
public static Map<String, String> delimitedToMap(String allNameValuePairs,
String listDelimiter, String nameValueSeparator) {
HashMap<String, String> params = new HashMap<>();
if ((allNameValuePairs == null) || (allNameValuePairs.isEmpty())) {
return params;
}
// trim off any leading listDelimiter...
allNameValuePairs = trimFront(allNameValuePairs, listDelimiter);
return getKeyValue(params, 0, allNameValuePairs, listDelimiter, nameValueSeparator);
}
/**
* Trims off recurring strings from the front of a string.
*
* @param source the source string
* @param trim the string to trim off the front
*/
public static String trimFront(String source, String trim) {
while (true) {
if (source == null) {
return null;
}
if (source.indexOf(trim) == 0) {
// dp("trim ...");
source = source.substring(trim.length());
} else {
return source;
}
}
}
/**
* Return true if the value is null or an empty string.
*/
@@ -20,76 +163,110 @@ public class StringHelper {
return value == null || value.trim().isEmpty();
}
/**
* Parses out a list of Name Value pairs that are delimited together. Will
* always return a StringMap. If allNameValuePairs is null, or no name values
* can be parsed out an empty StringMap is returned.
*
* @param source the entire string to be parsed.
* @param listDelimiter (typically ';') the delimited between the list
* @param nameValueSeparator (typically '=') the separator between the name and value
*/
public static Map<String, String> delimitedToMap(String source, String listDelimiter, String nameValueSeparator) {
Map<String, String> params = new HashMap<>();
if (source == null || source.isEmpty()) {
return params;
}
// trim off any leading listDelimiter...
source = trimFront(source, listDelimiter);
return delimitedToMap(params, source, listDelimiter, nameValueSeparator);
}
/**
* Trims off recurring strings from the front of a string.
*
* @param source the source string
* @param trim the string to trim off the front
*/
private static String trimFront(String source, String trim) {
while (true) {
if (source.indexOf(trim) == 0) {
source = source.substring(trim.length());
} else {
return source;
}
}
}
/**
* Recursively pulls out the key value pairs from a raw string.
*/
private static Map<String, String> delimitedToMap(Map<String, String> map, String source, String listDelimiter, String nameValueSeparator) {
int pos = 0;
private static HashMap<String, String> getKeyValue(HashMap<String, String> map, int pos,
String allNameValuePairs, String listDelimiter, String nameValueSeparator) {
while (true) {
if (pos >= source.length()) {
if (pos >= allNameValuePairs.length()) {
// dp("end as "+pos+" >= "+allNameValuePairs.length() );
return map;
}
int equalsPos = source.indexOf(nameValueSeparator, pos);
int delimPos = source.indexOf(listDelimiter, pos);
int equalsPos = allNameValuePairs.indexOf(nameValueSeparator, pos);
int delimPos = allNameValuePairs.indexOf(listDelimiter, pos);
if (delimPos == -1) {
delimPos = source.length();
delimPos = allNameValuePairs.length();
}
if (equalsPos == -1) {
// dp("no more equals...");
return map;
}
if (delimPos == (equalsPos + 1)) {
// dp("Ignoring as nothing between delim and equals...
// delim:"+delimPos+" eq:"+equalsPos);
pos = delimPos + 1;
continue;
}
if (equalsPos > delimPos) {
// there is a key without a value?
String key = source.substring(pos, delimPos);
String key = allNameValuePairs.substring(pos, delimPos);
key = key.trim();
if (!key.isEmpty()) {
map.put(key, null);
}
pos = delimPos + 1;
continue;
}
String key = source.substring(pos, equalsPos);
String value = source.substring(equalsPos + 1, delimPos);
map.put(key.trim(), value);
pos = delimPos + 1;
String key = allNameValuePairs.substring(pos, equalsPos);
if (delimPos > -1) {
String value = allNameValuePairs.substring(equalsPos + 1, delimPos);
// dp("cont "+key+","+value+" pos:"+pos+"
// len:"+allNameValuePairs.length());
key = key.trim();
map.put(key, value);
pos = delimPos + 1;
// recurse the rest of the values...
} else {
// dp("ERROR: delimPos < 0 ???");
return map;
}
}
}
/**
* Convert a string that has delimited values (say comma delimited) in a
* String[]. You must explicitly choose whether or not to include empty values
* (say two commas that a right beside each other.
* <p>
* e.g. "alpha,beta,,theta"<br>
* With keepEmpties true, this results in a String[] of size 4 with the third
* one having a String of 0 length. With keepEmpties false, this results in a
* String[] of size 3.
* <p>
* e.g. ",alpha,beta,,theta,"<br>
* With keepEmpties true, this results in a String[] of size 6 with the
* 1st,4th and 6th one having a String of 0 length. With keepEmpties false,
* this results in a String[] of size 3.
*/
public static String[] delimitedToArray(String str, String delimiter, boolean keepEmpties) {
ArrayList<String> list = new ArrayList<>();
int startPos = 0;
delimiter(str, delimiter, keepEmpties, startPos, list);
String[] result = new String[list.size()];
return list.toArray(result);
}
private static void delimiter(String str, String delimiter, boolean keepEmpties, int startPos,
ArrayList<String> list) {
int endPos = str.indexOf(delimiter, startPos);
if (endPos == -1) {
if (startPos <= str.length()) {
String lastValue = str.substring(startPos, str.length());
if (keepEmpties || !lastValue.isEmpty()) {
list.add(lastValue);
}
}
// we have finished parsing the string...
} else {
// get the delimited value... add it..
String value = str.substring(startPos, endPos);
if (keepEmpties || !value.isEmpty()) {
list.add(value);
}
// recursively search as we are not at the end yet...
delimiter(str, delimiter, keepEmpties, endPos + 1, list);
}
}
@@ -102,22 +279,177 @@ public class StringHelper {
* @param replace the string used to replace match with
* @return the source string after the search and replace
*/
public static String replace(String source, String match, String replace) {
public static String replaceString(String source, String match, String replace) {
if (source == null) {
return null;
}
if (replace == null) {
return source;
}
return source.replace(match, replace);
if (match == null) {
throw new NullPointerException("match is null?");
}
if (match.equals(replace)) {
return source;
}
return replaceString(source, match, replace, 30, 0, source.length());
}
/**
* Return new line and carriage return with space.
* Additionally specify the additionalSize to add to the buffer. This will
* make the buffer bigger so that it doesn't have to grow when replacement
* occurs.
*/
public static String removeNewLines(String source) {
source = source.replace('\n', ' ');
return source.replace('\r', ' ');
public static String replaceString(String source, String match, String replace,
int additionalSize, int startPos, int endPos) {
if (source == null) {
return null;
}
char match0 = match.charAt(0);
int matchLength = match.length();
if (matchLength == 1 && replace.length() == 1) {
char replace0 = replace.charAt(0);
return source.replace(match0, replace0);
}
if (matchLength >= replace.length()) {
additionalSize = 0;
}
int sourceLength = source.length();
int lastMatch = endPos - matchLength;
StringBuilder sb = new StringBuilder(sourceLength + additionalSize);
if (startPos > 0) {
sb.append(source.substring(0, startPos));
}
char sourceChar;
boolean isMatch;
int sourceMatchPos;
for (int i = startPos; i < sourceLength; i++) {
sourceChar = source.charAt(i);
if (i > lastMatch || sourceChar != match0) {
sb.append(sourceChar);
} else {
// check to see if this is a match
isMatch = true;
sourceMatchPos = i;
// check each following character...
for (int j = 1; j < matchLength; j++) {
sourceMatchPos++;
if (source.charAt(sourceMatchPos) != match.charAt(j)) {
isMatch = false;
break;
}
}
if (isMatch) {
i = i + matchLength - 1;
sb.append(replace);
} else {
// was not a match
sb.append(sourceChar);
}
}
}
return sb.toString();
}
/**
* A search and replace with multiple matching strings.
* <p>
* Useful when converting CRNL CR and NL all to a BR tag for example.
* <pre>{@code
*
* String[] multi = { "\r\n", "\r", "\n" };
* content = StringHelper.replaceStringMulti(content, multi, "<br/>");
*
* }</pre>
*/
public static String replaceStringMulti(String source, String[] match, String replace) {
if (source == null) {
return null;
}
return replaceStringMulti(source, match, replace, 30, 0, source.length());
}
/**
* Additionally specify an additional size estimate for the buffer plus start
* and end positions.
* <p>
* The start and end positions can limit the search and replace. Otherwise
* these default to startPos = 0 and endPos = source.length().
* </p>
*/
public static String replaceStringMulti(String source, String[] match, String replace,
int additionalSize, int startPos, int endPos) {
if (source == null) {
return null;
}
int shortestMatch = match[0].length();
char[] match0 = new char[match.length];
for (int i = 0; i < match0.length; i++) {
match0[i] = match[i].charAt(0);
if (match[i].length() < shortestMatch) {
shortestMatch = match[i].length();
}
}
StringBuilder sb = new StringBuilder(source.length() + additionalSize);
char sourceChar;
int len = source.length();
int lastMatch = endPos - shortestMatch;
if (startPos > 0) {
sb.append(source.substring(0, startPos));
}
int matchCount;
for (int i = startPos; i < len; i++) {
sourceChar = source.charAt(i);
if (i > lastMatch) {
sb.append(sourceChar);
} else {
matchCount = 0;
for (int k = 0; k < match0.length; k++) {
if (matchCount == 0 && sourceChar == match0[k]) {
if (match[k].length() + i <= len) {
++matchCount;
int j = 1;
for (; j < match[k].length(); j++) {
if (source.charAt(i + j) != match[k].charAt(j)) {
--matchCount;
break;
}
}
if (matchCount > 0) {
i = i + j - 1;
sb.append(replace);
break;
}
}
}
}
if (matchCount == 0) {
sb.append(sourceChar);
}
}
}
return sb.toString();
}
/**
@@ -130,13 +462,13 @@ public class StringHelper {
}
String[] result = SPLIT_NAMES.split(names);
if (result.length == 0) {
return EMPTY_STRING_ARRAY;
return EMPTY_STRING_ARRAY; // don't know if this ever can happen
}
if ("".equals(result[0])) { // input string starts with whitespace
if (result.length == 1) { // input string contains only whitespace
if ("".equals(result[0])) { // = input string starts with whitespace
if (result.length == 1) { // = input string contains only whitespace
return EMPTY_STRING_ARRAY;
} else {
String[] ret = new String[result.length-1]; // remove first entry
String ret[] = new String[result.length-1]; // remove first entry
System.arraycopy(result, 1, ret, 0, ret.length);
return ret;
}
@@ -1,16 +1,9 @@
package io.ebeaninternal.api;
import io.ebean.bean.BeanCollection;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
/**
* Controls the loading of ManyToOne and OneToOne relationships.
*/
public interface LoadBeanContext extends LoadSecondaryQuery {
/**
* Register a BeanCollection into the load context.
*/
void register(BeanPropertyAssocMany<?> many, BeanCollection<?> collection);
}
@@ -5,7 +5,6 @@ import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.bean.PersistenceContext;
import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
/**
@@ -57,5 +56,6 @@ public interface LoadContext {
/**
* Register a collection for lazy loading.
*/
void register(String path, BeanPropertyAssocMany<?> many, BeanCollection<?> bc);
void register(String path, BeanCollection<?> bc);
}
@@ -2,6 +2,7 @@ package io.ebeaninternal.api;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.util.StringHelper;
import io.ebeaninternal.server.core.BindPadding;
import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
@@ -124,7 +125,8 @@ 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"));
String ew = StringHelper.replaceString(extraWhere, "${ta}", "t0");
query.where().raw(ew);
}
query.setLazyLoadForParents(many);
@@ -71,13 +71,13 @@ public class ScopeTrans {
restoreBatchOnCascade = transaction.isBatchOnCascade();
restoreBatchSize = transaction.getBatchSize();
restoreBatchGeneratedKeys = transaction.getBatchGetGeneratedKeys();
restoreBatchFlushOnQuery = transaction.isFlushOnQuery();
restoreBatchFlushOnQuery = transaction.isBatchFlushOnQuery();
}
if (txScope.isBatchSet()) {
transaction.setBatchMode(txScope.isBatchMode());
}
if (!txScope.isFlushOnQuery()) {
transaction.setFlushOnQuery(false);
transaction.setBatchFlushOnQuery(false);
}
if (txScope.isBatchOnCascadeSet()) {
transaction.setBatchOnCascade(txScope.isBatchOnCascade());
@@ -86,7 +86,7 @@ public class ScopeTrans {
transaction.setBatchSize(txScope.getBatchSize());
}
if (txScope.isSkipGeneratedKeys()) {
transaction.setGetGeneratedKeys(false);
transaction.setBatchGetGeneratedKeys(false);
}
}
@@ -136,7 +136,7 @@ public class ScopeTrans {
transaction.commit();
} else {
nestedCommit = true;
transaction.setFlushOnQuery(restoreBatchFlushOnQuery);
transaction.setBatchFlushOnQuery(restoreBatchFlushOnQuery);
if (restoreBatch != null) {
transaction.setBatchMode(restoreBatch);
}
@@ -147,7 +147,7 @@ public class ScopeTrans {
transaction.setBatchSize(restoreBatchSize);
}
if (restoreBatchGeneratedKeys != null) {
transaction.setGetGeneratedKeys(restoreBatchGeneratedKeys);
transaction.setBatchGetGeneratedKeys(restoreBatchGeneratedKeys);
}
}
}
@@ -33,11 +33,6 @@ import java.util.function.Predicate;
*/
public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollectionLoader {
/**
* Return true if the L2 cache has been disabled.
*/
boolean isDisableL2Cache();
/**
* Return the log manager.
*/
@@ -152,7 +147,7 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect
*
* @param tenantId For multi-tenant lazy loading provide the tenantId to use.
*/
SpiTransaction createReadOnlyTransaction(Object tenantId);
SpiTransaction createQueryTransaction(Object tenantId);
/**
* An event from another server in the cluster used to notify local
@@ -458,7 +458,7 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
/**
* Set the query to be a delete query.
*/
void setupForDeleteOrUpdate();
void setupForDeleteOrUpdate(boolean deleteRequest);
/**
* Set the query to be delete by ids due to cascading delete.
@@ -788,7 +788,7 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
/**
* Set to true when we only include the Id property if it is explicitly included in the select().
*/
void setManualId();
void setManualId(boolean manualId);
/**
* Set default select clauses where none have been explicitly defined.
@@ -313,9 +313,4 @@ public interface SpiTransaction extends Transaction {
* Return true when nested transactions should create Savepoints.
*/
boolean isNestedUseSavepoint();
/**
* Return true if explicitly set to skip cache (ignores skipOnWrite).
*/
boolean isSkipCacheExplicit();
}
@@ -158,11 +158,6 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
transaction.setSkipCache(skipCache);
}
@Override
public boolean isSkipCacheExplicit() {
return transaction.isSkipCacheExplicit();
}
@Override
public boolean isSkipCache() {
return transaction.isSkipCache();
@@ -274,8 +269,8 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void setGetGeneratedKeys(boolean getGeneratedKeys) {
transaction.setGetGeneratedKeys(getGeneratedKeys);
public void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
transaction.setBatchGetGeneratedKeys(getGeneratedKeys);
}
@Override
@@ -284,18 +279,18 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void setFlushOnMixed(boolean batchFlushOnMixed) {
transaction.setFlushOnMixed(batchFlushOnMixed);
public void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
transaction.setBatchFlushOnMixed(batchFlushOnMixed);
}
@Override
public void setFlushOnQuery(boolean batchFlushOnQuery) {
transaction.setFlushOnQuery(batchFlushOnQuery);
public void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
transaction.setBatchFlushOnQuery(batchFlushOnQuery);
}
@Override
public boolean isFlushOnQuery() {
return transaction.isFlushOnQuery();
public boolean isBatchFlushOnQuery() {
return transaction.isBatchFlushOnQuery();
}
@Override
@@ -14,8 +14,6 @@ import io.ebean.config.dbplatform.cockroach.CockroachPlatform;
import io.ebean.config.dbplatform.db2.DB2Platform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
import io.ebean.config.dbplatform.mariadb.MariaDbPlatform;
import io.ebean.config.dbplatform.mysql.MySql55Platform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
@@ -815,14 +813,10 @@ public class DefaultDbMigration implements DbMigration {
switch (platform) {
case H2:
return new H2Platform();
case HSQLDB:
return new HsqldbPlatform();
case POSTGRES9:
return new Postgres9Platform();
case POSTGRES:
return new PostgresPlatform();
case MARIADB:
return new MariaDbPlatform();
case MYSQL55:
return new MySql55Platform();
case MYSQL:
@@ -33,13 +33,13 @@ import io.ebeaninternal.dbmigration.model.MTableIdentity;
import io.ebeaninternal.server.deploy.IdentityMode;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static io.ebean.util.StringHelper.replace;
import static io.ebeaninternal.api.PlatformMatch.matchPlatform;
import static io.ebeaninternal.dbmigration.ddlgeneration.platform.SplitColumns.split;
@@ -111,9 +111,11 @@ public class BaseTableDdl implements TableDdl {
this.columnName = column.getName();
this.defaultValue = platformDdl.convertDefaultValue(column.getDefaultValue());
boolean alterNotNull = Boolean.TRUE.equals(column.isNotnull());
if (column.getBefore().isEmpty() && alterNotNull && defaultValue == null) {
handleStrictError(tableName, columnName);
}
before = getScriptsForPlatform(column.getBefore());
after = getScriptsForPlatform(column.getAfter());
this.withHistory = withHistory;
@@ -125,8 +127,10 @@ public class BaseTableDdl implements TableDdl {
DdlMigrationHelp(AlterColumn alter) {
this.tableName = alter.getTableName();
this.columnName = alter.getColumnName();
String tmp = alter.getDefaultValue() != null ? alter.getDefaultValue() : alter.getCurrentDefaultValue();
this.defaultValue = platformDdl.convertDefaultValue(tmp);
boolean alterNotNull = Boolean.TRUE.equals(alter.isNotnull());
// here we add the platform's default update script
withHistory = isTrue(alter.isWithHistory());
@@ -182,9 +186,9 @@ public class BaseTableDdl implements TableDdl {
* Replaces Table name (${table}), Column name (${column}) and default value (${default}) in DDL.
*/
private String translate(String ddl, String tableName, String columnName, String defaultValue) {
String ret = replace(ddl, "${table}", tableName);
ret = replace(ret, "${column}", columnName);
return replace(ret, "${default}", defaultValue);
String ret = StringHelper.replaceString(ddl, "${table}", tableName);
ret = StringHelper.replaceString(ret, "${column}", columnName);
return StringHelper.replaceString(ret, "${default}", defaultValue);
}
private void handleStrictError(String tableName, String columnName) {
@@ -197,6 +201,7 @@ public class BaseTableDdl implements TableDdl {
public String getDefaultValue() {
return defaultValue;
}
}
/**
@@ -236,6 +241,7 @@ public class BaseTableDdl implements TableDdl {
*/
@Override
public void generate(DdlWrite writer, CreateTable createTable) throws IOException {
reset();
String tableName = lowerTableName(createTable.getName());
@@ -269,6 +275,7 @@ public class BaseTableDdl implements TableDdl {
if (platformDdl.isInlineForeignKeys()) {
writeInlineForeignKeys(writer, createTable);
}
apply.newLine().append(")");
addTableStorageEngine(apply, createTable);
addTableCommentInline(apply, createTable);
@@ -278,7 +285,9 @@ public class BaseTableDdl implements TableDdl {
apply.endOfStatement();
addComments(apply, createTable);
writeUniqueOneToOneConstraints(writer, createTable);
if (isTrue(createTable.isWithHistory())) {
// create history with rollback before the
// associated drop table is written to rollback
@@ -296,6 +305,7 @@ public class BaseTableDdl implements TableDdl {
// add blank line for a bit of whitespace between tables
apply.end();
writer.dropAll().end();
if (!platformDdl.isInlineForeignKeys()) {
writeAddForeignKeys(writer, createTable);
}
@@ -314,6 +324,7 @@ public class BaseTableDdl implements TableDdl {
if (hasValue(tableComment)) {
platformDdl.addTableComment(apply, createTable.getName(), tableComment);
}
for (Column column : createTable.getColumn()) {
if (!StringHelper.isNull(column.getComment())) {
platformDdl.addColumnComment(apply, createTable.getName(), column.getName(), column.getComment());
@@ -416,6 +427,7 @@ public class BaseTableDdl implements TableDdl {
writeForeignKey(write, createTable.getName(), column);
}
}
writeAddCompoundForeignKeys(write, createTable);
}
@@ -436,6 +448,7 @@ public class BaseTableDdl implements TableDdl {
// no matching unique constraint so add the index
fkeyBuffer.appendStatement(platformDdl.createIndex(new WriteCreateIndex(request.indexName(), tableName, request.cols(), false)));
}
alterTableAddForeignKey(write.getOptions(), fkeyBuffer, request);
fkeyBuffer.end();
@@ -443,6 +456,7 @@ public class BaseTableDdl implements TableDdl {
if (hasValue(request.indexName())) {
write.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(request.indexName(), tableName));
}
write.dropAllForeignKeys().end();
}
@@ -499,7 +513,9 @@ public class BaseTableDdl implements TableDdl {
* Write the unique constraints inline with the create table statement.
*/
protected void writeUniqueConstraints(DdlBuffer apply, CreateTable createTable) throws IOException {
boolean inlineUniqueWhenNullable = platformDdl.isInlineUniqueWhenNullable();
List<Column> columns = new WriteUniqueConstraint(createTable.getColumn()).uniqueKeys();
for (Column column : columns) {
if (Boolean.TRUE.equals(column.isNotnull()) || inlineUniqueWhenNullable) {
@@ -516,10 +532,12 @@ public class BaseTableDdl implements TableDdl {
* Write the unique constraint inline with the create table statement.
*/
protected void inlineUniqueConstraintSingle(DdlBuffer buffer, Column column) throws IOException {
String uqName = column.getUnique();
if (uqName == null) {
uqName = column.getUniqueOneToOne();
}
buffer.append(",").newLine();
buffer.append(" constraint ").append(uqName).append(" unique ");
buffer.append("(");
@@ -531,6 +549,7 @@ public class BaseTableDdl implements TableDdl {
* Write the primary key constraint inline with the create table statement.
*/
protected void writePrimaryKeyConstraint(DdlBuffer buffer, String pkName, String[] pkColumns) throws IOException {
buffer.append(",").newLine();
buffer.append(" constraint ").append(pkName).append(" primary key");
appendColumns(pkColumns, buffer);
@@ -540,6 +559,7 @@ public class BaseTableDdl implements TableDdl {
* Return as an array of string column names.
*/
protected String[] toColumnNames(List<Column> columns) {
String[] cols = new String[columns.size()];
for (int i = 0; i < cols.length; i++) {
cols[i] = columns.get(i).getName();
@@ -662,11 +682,13 @@ public class BaseTableDdl implements TableDdl {
*/
@Override
public void generate(DdlWrite writer, AddColumn addColumn) throws IOException {
String tableName = addColumn.getTableName();
List<Column> columns = addColumn.getColumn();
for (Column column : columns) {
alterTableAddColumn(writer.apply(), tableName, column, false, isTrue(addColumn.isWithHistory()));
}
if (isTrue(addColumn.isWithHistory()) && historySupport == HistorySupport.TRIGGER_BASED) {
// make same changes to the history table
String historyTable = historyTable(tableName);
@@ -675,11 +697,14 @@ public class BaseTableDdl implements TableDdl {
alterTableAddColumn(writer.apply(), historyTable, column, true, true);
}
}
for (Column column : columns) {
if (hasValue(column.getReferences())) {
writeForeignKey(writer, tableName, column);
}
}
// add a bit of whitespace
writer.apply().end();
}
@@ -688,7 +713,9 @@ public class BaseTableDdl implements TableDdl {
*/
@Override
public void generate(DdlWrite writer, DropTable dropTable) throws IOException {
dropTable(writer.apply(), dropTable.getName());
if (hasValue(dropTable.getSequenceCol())
&& platformDdl.getPlatform().getDbIdentity().isSupportsSequence()) {
String sequenceName = dropTable.getSequenceName();
@@ -704,7 +731,9 @@ public class BaseTableDdl implements TableDdl {
*/
@Override
public void generate(DdlWrite writer, DropColumn dropColumn) throws IOException {
String tableName = dropColumn.getTableName();
alterTableDropColumn(writer.apply(), tableName, dropColumn.getColumnName());
if (isTrue(dropColumn.isWithHistory()) && historySupport == HistorySupport.TRIGGER_BASED) {
@@ -712,6 +741,7 @@ public class BaseTableDdl implements TableDdl {
regenerateHistoryTriggers(tableName, HistoryTableUpdate.Change.DROP, dropColumn.getColumnName());
alterTableDropColumn(writer.apply(), historyTable(tableName), dropColumn.getColumnName());
}
writer.apply().end();
}
@@ -796,6 +826,7 @@ public class BaseTableDdl implements TableDdl {
* Register the base table that we need to regenerate the history triggers on.
*/
protected void regenerateHistoryTriggers(String baseTableName, HistoryTableUpdate.Change change, String column) {
HistoryTableUpdate update = regenerateHistoryTriggers.computeIfAbsent(baseTableName, HistoryTableUpdate::new);
update.add(change, column);
}
@@ -805,6 +836,7 @@ public class BaseTableDdl implements TableDdl {
* Will be called, if there is a type, dbdefault or notnull change.
*/
protected void alterColumnBaseAttributes(DdlWrite writer, AlterColumn alter) throws IOException {
String ddl = platformDdl.alterColumnBaseAttributes(alter);
if (hasValue(ddl)) {
writer.apply().appendStatement(ddl);
@@ -825,22 +857,27 @@ public class BaseTableDdl implements TableDdl {
}
protected void alterColumnDefaultValue(DdlWrite writer, AlterColumn alter) throws IOException {
writer.apply().appendStatement(platformDdl.alterColumnDefaultValue(alter.getTableName(), alter.getColumnName(), alter.getDefaultValue()));
}
protected void dropCheckConstraint(DdlWrite writer, AlterColumn alter, String constraintName) throws IOException {
writer.apply().appendStatement(platformDdl.alterTableDropConstraint(alter.getTableName(), constraintName));
}
protected void addCheckConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
writer.apply().appendStatement(platformDdl.alterTableAddCheckConstraint(alter.getTableName(), alter.getCheckConstraintName(), alter.getCheckConstraint()));
}
protected void alterColumnNotnull(DdlWrite writer, AlterColumn alter) throws IOException {
writer.apply().appendStatement(platformDdl.alterColumnNotnull(alter.getTableName(), alter.getColumnName(), alter.isNotnull()));
}
protected void alterColumnType(DdlWrite writer, AlterColumn alter) throws IOException {
String ddl = platformDdl.alterColumnType(alter.getTableName(), alter.getColumnName(), alter.getType());
if (hasValue(ddl)) {
writer.apply().appendStatement(ddl);
@@ -854,26 +891,33 @@ public class BaseTableDdl implements TableDdl {
}
protected void alterColumnAddForeignKey(DdlWrite writer, AlterColumn alterColumn) throws IOException {
alterTableAddForeignKey(writer.getOptions(), writer.apply(), new WriteForeignKey(alterColumn));
}
protected void alterColumnDropForeignKey(DdlWrite writer, AlterColumn alter) throws IOException {
writer.apply().appendStatement(platformDdl.alterTableDropForeignKey(alter.getTableName(), alter.getDropForeignKey()));
}
protected void alterColumnDropUniqueConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
writer.apply().appendStatement(platformDdl.alterTableDropUniqueConstraint(alter.getTableName(), alter.getDropUnique()));
}
protected void alterColumnAddUniqueOneToOneConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
addUniqueConstraint(writer, alter, alter.getUniqueOneToOne());
}
protected void alterColumnAddUniqueConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
addUniqueConstraint(writer, alter, alter.getUnique());
}
protected void addUniqueConstraint(DdlWrite writer, AlterColumn alter, String uqName) throws IOException {
String[] cols = {alter.getColumnName()};
boolean notNull = alter.isNotnull() != null ? alter.isNotnull() : Boolean.TRUE.equals(alter.isNotnull());
writer.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(alter.getTableName(), uqName, cols, notNull ? null : cols));
@@ -921,4 +965,11 @@ public class BaseTableDdl implements TableDdl {
return Boolean.TRUE.equals(value);
}
/**
* Return as an int value with 0 when it is null.
*/
protected int toInt(BigInteger value) {
return (value == null) ? 0 : value.intValue();
}
}
@@ -1,14 +0,0 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.DatabasePlatform;
/**
* MariaDB platform DDL.
*/
public class MariaDbDdl extends MySqlDdl {
public MariaDbDdl(DatabasePlatform platform) {
super(platform);
this.historyDdl = new MariaDbHistoryDdl();
}
}
@@ -1,53 +0,0 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.ServerConfig;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.model.MTable;
import java.io.IOException;
/**
* History DDL for MariaDB.
*/
public class MariaDbHistoryDdl implements PlatformHistoryDdl {
@Override
public void configure(ServerConfig serverConfig, PlatformDdl platformDdl) {
// do nothing
}
@Override
public void createWithHistory(DdlWrite writer, MTable table) throws IOException {
String baseTable = table.getName();
enableSystemVersioning(writer, baseTable);
}
private void enableSystemVersioning(DdlWrite writer, String baseTable) throws IOException {
DdlBuffer apply = writer.applyHistoryView();
apply.append("alter table ").append(baseTable).append(" add system versioning").endOfStatement();
DdlBuffer drop = writer.dropAll();
drop.append("alter table ").append(baseTable).append(" drop system versioning").endOfStatement();
}
@Override
public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException {
String baseTable = dropHistoryTable.getBaseTable();
DdlBuffer apply = writer.applyHistoryView();
apply.append("alter table ").append(baseTable).append(" drop system versioning").endOfStatement();
}
@Override
public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException {
String baseTable = addHistoryTable.getBaseTable();
enableSystemVersioning(writer, baseTable);
}
@Override
public void updateTriggers(DdlWrite writer, HistoryTableUpdate baseTable) {
// do nothing
}
}
@@ -6,9 +6,9 @@ import io.ebean.config.dbplatform.DatabasePlatform;
/**
* Oracle platform specific DDL.
*/
public class OracleDdl extends PlatformDdl {
public class Oracle10Ddl extends PlatformDdl {
public OracleDdl(DatabasePlatform platform) {
public Oracle10Ddl(DatabasePlatform platform) {
super(platform);
this.dropTableIfExists = "drop table ";
this.dropSequenceIfExists = "drop sequence ";
@@ -48,12 +48,4 @@ public class OracleDdl extends PlatformDdl {
}
}
/**
* Modify and return the column definition for autoincrement or identity definition.
*/
@Override
public String asIdentityColumn(String columnDefn, DdlIdentity identity) {
return asIdentityStandardOptions(columnDefn, identity);
}
}
@@ -195,6 +195,7 @@ public class PlatformDdl {
* SQl2003 standard identity definition.
*/
protected String asIdentityStandardOptions(String columnDefn, DdlIdentity identity) {
StringBuilder sb = new StringBuilder(columnDefn.length() + 60);
sb.append(columnDefn).append(identity.optionGenerated());
sb.append(identity.identityOptions(identityStartWith, identityIncrementBy, identityCache));
@@ -54,9 +54,4 @@ public class PostgresDdl extends PlatformDdl {
public String asIdentityColumn(String columnDefn, DdlIdentity identity) {
return asIdentityStandardOptions(columnDefn, identity);
}
@Override
public String alterColumnType(String tableName, String columnName, String type) {
return super.alterColumnType(tableName, columnName, type) + " using " + columnName + "::" + convert(type);
}
}
@@ -58,7 +58,7 @@ public class PlatformTypeConverter {
} else {
// scale - varchar(10)
int scale = Integer.parseInt(columnDefinition.substring(open + 1, close));
return dbType.renderType(scale, -1) + suffix;
return dbType.renderType(scale, 0) + suffix;
}
} catch (IllegalArgumentException e) {
@@ -15,7 +15,7 @@ import java.util.Objects;
*/
public class MColumn {
private String name;
private final String name;
private String type;
private String checkConstraint;
private String checkConstraintName;
@@ -88,6 +88,7 @@ public class MColumn {
* Return a copy of this column used for creating the associated draft table.
*/
public MColumn copyForDraft() {
MColumn copy = new MColumn(name, type);
copy.draftOnly = draftOnly;
copy.checkConstraint = checkConstraint;
@@ -469,14 +470,6 @@ public class MColumn {
this.dbMigrationInfos = dbMigrationInfos;
}
/**
* Rename the column.
*/
public MColumn rename(String newName) {
this.name = newName;
return this;
}
/**
* Apply changes based on the AlterColumn request.
*/
@@ -55,7 +55,7 @@ public class MCompoundUniqueConstraint {
public MCompoundUniqueConstraint(AddUniqueConstraint change) {
this.name = change.getConstraintName();
this.columns = split(change.getColumnNames());
this.oneToOne = Boolean.TRUE.equals(change.isOneToOne());
this.oneToOne = change.isOneToOne();
this.platforms = change.getPlatforms();
this.nullableColumns = splitWithNull(change.getNullableColumns());
}
@@ -63,7 +63,7 @@ public class MCompoundUniqueConstraint {
public MCompoundUniqueConstraint(UniqueConstraint uq) {
this.name = uq.getName();
this.columns = split(uq.getColumnNames());
this.oneToOne = Boolean.TRUE.equals(uq.isOneToOne());
this.oneToOne = uq.isOneToOne();
this.platforms = uq.getPlatforms();
this.nullableColumns = splitWithNull(uq.getNullableColumns());
}
@@ -11,7 +11,6 @@ import io.ebeaninternal.dbmigration.migration.DropColumn;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.migration.DropTable;
import io.ebeaninternal.dbmigration.migration.ForeignKey;
import io.ebeaninternal.dbmigration.migration.RenameColumn;
import io.ebeaninternal.dbmigration.migration.UniqueConstraint;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
@@ -416,15 +415,6 @@ public class MTable {
}
}
public void apply(RenameColumn renameColumn) {
checkTableName(renameColumn.getTableName());
MColumn column = columns.remove(renameColumn.getOldName());
if (column == null) {
throw new IllegalStateException("Column [" + renameColumn.getOldName() + "] does not exist for RenameColumn change on table [" + renameColumn.getTableName() + "]?");
}
addColumn(column.rename(renameColumn.getNewName()));
}
public String getName() {
return name;
}
@@ -17,7 +17,6 @@ import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.migration.DropIndex;
import io.ebeaninternal.dbmigration.migration.DropTable;
import io.ebeaninternal.dbmigration.migration.Migration;
import io.ebeaninternal.dbmigration.migration.RenameColumn;
import io.ebeaninternal.dbmigration.migration.Sql;
import java.util.ArrayList;
@@ -164,8 +163,6 @@ public class ModelContainer {
applyChange((AddColumn) change);
} else if (change instanceof DropColumn) {
applyChange((DropColumn) change);
} else if (change instanceof RenameColumn) {
applyChange((RenameColumn) change);
} else if (change instanceof CreateIndex) {
applyChange((CreateIndex) change);
} else if (change instanceof DropIndex) {
@@ -317,14 +314,6 @@ public class ModelContainer {
table.apply(dropColumn);
}
protected void applyChange(RenameColumn renameColumn) {
MTable table = tables.get(renameColumn.getTableName());
if (table == null) {
throw new IllegalStateException("Table [" + renameColumn.getTableName() + "] does not exist in model?");
}
table.apply(renameColumn);
}
/**
* Add a table (typically from reading EbeanServer meta data).
*/
@@ -1,54 +0,0 @@
package io.ebeaninternal.server.cache;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* Bean Id value plus discriminator type.
* <p>
* Put into L2 cache such that we know the type of a bean with inheritance.
*/
public class CachedBeanId implements Externalizable {
private String discValue;
private Object id;
public CachedBeanId(String discValue, Object id) {
this.discValue = discValue;
this.id = id;
}
/**
* Construct from serialisation.
*/
public CachedBeanId() {
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(discValue);
out.writeObject(id);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
discValue = in.readUTF();
id = in.readObject();
}
@Override
public String toString() {
return discValue + ":" + id;
}
public String getDiscValue() {
return discValue;
}
public Object getId() {
return id;
}
}

Some files were not shown because too many files have changed in this diff Show More