mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08d241d946 | ||
|
|
1c24ebf6dc |
@@ -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.4.1</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.4.1</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>
|
||||
@@ -111,7 +111,7 @@
|
||||
<dependency>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-runtime</artifactId>
|
||||
<version>4.8-1</version>
|
||||
<version>4.7.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Jackson core used internally by Ebean -->
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
* }
|
||||
|
||||
@@ -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);
|
||||
* }
|
||||
|
||||
@@ -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,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;
|
||||
* ...
|
||||
* }
|
||||
|
||||
@@ -2,13 +2,6 @@ package io.ebean;
|
||||
|
||||
import io.ebean.config.ContainerConfig;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.service.SpiContainer;
|
||||
import io.ebean.service.SpiContainerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* Creates Database instances.
|
||||
@@ -30,57 +23,35 @@ import java.util.ServiceLoader;
|
||||
*/
|
||||
public class DatabaseFactory {
|
||||
|
||||
private static SpiContainer container;
|
||||
|
||||
static {
|
||||
EbeanVersion.getVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the container with clustering configuration.
|
||||
* <p>
|
||||
* Call this prior to creating any Database instances or alternatively set the
|
||||
* ContainerConfig on the DatabaseConfig when creating the first Database instance.
|
||||
* ContainerConfig on the ServerConfig when creating the first Database instance.
|
||||
*/
|
||||
public static synchronized void initialiseContainer(ContainerConfig containerConfig) {
|
||||
getContainer(containerConfig);
|
||||
EbeanServerFactory.initialiseContainer(containerConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create using properties to configure the database.
|
||||
* Create using ebean.properties to configure the database.
|
||||
*/
|
||||
public static synchronized Database create(String name) {
|
||||
// construct based on loading properties files
|
||||
return getContainer(null).createServer(name);
|
||||
return EbeanServerFactory.create(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create using the DatabaseConfig object to configure the database.
|
||||
* Create using the ServerConfig object to configure the database.
|
||||
*/
|
||||
public static synchronized Database create(DatabaseConfig config) {
|
||||
if (config.getName() == null) {
|
||||
throw new PersistenceException("The name is null (it is required)");
|
||||
}
|
||||
Database server = createInternal(config);
|
||||
if (config.isRegister()) {
|
||||
DbPrimary.setSkip(true);
|
||||
DbContext.getInstance().register(server, config.isDefaultServer());
|
||||
}
|
||||
return server;
|
||||
return EbeanServerFactory.create(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create using the DatabaseConfig additionally specifying a classLoader to use as the context class loader.
|
||||
* Create using the ServerConfig additionally specifying a classLoader to use as the context class loader.
|
||||
*/
|
||||
public static synchronized Database createWithContextClassLoader(DatabaseConfig config, ClassLoader classLoader) {
|
||||
ClassLoader currentContextLoader = Thread.currentThread().getContextClassLoader();
|
||||
Thread.currentThread().setContextClassLoader(classLoader);
|
||||
try {
|
||||
return DatabaseFactory.create(config);
|
||||
} finally {
|
||||
// set the currentContextLoader back
|
||||
Thread.currentThread().setContextClassLoader(currentContextLoader);
|
||||
}
|
||||
return EbeanServerFactory.createWithContextClassLoader(config, classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,44 +61,7 @@ public class DatabaseFactory {
|
||||
* </p>
|
||||
*/
|
||||
public static synchronized void shutdown() {
|
||||
container.shutdown();
|
||||
EbeanServerFactory.shutdown();
|
||||
}
|
||||
|
||||
private static Database createInternal(DatabaseConfig config) {
|
||||
return getContainer(config.getContainerConfig()).createServer(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the EbeanContainer initialising it if necessary.
|
||||
*
|
||||
* @param containerConfig the configuration controlling clustering communication
|
||||
*/
|
||||
private static SpiContainer getContainer(ContainerConfig containerConfig) {
|
||||
|
||||
// thread safe in that all calling methods are synchronized
|
||||
if (container != null) {
|
||||
return container;
|
||||
}
|
||||
|
||||
if (containerConfig == null) {
|
||||
// effectively load configuration from ebean.properties
|
||||
Properties properties = DbPrimary.getProperties();
|
||||
containerConfig = new ContainerConfig();
|
||||
containerConfig.loadFromProperties(properties);
|
||||
}
|
||||
container = createContainer(containerConfig);
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the container instance using the configuration.
|
||||
*/
|
||||
protected static SpiContainer createContainer(ContainerConfig containerConfig) {
|
||||
|
||||
Iterator<SpiContainerFactory> factories = ServiceLoader.load(SpiContainerFactory.class).iterator();
|
||||
if (factories.hasNext()) {
|
||||
return factories.next().create(containerConfig);
|
||||
}
|
||||
throw new IllegalStateException("Service loader didn't find a SpiContainerFactory?");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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,9 +28,15 @@ import java.util.ServiceLoader;
|
||||
* convenient way of using the 'default/primary' EbeanServer.
|
||||
* </p>
|
||||
*/
|
||||
@Deprecated
|
||||
public class EbeanServerFactory {
|
||||
|
||||
|
||||
private static SpiContainer container;
|
||||
|
||||
static {
|
||||
EbeanVersion.getVersion(); // initalizes the version class and logs the version.
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the container with clustering configuration.
|
||||
* <p>
|
||||
@@ -40,28 +44,53 @@ public class EbeanServerFactory {
|
||||
* ContainerConfig on the ServerConfig when creating the first EbeanServer instance.
|
||||
*/
|
||||
public static synchronized void initialiseContainer(ContainerConfig containerConfig) {
|
||||
DatabaseFactory.initialiseContainer(containerConfig);
|
||||
getContainer(containerConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create using ebean.properties to configure the database.
|
||||
*/
|
||||
public static synchronized EbeanServer create(String name) {
|
||||
return (EbeanServer)DatabaseFactory.create(name);
|
||||
|
||||
// construct based on loading properties files
|
||||
// and if invoked by Ebean then it handles registration
|
||||
SpiContainer serverFactory = getContainer(null);
|
||||
return serverFactory.createServer(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create using the ServerConfig object to configure the database.
|
||||
*/
|
||||
public static synchronized EbeanServer create(ServerConfig config) {
|
||||
return (EbeanServer)DatabaseFactory.create(config);
|
||||
|
||||
if (config.getName() == null) {
|
||||
throw new PersistenceException("The name is null (it is required)");
|
||||
}
|
||||
|
||||
EbeanServer server = createInternal(config);
|
||||
|
||||
if (config.isRegister()) {
|
||||
PrimaryServer.setSkip(true);
|
||||
Ebean.register(server, config.isDefaultServer());
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create using the ServerConfig additionally specifying a classLoader to use as the context class loader.
|
||||
*/
|
||||
public static synchronized EbeanServer createWithContextClassLoader(ServerConfig config, ClassLoader classLoader) {
|
||||
return (EbeanServer)DatabaseFactory.createWithContextClassLoader(config, classLoader);
|
||||
|
||||
ClassLoader currentContextLoader = Thread.currentThread().getContextClassLoader();
|
||||
Thread.currentThread().setContextClassLoader(classLoader);
|
||||
try {
|
||||
return EbeanServerFactory.create(config);
|
||||
|
||||
} finally {
|
||||
// set the currentContextLoader back
|
||||
Thread.currentThread().setContextClassLoader(currentContextLoader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,7 +100,46 @@ public class EbeanServerFactory {
|
||||
* </p>
|
||||
*/
|
||||
public static synchronized void shutdown() {
|
||||
DatabaseFactory.shutdown();
|
||||
container.shutdown();
|
||||
}
|
||||
|
||||
|
||||
private static EbeanServer createInternal(ServerConfig config) {
|
||||
|
||||
return getContainer(config.getContainerConfig()).createServer(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the EbeanContainer initialising it if necessary.
|
||||
*
|
||||
* @param containerConfig the configuration controlling clustering communication
|
||||
*/
|
||||
private static SpiContainer getContainer(ContainerConfig containerConfig) {
|
||||
|
||||
// thread safe in that all calling methods are synchronized
|
||||
if (container != null) {
|
||||
return container;
|
||||
}
|
||||
|
||||
if (containerConfig == null) {
|
||||
// effectively load configuration from ebean.properties
|
||||
Properties properties = PrimaryServer.getProperties();
|
||||
containerConfig = new ContainerConfig();
|
||||
containerConfig.loadFromProperties(properties);
|
||||
}
|
||||
container = createContainer(containerConfig);
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the container instance using the configuration.
|
||||
*/
|
||||
protected static SpiContainer createContainer(ContainerConfig containerConfig) {
|
||||
|
||||
Iterator<SpiContainerFactory> factories = ServiceLoader.load(SpiContainerFactory.class).iterator();
|
||||
if (factories.hasNext()) {
|
||||
return factories.next().create(containerConfig);
|
||||
}
|
||||
throw new IllegalStateException("Service loader didn't find a SpiContainerFactory?");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
*/
|
||||
|
||||
@@ -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>
|
||||
@@ -199,14 +199,6 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression gtOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Greater than or Equal to OR Null <code> >= or null </code>
|
||||
* <p>
|
||||
* A convenient expression combining GE and Is Null. Most often useful for range
|
||||
* expressions where the top range value is nullable.
|
||||
*/
|
||||
Expression geOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Greater Than - property greater than the given value.
|
||||
*/
|
||||
@@ -226,14 +218,6 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression ltOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Less Than or Equal to OR Null <code> <= or null </code>
|
||||
* <p>
|
||||
* A convenient expression combining LE and Is Null. Most often useful for range
|
||||
* expressions where the bottom range value is nullable.
|
||||
*/
|
||||
Expression leOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Less Than - property less than the given value.
|
||||
*/
|
||||
|
||||
@@ -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>
|
||||
@@ -886,11 +876,6 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> gtOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Greater Than or Equal to OR Null - <code> >= or null </code>.
|
||||
*/
|
||||
ExpressionList<T> geOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Greater Than or Equal to - property greater than or equal to the given
|
||||
* value.
|
||||
@@ -907,11 +892,6 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> ltOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Less Than or Equal to OR Null - <code> <= or null </code>.
|
||||
*/
|
||||
ExpressionList<T> leOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Less Than or Equal to - property less than or equal to the given value.
|
||||
*/
|
||||
@@ -1261,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>
|
||||
@@ -1299,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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -6,7 +6,7 @@ package io.ebean;
|
||||
* Ebean has traditionally used Transaction scope for the PersistenceContext. This is used to change the scope to
|
||||
* use (by default) and explicitly set the scope to use for an individual query.
|
||||
*
|
||||
* @see io.ebean.config.DatabaseConfig#setPersistenceContextScope(PersistenceContextScope)
|
||||
* @see io.ebean.config.ServerConfig#setPersistenceContextScope(PersistenceContextScope)
|
||||
* @see Query#setPersistenceContextScope(PersistenceContextScope)
|
||||
*/
|
||||
public enum PersistenceContextScope {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,7 +283,7 @@ public interface Query<T> {
|
||||
/**
|
||||
* Specify the PersistenceContextScope to use for this query.
|
||||
* <p/>
|
||||
* When this is not set the 'default' configured on {@link io.ebean.config.DatabaseConfig#setPersistenceContextScope(PersistenceContextScope)}
|
||||
* When this is not set the 'default' configured on {@link io.ebean.config.ServerConfig#setPersistenceContextScope(PersistenceContextScope)}
|
||||
* is used - this value defaults to {@link PersistenceContextScope#TRANSACTION}.
|
||||
* <p/>
|
||||
* Note that the same persistence Context is used for subsequent lazy loading and query join queries.
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 @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 {
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.ebean;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
import io.ebean.config.DocStoreConfig;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.config.ServerConfig;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.Connection;
|
||||
@@ -226,7 +226,7 @@ public interface Transaction extends AutoCloseable {
|
||||
/**
|
||||
* Set if the L2 cache should be skipped for "find by id" and "find by natural key" queries.
|
||||
* <p>
|
||||
* By default {@link DatabaseConfig#isSkipCacheAfterWrite()} is true and that means that for
|
||||
* By default {@link ServerConfig#isSkipCacheAfterWrite()} is true and that means that for
|
||||
* "find by id" and "find by natural key" queries which normally hit L2 bean cache automatically
|
||||
* - will not do so after a persist/write on the transaction.
|
||||
* </p>
|
||||
@@ -235,7 +235,7 @@ public interface Transaction extends AutoCloseable {
|
||||
* will skip the L2 bean cache or not (regardless of whether the transaction is considered "read only").
|
||||
* </p>
|
||||
* <p>
|
||||
* Refer to {@link DatabaseConfig#setSkipCacheAfterWrite(boolean)} for configuring the default behavior
|
||||
* Refer to {@link ServerConfig#setSkipCacheAfterWrite(boolean)} for configuring the default behavior
|
||||
* for using the L2 bean cache in transactions spanning multiple query/persist requests.
|
||||
* </p>
|
||||
*
|
||||
@@ -276,7 +276,7 @@ public interface Transaction extends AutoCloseable {
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see DatabaseConfig#isSkipCacheAfterWrite()
|
||||
* @see ServerConfig#isSkipCacheAfterWrite()
|
||||
*/
|
||||
void setSkipCache(boolean skipCache);
|
||||
|
||||
@@ -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>
|
||||
@@ -402,7 +402,7 @@ public interface Transaction extends AutoCloseable {
|
||||
* </p>
|
||||
*
|
||||
* @param batchMode the batch mode to use per save(), insert(), update() or delete()
|
||||
* @see io.ebean.config.DatabaseConfig#setPersistBatchOnCascade(PersistBatch)
|
||||
* @see io.ebean.config.ServerConfig#setPersistBatchOnCascade(PersistBatch)
|
||||
*/
|
||||
void setBatchOnCascade(boolean batchMode);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
package io.ebean.cache;
|
||||
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.config.ServerConfig;
|
||||
|
||||
/**
|
||||
* Plugin that provides a ServerCacheNotify implementation.
|
||||
@@ -13,5 +13,5 @@ public interface ServerCacheNotifyPlugin {
|
||||
/**
|
||||
* Create a ServerCacheNotify implementation given the server configuration.
|
||||
*/
|
||||
ServerCacheNotify create(DatabaseConfig config);
|
||||
ServerCacheNotify create(ServerConfig serverConfig);
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package io.ebean.cache;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.config.ServerConfig;
|
||||
|
||||
/**
|
||||
* The plugin interface that creates a ServerCacheFactory.
|
||||
@@ -11,5 +11,5 @@ public interface ServerCachePlugin {
|
||||
/**
|
||||
* Create the ServerCacheFactory given the server config and background executor service.
|
||||
*/
|
||||
ServerCacheFactory create(DatabaseConfig config, BackgroundExecutor executor);
|
||||
ServerCacheFactory create(ServerConfig config, BackgroundExecutor executor);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ package io.ebean.config;
|
||||
public interface AutoConfigure {
|
||||
|
||||
/**
|
||||
* Perform configuration for the DatabaseConfig prior to properties load.
|
||||
* Perform configuration for the ServerConfig prior to properties load.
|
||||
*/
|
||||
void preConfigure(DatabaseConfig config);
|
||||
void preConfigure(ServerConfig serverConfig);
|
||||
|
||||
/**
|
||||
* Provide some configuration the DatabaseConfig prior to server creation but after properties have been applied.
|
||||
* Provide some configuration the ServerConfig prior to server creation but after properties have been applied.
|
||||
*/
|
||||
void postConfigure(DatabaseConfig config);
|
||||
void postConfigure(ServerConfig serverConfig);
|
||||
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ public class ClassLoadConfig {
|
||||
|
||||
ClassLoaderContext(ClassLoader preferredLoader) {
|
||||
this.preferredLoader = preferredLoader;
|
||||
this.callerLoader = DatabaseConfig.class.getClassLoader();
|
||||
this.callerLoader = ServerConfig.class.getClassLoader();
|
||||
this.contextLoader = contextLoader();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import io.ebean.annotation.Platform;
|
||||
/**
|
||||
* Custom mappings for DB types that override the default.
|
||||
*
|
||||
* @see DatabaseConfig#addCustomMapping(DbType, String)
|
||||
* @see ServerConfig#addCustomMapping(DbType, String)
|
||||
*/
|
||||
public class CustomDbTypeMapping {
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,39 +0,0 @@
|
||||
package io.ebean.config;
|
||||
|
||||
/**
|
||||
* Provides a ServiceLoader based mechanism to configure a DatabaseConfig.
|
||||
* <p>
|
||||
* Provide an implementation and register it via the standard Java ServiceLoader mechanism
|
||||
* via a file at <code>META-INF/services/io.ebean.config.DatabaseConfigProvider</code>.
|
||||
* </p>
|
||||
* <p>
|
||||
* If you are using a DI container like Spring or Guice you are unlikely to use this but instead use a
|
||||
* spring specific configuration. When we are not using a DI container we may use this mechanism to
|
||||
* explicitly register the entity beans and avoid classpath scanning.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* public class EbeanConfigProvider implements DatabaseConfigProvider {
|
||||
*
|
||||
* @Override
|
||||
* public void apply(DatabaseConfig config) {
|
||||
*
|
||||
* // register the entity bean classes explicitly
|
||||
* config.addClass(Customer.class);
|
||||
* config.addClass(User.class);
|
||||
* ...
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public interface DatabaseConfigProvider {
|
||||
|
||||
/**
|
||||
* Apply the configuration to the DatabaseConfig.
|
||||
* <p>
|
||||
* Typically we explicitly register entity bean classes and thus avoid classpath scanning.
|
||||
* </p>
|
||||
*/
|
||||
void apply(DatabaseConfig config);
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -249,10 +229,10 @@ public class PlatformConfig {
|
||||
* <pre>{@code
|
||||
*
|
||||
* // set the default mapping for BigDecimal.class/decimal
|
||||
* config.addCustomMapping(DbType.DECIMAL, "decimal(18,6)");
|
||||
* serverConfig.addCustomMapping(DbType.DECIMAL, "decimal(18,6)");
|
||||
*
|
||||
* // set the default mapping for String.class/varchar but only for Postgres
|
||||
* config.addCustomMapping(DbType.VARCHAR, "text", Platform.POSTGRES);
|
||||
* serverConfig.addCustomMapping(DbType.VARCHAR, "text", Platform.POSTGRES);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
@@ -270,10 +250,10 @@ public class PlatformConfig {
|
||||
* <pre>{@code
|
||||
*
|
||||
* // set the default mapping for BigDecimal/decimal
|
||||
* config.addCustomMapping(DbType.DECIMAL, "decimal(18,6)");
|
||||
* serverConfig.addCustomMapping(DbType.DECIMAL, "decimal(18,6)");
|
||||
*
|
||||
* // set the default mapping for String/varchar
|
||||
* config.addCustomMapping(DbType.VARCHAR, "text");
|
||||
* serverConfig.addCustomMapping(DbType.VARCHAR, "text");
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
@@ -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);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,11 @@
|
||||
package io.ebean.config;
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to DatabaseConfigProvider.
|
||||
* <p>
|
||||
* Provides a ServiceLoader based mechanism to configure a ServerConfig.
|
||||
* <p>
|
||||
* Provide an implementation and register it via the standard Java ServiceLoader mechanism
|
||||
* via a file at <code>META-INF/services/io.ebean.config.ServerConfigProvider</code>.
|
||||
* </p>
|
||||
* <p>
|
||||
* If you are using a DI container like Spring or Guice you are unlikely to use this but instead use a
|
||||
* spring specific configuration. When we are not using a DI container we may use this mechanism to
|
||||
@@ -16,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
|
||||
@@ -28,7 +27,6 @@ package io.ebean.config;
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@Deprecated
|
||||
public interface ServerConfigProvider {
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,14 +231,13 @@ 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure UUID Storage etc based on DatabaseConfig settings.
|
||||
* Configure UUID Storage etc based on ServerConfig settings.
|
||||
*/
|
||||
protected void configure(PlatformConfig config, boolean allQuotedIdentifiers) {
|
||||
this.allQuotedIdentifiers = allQuotedIdentifiers;
|
||||
@@ -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));
|
||||
@@ -205,7 +205,7 @@ public class DbPlatformTypeMapping {
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the UUID appropriately based on native DB support and DatabaseConfig.DbUuid.
|
||||
* Map the UUID appropriately based on native DB support and ServerConfig.DbUuid.
|
||||
*/
|
||||
public void config(boolean nativeUuidType, PlatformConfig.DbUuid dbUuid) {
|
||||
if (nativeUuidType && dbUuid.useNativeType()) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package io.ebean.dbmigration;
|
||||
|
||||
import io.ebean.Database;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -86,9 +86,9 @@ public interface DbMigration {
|
||||
void setServer(Database database);
|
||||
|
||||
/**
|
||||
* Set the DatabaseConfig to use. Typically this is not called explicitly.
|
||||
* Set the serverConfig to use. Typically this is not called explicitly.
|
||||
*/
|
||||
void setServerConfig(DatabaseConfig config);
|
||||
void setServerConfig(ServerConfig config);
|
||||
|
||||
/**
|
||||
* Set the specific platform to generate DDL for.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebean.event;
|
||||
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.config.ServerConfig;
|
||||
|
||||
/**
|
||||
* A no operation implementation of BeanPersistController. Objects extending
|
||||
@@ -8,8 +8,8 @@ import io.ebean.config.DatabaseConfig;
|
||||
* <p>
|
||||
* A BeanPersistAdapter is either found automatically via class path search or
|
||||
* can be added programmatically via
|
||||
* {@link DatabaseConfig#add(BeanPersistController)} or
|
||||
* {@link DatabaseConfig#setPersistControllers(java.util.List)}.
|
||||
* {@link ServerConfig#add(BeanPersistController)} or
|
||||
* {@link ServerConfig#setPersistControllers(java.util.List)}.
|
||||
* </p>
|
||||
*/
|
||||
public abstract class BeanPersistAdapter implements BeanPersistController {
|
||||
|
||||
@@ -22,7 +22,6 @@ package io.ebean.event;
|
||||
* <p>
|
||||
* It is worth noting that BeanPersistListener is different in three main ways
|
||||
* from BeanPersistController postXXX methods.
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>BeanPersistListener only sees successfully committed events.
|
||||
* BeanController pre and post methods occur before the commit or a rollback and
|
||||
@@ -32,9 +31,10 @@ package io.ebean.event;
|
||||
* <li>BeanPersistListener can be notified of events from other servers in a
|
||||
* cluster.</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* A BeanPersistController is either found automatically via class path search
|
||||
* or can be added programmatically via DatabaseConfig.add().
|
||||
* or can be added programmatically via ServerConfiguration.addEntity().
|
||||
* </p>
|
||||
*/
|
||||
public interface BeanPersistController {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebean.event;
|
||||
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.config.ServerConfig;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@@ -29,10 +29,10 @@ import java.util.Set;
|
||||
* </p>
|
||||
* <p>
|
||||
* A BeanPersistListener is either found automatically via class path search or
|
||||
* can be added programmatically via {@link DatabaseConfig#add(BeanPersistListener)}}.
|
||||
* can be added programmatically via {@link ServerConfig#add(BeanPersistListener)}}.
|
||||
* </p>
|
||||
*
|
||||
* @see DatabaseConfig#add(BeanPersistListener)
|
||||
* @see ServerConfig#add(BeanPersistListener)
|
||||
*/
|
||||
public interface BeanPersistListener {
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebean.event;
|
||||
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.config.ServerConfig;
|
||||
|
||||
/**
|
||||
* Objects extending this modify queries prior their execution.
|
||||
@@ -10,7 +10,7 @@ import io.ebean.config.DatabaseConfig;
|
||||
* </p>
|
||||
* <p>
|
||||
* A BeanQueryAdapter is either found automatically via class path search or can
|
||||
* be added programmatically via {@link DatabaseConfig#add(BeanQueryAdapter)}.
|
||||
* be added programmatically via {@link ServerConfig#add(BeanQueryAdapter)}.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that a BeanQueryAdapter should be thread safe (stateless) and if
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebean.event;
|
||||
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.config.ServerConfig;
|
||||
|
||||
/**
|
||||
* Used to configure the server on startup.
|
||||
@@ -11,8 +11,8 @@ import io.ebean.config.DatabaseConfig;
|
||||
public interface ServerConfigStartup {
|
||||
|
||||
/**
|
||||
* On starting configure the DatabaseConfig.
|
||||
* On starting configure the ServerConfig.
|
||||
*/
|
||||
void onStart(DatabaseConfig config);
|
||||
void onStart(ServerConfig serverConfig);
|
||||
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public interface MetaInfoManager {
|
||||
/**
|
||||
* Initiate query plan collection by turning on "bind capture" on matching query plans.
|
||||
* <p>
|
||||
* Also refer to DatabaseConfig collectQueryPlans that needs to be set to true
|
||||
* Also refer to ServerConfig collectQueryPlans that needs to be set to true
|
||||
* and collectQueryPlanThresholdMicros which is a global defaults that can also
|
||||
* initiate query plan capture.
|
||||
*
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.ebean.plugin;
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.bean.BeanLoader;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
@@ -15,9 +15,9 @@ import java.util.List;
|
||||
public interface SpiServer extends EbeanServer, BeanLoader {
|
||||
|
||||
/**
|
||||
* Return the DatabaseConfig.
|
||||
* Return the serverConfig.
|
||||
*/
|
||||
DatabaseConfig getServerConfig();
|
||||
ServerConfig getServerConfig();
|
||||
|
||||
/**
|
||||
* Return the DatabasePlatform for this database.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebean.service;
|
||||
|
||||
import io.ebean.Database;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.config.ServerConfig;
|
||||
|
||||
/**
|
||||
* Creates the Database implementations. This is used internally by the EbeanServerFactory and is not currently
|
||||
@@ -14,7 +14,7 @@ public interface SpiContainer {
|
||||
*
|
||||
* @param configuration The configuration information for this database.
|
||||
*/
|
||||
Database createServer(DatabaseConfig configuration);
|
||||
EbeanServer createServer(ServerConfig configuration);
|
||||
|
||||
/**
|
||||
* Create an EbeanServer just using the name.
|
||||
@@ -23,7 +23,7 @@ public interface SpiContainer {
|
||||
* avaje.properties file.
|
||||
* </p>
|
||||
*/
|
||||
Database createServer(String name);
|
||||
EbeanServer createServer(String name);
|
||||
|
||||
/**
|
||||
* Shutdown any Ebean wide resources such as clustering.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebean.text.csv;
|
||||
|
||||
import io.ebean.Database;
|
||||
import io.ebean.EbeanServer;
|
||||
|
||||
/**
|
||||
* Provides callback methods for customisation of CSV processing.
|
||||
@@ -8,6 +8,9 @@ import io.ebean.Database;
|
||||
* You can provide your own CsvCallback implementation to customise the CSV
|
||||
* processing. It is expected that the DefaultCsvCallback provides a good base
|
||||
* class that you can extend.
|
||||
* </p>
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public interface CsvCallback<T> {
|
||||
|
||||
@@ -18,7 +21,7 @@ public interface CsvCallback<T> {
|
||||
* size etc.
|
||||
* </p>
|
||||
*/
|
||||
void begin(Database database);
|
||||
void begin(EbeanServer server);
|
||||
|
||||
/**
|
||||
* Read the header row.
|
||||
@@ -70,8 +73,8 @@ public interface CsvCallback<T> {
|
||||
* was created.
|
||||
* </p>
|
||||
*
|
||||
* @param row the row that the error has occurred on
|
||||
* @param e the error that occurred
|
||||
* @param row the row that the error has occured on
|
||||
* @param e the error that occured
|
||||
*/
|
||||
void endWithError(int row, Exception e);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebean.text.csv;
|
||||
|
||||
import io.ebean.Database;
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.Transaction;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -35,7 +35,7 @@ public class DefaultCsvCallback<T> implements CsvCallback<T> {
|
||||
/**
|
||||
* The EbeanServer used to save the beans.
|
||||
*/
|
||||
protected Database server;
|
||||
protected EbeanServer server;
|
||||
|
||||
/**
|
||||
* Used to log a message to indicate progress through large files.
|
||||
@@ -69,6 +69,7 @@ public class DefaultCsvCallback<T> implements CsvCallback<T> {
|
||||
* Construct with explicit batch size and logging info frequency.
|
||||
*/
|
||||
public DefaultCsvCallback(int persistBatchSize, int logInfoFrequency) {
|
||||
|
||||
this.persistBatchSize = persistBatchSize;
|
||||
this.logInfoFrequency = logInfoFrequency;
|
||||
}
|
||||
@@ -77,9 +78,10 @@ public class DefaultCsvCallback<T> implements CsvCallback<T> {
|
||||
* Create a transaction if required.
|
||||
*/
|
||||
@Override
|
||||
public void begin(Database server) {
|
||||
public void begin(EbeanServer server) {
|
||||
this.server = server;
|
||||
this.startTime = System.currentTimeMillis();
|
||||
|
||||
initTransactionIfRequired();
|
||||
}
|
||||
|
||||
@@ -169,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
|
||||
|
||||
@@ -73,14 +73,14 @@ public class JsonReadOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Jackson ObjectMapper to use (if not wanted to use the objectMapper set on the DatabaseConfig).
|
||||
* Return the Jackson ObjectMapper to use (if not wanted to use the objectMapper set on the ServerConfig).
|
||||
*/
|
||||
public Object getObjectMapper() {
|
||||
return objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Jackson ObjectMapper to use (if not wanted to use the objectMapper set on the DatabaseConfig).
|
||||
* Set the Jackson ObjectMapper to use (if not wanted to use the objectMapper set on the ServerConfig).
|
||||
*/
|
||||
public JsonReadOptions setObjectMapper(Object objectMapper) {
|
||||
this.objectMapper = objectMapper;
|
||||
|
||||
@@ -101,7 +101,7 @@ public class JsonWriteOptions {
|
||||
/**
|
||||
* Return the jackson object mapper to use.
|
||||
* <p/>
|
||||
* If null the ObjectMapper from DatabaseConfig will be used.
|
||||
* If null the ObjectMapper from serverConfig will be used.
|
||||
*/
|
||||
public Object getObjectMapper() {
|
||||
return objectMapper;
|
||||
@@ -110,7 +110,7 @@ public class JsonWriteOptions {
|
||||
/**
|
||||
* Set the jackson object mapper to use.
|
||||
* <p/>
|
||||
* If null the ObjectMapper from DatabaseConfig will be used.
|
||||
* If null the ObjectMapper from serverConfig will be used.
|
||||
*/
|
||||
public void setObjectMapper(Object objectMapper) {
|
||||
this.objectMapper = objectMapper;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user