mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
40
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34be7cc1f1 | ||
|
|
54f21836b0 | ||
|
|
4adf5a9eba | ||
|
|
b6a0653a3c | ||
|
|
c0810a375e | ||
|
|
90fe19eb8d | ||
|
|
43ecfe7c19 | ||
|
|
15be5dd4c7 | ||
|
|
d54cffacea | ||
|
|
965de6e84d | ||
|
|
56fd6b90be | ||
|
|
dd85eba3f0 | ||
|
|
d1a40d5676 | ||
|
|
b0462bcda6 | ||
|
|
59428bb214 | ||
|
|
73cf3e8511 | ||
|
|
beff4465b0 | ||
|
|
563cdd3b3b | ||
|
|
1bd7b99235 | ||
|
|
bb9d9f7e17 | ||
|
|
6bb38f2674 | ||
|
|
bb6571f426 | ||
|
|
1ee679c5ae | ||
|
|
e5264ca69e | ||
|
|
3a86be5f3d | ||
|
|
9f71b0df22 | ||
|
|
f271ea8a0d | ||
|
|
1ced574cd8 | ||
|
|
9dd412cd19 | ||
|
|
8237072f5b | ||
|
|
dd30b43d59 | ||
|
|
09f8967459 | ||
|
|
e3bf3629ee | ||
|
|
b6edd33f9a | ||
|
|
3a11a23e01 | ||
|
|
370267ab6f | ||
|
|
fe65a0ccb9 | ||
|
|
2837324a03 | ||
|
|
a6bd614f87 | ||
|
|
dfaf4b2f31 |
@@ -7,6 +7,6 @@ Maven Dependency
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>4.3.0</version>
|
||||
<version>4.4.1</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>4.3.1</version>
|
||||
<version>4.5.2</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>avaje-ebeanorm</name>
|
||||
@@ -89,6 +89,13 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>2.1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-agentloader</artifactId>
|
||||
@@ -99,14 +106,14 @@
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-agent</artifactId>
|
||||
<version>4.1.9</version>
|
||||
<version>4.5.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>1.3.153</version>
|
||||
<version>1.4.182</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -191,7 +198,7 @@
|
||||
<plugin>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
|
||||
<version>4.1.9</version>
|
||||
<version>4.5.1</version>
|
||||
<executions>
|
||||
<!-- Not going to enhance Model bean -->
|
||||
<execution>
|
||||
@@ -241,8 +248,27 @@
|
||||
<doctitle>Ebean 4</doctitle>
|
||||
<overview>src/main/java/com/avaje/ebean/overview.html</overview>
|
||||
<!-- <excludePackageNames>com.avaje.ebeaninternal.*:com.avaje.ebean.util</excludePackageNames> -->
|
||||
<additionalparam>-Xdoclint:none</additionalparam>
|
||||
<!--<additionalparam>-Xdoclint:none</additionalparam>-->
|
||||
<source>1.8</source>
|
||||
<doclet>org.avaje.doclet.PygmentsDoclet</doclet>
|
||||
<excludePackageNames>com.avaje.ebeaninternal.*:com.avaje.ebean.util</excludePackageNames>
|
||||
<docletArtifact>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>pygments-doclet</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</docletArtifact>
|
||||
<additionalparam>
|
||||
-Xdoclint:none
|
||||
-include-basedir ${project.basedir}
|
||||
-attributes "idseparator=-; project_name=${project.name}; \
|
||||
project_version=${project.version}; \
|
||||
project_desc=${project.description}"
|
||||
</additionalparam>
|
||||
<linksource>true</linksource>
|
||||
<overview>src/main/java/com/avaje/ebean/overview.html</overview>
|
||||
|
||||
</configuration>
|
||||
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
|
||||
@@ -81,10 +81,6 @@ public interface BeanState {
|
||||
* {@link EbeanServer#createEntityBean(Class)}, then populate its properties
|
||||
* and then call this method specifying which properties where loaded or null
|
||||
* for a fully loaded entity bean.
|
||||
*
|
||||
* @param loadedProperties
|
||||
* the properties that where loaded or null for a fully loaded entity
|
||||
* bean.
|
||||
*/
|
||||
public void setLoaded();
|
||||
}
|
||||
@@ -16,8 +16,6 @@ class DRawSqlParser {
|
||||
|
||||
public static final String $_WHERE = "${where}";
|
||||
|
||||
private static final String ORDER_BY = "order by";
|
||||
|
||||
private final SimpleTextParser textParser;
|
||||
|
||||
private String sql;
|
||||
@@ -35,6 +33,7 @@ class DRawSqlParser {
|
||||
private int groupByPos = -1;
|
||||
private int havingPos = -1;
|
||||
private int orderByPos = -1;
|
||||
private int orderByStmtPos = -1;
|
||||
|
||||
private boolean whereExprAnd;
|
||||
private int whereExprPos = -1;
|
||||
@@ -66,12 +65,12 @@ class DRawSqlParser {
|
||||
String preFrom = removeWhitespace(findPreFromSql());
|
||||
String preWhere = removeWhitespace(findPreWhereSql());
|
||||
String preHaving = removeWhitespace(findPreHavingSql());
|
||||
String orderByPrefix = findOrderByPrefixSql();
|
||||
String orderBySql = findOrderBySql();
|
||||
|
||||
preFrom = trimSelectKeyword(preFrom);
|
||||
|
||||
return new Sql(sql.hashCode(), preFrom, preWhere, whereExprAnd, preHaving, havingExprAnd,
|
||||
orderBySql, (distinctPos > -1));
|
||||
return new Sql(sql.hashCode(), preFrom, preWhere, whereExprAnd, preHaving, havingExprAnd, orderByPrefix, orderBySql, (distinctPos > -1));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,12 +141,12 @@ class DRawSqlParser {
|
||||
return preWhereExprSql;
|
||||
}
|
||||
|
||||
private String findOrderByPrefixSql() {
|
||||
return (orderByPos < 1) ? null : sql.substring(orderByPos, orderByStmtPos);
|
||||
}
|
||||
|
||||
private String findOrderBySql() {
|
||||
if (orderByPos > -1) {
|
||||
int pos = orderByPos + ORDER_BY.length();
|
||||
return sql.substring(pos).trim();
|
||||
}
|
||||
return null;
|
||||
return (orderByStmtPos < 1) ? null : sql.substring(orderByStmtPos).trim();
|
||||
}
|
||||
|
||||
private String findPreHavingSql() {
|
||||
@@ -226,6 +225,11 @@ class DRawSqlParser {
|
||||
}
|
||||
|
||||
orderByPos = textParser.findWordLower("order", startOrderBy);
|
||||
if (orderByPos > 1) {
|
||||
// there might be keywords like siblings in between the order
|
||||
// and by so search for the by keyword explicitly
|
||||
orderByStmtPos = 2 + textParser.findWordLower("by", orderByPos);
|
||||
}
|
||||
}
|
||||
|
||||
private int findWhereExprPosition() {
|
||||
|
||||
@@ -15,7 +15,6 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.annotation.CacheStrategy;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.text.csv.CsvReader;
|
||||
import com.avaje.ebean.text.json.JsonContext;
|
||||
@@ -155,14 +154,14 @@ public final class Ebean {
|
||||
|
||||
// skipDefaultServer is set by EbeanServerFactory
|
||||
// ... when it is creating the primaryServer
|
||||
if (GlobalProperties.isSkipPrimaryServer()) {
|
||||
if (PrimaryServer.isSkip()) {
|
||||
// primary server being created by EbeanServerFactory
|
||||
// ... so we should not try and create it here
|
||||
logger.debug("GlobalProperties.isSkipPrimaryServer()");
|
||||
logger.debug("PrimaryServer.isSkip()");
|
||||
|
||||
} else {
|
||||
// look to see if there is a default server defined
|
||||
String primaryName = getPrimaryServerName();
|
||||
String primaryName = PrimaryServer.getPrimaryServerName();
|
||||
logger.debug("primaryName:" + primaryName);
|
||||
if (primaryName != null && primaryName.trim().length() > 0) {
|
||||
primaryServer = getWithCreate(primaryName.trim());
|
||||
@@ -170,12 +169,6 @@ public final class Ebean {
|
||||
}
|
||||
}
|
||||
|
||||
private String getPrimaryServerName() {
|
||||
|
||||
String serverName = GlobalProperties.get("ebean.default.datasource", null);
|
||||
return GlobalProperties.get("datasource.default", serverName);
|
||||
}
|
||||
|
||||
private EbeanServer getPrimaryServer() {
|
||||
if (primaryServer == null) {
|
||||
String msg = "The default EbeanServer has not been defined?";
|
||||
@@ -678,7 +671,8 @@ public final class Ebean {
|
||||
/**
|
||||
* Refresh the values of a bean.
|
||||
* <p>
|
||||
* Note that this does not refresh any OneToMany or ManyToMany properties.
|
||||
* Note that this resets OneToMany and ManyToMany properties so that if they
|
||||
* are accessed a lazy load will refresh the many property.
|
||||
* </p>
|
||||
*/
|
||||
public static void refresh(Object bean) {
|
||||
@@ -1383,8 +1377,7 @@ public final class Ebean {
|
||||
/**
|
||||
* Return the BeanState for a given entity bean.
|
||||
* <p>
|
||||
* This will return null if the bean is not an enhanced (or subclassed) entity
|
||||
* bean.
|
||||
* This will return null if the bean is not an enhanced entity bean.
|
||||
* </p>
|
||||
*/
|
||||
public static BeanState getBeanState(Object bean) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,12 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.common.BootupEbeanManager;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebean.config.ContainerConfig;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.util.ClassUtil;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Creates EbeanServer instances.
|
||||
@@ -27,31 +28,43 @@ import com.avaje.ebean.util.ClassUtil;
|
||||
*/
|
||||
public class EbeanServerFactory {
|
||||
|
||||
private static BootupEbeanManager serverFactory = createServerFactory();
|
||||
|
||||
private static BootupEbeanManager bootupEbeanManager;
|
||||
|
||||
/**
|
||||
* Initialise the container with clustering configuration.
|
||||
*
|
||||
* Call this prior to creating any EbeanServer instances or alternatively set the
|
||||
* ContainerConfig on the ServerConfig when creating the first EbeanServer instance.
|
||||
*/
|
||||
public static synchronized void initialiseContainer(ContainerConfig containerConfig) {
|
||||
getServerFactory(containerConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create using ebean.properties to configure the server.
|
||||
*/
|
||||
public static EbeanServer create(String name) {
|
||||
public static synchronized EbeanServer create(String name) {
|
||||
|
||||
EbeanServer server = serverFactory.createServer(name);
|
||||
|
||||
return server;
|
||||
// construct based on loading properties files
|
||||
// and if invoked by Ebean then it handles registration
|
||||
BootupEbeanManager serverFactory = getServerFactory(null);
|
||||
return serverFactory.createServer(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create using the ServerConfig object to configure the server.
|
||||
*/
|
||||
public static EbeanServer create(ServerConfig config) {
|
||||
public static synchronized EbeanServer create(ServerConfig config) {
|
||||
|
||||
if (config.getName() == null) {
|
||||
throw new PersistenceException("The name is null (it is required)");
|
||||
}
|
||||
|
||||
EbeanServer server = serverFactory.createServer(config);
|
||||
EbeanServer server = createInternal(config);
|
||||
|
||||
if (config.isDefaultServer()) {
|
||||
GlobalProperties.setSkipPrimaryServer(true);
|
||||
PrimaryServer.setSkip(true);
|
||||
}
|
||||
if (config.isRegister()) {
|
||||
Ebean.register(server, config.isDefaultServer());
|
||||
@@ -60,13 +73,45 @@ public class EbeanServerFactory {
|
||||
return server;
|
||||
}
|
||||
|
||||
private static BootupEbeanManager createServerFactory() {
|
||||
|
||||
private static EbeanServer createInternal(ServerConfig config) {
|
||||
|
||||
return getServerFactory(config.getContainerConfig()).createServer(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the BootupEbeanManager initialising it if necessary.
|
||||
*
|
||||
* @param containerConfig the configuration controlling clustering communication
|
||||
*/
|
||||
private static BootupEbeanManager getServerFactory(ContainerConfig containerConfig) {
|
||||
|
||||
if (bootupEbeanManager != null) {
|
||||
return bootupEbeanManager;
|
||||
}
|
||||
|
||||
if (containerConfig == null) {
|
||||
// effectively load configuration from ebean.properties
|
||||
Properties properties = PrimaryServer.getProperties();
|
||||
containerConfig = new ContainerConfig();
|
||||
containerConfig.loadFromProperties(properties);
|
||||
}
|
||||
bootupEbeanManager = createServerFactory(containerConfig);
|
||||
return bootupEbeanManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the container instance using the configuration.
|
||||
*/
|
||||
private static BootupEbeanManager createServerFactory(ContainerConfig containerConfig) {
|
||||
|
||||
String dflt = "com.avaje.ebeaninternal.server.core.DefaultServerFactory";
|
||||
String implClassName = System.getProperty("ebean.serverfactory", dflt);
|
||||
|
||||
try {
|
||||
return (BootupEbeanManager) ClassUtil.newInstance(implClassName);
|
||||
Class<?> cls = Class.forName(implClassName);
|
||||
Constructor<?> constructor = cls.getConstructor(ContainerConfig.class);
|
||||
return (BootupEbeanManager) constructor.newInstance(containerConfig);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
@@ -208,25 +208,6 @@ public interface ExpressionList<T> extends Serializable {
|
||||
*/
|
||||
public FutureList<T> findFutureList();
|
||||
|
||||
/**
|
||||
* Return a PagingList for this query.
|
||||
* <p>
|
||||
* This can be used to break up a query into multiple queries to fetch the
|
||||
* data a page at a time.
|
||||
* </p>
|
||||
* <p>
|
||||
* This typically works by using a query per page and setting
|
||||
* {@link Query#setFirstRow(int)} and and {@link Query#setMaxRows(int)} on the
|
||||
* query. This usually would translate into SQL that uses limit offset, rownum
|
||||
* or row_number function to limit the result set.
|
||||
* </p>
|
||||
*
|
||||
* @param pageSize
|
||||
* the number of beans fetched per Page
|
||||
* @deprecated
|
||||
*/
|
||||
public PagingList<T> findPagingList(int pageSize);
|
||||
|
||||
/**
|
||||
* Return a PagedList for this query.
|
||||
* <p>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* FutureList represents the result of a background query execution that will
|
||||
@@ -15,13 +19,13 @@ import java.util.concurrent.Future;
|
||||
* A simple example:
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* // create a query to find all orders
|
||||
* Query<Order> query = Ebean.find(Order.class);
|
||||
* Query<Order> query = Ebean.find(Order.class);
|
||||
*
|
||||
* // execute the query in a background thread
|
||||
* // immediately returning the futureList
|
||||
* FutureList<Order> futureList = query.findFutureList();
|
||||
* FutureList<Order> futureList = query.findFutureList();
|
||||
*
|
||||
* // do something else ...
|
||||
*
|
||||
@@ -35,13 +39,11 @@ import java.util.concurrent.Future;
|
||||
*
|
||||
* if (!futureList.isCancelled()){
|
||||
* // wait for the query to finish and return the list
|
||||
* List<Order> list = futureList.get();
|
||||
* List<Order> list = futureList.get();
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author rbygrave
|
||||
* }</pre>
|
||||
*/
|
||||
public interface FutureList<T> extends Future<List<T>> {
|
||||
|
||||
@@ -50,4 +52,25 @@ public interface FutureList<T> extends Future<List<T>> {
|
||||
*/
|
||||
public Query<T> getQuery();
|
||||
|
||||
/**
|
||||
* Same as {@link #get()} but wraps InterruptedException and ExecutionException in the
|
||||
* unchecked PersistenceException.
|
||||
*
|
||||
* @return The query list result
|
||||
*
|
||||
* @throws PersistenceException when a InterruptedException or ExecutionException occurs.
|
||||
*/
|
||||
public List<T> getUnchecked();
|
||||
|
||||
/**
|
||||
* Same as {@link #get(long, java.util.concurrent.TimeUnit)} but wraps InterruptedException
|
||||
* and ExecutionException in the unchecked PersistenceException.
|
||||
*
|
||||
* @return The query list result
|
||||
*
|
||||
* @throws TimeoutException if the wait timed out
|
||||
* @throws PersistenceException if a InterruptedException or ExecutionException occurs.
|
||||
*/
|
||||
public List<T> getUnchecked(long timeout, TimeUnit unit) throws TimeoutException;
|
||||
|
||||
}
|
||||
|
||||
@@ -24,11 +24,94 @@ import javax.persistence.MappedSuperclass;
|
||||
* <p>
|
||||
* You may choose not use this Model mapped superclass if you don't like the 'Active Record' style
|
||||
* or if you believe it 'pollutes' your entity beans.
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* You can use Dependency Injection like Guice or Spring to construct and wire a EbeanServer instance
|
||||
* and have that same instance used with this Model and Finder. The way that works is that when the
|
||||
* DI container creates the EbeanServer instance it can be registered with the Ebean singleton. In this
|
||||
* way the EbeanServer instance can be injected as per normal Guice / Spring dependency injection and
|
||||
* that same instance also used to support the Model and Finder active record style.
|
||||
*
|
||||
* <p>
|
||||
* If you choose to use the Model mapped superclass you will probably also chose to additionally add
|
||||
* a {@link Finder} as a public static field to complete the active record pattern and provide a
|
||||
* relatively nice clean way to write queries.
|
||||
*
|
||||
* <h3>Typical common @MappedSuperclass</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Typically there is a common base model that has some
|
||||
* // common properties like the ones below
|
||||
*
|
||||
* @MappedSuperclass
|
||||
* public class BaseModel extends Model {
|
||||
*
|
||||
* @Id Long id;
|
||||
*
|
||||
* @Version Long version;
|
||||
*
|
||||
* @CreatedTimestamp Timestamp whenCreated;
|
||||
*
|
||||
* @UpdatedTimestamp Timestamp whenUpdated;
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Extend the Model</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Extend the mappedSuperclass
|
||||
*
|
||||
* @Entity @Table(name="oto_account")
|
||||
* public class Account extends BaseModel {
|
||||
*
|
||||
* // add a static Finder
|
||||
* // ... with Long being the type of our ID property ...
|
||||
*
|
||||
* public static final Finder<Long,Account> find =
|
||||
* new Finder<Long,Account>(Long.class, Account.class);
|
||||
*
|
||||
* String name;
|
||||
*
|
||||
* @OneToOne(mappedBy = "account",optional = true)
|
||||
* User user;
|
||||
*
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Modal: save()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Active record style ... save(), delete() etc
|
||||
* Account account = new Account();
|
||||
* account.setName("AC234");
|
||||
*
|
||||
* // save() method inherited from Model
|
||||
* account.save();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Finder: find byId</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // find byId
|
||||
* Account account = Account.find.byId(42);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Finder: find where</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // find where ...
|
||||
* List<Account> accounts =
|
||||
* Account.find
|
||||
* .where().gt("startDate", lastMonth)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public abstract class Model {
|
||||
@@ -105,7 +188,9 @@ public abstract class Model {
|
||||
* customer.markAsDirty();
|
||||
* customer.save();
|
||||
*
|
||||
* </pre>
|
||||
* </pre>
|
||||
*
|
||||
* @see EbeanServer#markAsDirty(Object)
|
||||
*/
|
||||
public void markAsDirty() {
|
||||
db().markAsDirty(this);
|
||||
@@ -117,6 +202,8 @@ public abstract class Model {
|
||||
* <p>
|
||||
* Ebean will detect if this is a new bean or a previously fetched bean and perform either an
|
||||
* insert or an update based on that.
|
||||
*
|
||||
* @see EbeanServer#save(Object)
|
||||
*/
|
||||
public void save() {
|
||||
db().save(this);
|
||||
@@ -124,6 +211,8 @@ public abstract class Model {
|
||||
|
||||
/**
|
||||
* Update this entity.
|
||||
*
|
||||
* @see EbeanServer#update(Object)
|
||||
*/
|
||||
public void update() {
|
||||
db().update(this);
|
||||
@@ -131,6 +220,8 @@ public abstract class Model {
|
||||
|
||||
/**
|
||||
* Insert this entity.
|
||||
*
|
||||
* @see EbeanServer#insert(Object)
|
||||
*/
|
||||
public void insert() {
|
||||
db().insert(this);
|
||||
@@ -138,6 +229,8 @@ public abstract class Model {
|
||||
|
||||
/**
|
||||
* Delete this entity.
|
||||
*
|
||||
* @see EbeanServer#delete(Object)
|
||||
*/
|
||||
public void delete() {
|
||||
db().delete(this);
|
||||
@@ -166,6 +259,8 @@ public abstract class Model {
|
||||
|
||||
/**
|
||||
* Refreshes this entity from the database.
|
||||
*
|
||||
* @see EbeanServer#refresh(Object)
|
||||
*/
|
||||
public void refresh() {
|
||||
db().refresh(this);
|
||||
@@ -248,10 +343,14 @@ public abstract class Model {
|
||||
return new Finder<I, T>(server, idType, type);
|
||||
}
|
||||
|
||||
// Does not exist yet but I think should
|
||||
// public void deleteById(I id) {
|
||||
// return db().deleteById(type, id);
|
||||
// }
|
||||
/**
|
||||
* Delete a bean by Id.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#delete(Class, Object)}
|
||||
*/
|
||||
public void deleteById(I id) {
|
||||
db().delete(type, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all entities of the given type.
|
||||
@@ -304,6 +403,8 @@ public abstract class Model {
|
||||
|
||||
/**
|
||||
* Creates a query applying the path properties to set the select and fetch clauses.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#apply(com.avaje.ebean.text.PathProperties)}
|
||||
*/
|
||||
public Query<T> apply(PathProperties pathProperties) {
|
||||
return db().find(type).apply(pathProperties);
|
||||
@@ -344,6 +445,8 @@ public abstract class Model {
|
||||
/**
|
||||
* Execute the query consuming each bean one at a time.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#findEachWhile(QueryEachWhileConsumer)}
|
||||
* <p>
|
||||
* This is similar to #findEach except that you return boolean
|
||||
* true to continue processing beans and return false to stop
|
||||
* processing early.
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a Page of results that is part of a PagingList.
|
||||
* <p>
|
||||
* Typically a Page represents the data that is shown to the user at a single
|
||||
* time - and the user 'pages' through a large list.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*
|
||||
* @param <T>
|
||||
* the entity bean type
|
||||
*
|
||||
* @see Query#findPagingList(int)
|
||||
* @see PagingList
|
||||
*/
|
||||
public interface Page<T> {
|
||||
|
||||
/**
|
||||
* Return the list of entities for this page.
|
||||
*/
|
||||
public List<T> getList();
|
||||
|
||||
/**
|
||||
* Return the total row count for all pages.
|
||||
*/
|
||||
public int getTotalRowCount();
|
||||
|
||||
/**
|
||||
* Return the total number of pages.
|
||||
*/
|
||||
public int getTotalPageCount();
|
||||
|
||||
/**
|
||||
* Return the index position of this page.
|
||||
*/
|
||||
public int getPageIndex();
|
||||
|
||||
/**
|
||||
* Return true if there is a next page.
|
||||
*/
|
||||
public boolean hasNext();
|
||||
|
||||
/**
|
||||
* Return true if there is a previous page.
|
||||
*/
|
||||
public boolean hasPrev();
|
||||
|
||||
/**
|
||||
* Return the next page.
|
||||
*/
|
||||
public Page<T> next();
|
||||
|
||||
/**
|
||||
* Return the previous page.
|
||||
*/
|
||||
public Page<T> prev();
|
||||
|
||||
/**
|
||||
* Helper method to return a "X to Y of Z" string for this page where X is the
|
||||
* first row, Y the last row and Z the total row count.
|
||||
*
|
||||
* @param to
|
||||
* String to put between the first and last row
|
||||
* @param of
|
||||
* String to put between the last row and the total row count
|
||||
*
|
||||
* @return String of the format XtoYofZ.
|
||||
*/
|
||||
public String getDisplayXtoYofZ(String to, String of);
|
||||
}
|
||||
@@ -16,7 +16,44 @@ import java.util.concurrent.Future;
|
||||
* the query. This translates into SQL that uses limit offset, rownum or row_number function to
|
||||
* limit the result set.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* <h4>Example: typical use including total row count</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // We want to find the first 100 new orders
|
||||
* // ... 0 means first page
|
||||
* // ... page size is 100
|
||||
*
|
||||
* PagedList<Order> pagedList
|
||||
* = ebeanServer.find(Order.class)
|
||||
* .where().eq("status", Order.Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .findPagedList(0, 100);
|
||||
*
|
||||
* // Optional: initiate the loading of the total
|
||||
* // row count in a background thread
|
||||
* pagedList.loadRowCount();
|
||||
*
|
||||
* // fetch and return the list in the foreground thread
|
||||
* List<Order> orders = pagedList.getList();
|
||||
*
|
||||
* // get the total row count (from the future)
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h4>Example: No total row count required</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // If you are not getting the 'first page' often
|
||||
* // you do not bother getting the total row count again
|
||||
* // so instead just get the page list of data
|
||||
*
|
||||
* // fetch and return the list in the foreground thread
|
||||
* List<Order> orders = pagedList.getList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T>
|
||||
* the entity bean type
|
||||
*
|
||||
@@ -26,12 +63,53 @@ public interface PagedList<T> {
|
||||
|
||||
/**
|
||||
* Initiate the loading of the total row count in the background.
|
||||
* <pre>{@code
|
||||
*
|
||||
* // initiate the loading of the total row count
|
||||
* // in a background thread
|
||||
* pagedList.loadRowCount();
|
||||
*
|
||||
* // fetch and return the list in the foreground thread
|
||||
* List<Order> orders = pagedList.getList();
|
||||
*
|
||||
* // get the total row count (from the future)
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Also note that using loadRowCount() and getTotalRowCount() rather than getFutureRowCount()
|
||||
* means that exceptions ExecutionException, InterruptedException, TimeoutException are instead
|
||||
* wrapped in the unchecked PersistenceException (which might be preferrable).
|
||||
* </p>
|
||||
*/
|
||||
public void loadRowCount();
|
||||
|
||||
/**
|
||||
* Return the Future row count. You might get this if you wish to cancel the total row count query
|
||||
* or specify a timeout for that query.
|
||||
* or specify a timeout for the row count query.
|
||||
* <p>
|
||||
* The loadRowCount() & getTotalRowCount() methods internally make use of this getFutureRowCount() method.
|
||||
* Generally I expect people to prefer loadRowCount() & getTotalRowCount() over getFutureRowCount().
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // initiate the row count query in the background thread
|
||||
* Future<Integer> rowCount = pagedList.getFutureRowCount();
|
||||
*
|
||||
* // fetch and return the list in the foreground thread
|
||||
* List<Order> orders = pagedList.getList();
|
||||
*
|
||||
* // now get the total count with a timeout
|
||||
* Integer totalRowCount = rowCount.get(30, TimeUnit.SECONDS);
|
||||
*
|
||||
* // or ge the total count without a timeout
|
||||
* Integer totalRowCountViaFuture = rowCount.get();
|
||||
*
|
||||
* // which is actually the same as ...
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public Future<Integer> getFutureRowCount();
|
||||
|
||||
@@ -42,11 +120,37 @@ public interface PagedList<T> {
|
||||
|
||||
/**
|
||||
* Return the total row count for all pages.
|
||||
* <p>
|
||||
* If loadRowCount() has already been called then the row count query is already executing in a background thread
|
||||
* and this gets the associated Future and gets the value waiting for the future to finish.
|
||||
* </p>
|
||||
* <p>
|
||||
* If loadRowCount() has not been called then this executes the find row count query and returns the result and this
|
||||
* will just occur in the current thread and not use a background thread.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Optional: initiate the loading of the total
|
||||
* // row count in a background thread
|
||||
* pagedList.loadRowCount();
|
||||
*
|
||||
* // fetch and return the list in the foreground thread
|
||||
* List<Order> orders = pagedList.getList();
|
||||
*
|
||||
* // get the total row count (which was being executed
|
||||
* // in a background thread if loadRowCount() was used)
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public int getTotalRowCount();
|
||||
|
||||
/**
|
||||
* Return the total number of pages based on the page size and total row count.
|
||||
* <p>
|
||||
* This method requires that the total row count has been fetched and will invoke
|
||||
* the total row count query if it has not already been invoked.
|
||||
* </p>
|
||||
*/
|
||||
public int getTotalPageCount();
|
||||
|
||||
@@ -57,6 +161,10 @@ public interface PagedList<T> {
|
||||
|
||||
/**
|
||||
* Return true if there is a next page.
|
||||
* <p>
|
||||
* This method requires that the total row count has been fetched and will invoke
|
||||
* the total row count query if it has not already been invoked.
|
||||
* </p>
|
||||
*/
|
||||
public boolean hasNext();
|
||||
|
||||
@@ -68,7 +176,11 @@ public interface PagedList<T> {
|
||||
/**
|
||||
* Helper method to return a "X to Y of Z" string for this page where X is the first row, Y the
|
||||
* last row and Z the total row count.
|
||||
*
|
||||
* <p>
|
||||
* This method requires that the total row count has been fetched and will invoke
|
||||
* the total row count query if it has not already been invoked.
|
||||
* </p>
|
||||
*
|
||||
* @param to
|
||||
* String to put between the first and last row
|
||||
* @param of
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* Used to page through a query result rather than fetching all the results in a
|
||||
* single query.
|
||||
* <p>
|
||||
* Has the ability to use background threads to 'fetch ahead' the next page and
|
||||
* get the total row count.
|
||||
* </p>
|
||||
* <p>
|
||||
* If you are building a stateless web application and not keeping the
|
||||
* PagingList over multiple requests then there is not much to be gained in
|
||||
* using PagingList. Instead you can just use {@link Query#setFirstRow(int)} and
|
||||
* {@link Query#setMaxRows(int)}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If you are using PagingList is a stateful web application where the
|
||||
* PagingList is held over multiple requests then PagingList provides the extra
|
||||
* benefits of
|
||||
* <ul>
|
||||
* <li>Fetch ahead - automatically fetching the next page via background query
|
||||
* execution</li>
|
||||
* <li>Automatic propagation of the persistence context</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* So with PagingList when you use Page 2 it can automatically fetch Page 3 data
|
||||
* in the background (using a findFutureList() query). It also automatically
|
||||
* propagates the persistence context so that all the queries executed by the
|
||||
* PagingList all use the same persistence context.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* PagingList<TOne> pagingList =
|
||||
* Ebean.find(TOne.class)
|
||||
* .where().gt("name", "2")
|
||||
* .findPagingList(10);
|
||||
*
|
||||
* // get the row count in the background...
|
||||
* // ... otherwise it is fetched on demand
|
||||
* // ... when getRowCount() or getPageCount()
|
||||
* // ... is called
|
||||
* pagingList.getFutureRowCount();
|
||||
*
|
||||
* // get the first page
|
||||
* Page<TOne> page = pagingList.getPage(0);
|
||||
*
|
||||
* // get the beans from the page as a list
|
||||
* List<TOne> list = page.getList();
|
||||
* </pre>
|
||||
*
|
||||
* @author rbygrave
|
||||
*
|
||||
* @param <T>
|
||||
* the entity bean type
|
||||
*/
|
||||
public interface PagingList<T> {
|
||||
|
||||
/**
|
||||
* Refresh will clear all the pages and row count forcing them to be
|
||||
* re-fetched when next required.
|
||||
*/
|
||||
public void refresh();
|
||||
|
||||
// public void fetchAll();
|
||||
// public String? getOrderBy();
|
||||
// public void setOrderBy(String?);
|
||||
|
||||
/**
|
||||
* By default fetchAhead is true so use this to turn off fetchAhead.
|
||||
* <p>
|
||||
* Set this to false if you don't want to fetch ahead using background
|
||||
* fetching.
|
||||
* <p>
|
||||
* If set to true (or left as to default) then the next page is fetched in the
|
||||
* background as soon as the list is accessed.
|
||||
* </p>
|
||||
*/
|
||||
public PagingList<T> setFetchAhead(boolean fetchAhead);
|
||||
|
||||
/**
|
||||
* Return the Future for getting the total row count.
|
||||
*/
|
||||
public Future<Integer> getFutureRowCount();
|
||||
|
||||
/**
|
||||
* Return the data for all the pages in the form of a single List.
|
||||
* <p>
|
||||
* Iterating through this list will automatically fire the paging queries as
|
||||
* required.
|
||||
* </p>
|
||||
*/
|
||||
public List<T> getAsList();
|
||||
|
||||
/**
|
||||
* Return the page size. This is the number of rows per page.
|
||||
*/
|
||||
public int getPageSize();
|
||||
|
||||
/**
|
||||
* Return the total row count.
|
||||
* <p>
|
||||
* This gets the result from getFutureRowCount and will wait until that query
|
||||
* has completed.
|
||||
* </p>
|
||||
*/
|
||||
public int getTotalRowCount();
|
||||
|
||||
/**
|
||||
* Return the total page count.
|
||||
* <p>
|
||||
* This is based on the total row count. This will wait until the row count
|
||||
* has returned if it has not already.
|
||||
* </p>
|
||||
*/
|
||||
public int getTotalPageCount();
|
||||
|
||||
/**
|
||||
* Return the page for a given page position (starting at 0).
|
||||
*/
|
||||
public Page<T> getPage(int i);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.config.PropertyMap;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Provides singleton state for the default server.
|
||||
* <p/>
|
||||
* Intended for internal use as part of bootup, construction, registration of the default server.
|
||||
*/
|
||||
class PrimaryServer {
|
||||
|
||||
private static Properties globalProperties;
|
||||
|
||||
private static String defaultServerName;
|
||||
|
||||
private static boolean skip;
|
||||
|
||||
/**
|
||||
* Set whether to skip automatically creating the primary server.
|
||||
*/
|
||||
static synchronized void setSkip(boolean skip) {
|
||||
PrimaryServer.skip = skip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true to skip automatically creating the primary server.
|
||||
*/
|
||||
static synchronized boolean isSkip() {
|
||||
return skip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default server name.
|
||||
*/
|
||||
static synchronized String getPrimaryServerName() {
|
||||
getProperties();
|
||||
return defaultServerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default configuration Properties.
|
||||
*/
|
||||
static synchronized Properties getProperties() {
|
||||
if (globalProperties == null) {
|
||||
globalProperties = PropertyMap.defaultProperties();
|
||||
}
|
||||
defaultServerName = globalProperties.getProperty("datasource.default");
|
||||
if (defaultServerName == null) {
|
||||
defaultServerName = globalProperties.getProperty("ebean.default.datasource");
|
||||
}
|
||||
return globalProperties;
|
||||
}
|
||||
}
|
||||
@@ -13,54 +13,57 @@ import java.util.Set;
|
||||
* Example: Create the query using the API.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* List<Order> orderList =
|
||||
* Ebean.find(Order.class)
|
||||
* .fetch("customer")
|
||||
* .fetch("details")
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Order> orderList =
|
||||
* ebeanServer.find(Order.class)
|
||||
* .fetch("customer")
|
||||
* .fetch("details")
|
||||
* .where()
|
||||
* .like("customer.name","rob%")
|
||||
* .gt("orderDate",lastWeek)
|
||||
* .orderBy("customer.id, id desc")
|
||||
* .like("customer.name","rob%")
|
||||
* .gt("orderDate",lastWeek)
|
||||
* .orderBy("customer.id, id desc")
|
||||
* .setMaxRows(50)
|
||||
* .findList();
|
||||
*
|
||||
* ...
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Example: The same query using the query language
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* String oql =
|
||||
* " find order "
|
||||
* +" fetch customer "
|
||||
* +" fetch details "
|
||||
* +" where customer.name like :custName and orderDate > :minOrderDate "
|
||||
* +" order by customer.id, id desc "
|
||||
* +" limit 50 ";
|
||||
* " find order "
|
||||
* +" fetch customer "
|
||||
* +" fetch details "
|
||||
* +" where customer.name like :custName and orderDate > :minOrderDate "
|
||||
* +" order by customer.id, id desc "
|
||||
* +" limit 50 ";
|
||||
*
|
||||
* Query<Order> query = Ebean.createQuery(Order.class, oql);
|
||||
* query.setParameter("custName", "Rob%");
|
||||
* query.setParameter("minOrderDate", lastWeek);
|
||||
* Query<Order> query = ebeanServer.createQuery(Order.class, oql);
|
||||
* query.setParameter("custName", "Rob%");
|
||||
* query.setParameter("minOrderDate", lastWeek);
|
||||
*
|
||||
* List<Order> orderList = query.findList();
|
||||
* List<Order> orderList = query.findList();
|
||||
* ...
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Example: Using a named query called "with.cust.and.details"
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* Query<Order> query = Ebean.createNamedQuery(Order.class,"with.cust.and.details");
|
||||
* query.setParameter("custName", "Rob%");
|
||||
* query.setParameter("minOrderDate", lastWeek);
|
||||
* <pre>{@code
|
||||
*
|
||||
* Query<Order> query = ebeanServer.createNamedQuery(Order.class,"with.cust.and.details");
|
||||
* query.setParameter("custName", "Rob%");
|
||||
* query.setParameter("minOrderDate", lastWeek);
|
||||
*
|
||||
* List<Order> orderList = query.findList();
|
||||
* List<Order> orderList = query.findList();
|
||||
* ...
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Autofetch</h3>
|
||||
* <p>
|
||||
@@ -98,13 +101,13 @@ import java.util.Set;
|
||||
* Refer to "ALL Properties/Columns" mode of Optimistic Concurrency checking.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* [ find {bean type} [ ( * | {fetch properties} ) ] ]
|
||||
* [ fetch {associated bean} [ ( * | {fetch properties} ) ] ]
|
||||
* [ where {predicates} ]
|
||||
* [ order by {order by properties} ]
|
||||
* [ limit {max rows} [ offset {first row} ] ]
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* <b>FIND</b> <b>{bean type}</b> [ ( <i>*</i> | <i>{fetch properties}</i> ) ]
|
||||
@@ -160,17 +163,17 @@ import java.util.Set;
|
||||
* Find orders fetching all its properties
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* find order
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Find orders fetching all its properties
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* find order (*)
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Find orders fetching its id, shipDate and status properties. Note that the id
|
||||
@@ -178,30 +181,30 @@ import java.util.Set;
|
||||
* properties.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* find order (shipDate, status)
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Find orders with a named bind variable (that will need to be bound via
|
||||
* {@link Query#setParameter(String, Object)}).
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* find order
|
||||
* where customer.name like :custLike
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Find orders and also fetch the customer with a named bind parameter. This
|
||||
* will fetch and populate both the order and customer objects.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* find order
|
||||
* fetch customer
|
||||
* where customer.id = :custId
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Find orders and also fetch the customer, customer shippingAddress, order
|
||||
@@ -212,13 +215,13 @@ import java.util.Set;
|
||||
* populated.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* find order
|
||||
* fetch customer (name)
|
||||
* fetch customer.shippingAddress
|
||||
* fetch details
|
||||
* fetch details.product (sku, name)
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Early parsing of the Query</h3>
|
||||
* <p>
|
||||
@@ -352,19 +355,24 @@ public interface Query<T> extends Serializable {
|
||||
|
||||
/**
|
||||
* Explicitly set a comma delimited list of the properties to fetch on the
|
||||
* 'main' entity bean (aka partial object). Note that '*' means all
|
||||
* 'main' root level entity bean (aka partial object). Note that '*' means all
|
||||
* properties.
|
||||
* <p>
|
||||
* You use {@link #fetch(String, String)} to specify specific properties to fetch
|
||||
* on other non-root level paths of the object graph.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* Query<Customer> query = Ebean.createQuery(Customer.class);
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Only fetch the customer id, name and status.
|
||||
* // This is described as a "Partial Object"
|
||||
* query.select("name, status");
|
||||
* query.where("lower(name) like :custname").setParameter("custname", "rob%");
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* // Only fetch the customer id, name and status.
|
||||
* // This is described as a "Partial Object"
|
||||
* .select("name, status")
|
||||
* .where.ilike("name", "rob%")
|
||||
* .findList();
|
||||
*
|
||||
* List<Customer> customerList = query.findList();
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* @param fetchProperties
|
||||
* the properties to fetch for this bean (* = all properties).
|
||||
@@ -383,31 +391,35 @@ public interface Query<T> extends Serializable {
|
||||
* "Partial Object" - a bean that only has some of its properties populated.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* // query orders...
|
||||
* Query<Order> query = Ebean.createQuery(Order.class);
|
||||
* List<Order> orders =
|
||||
* ebeanserver.find(Order.class)
|
||||
* // fetch the customer...
|
||||
* // ... getting the customers name and phone number
|
||||
* .fetch("customer", "name, phoneNumber")
|
||||
*
|
||||
* // fetch the customer...
|
||||
* // ... getting the customer's name and phone number
|
||||
* query.fetch("customer", "name, phNumber");
|
||||
*
|
||||
* // ... also fetch the customers billing address (* = all properties)
|
||||
* query.fetch("customer.billingAddress", "*");
|
||||
* </pre>
|
||||
* // ... also fetch the customers billing address (* = all properties)
|
||||
* .fetch("customer.billingAddress", "*")
|
||||
* .findList();
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* If columns is null or "*" then all columns/properties for that path are
|
||||
* fetched.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch customers (their id, name and status)
|
||||
* Query<Customer> query = Ebean.createQuery(Customer.class);
|
||||
*
|
||||
* // only fetch some of the properties of the customers
|
||||
* query.select("name, status");
|
||||
* List<Customer> list = query.findList();
|
||||
* </pre>
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* .select("name, status")
|
||||
* .fetch("contacts", "firstName,lastName,email")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param path
|
||||
* the path of an associated (1-1,1-M,M-1,M-M) bean.
|
||||
@@ -420,6 +432,17 @@ public interface Query<T> extends Serializable {
|
||||
/**
|
||||
* Additionally specify a FetchConfig to use a separate query or lazy loading
|
||||
* to load this path.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* .select("name, status")
|
||||
* .fetch("contacts", "firstName,lastName,email", new FetchConfig().lazy(10))
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public Query<T> fetch(String assocProperty, String fetchProperties, FetchConfig fetchConfig);
|
||||
|
||||
@@ -428,7 +451,17 @@ public interface Query<T> extends Serializable {
|
||||
* <p>
|
||||
* The same as {@link #fetch(String, String)} with the fetchProperties as "*".
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* // eager fetch the contacts
|
||||
* .fetch("contacts")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param path
|
||||
* the property of an associated (1-1,1-M,M-1,M-M) bean.
|
||||
*/
|
||||
@@ -437,6 +470,18 @@ public interface Query<T> extends Serializable {
|
||||
/**
|
||||
* Additionally specify a JoinConfig to specify a "query join" and or define
|
||||
* the lazy loading query.
|
||||
*
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* // lazy fetch contacts with a batch size of 100
|
||||
* .fetch("contacts", new FetchConfig().lazy(100))
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public Query<T> fetch(String path, FetchConfig joinConfig);
|
||||
|
||||
@@ -466,6 +511,10 @@ public interface Query<T> extends Serializable {
|
||||
* (typically in a finally block).
|
||||
* </p>
|
||||
* <p>
|
||||
* findEach() and findEachWhile() are preferred to findIterate() as they ensure
|
||||
* the jdbc statement and resultSet are closed at the end of the iteration.
|
||||
* </p>
|
||||
* <p>
|
||||
* This query will execute against the EbeanServer that was used to create it.
|
||||
* </p>
|
||||
*/
|
||||
@@ -493,6 +542,12 @@ public interface Query<T> extends Serializable {
|
||||
* (unlike #findList #findSet etc)
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that internally Ebean can inform the JDBC driver that it is expecting larger
|
||||
* resultSet and specifically for MySQL this hint is required to stop it's JDBC driver
|
||||
* from buffering the entire resultSet. As such, for smaller resultSets findList() is
|
||||
* generally preferable.
|
||||
* </p>
|
||||
* <p>
|
||||
* Compared with #findEachWhile this will always process all the beans where as
|
||||
* #findEachWhile provides a way to stop processing the query result early before
|
||||
* all the beans have been read.
|
||||
@@ -503,19 +558,18 @@ public interface Query<T> extends Serializable {
|
||||
* with Java8 closures.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* Query<Customer> query = server.find(Customer.class)
|
||||
* .where().gt("id", 0)
|
||||
* .orderBy("id")
|
||||
* .setMaxRows(2);
|
||||
* ebeanServer.find(Customer.class)
|
||||
* .where().eq("status", Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .findEach((Customer customer) -> {
|
||||
*
|
||||
* query.findVisit((Customer customer) -> {
|
||||
* // do something with customer
|
||||
* System.out.println("-- visit " + customer);
|
||||
* });
|
||||
*
|
||||
* // do something with customer
|
||||
* System.out.println("-- visit " + customer);
|
||||
* });
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* @param consumer
|
||||
* the consumer used to process the queried beans.
|
||||
@@ -532,23 +586,23 @@ public interface Query<T> extends Serializable {
|
||||
* </p>
|
||||
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* Query<Customer> query = server.find(Customer.class)
|
||||
* .fetch("contacts", new FetchConfig().query(2))
|
||||
* .where().gt("id", 0)
|
||||
* .orderBy("id")
|
||||
* .setMaxRows(2);
|
||||
* ebeanServer.find(Customer.class)
|
||||
* .fetch("contacts", new FetchConfig().query(2))
|
||||
* .where().eq("status", Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .setMaxRows(2000)
|
||||
* .findEachWhile((Customer customer) -> {
|
||||
*
|
||||
* query.findEachWhile((Customer customer) -> {
|
||||
* // do something with customer
|
||||
* System.out.println("-- visit " + customer);
|
||||
*
|
||||
* // do something with customer
|
||||
* System.out.println("-- visit " + customer);
|
||||
* // return true to continue processing or false to stop
|
||||
* return (customer.getId() < 40);
|
||||
* });
|
||||
*
|
||||
* // return true to continue processing or false to stop
|
||||
* return (customer.getId() < 40);
|
||||
* });
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* @param consumer
|
||||
* the consumer used to process the queried beans.
|
||||
@@ -560,7 +614,16 @@ public interface Query<T> extends Serializable {
|
||||
* <p>
|
||||
* This query will execute against the EbeanServer that was used to create it.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* .where().ilike("name", "rob%")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see EbeanServer#findList(Query, Transaction)
|
||||
*/
|
||||
public List<T> findList();
|
||||
@@ -570,7 +633,16 @@ public interface Query<T> extends Serializable {
|
||||
* <p>
|
||||
* This query will execute against the EbeanServer that was used to create it.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* Set<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* .where().ilike("name", "rob%")
|
||||
* .findSet();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see EbeanServer#findSet(Query, Transaction)
|
||||
*/
|
||||
public Set<T> findSet();
|
||||
@@ -585,11 +657,14 @@ public interface Query<T> extends Serializable {
|
||||
* on the map. If one is not specified then the id property is used.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* Query<Product> query = Ebean.createQuery(Product.class);
|
||||
* query.setMapKey("sku");
|
||||
* Map<?, Product> map = query.findMap();
|
||||
* </pre>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Map<?, Product> map =
|
||||
* ebeanServer.find(Product.class)
|
||||
* .setMapKey("sku")
|
||||
* .findMap();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see EbeanServer#findMap(Query, Transaction)
|
||||
*/
|
||||
@@ -612,32 +687,34 @@ public interface Query<T> extends Serializable {
|
||||
* return 0 or 1 results.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* // assuming the sku of products is unique...
|
||||
* Product product =
|
||||
* Ebean.find(Product.class)
|
||||
* .where("sku = ?")
|
||||
* .set(1, "aa113")
|
||||
* ebeanServer.find(Product.class)
|
||||
* .where().eq("sku", "aa113")
|
||||
* .findUnique();
|
||||
* ...
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* It is also useful with finding objects by their id when you want to specify
|
||||
* further join information.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Fetch order 1 and additionally fetch join its order details...
|
||||
* Order order =
|
||||
* Ebean.find(Order.class)
|
||||
* ebeanServer.find(Order.class)
|
||||
* .setId(1)
|
||||
* .fetch("details")
|
||||
* .fetch("details")
|
||||
* .findUnique();
|
||||
*
|
||||
* List<OrderDetail> details = order.getDetails();
|
||||
*
|
||||
* // the order details were eagerly loaded
|
||||
* List<OrderDetail> details = order.getDetails();
|
||||
* ...
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*/
|
||||
public T findUnique();
|
||||
|
||||
@@ -676,21 +753,14 @@ public interface Query<T> extends Serializable {
|
||||
/**
|
||||
* Execute find list query in a background thread.
|
||||
* <p>
|
||||
* Deprecated with a view to simplifying internals.
|
||||
* This query will execute in it's own PersistenceContext and using its own transaction.
|
||||
* What that means is that it will not share any bean instances with other queries.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a Future object for the list result of the query
|
||||
* @deprecated
|
||||
*/
|
||||
public FutureList<T> findFutureList();
|
||||
|
||||
/**
|
||||
* This is being deprecated in favour of the simplier {@link Query#findPagedList(int, int)}.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public PagingList<T> findPagingList(int pageSize);
|
||||
|
||||
/**
|
||||
* Return a PagedList for this query.
|
||||
* <p>
|
||||
@@ -703,7 +773,32 @@ public interface Query<T> extends Serializable {
|
||||
* the query. This translates into SQL that uses limit offset, rownum or row_number function to
|
||||
* limit the result set.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* <h4>Example: typical use including total row count</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // We want to find the first 100 new orders
|
||||
* // ... 0 means first page
|
||||
* // ... page size is 100
|
||||
*
|
||||
* PagedList<Order> pagedList
|
||||
* = ebeanServer.find(Order.class)
|
||||
* .where().eq("status", Order.Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .findPagedList(0, 100);
|
||||
*
|
||||
* // Optional: initiate the loading of the total
|
||||
* // row count in a background thread
|
||||
* pagedList.loadRowCount();
|
||||
*
|
||||
* // fetch and return the list in the foreground thread
|
||||
* List<Order> orders = pagedList.getList();
|
||||
*
|
||||
* // get the total row count (from the future)
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param pageIndex
|
||||
* The zero based index of the page.
|
||||
* @param pageSize
|
||||
@@ -713,19 +808,20 @@ public interface Query<T> extends Serializable {
|
||||
public PagedList<T> findPagedList(int pageIndex, int pageSize);
|
||||
|
||||
/**
|
||||
* Set a named bind parameter. Named parameters have a colon to prefix the
|
||||
* name.
|
||||
* Set a named bind parameter. Named parameters have a colon to prefix the name.
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* // a query with a named parameter
|
||||
* String oql = "find order where status = :orderStatus";
|
||||
* String oql = "find order where status = :orderStatus";
|
||||
*
|
||||
* Query<Order> query = Ebean.createQuery(Order.class, oql);
|
||||
* Query<Order> query = ebeanServer.find(Order.class, oql);
|
||||
*
|
||||
* // bind the named parameter
|
||||
* query.bind("orderStatus", OrderStatus.NEW);
|
||||
* List<Order> list = query.findList();
|
||||
* </pre>
|
||||
* query.bind("orderStatus", OrderStatus.NEW);
|
||||
* List<Order> list = query.findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param name
|
||||
* the parameter name
|
||||
@@ -739,17 +835,19 @@ public interface Query<T> extends Serializable {
|
||||
* position starts at 1 to be consistent with JDBC PreparedStatement. You need
|
||||
* to set a parameter value for each ? you have in the query.
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* // a query with a positioned parameter
|
||||
* String oql = "where status = ? order by id desc";
|
||||
* String oql = "where status = ? order by id desc";
|
||||
*
|
||||
* Query<Order> query = Ebean.createQuery(Order.class, oql);
|
||||
* Query<Order> query = ebeanServer.createQuery(Order.class, oql);
|
||||
*
|
||||
* // bind the parameter
|
||||
* query.setParameter(1, OrderStatus.NEW);
|
||||
*
|
||||
* List<Order> list = query.findList();
|
||||
* </pre>
|
||||
* List<Order> list = query.findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param position
|
||||
* the parameter bind position starting from 1 (not 0)
|
||||
@@ -765,12 +863,18 @@ public interface Query<T> extends Serializable {
|
||||
* fetch joins.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* Query<Order> query = Ebean.createQuery(Order.class);
|
||||
* Order order = query.setId(1).join("details").findUnique();
|
||||
* List<OrderDetail> details = order.getDetails();
|
||||
* ...
|
||||
* </pre>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Order order =
|
||||
* ebeanServer.find(Order.class)
|
||||
* .setId(1)
|
||||
* .fetch("details")
|
||||
* .findUnique();
|
||||
*
|
||||
* // the order details were eagerly fetched
|
||||
* List<OrderDetail> details = order.getDetails();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public Query<T> setId(Object id);
|
||||
|
||||
@@ -781,15 +885,17 @@ public interface Query<T> extends Serializable {
|
||||
* {@link #setParameter(String, Object)}.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* Query<Order> query = Ebean.createQuery(Order.class, "top");
|
||||
* <pre>{@code
|
||||
*
|
||||
* Query<Order> query = ebeanServer.createQuery(Order.class, "top");
|
||||
* ...
|
||||
* if (...) {
|
||||
* query.where("status = :status and lower(customer.name) like :custName");
|
||||
* query.setParameter("status", Order.NEW);
|
||||
* query.setParameter("custName", "rob%");
|
||||
* query.where("status = :status and lower(customer.name) like :custName");
|
||||
* query.setParameter("status", Order.NEW);
|
||||
* query.setParameter("custName", "rob%");
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Internally the addToWhereClause string is processed by removing named
|
||||
@@ -809,13 +915,15 @@ public interface Query<T> extends Serializable {
|
||||
/**
|
||||
* Add a single Expression to the where clause returning the query.
|
||||
*
|
||||
* <pre class="code">
|
||||
* List<Order> newOrders =
|
||||
* Ebean.find(Order.class)
|
||||
* .where().eq("status", Order.NEW)
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Order> newOrders =
|
||||
* ebeanServer.find(Order.class)
|
||||
* .where().eq("status", Order.NEW)
|
||||
* .findList();
|
||||
* ...
|
||||
* </pre>
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public Query<T> where(Expression expression);
|
||||
|
||||
@@ -824,15 +932,16 @@ public interface Query<T> extends Serializable {
|
||||
* ExpressionList. You can use this for adding multiple expressions to the
|
||||
* where clause.
|
||||
*
|
||||
* <pre class="code">
|
||||
* Query<Order> query = Ebean.createQuery(Order.class, "top");
|
||||
* ...
|
||||
* if (...) {
|
||||
* query.where()
|
||||
* .eq("status", Order.NEW)
|
||||
* .ilike("customer.name","rob%");
|
||||
* }
|
||||
* </pre>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Order> orders =
|
||||
* ebeanServer.find(Order.class)
|
||||
* .where()
|
||||
* .eq("status", Order.NEW)
|
||||
* .ilike("customer.name","rob%")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see Expr
|
||||
* @return The ExpressionList for adding expressions to.
|
||||
@@ -850,17 +959,18 @@ public interface Query<T> extends Serializable {
|
||||
* week. In this case you can use filterMany() to filter the orders.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> list = Ebean
|
||||
* .find(Customer.class)
|
||||
* // .fetch("orders", new FetchConfig().lazy())
|
||||
* // .fetch("orders", new FetchConfig().query())
|
||||
* .fetch("orders").where().ilike("name", "rob%").filterMany("orders")
|
||||
* .eq("status", Order.Status.NEW).gt(
|
||||
* "orderDate", lastWeek).findList();
|
||||
* List<Customer> list =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* // .fetch("orders", new FetchConfig().lazy())
|
||||
* // .fetch("orders", new FetchConfig().query())
|
||||
* .fetch("orders")
|
||||
* .where().ilike("name", "rob%")
|
||||
* .filterMany("orders").eq("status", Order.Status.NEW).gt("orderDate", lastWeek)
|
||||
* .findList();
|
||||
*
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Please note you have to be careful that you add expressions to the correct
|
||||
@@ -898,14 +1008,14 @@ public interface Query<T> extends Serializable {
|
||||
* {@link #setParameter(String, Object)}.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* Query<ReportOrder> query = Ebean.createQuery(ReportOrder.class);
|
||||
* ...
|
||||
* if (...) {
|
||||
* query.having("score > :min");
|
||||
* query.setParameter("min", 1);
|
||||
* }
|
||||
* </pre>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<ReportOrder> query =
|
||||
* ebeanServer.find(ReportOrder.class)
|
||||
* .having("score > :min").setParameter("min", 1)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param addToHavingClause
|
||||
* the clause to append to the having clause which typically contains
|
||||
@@ -1037,17 +1147,17 @@ public interface Query<T> extends Serializable {
|
||||
* If no property is set then the id property is used.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Assuming sku is unique for products...
|
||||
*
|
||||
* Query<Product> query = Ebean.createQuery(Product.class);
|
||||
*
|
||||
* // use sku for keys...
|
||||
* query.setMapKey("sku");
|
||||
*
|
||||
* Map<?,Product> productMap = query.findMap();
|
||||
* ...
|
||||
* </pre>
|
||||
* Map<?,Product> productMap =
|
||||
* ebeanServer.find(Product.class)
|
||||
* // use sku for keys...
|
||||
* .setMapKey("sku")
|
||||
* .findMap();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param mapKey
|
||||
* the property to use as keys for a map.
|
||||
|
||||
@@ -12,19 +12,19 @@ package com.avaje.ebean;
|
||||
* QueryResultVisitor useful for processing large queries.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* Query<Customer> query = server.find(Customer.class)
|
||||
* .where().gt("id", 0)
|
||||
* .orderBy("id")
|
||||
* .setMaxRows(2);
|
||||
* Query<Customer> query = server.find(Customer.class)
|
||||
* .where().eq("status", Status.NEW)
|
||||
* .order().asc("id");
|
||||
*
|
||||
* query.findVisit((Customer customer) -> {
|
||||
* query.findEach((Customer customer) -> {
|
||||
*
|
||||
* // do something with customer
|
||||
* System.out.println("-- visit " + customer);
|
||||
* System.out.println("-- visit " + customer);
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T>
|
||||
* the type of entity bean being queried.
|
||||
|
||||
@@ -2,12 +2,7 @@ package com.avaje.ebean;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import com.avaje.ebean.util.CamelCaseHelper;
|
||||
|
||||
@@ -22,25 +17,25 @@ import com.avaje.ebean.util.CamelCaseHelper;
|
||||
* <b>Unparsed RawSql:</b>
|
||||
* </p>
|
||||
* <p>
|
||||
* When RawSql is created via RawSqlBuilder.unparsed(sql) then Ebean can not
|
||||
* 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 RawSqlBuilder.parse(sql) then Ebean will parse the
|
||||
* 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
|
||||
* extra having expressions or replace the order by clause. If you want to
|
||||
* explicitly tell Ebean where these insertion points are you can place special
|
||||
* strings into your SQL (${where} or ${andWhere} and ${having} or
|
||||
* ${andHaving}).
|
||||
* 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 ${andWhere} in the location
|
||||
* 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 ${where} in instead. Similarly you can put in ${having} or
|
||||
* ${andHaving} where you want Ebean put add extra having expressions.
|
||||
* 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>
|
||||
@@ -56,23 +51,19 @@ import com.avaje.ebean.util.CamelCaseHelper;
|
||||
* to hold the values for the aggregate functions (sum etc) and a @OneToOne
|
||||
* to Order.
|
||||
* </p>
|
||||
* <p>
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Example OrderAggregate</b>
|
||||
* </p>
|
||||
*
|
||||
* <h3>Example OrderAggregate</h3>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* ...
|
||||
* // @Sql indicates to that this bean
|
||||
* // @Sql indicates to that this bean
|
||||
* // is based on RawSql rather than a table
|
||||
*
|
||||
* @Entity
|
||||
* @Sql
|
||||
* @Entity
|
||||
* @Sql
|
||||
* public class OrderAggregate {
|
||||
*
|
||||
* @OneToOne
|
||||
* @OneToOne
|
||||
* Order order;
|
||||
*
|
||||
* Double totalAmount;
|
||||
@@ -81,35 +72,38 @@ import com.avaje.ebean.util.CamelCaseHelper;
|
||||
*
|
||||
* // getters and setters
|
||||
* ...
|
||||
* </pre>
|
||||
* <p>
|
||||
* <b>Example 1:</b>
|
||||
* </p>
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Example 1:</h3>
|
||||
*
|
||||
* <pre class="code">
|
||||
* 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 ";
|
||||
* <pre>{@code
|
||||
*
|
||||
* 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)
|
||||
* RawSql rawSql = RawSqlBuilder.parse(sql)
|
||||
* // map the sql result columns to bean properties
|
||||
* .columnMapping("order_id", "order.id").columnMapping("o.status", "order.status")
|
||||
* .columnMapping("c.id", "order.customer.id")
|
||||
* .columnMapping("c.name", "order.customer.name")
|
||||
* .columnMapping("order_id", "order.id")
|
||||
* .columnMapping("o.status", "order.status")
|
||||
* .columnMapping("c.id", "order.customer.id")
|
||||
* .columnMapping("c.name", "order.customer.name")
|
||||
* // we don't need to map this one due to the sql column alias
|
||||
* // .columnMapping("sum(d.order_qty*d.unit_price)", "totalAmount")
|
||||
* // .columnMapping("sum(d.order_qty*d.unit_price)", "totalAmount")
|
||||
* .create();
|
||||
*
|
||||
* Query<OrderAggregate> query = Ebean.find(OrderAggregate.class);
|
||||
* query.setRawSql(rawSql).where().gt("order.id", 0).having().gt("totalAmount", 20);
|
||||
* List<OrderAggregate> list = Ebean.find(OrderAggregate.class)
|
||||
* .setRawSql(rawSql)
|
||||
* .where().gt("order.id", 0)
|
||||
* .having().gt("totalAmount", 20)
|
||||
* .findList();
|
||||
*
|
||||
* List<OrderAggregate> list = query.findList();
|
||||
* </pre>
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* <b>Example 2:</b>
|
||||
* </p>
|
||||
* <h3>Example 2:</h3>
|
||||
*
|
||||
* <p>
|
||||
* The following example uses a FetchConfig().query() so that after the initial
|
||||
@@ -117,21 +111,59 @@ import com.avaje.ebean.util.CamelCaseHelper;
|
||||
* associated order status, orderDate along with the customer name.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* String sql = " select order_id, 'ignoreMe', sum(d.order_qty*d.unit_price) as totalAmount "
|
||||
* + " from o_order_detail d"
|
||||
* + " group by order_id ";
|
||||
* <pre>{@code
|
||||
*
|
||||
* 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).columnMapping("order_id", "order.id")
|
||||
* .columnMappingIgnore("'ignoreMe'").create();
|
||||
* RawSql rawSql = RawSqlBuilder.parse(sql)
|
||||
* .columnMapping("order_id", "order.id")
|
||||
* .columnMappingIgnore("'ignoreMe'")
|
||||
* .create();
|
||||
*
|
||||
* Query<OrderAggregate> query = Ebean.find(OrderAggregate.class);
|
||||
* query.setRawSql(rawSql).fetch("order", "status,orderDate", new FetchConfig().query())
|
||||
* .fetch("order.customer", "name").where()
|
||||
* .gt("order.id", 0).having().gt("totalAmount", 20).order().desc("totalAmount").setMaxRows(10);
|
||||
*
|
||||
* </pre>
|
||||
* List<OrderAggregate> orders = Ebean.find(OrderAggregate.class)
|
||||
* .setRawSql(rawSql)
|
||||
* .fetch("order", "status,orderDate", new FetchConfig().query())
|
||||
* .fetch("order.customer", "name")
|
||||
* .where().gt("order.id", 0)
|
||||
* .having().gt("totalAmount", 20)
|
||||
* .order().desc("totalAmount")
|
||||
* .setMaxRows(10)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*
|
||||
* <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, "+
|
||||
* " 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, d.id asc";
|
||||
*
|
||||
* RawSql rawSql = RawSqlBuilder.parse(rs)
|
||||
* .tableAliasMapping("c", "customer")
|
||||
* .tableAliasMapping("d", "details")
|
||||
* .tableAliasMapping("p", "details.product")
|
||||
* .create();
|
||||
*
|
||||
* List<Order> ordersFromRaw = Ebean.find(Order.class)
|
||||
* .setRawSql(rawSql)
|
||||
* .setParameter("maxOrderId", 2)
|
||||
* .setParameter("productId", 1)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Note that lazy loading also works with object graphs built with RawSql.
|
||||
* </p>
|
||||
@@ -222,6 +254,8 @@ public final class RawSql implements Serializable {
|
||||
|
||||
private final boolean andHavingExpr;
|
||||
|
||||
private final String orderByPrefix;
|
||||
|
||||
private final String orderBy;
|
||||
|
||||
private final boolean distinct;
|
||||
@@ -240,6 +274,7 @@ public final class RawSql implements Serializable {
|
||||
this.preWhere = null;
|
||||
this.andHavingExpr = false;
|
||||
this.andWhereExpr = false;
|
||||
this.orderByPrefix = null;
|
||||
this.orderBy = null;
|
||||
this.distinct = false;
|
||||
}
|
||||
@@ -248,8 +283,7 @@ public final class RawSql implements Serializable {
|
||||
* Construct for parsed SQL.
|
||||
*/
|
||||
protected Sql(int queryHashCode, String preFrom, String preWhere, boolean andWhereExpr,
|
||||
String preHaving, boolean andHavingExpr,
|
||||
String orderBy, boolean distinct) {
|
||||
String preHaving, boolean andHavingExpr, String orderByPrefix, String orderBy, boolean distinct) {
|
||||
|
||||
this.queryHashCode = queryHashCode;
|
||||
this.parsed = true;
|
||||
@@ -259,6 +293,7 @@ public final class RawSql implements Serializable {
|
||||
this.preWhere = preWhere;
|
||||
this.andHavingExpr = andHavingExpr;
|
||||
this.andWhereExpr = andWhereExpr;
|
||||
this.orderByPrefix = orderByPrefix;
|
||||
this.orderBy = orderBy;
|
||||
this.distinct = distinct;
|
||||
}
|
||||
@@ -337,6 +372,14 @@ public final class RawSql implements Serializable {
|
||||
return andHavingExpr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the 'order by' keywords.
|
||||
* This can contain additional keywords, for example 'order siblings by' as Oracle syntax.
|
||||
*/
|
||||
public String getOrderByPrefix() {
|
||||
return (orderByPrefix == null) ? "order by" : orderByPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL ORDER BY clause.
|
||||
*/
|
||||
@@ -429,9 +472,8 @@ public final class RawSql implements Serializable {
|
||||
for (Column c : dbColumnMap.values()) {
|
||||
pMap.put(c.getPropertyName(), c.getDbColumn());
|
||||
pcMap.put(c.getPropertyName(), c);
|
||||
|
||||
hc = 31 * hc + c.getPropertyName() == null ? 0 : c.getPropertyName().hashCode();
|
||||
hc = 31 * hc + c.getDbColumn() == null ? 0 : c.getDbColumn().hashCode();
|
||||
hc = 31 * hc + ((c.getPropertyName() == null) ? 0 : c.getPropertyName().hashCode());
|
||||
hc = 31 * hc + ((c.getDbColumn() == null) ? 0 : c.getDbColumn().hashCode());
|
||||
}
|
||||
this.propertyMap = Collections.unmodifiableMap(pMap);
|
||||
this.propertyColumnMap = Collections.unmodifiableMap(pcMap);
|
||||
@@ -531,6 +573,22 @@ public final class RawSql implements Serializable {
|
||||
return dbColumnMap.values().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify any column mappings with the given table alias to have the path prefix.
|
||||
* <p>
|
||||
* For example modify all mappings with table alias "c" to have the path prefix "customer".
|
||||
* </p>
|
||||
*/
|
||||
public void tableAliasMapping(String tableAlias, String path) {
|
||||
|
||||
String startMatch = tableAlias+".";
|
||||
for (Map.Entry<String, Column> entry : dbColumnMap.entrySet()) {
|
||||
if (entry.getKey().startsWith(startMatch)) {
|
||||
entry.getValue().tableAliasMapping(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Column of the RawSql that is mapped to a bean property (or ignored).
|
||||
*/
|
||||
@@ -612,10 +670,23 @@ public final class RawSql implements Serializable {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the property name mapped to this db column.
|
||||
*/
|
||||
private void setPropertyName(String propertyName) {
|
||||
this.propertyName = propertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend the path to the property name.
|
||||
* <p/>
|
||||
* For example if path is "customer" then "name" becomes "customer.name".
|
||||
*/
|
||||
public void tableAliasMapping(String path) {
|
||||
if (path != null) {
|
||||
propertyName = path + "." + propertyName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,13 +70,6 @@ public class RawSqlBuilder {
|
||||
ColumnMapping mapping = DRawSqlColumnsParser.parse(select);
|
||||
return new RawSqlBuilder(sql2, mapping);
|
||||
}
|
||||
|
||||
|
||||
private RawSqlBuilder(ResultSet resultSet, ColumnMapping columnMapping) {
|
||||
this.resultSet = resultSet;
|
||||
this.columnMapping = columnMapping;
|
||||
this.sql = null;
|
||||
}
|
||||
|
||||
private RawSqlBuilder(Sql sql, ColumnMapping columnMapping) {
|
||||
this.sql = sql;
|
||||
@@ -108,6 +101,17 @@ public class RawSqlBuilder {
|
||||
return columnMapping(dbColumn, IGNORE_COLUMN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify any column mappings with the given table alias to have the path prefix.
|
||||
* <p>
|
||||
* For example modify all mappings with table alias "c" to have the path prefix "customer".
|
||||
* </p>
|
||||
*/
|
||||
public RawSqlBuilder tableAliasMapping(String tableAlias, String path) {
|
||||
columnMapping.tableAliasMapping(tableAlias, path);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the immutable RawSql object. Do this after all the column mapping
|
||||
* has been defined.
|
||||
@@ -122,6 +126,7 @@ public class RawSqlBuilder {
|
||||
protected Sql getSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.config.PersistBatch;
|
||||
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.RollbackException;
|
||||
@@ -128,47 +130,93 @@ public interface Transaction extends Closeable {
|
||||
* Example: batch processing executing every 3 rows
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* String data = "This is a simple test of the batch processing"
|
||||
* + " mode and the transaction execute batch method";
|
||||
* <pre>{@code
|
||||
*
|
||||
* String data = "This is a simple test of the batch processing"
|
||||
* + " mode and the transaction execute batch method";
|
||||
*
|
||||
* String[] da = data.split(" ");
|
||||
* String[] da = data.split(" ");
|
||||
*
|
||||
* String sql = "{call sp_t3(?,?)}";
|
||||
* String sql = "{call sp_t3(?,?)}";
|
||||
*
|
||||
* CallableSql cs = new CallableSql(sql);
|
||||
* cs.registerOut(2, Types.INTEGER);
|
||||
*
|
||||
* // (optional) inform eBean this stored procedure
|
||||
* // inserts into a table called sp_test
|
||||
* cs.addModification("sp_test", true, false, false);
|
||||
* cs.addModification("sp_test", true, false, false);
|
||||
*
|
||||
* Transaction t = Ebean.beginTransaction();
|
||||
* t.setBatchMode(true);
|
||||
* t.setBatchSize(3);
|
||||
* Transaction txn = ebeanServer.beginTransaction();
|
||||
* txn.setBatchMode(true);
|
||||
* txn.setBatchSize(3);
|
||||
* try {
|
||||
* for (int i = 0; i < da.length;) {
|
||||
*
|
||||
* for (int i = 0; i < da.length;) {
|
||||
* cs.setParameter(1, da[i]);
|
||||
* Ebean.execute(cs);
|
||||
* ebeanServer.execute(cs);
|
||||
* }
|
||||
*
|
||||
* // NB: commit implicitly flushes
|
||||
* Ebean.commitTransaction();
|
||||
* txn.commit();
|
||||
*
|
||||
* } finally {
|
||||
* Ebean.endTransaction();
|
||||
* txn.end();
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*/
|
||||
public void setBatchMode(boolean useBatch);
|
||||
|
||||
/**
|
||||
* The JDBC batch mode to use for this transaction.
|
||||
* <p>
|
||||
* If this is NONE then JDBC batch can still be used for each request - save(), insert(), update() or delete()
|
||||
* and this would be useful if the request cascades to detail beans.
|
||||
* </p>
|
||||
*
|
||||
* @param persistBatchMode the batch mode to use for this transaction
|
||||
*
|
||||
* @see com.avaje.ebean.config.ServerConfig#setPersistBatch(com.avaje.ebean.config.PersistBatch)
|
||||
*/
|
||||
public void setBatch(PersistBatch persistBatchMode);
|
||||
|
||||
/**
|
||||
* Return the batch mode at the transaction level.
|
||||
*/
|
||||
public PersistBatch getBatch();
|
||||
|
||||
/**
|
||||
* Set the JDBC batch mode to use for a save() or delete() request.
|
||||
* <p>
|
||||
* This only takes effect when batch mode on the transaction has not already meant that
|
||||
* JDBC batch mode is being used.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is useful when the single save() or delete() cascades. For example, inserting a 'master' cascades
|
||||
* and inserts a collection of 'detail' beans. The detail beans can be inserted using JDBC batch.
|
||||
* </p>
|
||||
*
|
||||
* @param batchOnCascadeMode the batch mode to use per save(), insert(), update() or delete()
|
||||
*
|
||||
* @see com.avaje.ebean.config.ServerConfig#setPersistBatchOnCascade(com.avaje.ebean.config.PersistBatch)
|
||||
*/
|
||||
public void setBatchOnCascade(PersistBatch batchOnCascadeMode);
|
||||
|
||||
/**
|
||||
* Return the batch mode at the request level (for each save(), insert(), update() or delete()).
|
||||
*/
|
||||
public PersistBatch getBatchOnCascade();
|
||||
|
||||
/**
|
||||
* Specify the number of statements before a batch is flushed automatically.
|
||||
*/
|
||||
public void setBatchSize(int batchSize);
|
||||
|
||||
/**
|
||||
* Return the current batch size.
|
||||
*/
|
||||
public int getBatchSize();
|
||||
|
||||
/**
|
||||
* Specify if you want batched inserts to use getGeneratedKeys.
|
||||
* <p>
|
||||
@@ -228,20 +276,11 @@ public interface Transaction extends Closeable {
|
||||
* <li>the batch size is reached</li>
|
||||
* <li>A query is executed on the same transaction</li>
|
||||
* <li>UpdateSql or CallableSql are mixed with bean save and delete</li>
|
||||
* <li>Transaction commit occurs</li>
|
||||
* </ul>
|
||||
*/
|
||||
public void flushBatch() throws PersistenceException, OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Deprecated in favour of {@link #flushBatch()}.
|
||||
* <p>
|
||||
* Exactly the same as flushBatch. Deprecated as a name change.
|
||||
* </p>
|
||||
*
|
||||
* @deprecated Please use flushBatch
|
||||
*/
|
||||
public void batchFlush() throws PersistenceException, OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Return the underlying Connection object.
|
||||
* <p>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.config.PersistBatch;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
@@ -28,12 +30,39 @@ public final class TxScope {
|
||||
|
||||
TxIsolation isolation;
|
||||
|
||||
PersistBatch batch;
|
||||
|
||||
PersistBatch batchOnCascade;
|
||||
|
||||
int batchSize;
|
||||
|
||||
boolean readOnly;
|
||||
|
||||
ArrayList<Class<? extends Throwable>> rollbackFor;
|
||||
|
||||
ArrayList<Class<? extends Throwable>> noRollbackFor;
|
||||
|
||||
/**
|
||||
* Return true if PersistBatch has been set.
|
||||
*/
|
||||
public boolean isBatchSet() {
|
||||
return batch != null && batch != PersistBatch.INHERIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if batch on cascade has been set.
|
||||
*/
|
||||
public boolean isBatchOnCascadeSet() {
|
||||
return batchOnCascade != null && batchOnCascade != PersistBatch.INHERIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if batch size has been set.
|
||||
*/
|
||||
public boolean isBatchSizeSet() {
|
||||
return batchSize > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a TxScope with REQUIRES.
|
||||
*/
|
||||
@@ -114,6 +143,51 @@ public final class TxScope {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch mode.
|
||||
*/
|
||||
public PersistBatch getBatch() {
|
||||
return batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the batch mode to use.
|
||||
*/
|
||||
public TxScope setBatch(PersistBatch batch) {
|
||||
this.batch = batch;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch on cascade mode.
|
||||
*/
|
||||
public PersistBatch getBatchOnCascade() {
|
||||
return batchOnCascade;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the batch on cascade mode.
|
||||
*/
|
||||
public TxScope setBatchOnCascade(PersistBatch batchOnCascade) {
|
||||
this.batchOnCascade = batchOnCascade;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch size. 0 means use the default value.
|
||||
*/
|
||||
public int getBatchSize() {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the batch size to use.
|
||||
*/
|
||||
public TxScope setBatchSize(int batchSize) {
|
||||
this.batchSize = batchSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the transaction should be treated as read only.
|
||||
*/
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.avaje.ebean.Query;
|
||||
* You may also put use the Transient annotation with the Formula annotation.
|
||||
* The effect of the Transient annotation in this case is that the formula will
|
||||
* <b>NOT</b> be included in queries by default - you have to explicitly include
|
||||
* it via {@link Query#select(String)} or {@link Query#join(String, String)}.
|
||||
* it via {@link Query#select(String)} or {@link Query#fetch(String, String, com.avaje.ebean.FetchConfig)}.
|
||||
* You may want to do this if the Formula is relatively expensive and only want
|
||||
* it included in the query when you explicitly state it.
|
||||
* </p>
|
||||
|
||||
@@ -1,103 +1,128 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.avaje.ebean.TxIsolation;
|
||||
import com.avaje.ebean.TxType;
|
||||
|
||||
/**
|
||||
* Specify transaction scoping for a method.
|
||||
* <p>
|
||||
* <b><i> This is only supported if "Enhancement" is used via javaagent, ANT
|
||||
* task or IDE enhancement plugin etc. </i></b>
|
||||
* </p>
|
||||
* <p>
|
||||
* Note: Currently there are 3 known annotations that perform this role.
|
||||
* <ul>
|
||||
* <li>EJB's javax.ejb.TransactionAttribute</li>
|
||||
* <li>Spring's org.springframework.transaction.annotation.Transactional</li>
|
||||
* <li>and this one, Ebean's own com.avaje.ebean.annotation.Transactional</li>
|
||||
* </ul>
|
||||
* Spring created their one because the EJB annotation does not support features
|
||||
* such as isolation level and specifying rollbackOn, noRollbackOn exceptions.
|
||||
* This one exists for Ebean because I agree that the standard one is
|
||||
* insufficient and don't want to include a dependency on Spring.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default behaviour of EJB (and hence Spring) is to NOT ROLLBACK on checked
|
||||
* exceptions. I find this very counter-intuitive. Ebean will provide a property
|
||||
* to set the default behaviour to rollback on any exception and optionally
|
||||
* change the setting to be consistent with EJB/Spring if people wish to do so.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
*
|
||||
* // a normal class
|
||||
* public class MySimpleUserService {
|
||||
*
|
||||
* // this method is transactional automatically handling
|
||||
* // transaction begin, commit and rollback etc
|
||||
* @Transactional
|
||||
* public void runInTrans() throws IOException {
|
||||
*
|
||||
* // tasks performed within the transaction
|
||||
* ...
|
||||
* // find some objects
|
||||
* Customer cust = Ebean.find(Customer.class, 1);
|
||||
*
|
||||
* Order order = ...;
|
||||
* ...
|
||||
* // save some objects
|
||||
* Ebean.save(customer);
|
||||
* Ebean.save(order);
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Transactional {
|
||||
|
||||
/**
|
||||
* The type of transaction scoping. Defaults to REQUIRED.
|
||||
*/
|
||||
TxType type() default TxType.REQUIRED;
|
||||
|
||||
/**
|
||||
* The transaction isolation level this transaction should have.
|
||||
* <p>
|
||||
* This will only be used if this scope creates the transaction. If the
|
||||
* transaction has already started then this will currently be ignored (you
|
||||
* could argue that it should throw an exception).
|
||||
* </p>
|
||||
*/
|
||||
TxIsolation isolation() default TxIsolation.DEFAULT;
|
||||
|
||||
/**
|
||||
* Set this to true if the transaction should be only contain queries.
|
||||
*/
|
||||
boolean readOnly() default false;
|
||||
|
||||
/**
|
||||
* The name of the server that you want the transaction to be created from.
|
||||
* <p>
|
||||
* If left blank the 'default' server is used.
|
||||
* </p>
|
||||
*/
|
||||
String serverName() default "";
|
||||
|
||||
// int timeout() default 0;
|
||||
|
||||
/**
|
||||
* The throwable's that will explicitly cause a rollback to occur.
|
||||
*/
|
||||
Class<? extends Throwable>[] rollbackFor() default {};
|
||||
|
||||
/**
|
||||
* The throwable's that will explicitly NOT cause a rollback to occur.
|
||||
*/
|
||||
Class<? extends Throwable>[] noRollbackFor() default {};
|
||||
|
||||
};
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.avaje.ebean.TxIsolation;
|
||||
import com.avaje.ebean.TxType;
|
||||
import com.avaje.ebean.config.PersistBatch;
|
||||
|
||||
/**
|
||||
* Specify transaction scoping for a method.
|
||||
* <p>
|
||||
* <b><i> This is only supported if "Enhancement" is used via javaagent, ANT
|
||||
* task or IDE enhancement plugin etc. </i></b>
|
||||
* </p>
|
||||
* <p>
|
||||
* Note: Currently there are 3 known annotations that perform this role.
|
||||
* <ul>
|
||||
* <li>EJB's javax.ejb.TransactionAttribute</li>
|
||||
* <li>Spring's org.springframework.transaction.annotation.Transactional</li>
|
||||
* <li>and this one, Ebean's own com.avaje.ebean.annotation.Transactional</li>
|
||||
* </ul>
|
||||
* Spring created their one because the EJB annotation does not support features
|
||||
* such as isolation level and specifying rollbackOn, noRollbackOn exceptions.
|
||||
* This one exists for Ebean because I agree that the standard one is
|
||||
* insufficient and don't want to include a dependency on Spring.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default behaviour of EJB (and hence Spring) is to NOT ROLLBACK on checked
|
||||
* exceptions. I find this very counter-intuitive. Ebean will provide a property
|
||||
* to set the default behaviour to rollback on any exception and optionally
|
||||
* change the setting to be consistent with EJB/Spring if people wish to do so.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // a normal class
|
||||
* public class MySimpleUserService {
|
||||
*
|
||||
* // this method is transactional automatically handling
|
||||
* // transaction begin, commit and rollback etc
|
||||
* @Transactional
|
||||
* public void runInTrans() throws IOException {
|
||||
*
|
||||
* // tasks performed within the transaction
|
||||
* ...
|
||||
* // find some objects
|
||||
* Customer cust = ebeanServer.find(Customer.class, 42);
|
||||
*
|
||||
* Order order = ...;
|
||||
* ...
|
||||
* // save some objects
|
||||
* ebeanServer.save(customer);
|
||||
* ebeanServer.save(order);
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Transactional {
|
||||
|
||||
/**
|
||||
* The type of transaction scoping. Defaults to REQUIRED.
|
||||
*/
|
||||
TxType type() default TxType.REQUIRED;
|
||||
|
||||
/**
|
||||
* Persist batch mode for the transaction.
|
||||
*/
|
||||
PersistBatch batch() default PersistBatch.INHERIT;
|
||||
|
||||
/**
|
||||
* Persist batch mode for the request if not set on the transaction.
|
||||
* <p>
|
||||
* If batch is set to NONE then batchOnCascade can be set to INSERT or ALL
|
||||
* and then each save(), delete(), insert(), update() request that cascades
|
||||
* to child beans can use JDBC batch.
|
||||
* </p>
|
||||
*/
|
||||
PersistBatch batchOnCascade() default PersistBatch.INHERIT;
|
||||
|
||||
/**
|
||||
* The batch size to use when using JDBC batch mode.
|
||||
* <p>
|
||||
* If unset this defaults to the value set in ServerConfig.
|
||||
* </p>
|
||||
*/
|
||||
int batchSize() default 0;
|
||||
|
||||
/**
|
||||
* The transaction isolation level this transaction should have.
|
||||
* <p>
|
||||
* This will only be used if this scope creates the transaction. If the
|
||||
* transaction has already started then this will currently be ignored (you
|
||||
* could argue that it should throw an exception).
|
||||
* </p>
|
||||
*/
|
||||
TxIsolation isolation() default TxIsolation.DEFAULT;
|
||||
|
||||
/**
|
||||
* Set this to true if the transaction should be only contain queries.
|
||||
*/
|
||||
boolean readOnly() default false;
|
||||
|
||||
/**
|
||||
* The name of the server that you want the transaction to be created from.
|
||||
* <p>
|
||||
* If left blank the 'default' server is used.
|
||||
* </p>
|
||||
*/
|
||||
String serverName() default "";
|
||||
|
||||
// int timeout() default 0;
|
||||
|
||||
/**
|
||||
* The Throwable's that will explicitly cause a rollback to occur.
|
||||
*/
|
||||
Class<? extends Throwable>[] rollbackFor() default {};
|
||||
|
||||
/**
|
||||
* The Throwable's that will explicitly NOT cause a rollback to occur.
|
||||
*/
|
||||
Class<? extends Throwable>[] noRollbackFor() default {};
|
||||
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Provides some base implementation for NamingConventions.
|
||||
*
|
||||
*
|
||||
* @author emcgreal
|
||||
*/
|
||||
public abstract class AbstractNamingConvention implements NamingConvention {
|
||||
@@ -41,7 +41,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
|
||||
/** Used to trim off extra prefix for M2M. */
|
||||
protected int rhsPrefixLength = 3;
|
||||
|
||||
protected boolean useForeignKeyPrefix = true;
|
||||
protected boolean useForeignKeyPrefix;
|
||||
|
||||
/**
|
||||
* Construct with a sequence format and useForeignKeyPrefix setting.
|
||||
@@ -53,12 +53,12 @@ public abstract class AbstractNamingConvention implements NamingConvention {
|
||||
|
||||
/**
|
||||
* Construct with a sequence format.
|
||||
*
|
||||
* @param sequenceFormat
|
||||
* the sequence format
|
||||
*
|
||||
* @param sequenceFormat the sequence format
|
||||
*/
|
||||
public AbstractNamingConvention(String sequenceFormat) {
|
||||
this.sequenceFormat = sequenceFormat;
|
||||
this.useForeignKeyPrefix = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,10 +125,9 @@ public abstract class AbstractNamingConvention implements NamingConvention {
|
||||
* The format should include "{table}". When generating the sequence name
|
||||
* {table} is replaced with the actual table name.
|
||||
* </p>
|
||||
*
|
||||
* @param sequenceFormat
|
||||
* string containing "{table}" which is replaced with the actual
|
||||
* table name to generate the sequence name.
|
||||
*
|
||||
* @param sequenceFormat string containing "{table}" which is replaced with the actual
|
||||
* table name to generate the sequence name.
|
||||
*/
|
||||
public void setSequenceFormat(String sequenceFormat) {
|
||||
this.sequenceFormat = sequenceFormat;
|
||||
@@ -173,7 +172,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
|
||||
* This first checks for the @Table annotation and if not present uses the
|
||||
* naming convention to define the table name.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @see #getTableNameFromAnnotation(Class)
|
||||
* @see #getTableNameByConvention(Class)
|
||||
*/
|
||||
@@ -240,8 +239,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
|
||||
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(t.catalog()), quoteIdentifiers(t.schema()),
|
||||
quoteIdentifiers(t.name()));
|
||||
return new TableName(quoteIdentifiers(t.catalog()), quoteIdentifiers(t.schema()), quoteIdentifiers(t.name()));
|
||||
}
|
||||
|
||||
// No annotation
|
||||
@@ -279,4 +277,16 @@ public abstract class AbstractNamingConvention implements NamingConvention {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings from properties.
|
||||
*/
|
||||
@Override
|
||||
public void loadFromProperties(PropertiesWrapper properties) {
|
||||
|
||||
useForeignKeyPrefix = properties.getBoolean("namingConvention.useForeignKeyPrefix", useForeignKeyPrefix);
|
||||
sequenceFormat = properties.get("namingConvention.sequenceFormat", sequenceFormat);
|
||||
schema = properties.get("namingConvention.schema", schema);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -155,14 +155,6 @@ public class AutofetchConfig {
|
||||
return logDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the log directory substituting any expressions such as
|
||||
* ${catalina.base} etc.
|
||||
*/
|
||||
public String getLogDirectoryWithEval() {
|
||||
return GlobalProperties.evaluateExpressions(logDirectory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the directory to put the autofetch log in.
|
||||
*/
|
||||
@@ -234,22 +226,20 @@ public class AutofetchConfig {
|
||||
/**
|
||||
* Load the settings from the properties file.
|
||||
*/
|
||||
public void loadSettings(GlobalProperties.PropertySource p) {
|
||||
public void loadSettings(PropertiesWrapper p) {
|
||||
|
||||
logDirectory = p.get("autofetch.logDirectory", null);
|
||||
queryTuning = p.getBoolean("autofetch.querytuning", false);
|
||||
queryTuningAddVersion = p.getBoolean("autofetch.queryTuningAddVersion", false);
|
||||
garbageCollectionOnShutdown = p.getBoolean("autofetch.garbageCollectionOnShutdown", false);
|
||||
logDirectory = p.get("autofetch.logDirectory", logDirectory);
|
||||
queryTuning = p.getBoolean("autofetch.querytuning", queryTuning);
|
||||
queryTuningAddVersion = p.getBoolean("autofetch.queryTuningAddVersion", queryTuningAddVersion);
|
||||
garbageCollectionOnShutdown = p.getBoolean("autofetch.garbageCollectionOnShutdown", garbageCollectionOnShutdown);
|
||||
|
||||
profiling = p.getBoolean("autofetch.profiling", false);
|
||||
mode = p.getEnum(AutofetchMode.class, "autofetch.implicitmode", AutofetchMode.DEFAULT_ONIFEMPTY);
|
||||
profiling = p.getBoolean("autofetch.profiling", profiling);
|
||||
mode = p.getEnum(AutofetchMode.class, "autofetch.implicitmode", mode);
|
||||
|
||||
profilingMin = p.getInt("autofetch.profiling.min", 1);
|
||||
profilingBase = p.getInt("autofetch.profiling.base", 10);
|
||||
profilingMin = p.getInt("autofetch.profiling.min", profilingMin);
|
||||
profilingBase = p.getInt("autofetch.profiling.base", profilingBase);
|
||||
|
||||
String rate = p.get("autofetch.profiling.rate", "0.05");
|
||||
profilingRate = Double.parseDouble(rate);
|
||||
|
||||
profileUpdateFrequency = p.getInt("autofetch.profiling.updatefrequency", 60);
|
||||
profilingRate = p.getDouble("autofetch.profiling.rate", profilingRate);
|
||||
profileUpdateFrequency = p.getInt("autofetch.profiling.updatefrequency", profileUpdateFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
import com.avaje.ebean.config.GlobalProperties.PropertySource;
|
||||
|
||||
/**
|
||||
* Helper to read server specific properties from ebean.properties.
|
||||
*/
|
||||
class ConfigPropertyMap implements PropertySource {
|
||||
|
||||
private final String serverName;
|
||||
|
||||
public ConfigPropertyMap(String serverName) {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
|
||||
public String get(String key, String defaultValue) {
|
||||
String namedKey = "ebean." + serverName + "." + key;
|
||||
String inheritKey = "ebean." + key;
|
||||
String value = GlobalProperties.get(namedKey, null);
|
||||
if (value == null) {
|
||||
value = GlobalProperties.get(inheritKey, null);
|
||||
}
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public int getInt(String key, int defaultValue) {
|
||||
|
||||
String value = get(key, String.valueOf(defaultValue));
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public boolean getBoolean(String key, boolean defaultValue) {
|
||||
|
||||
String value = get(key, String.valueOf(defaultValue));
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
public <T extends Enum<T>> T getEnum(Class<T> enumType, String key, T defaultValue) {
|
||||
String level = get(key, defaultValue.name());
|
||||
return Enum.valueOf(enumType, level.toUpperCase());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,464 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Configuration for the container that holds the EbeanServer instances.
|
||||
* <p>
|
||||
* Provides configuration for cluster communication (if clustering is used). The cluster communication is
|
||||
* used to invalidate appropriate parts of the L2 cache across the cluster.
|
||||
*/
|
||||
public class ContainerConfig {
|
||||
|
||||
/**
|
||||
* Communication mode used for clustering.
|
||||
*/
|
||||
public enum ClusterMode {
|
||||
|
||||
/**
|
||||
* No clustering.
|
||||
*/
|
||||
NONE,
|
||||
|
||||
/**
|
||||
* Use Multicast networking for cluster wide communication.
|
||||
*/
|
||||
MULTICAST,
|
||||
|
||||
/**
|
||||
* Use TCP Sockets for cluster wide communication.
|
||||
*/
|
||||
SOCKET
|
||||
}
|
||||
|
||||
/**
|
||||
* The cluster mode to use.
|
||||
*/
|
||||
ClusterMode mode = ClusterMode.NONE;
|
||||
|
||||
/**
|
||||
* Configuration if using TCP sockets for clustering communication.
|
||||
*/
|
||||
SocketConfig socketConfig = new SocketConfig();
|
||||
|
||||
/**
|
||||
* Configuration if using Multicast for clustering communication.
|
||||
*/
|
||||
MulticastConfig multicastConfig = new MulticastConfig();
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// MulticastConfig
|
||||
|
||||
/**
|
||||
* The configuration for clustering using Multicast networking.
|
||||
*/
|
||||
public static class MulticastConfig {
|
||||
|
||||
int managerSleepMillis = 80;
|
||||
int lastSendTimeFreqSecs = 300;//5mins
|
||||
int lastStatusTimeFreqSecs = 600;//10mins
|
||||
int maxResendOutgoingAttempts = 200;
|
||||
int maxResendIncomingRequests = 50;
|
||||
|
||||
int listenPort;
|
||||
String listenAddress;
|
||||
int sendPort;
|
||||
String sendAddress;
|
||||
|
||||
// Note 1500 is Ethernet MTU and this must be less than UDP max packet size of 65507
|
||||
int maxSendPacketSize = 1500;
|
||||
|
||||
// Whether to send packets even when there are no other members online
|
||||
boolean sendWithNoMembers = true;
|
||||
|
||||
// When multiple instances are on same box you need to broadcast back locally
|
||||
boolean disableLoopback;
|
||||
int listenTimeToLive = -1;
|
||||
int listenTimeout = 1000;
|
||||
int listenBufferSize = 65500;
|
||||
// For multihomed environment the address the listener should bind to
|
||||
String listenBindAddress;
|
||||
|
||||
/**
|
||||
* Return the manager sleep millis.
|
||||
*/
|
||||
public int getManagerSleepMillis() {
|
||||
return managerSleepMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the manager sleep millis.
|
||||
*/
|
||||
public void setManagerSleepMillis(int managerSleepMillis) {
|
||||
this.managerSleepMillis = managerSleepMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last send time frequency.
|
||||
*/
|
||||
public int getLastSendTimeFreqSecs() {
|
||||
return lastSendTimeFreqSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last send time frequency.
|
||||
*/
|
||||
public void setLastSendTimeFreqSecs(int lastSendTimeFreqSecs) {
|
||||
this.lastSendTimeFreqSecs = lastSendTimeFreqSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last status time frequency.
|
||||
*/
|
||||
public int getLastStatusTimeFreqSecs() {
|
||||
return lastStatusTimeFreqSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last status time frequency.
|
||||
*/
|
||||
public void setLastStatusTimeFreqSecs(int lastStatusTimeFreqSecs) {
|
||||
this.lastStatusTimeFreqSecs = lastStatusTimeFreqSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum number of times we will try to re-send a given packet before giving up sending
|
||||
*/
|
||||
public int getMaxResendOutgoingAttempts() {
|
||||
return maxResendOutgoingAttempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum retry attempts for outgoing messages.
|
||||
*/
|
||||
public void setMaxResendOutgoingAttempts(int maxResendOutgoingAttempts) {
|
||||
this.maxResendOutgoingAttempts = maxResendOutgoingAttempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum number of times we will ask for a packet to be resent to us before giving up asking.
|
||||
*/
|
||||
public int getMaxResendIncomingRequests() {
|
||||
return maxResendIncomingRequests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum retry attempts for incoming messages.
|
||||
*/
|
||||
public void setMaxResendIncomingRequests(int maxResendIncomingRequests) {
|
||||
this.maxResendIncomingRequests = maxResendIncomingRequests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the listen port.
|
||||
*/
|
||||
public int getListenPort() {
|
||||
return listenPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listen port.
|
||||
*/
|
||||
public void setListenPort(int port) {
|
||||
this.listenPort = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the listen address.
|
||||
*/
|
||||
public String getListenAddress() {
|
||||
return listenAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listen address.
|
||||
*/
|
||||
public void setListenAddress(String listenAddress) {
|
||||
this.listenAddress = listenAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the send port.
|
||||
*/
|
||||
public int getSendPort() {
|
||||
return sendPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the send port.
|
||||
*/
|
||||
public void setSendPort(int sendPort) {
|
||||
this.sendPort = sendPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the send address.
|
||||
*/
|
||||
public String getSendAddress() {
|
||||
return sendAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the send address.
|
||||
*/
|
||||
public void setSendAddress(String sendAddress) {
|
||||
this.sendAddress = sendAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum send packet size.
|
||||
*/
|
||||
public int getMaxSendPacketSize() {
|
||||
return maxSendPacketSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum send packet size. Note 1500 is Ethernet MTU and this must be less than UDP max packet size of 65507.
|
||||
*/
|
||||
public void setMaxSendPacketSize(int maxSendPacketSize) {
|
||||
this.maxSendPacketSize = maxSendPacketSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if send messages when no other members in the cluster are up.
|
||||
*/
|
||||
public boolean isSendWithNoMembers() {
|
||||
return sendWithNoMembers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set true if send messages when no other members in the cluster are up.
|
||||
*/
|
||||
public void setSendWithNoMembers(boolean sendWithNoMembers) {
|
||||
this.sendWithNoMembers = sendWithNoMembers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if loopback is disabled. When multiple instances are on same box you need to broadcast back locally.
|
||||
*/
|
||||
public boolean isDisableLoopback() {
|
||||
return disableLoopback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if loopback is disabled. When multiple instances are on same box you need to broadcast back locally.
|
||||
*/
|
||||
public void setDisableLoopback(boolean disableLoopback) {
|
||||
this.disableLoopback = disableLoopback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the listen time to live.
|
||||
*/
|
||||
public int getListenTimeToLive() {
|
||||
return listenTimeToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listen time to live.
|
||||
*/
|
||||
public void setListenTimeToLive(int listenTimeToLive) {
|
||||
this.listenTimeToLive = listenTimeToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the listen timeout.
|
||||
*/
|
||||
public int getListenTimeout() {
|
||||
return listenTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the listen timeout.
|
||||
*/
|
||||
public void setListenTimeout(int listenTimeout) {
|
||||
this.listenTimeout = listenTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the listen buffer size.
|
||||
*/
|
||||
public int getListenBufferSize() {
|
||||
return listenBufferSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listen buffer size.
|
||||
*/
|
||||
public void setListenBufferSize(int listenBufferSize) {
|
||||
this.listenBufferSize = listenBufferSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the listener bind address (optional). For multihomed environment the address the listener should bind to.
|
||||
*/
|
||||
public String getListenBindAddress() {
|
||||
return listenBindAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listener bind address (optional). For multihomed environment the address the listener should bind to.
|
||||
*/
|
||||
public void setListenBindAddress(String listenBindAddress) {
|
||||
this.listenBindAddress = listenBindAddress;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// SocketConfig
|
||||
|
||||
/**
|
||||
* Configuration for clustering using TCP sockets.
|
||||
* <p>
|
||||
* This is good for when there are relatively small number of cluster members.
|
||||
*/
|
||||
public static class SocketConfig {
|
||||
|
||||
/**
|
||||
* This local server in host:port format.
|
||||
*/
|
||||
String localHostPort;
|
||||
|
||||
/**
|
||||
* All the cluster members in host:port format.
|
||||
*/
|
||||
List<String> members = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* core threads for the associated thread pool.
|
||||
*/
|
||||
int coreThreads = 2;
|
||||
|
||||
/**
|
||||
* Max threads for the associated thread pool.
|
||||
*/
|
||||
int maxThreads = 16;
|
||||
|
||||
String threadPoolName = "EbeanCluster";
|
||||
|
||||
/**
|
||||
* Return the host and port for this server instance.
|
||||
*/
|
||||
public String getLocalHostPort() {
|
||||
return localHostPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the host and port for this server instance.
|
||||
*/
|
||||
public void setLocalHostPort(String localHostPort) {
|
||||
this.localHostPort = localHostPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the host and port for all the members of the cluster.
|
||||
*/
|
||||
public List<String> getMembers() {
|
||||
return members;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all the host and port for all the members of the cluster.
|
||||
*/
|
||||
public void setMembers(List<String> members) {
|
||||
this.members = members;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of core threads to use.
|
||||
*/
|
||||
public int getCoreThreads() {
|
||||
return coreThreads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of core threads to use.
|
||||
*/
|
||||
public void setCoreThreads(int coreThreads) {
|
||||
this.coreThreads = coreThreads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of max threads to use.
|
||||
*/
|
||||
public int getMaxThreads() {
|
||||
return maxThreads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of max threads to use.
|
||||
*/
|
||||
public void setMaxThreads(int maxThreads) {
|
||||
this.maxThreads = maxThreads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the thread pool name.
|
||||
*/
|
||||
public String getThreadPoolName() {
|
||||
return threadPoolName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the thread pool name.
|
||||
*/
|
||||
public void setThreadPoolName(String threadPoolName) {
|
||||
this.threadPoolName = threadPoolName;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// Members
|
||||
|
||||
/**
|
||||
* Load the settings from properties.
|
||||
*/
|
||||
public void loadFromProperties(Properties properties) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cluster mode.
|
||||
*/
|
||||
public ClusterMode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cluster mode.
|
||||
*/
|
||||
public void setMode(ClusterMode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the socket communication configuration.
|
||||
*/
|
||||
public SocketConfig getSocketConfig() {
|
||||
return socketConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the socket communication configuration.
|
||||
*/
|
||||
public void setSocketConfig(SocketConfig socketConfig) {
|
||||
this.socketConfig = socketConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the multicast communication configuration.
|
||||
*/
|
||||
public MulticastConfig getMulticastConfig() {
|
||||
return multicastConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the multicast communication configuration.
|
||||
*/
|
||||
public void setMulticastConfig(MulticastConfig multicastConfig) {
|
||||
this.multicastConfig = multicastConfig;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.avaje.ebean.config;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.util.StringHelper;
|
||||
@@ -448,48 +449,63 @@ public class DataSourceConfig {
|
||||
this.customProperties = customProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the settings by reading the ebean.properties file.
|
||||
*
|
||||
* @param serverName name of the server
|
||||
*/
|
||||
public void loadSettings(String serverName) {
|
||||
loadSettingsCustomPrefix("datasource." + serverName + ".", new GlobalProperties.DelegatedGlobalPropertySource(serverName));
|
||||
loadSettings(new PropertiesWrapper("datasource", serverName, PropertyMap.defaultProperties()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the settings from ebean.properties.
|
||||
* Load the settings from the properties supplied.
|
||||
* <p>
|
||||
* You can use this when you have your own properties to use for configuration.
|
||||
* </p>
|
||||
*
|
||||
* @param properties the properties to configure the datasource
|
||||
* @param serverName the name of the specific datasource (optional)
|
||||
*/
|
||||
public void loadSettingsCustomPrefix(String prefix, GlobalProperties.PropertySource properties) {
|
||||
public void loadSettings(Properties properties, String serverName) {
|
||||
PropertiesWrapper dbProps = new PropertiesWrapper("datasource", serverName, properties);
|
||||
loadSettings(dbProps);
|
||||
}
|
||||
|
||||
this.username = properties.get(prefix + "username", null);
|
||||
this.password = properties.get(prefix + "password", null);
|
||||
/**
|
||||
* Load the settings from the PropertiesWrapper.
|
||||
*/
|
||||
public void loadSettings(PropertiesWrapper properties) {
|
||||
|
||||
String dbDriver = properties.get(prefix + "databaseDriver", null);
|
||||
this.driver = properties.get(prefix + "driver", dbDriver);
|
||||
username = properties.get("username", username);
|
||||
password = properties.get("password", password);
|
||||
driver = properties.get("driver", properties.get("databaseDriver", driver));
|
||||
url = properties.get("url", properties.get("databaseUrl", url));
|
||||
|
||||
String dbUrl = properties.get(prefix + "databaseUrl", null);
|
||||
this.url = properties.get(prefix + "url", dbUrl);
|
||||
autoCommit = properties.getBoolean("autoCommit", autoCommit);
|
||||
captureStackTrace = properties.getBoolean("captureStackTrace", captureStackTrace);
|
||||
maxStackTraceSize = properties.getInt("maxStackTraceSize", maxStackTraceSize);
|
||||
leakTimeMinutes = properties.getInt("leakTimeMinutes", leakTimeMinutes);
|
||||
maxInactiveTimeSecs = properties.getInt("maxInactiveTimeSecs", maxInactiveTimeSecs);
|
||||
trimPoolFreqSecs = properties.getInt("trimPoolFreqSecs", trimPoolFreqSecs);
|
||||
maxAgeMinutes = properties.getInt("maxAgeMinutes", maxAgeMinutes);
|
||||
|
||||
this.autoCommit = properties.getBoolean(prefix + "autoCommit", false);
|
||||
this.captureStackTrace = properties.getBoolean(prefix + "captureStackTrace", false);
|
||||
this.maxStackTraceSize = properties.getInt(prefix + "maxStackTraceSize", 5);
|
||||
this.leakTimeMinutes = properties.getInt(prefix + "leakTimeMinutes", 30);
|
||||
this.maxInactiveTimeSecs = properties.getInt(prefix + "maxInactiveTimeSecs", 720);
|
||||
this.trimPoolFreqSecs = properties.getInt(prefix + "trimPoolFreqSecs", 59);
|
||||
this.maxAgeMinutes = properties.getInt(prefix + "maxAgeMinutes", 0);
|
||||
minConnections = properties.getInt("minConnections", minConnections);
|
||||
maxConnections = properties.getInt("maxConnections", maxConnections);
|
||||
pstmtCacheSize = properties.getInt("pstmtCacheSize", pstmtCacheSize);
|
||||
cstmtCacheSize = properties.getInt("cstmtCacheSize", cstmtCacheSize);
|
||||
|
||||
this.minConnections = properties.getInt(prefix + "minConnections", 0);
|
||||
this.maxConnections = properties.getInt(prefix + "maxConnections", 20);
|
||||
this.pstmtCacheSize = properties.getInt(prefix + "pstmtCacheSize", 20);
|
||||
this.cstmtCacheSize = properties.getInt(prefix + "cstmtCacheSize", 20);
|
||||
waitTimeoutMillis = properties.getInt("waitTimeout", waitTimeoutMillis);
|
||||
|
||||
this.waitTimeoutMillis = properties.getInt(prefix + "waitTimeout", 1000);
|
||||
heartbeatSql = properties.get("heartbeatSql", heartbeatSql);
|
||||
heartbeatTimeoutSeconds = properties.getInt("heartbeatTimeoutSeconds", heartbeatTimeoutSeconds);
|
||||
poolListener = properties.get("poolListener", poolListener);
|
||||
offline = properties.getBoolean("offline", offline);
|
||||
|
||||
this.heartbeatSql = properties.get(prefix + "heartbeatSql", null);
|
||||
this.heartbeatTimeoutSeconds = properties.getInt(prefix + "heartbeatTimeoutSeconds", 3);
|
||||
this.poolListener = properties.get(prefix + "poolListener", null);
|
||||
this.offline = properties.getBoolean(prefix + "offline", false);
|
||||
|
||||
String isoLevel = properties.get(prefix + "isolationlevel", "READ_COMMITTED");
|
||||
String isoLevel = properties.get("isolationlevel", getTransactionIsolationLevel(isolationLevel));
|
||||
this.isolationLevel = getTransactionIsolationLevel(isoLevel);
|
||||
|
||||
String customProperties = properties.get(prefix + "customProperties", null);
|
||||
String customProperties = properties.get("customProperties", null);
|
||||
if (customProperties != null && customProperties.length() > 0) {
|
||||
Map<String, String> custProps = StringHelper.delimitedToMap(customProperties, ";", "=");
|
||||
this.customProperties = custProps;
|
||||
@@ -498,7 +514,21 @@ public class DataSourceConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* return the isolation level for a given string description.
|
||||
* Return the isolation level description from the associated Connection int value.
|
||||
*/
|
||||
public String getTransactionIsolationLevel(int level) {
|
||||
switch (level) {
|
||||
case Connection.TRANSACTION_NONE : return "NONE";
|
||||
case Connection.TRANSACTION_READ_COMMITTED : return "READ_COMMITTED";
|
||||
case Connection.TRANSACTION_READ_UNCOMMITTED : return "READ_UNCOMMITTED";
|
||||
case Connection.TRANSACTION_REPEATABLE_READ : return "REPEATABLE_READ";
|
||||
case Connection.TRANSACTION_SERIALIZABLE : return "SERIALIZABLE";
|
||||
default: throw new RuntimeException("Transaction Isolation level [" + level + "] is not known.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the isolation level for a given string description.
|
||||
*/
|
||||
public int getTransactionIsolationLevel(String level) {
|
||||
level = level.toUpperCase();
|
||||
@@ -522,6 +552,6 @@ public class DataSourceConfig {
|
||||
return Connection.TRANSACTION_SERIALIZABLE;
|
||||
}
|
||||
|
||||
throw new RuntimeException("Transaction Isolaction level [" + level + "] is not known.");
|
||||
throw new RuntimeException("Transaction Isolation level [" + level + "] is not known.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import com.avaje.ebean.util.ClassUtil;
|
||||
|
||||
/**
|
||||
* Provides access to properties loaded from the ebean.properties file.
|
||||
*/
|
||||
public final class GlobalProperties {
|
||||
|
||||
private static volatile PropertyMap globalMap;
|
||||
|
||||
private static boolean skipPrimaryServer;
|
||||
|
||||
/**
|
||||
* Set whether to skip automatically creating the primary server.
|
||||
*/
|
||||
public static synchronized void setSkipPrimaryServer(boolean skip) {
|
||||
skipPrimaryServer = skip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true to skip automatically creating the primary server.
|
||||
*/
|
||||
public static synchronized boolean isSkipPrimaryServer() {
|
||||
return skipPrimaryServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the string replacing any expressions like ${catalina.base}.
|
||||
* <p>
|
||||
* This will evaluate expressions using first environment variables, than java
|
||||
* system variables and lastly properties in ebean.properties - in that order.
|
||||
* </p>
|
||||
* <p>
|
||||
* Expressions start with "${" and end with "}".
|
||||
* </p>
|
||||
*/
|
||||
public static String evaluateExpressions(String val) {
|
||||
return getPropertyMap().eval(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and evaluate any expressions that have not already been evaluated.
|
||||
*/
|
||||
public static synchronized void evaluateExpressions() {
|
||||
getPropertyMap().evaluateProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* In a servlet container environment this will additionally look in WEB-INF
|
||||
* for the ebean.properties file.
|
||||
*/
|
||||
public static synchronized void setServletContext(ServletContext servletContext) {
|
||||
|
||||
PropertyMapLoader.setServletContext(servletContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ServletContext (if setup in a servlet container environment).
|
||||
*/
|
||||
public static synchronized ServletContext getServletContext() {
|
||||
|
||||
return PropertyMapLoader.getServletContext();
|
||||
}
|
||||
|
||||
private static void initPropertyMap() {
|
||||
|
||||
String fileName = System.getenv("EBEAN_PROPS_FILE");
|
||||
if (fileName == null) {
|
||||
fileName = System.getProperty("ebean.props.file");
|
||||
if (fileName == null) {
|
||||
fileName = "ebean.properties";
|
||||
}
|
||||
}
|
||||
|
||||
globalMap = PropertyMapLoader.load(null, fileName);
|
||||
if (globalMap == null) {
|
||||
// ebean.properties file was not found... but that
|
||||
// is ok because we are likely doing programmatic config
|
||||
globalMap = new PropertyMap();
|
||||
}
|
||||
|
||||
String loaderCn = globalMap.get("ebean.properties.loader");
|
||||
if (loaderCn != null) {
|
||||
// a Runnable that can be used to customise the initialisation
|
||||
// of the GlobalProperties
|
||||
try {
|
||||
Runnable r = (Runnable) ClassUtil.newInstance(loaderCn);
|
||||
r.run();
|
||||
} catch (Exception e) {
|
||||
String m = "Error creating or running properties loader " + loaderCn;
|
||||
throw new RuntimeException(m, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property map loading it if required.
|
||||
*/
|
||||
private static synchronized PropertyMap getPropertyMap() {
|
||||
|
||||
if (globalMap == null) {
|
||||
initPropertyMap();
|
||||
}
|
||||
|
||||
return globalMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a String property with a default value.
|
||||
*/
|
||||
public static synchronized String get(String key, String defaultValue) {
|
||||
return getPropertyMap().get(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a int property with a default value.
|
||||
*/
|
||||
public static synchronized int getInt(String key, int defaultValue) {
|
||||
return getPropertyMap().getInt(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a boolean property with a default value.
|
||||
*/
|
||||
public static synchronized boolean getBoolean(String key, boolean defaultValue) {
|
||||
return getPropertyMap().getBoolean(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a property return the previous value. This will evaluate any
|
||||
* expressions in the value.
|
||||
*/
|
||||
public static synchronized String put(String key, String value) {
|
||||
return getPropertyMap().putEval(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a Map of key value properties.
|
||||
*/
|
||||
public static synchronized void putAll(Map<String, String> keyValueMap) {
|
||||
for (Entry<String, String> e : keyValueMap.entrySet()) {
|
||||
getPropertyMap().putEval(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public static PropertySource getPropertySource(String name) {
|
||||
return new ConfigPropertyMap(name);
|
||||
}
|
||||
|
||||
public static interface PropertySource {
|
||||
|
||||
/**
|
||||
* Return the name of the server. This is also the dataSource name.
|
||||
*/
|
||||
public String getServerName();
|
||||
|
||||
/**
|
||||
* Get a property. This will prepend "ebean" and the server name to lookup
|
||||
* the value.
|
||||
*/
|
||||
public String get(String key, String defaultValue);
|
||||
|
||||
public int getInt(String key, int defaultValue);
|
||||
|
||||
public boolean getBoolean(String key, boolean defaultValue);
|
||||
|
||||
public <T extends Enum<T>> T getEnum(Class<T> enumType, String key, T defaultValue);
|
||||
|
||||
}
|
||||
|
||||
public static class DelegatedGlobalPropertySource implements PropertySource {
|
||||
|
||||
private String serverName;
|
||||
|
||||
public DelegatedGlobalPropertySource(String serverName) {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String key, String defaultValue) {
|
||||
return GlobalProperties.get(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(String key, int defaultValue) {
|
||||
return GlobalProperties.getInt(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String key, boolean defaultValue) {
|
||||
return GlobalProperties.getBoolean(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Enum<T>> T getEnum(Class<T> enumType, String key, T defaultValue) {
|
||||
String level = get(key, defaultValue.name());
|
||||
return Enum.valueOf(enumType, level.toUpperCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,4 +112,9 @@ public interface NamingConvention {
|
||||
*/
|
||||
public boolean isUseForeignKeyPrefix();
|
||||
|
||||
/**
|
||||
* Load setting from properties.
|
||||
*/
|
||||
public void loadFromProperties(PropertiesWrapper properties);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
/**
|
||||
* Defines the mode for JDBC batch processing.
|
||||
* <p>
|
||||
* Used both at a per transaction basis and per request basis.
|
||||
* </p>
|
||||
*
|
||||
* @see com.avaje.ebean.config.ServerConfig#setPersistBatch(PersistBatch)
|
||||
* @see com.avaje.ebean.config.ServerConfig#setPersistBatchOnCascade(PersistBatch)
|
||||
*
|
||||
* @see com.avaje.ebean.Transaction#setBatch(PersistBatch)
|
||||
* @see com.avaje.ebean.Transaction#setBatchOnCascade(PersistBatch)
|
||||
*/
|
||||
public enum PersistBatch {
|
||||
|
||||
/**
|
||||
* Do not use JDBC Batch mode.
|
||||
*/
|
||||
NONE(false),
|
||||
|
||||
/**
|
||||
* Use JDBC Batch mode on Inserts.
|
||||
*/
|
||||
INSERT(true),
|
||||
|
||||
/**
|
||||
* Use JDBC Batch mode on Inserts, Updates and Deletes.
|
||||
*/
|
||||
ALL(true),
|
||||
|
||||
/**
|
||||
* You should not use this value explicitly. It should only used on the Transactional annotation
|
||||
* to indicate that the value should inherit from the ServerConfig setting.
|
||||
*/
|
||||
INHERIT(false);
|
||||
|
||||
|
||||
boolean forInsert;
|
||||
|
||||
PersistBatch(boolean forInsert) {
|
||||
this.forInsert = forInsert;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if persist cascade should use JDBC batch for inserts.
|
||||
*/
|
||||
public boolean forInsert() {
|
||||
return forInsert;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
public class PropertiesWrapper {
|
||||
|
||||
protected final Properties properties;
|
||||
|
||||
protected final String prefix;
|
||||
|
||||
protected final String serverName;
|
||||
|
||||
protected final PropertyMap propertyMap;
|
||||
|
||||
/**
|
||||
* Construct with a prefix, serverName and properties.
|
||||
*/
|
||||
public PropertiesWrapper(String prefix, String serverName, Properties properties) {
|
||||
this.serverName = serverName;
|
||||
this.prefix = prefix;
|
||||
this.propertyMap = PropertyMapLoader.load(null, properties);
|
||||
this.properties = propertyMap.asProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct without prefix of serverName.
|
||||
*/
|
||||
public PropertiesWrapper(Properties properties) {
|
||||
this(null, null, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal copy constructor when changing prefix.
|
||||
*/
|
||||
protected PropertiesWrapper(String prefix, String serverName, PropertyMap propertyMap, Properties properties) {
|
||||
this.serverName = serverName;
|
||||
this.prefix = prefix;
|
||||
this.propertyMap = propertyMap;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a PropertiesWrapper instance with a different prefix but same underlying properties.
|
||||
* <p/>
|
||||
* Used when wanting to use "datasource" as the prefix rather than "ebean".
|
||||
* <p/>
|
||||
* The returning instance should only be used in a read only fashion.
|
||||
*/
|
||||
public PropertiesWrapper withPrefix(String prefix) {
|
||||
return new PropertiesWrapper(prefix, serverName, propertyMap, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the serverName (optional).
|
||||
*/
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return as Properties with lower case keys and after evaluation and additional properties loading has occurred.
|
||||
* <p>
|
||||
* Ebean has historically ignored the case of keys hence returning the Properties with all the keys lower cased.
|
||||
* </p>
|
||||
*/
|
||||
public Properties asPropertiesLowerCase() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a property with no default value.
|
||||
*/
|
||||
public String get(String key) {
|
||||
return get(key, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a property with a default value.
|
||||
* <p>
|
||||
* This performs a search using the prefix and server name (if supplied) to search for the property
|
||||
* value in order based on:
|
||||
* <pre>{@code
|
||||
* prefix.serverName.key
|
||||
* prefix.key
|
||||
* key
|
||||
* }</pre>
|
||||
* </p>
|
||||
*/
|
||||
public String get(String key, String defaultValue) {
|
||||
|
||||
String value = null;
|
||||
if (serverName != null && prefix != null) {
|
||||
value = propertyMap.get(prefix + "." + serverName + "." + key, null);
|
||||
}
|
||||
if (value == null && prefix != null) {
|
||||
value = propertyMap.get(prefix + "." + key, null);
|
||||
}
|
||||
if (value == null) {
|
||||
value = propertyMap.get(key, null);
|
||||
}
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a double property value.
|
||||
*/
|
||||
public double getDouble(String key, double defaultValue) {
|
||||
|
||||
String value = get(key, String.valueOf(defaultValue));
|
||||
return Double.parseDouble(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an int property value.
|
||||
*/
|
||||
public int getInt(String key, int defaultValue) {
|
||||
|
||||
String value = get(key, String.valueOf(defaultValue));
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a long property value.
|
||||
*/
|
||||
public long getLong(String key, long defaultValue) {
|
||||
|
||||
String value = get(key, String.valueOf(defaultValue));
|
||||
return Long.parseLong(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a boolean property value.
|
||||
*/
|
||||
public boolean getBoolean(String key, boolean defaultValue) {
|
||||
|
||||
String value = get(key, String.valueOf(defaultValue));
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Enum property value.
|
||||
*/
|
||||
public <T extends Enum<T>> T getEnum(Class<T> enumType, String key, T defaultValue) {
|
||||
String level = get(key, defaultValue.name());
|
||||
return Enum.valueOf(enumType, level.toUpperCase());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,21 +4,40 @@ import java.io.Serializable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A map like structure of properties.
|
||||
* <p/>
|
||||
* Handles evaluation of expressions like ${home} and provides convenience methods for int, long and boolean.
|
||||
*/
|
||||
final class PropertyMap implements Serializable {
|
||||
public final class PropertyMap implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
|
||||
|
||||
public static Properties defaultProperties() {
|
||||
PropertyMap propertyMap = PropertyMapLoader.loadGlobalProperties();
|
||||
return (propertyMap == null) ? new Properties() : propertyMap.asProperties();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return map.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return as standard Properties.
|
||||
*/
|
||||
public Properties asProperties() {
|
||||
Properties properties = new Properties();
|
||||
for (Entry<String, String> e : entrySet()) {
|
||||
properties.put(e.getKey(), e.getValue());
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through all the properties and evaluate any expressions that have not
|
||||
* been resolved.
|
||||
@@ -35,10 +54,16 @@ final class PropertyMap implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value with expressions like ${home} evaluated using system properties and environment variables.
|
||||
*/
|
||||
public synchronized String eval(String val) {
|
||||
return PropertyExpression.eval(val, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the boolean property value with a given default.
|
||||
*/
|
||||
public synchronized boolean getBoolean(String key, boolean defaultValue) {
|
||||
String value = get(key);
|
||||
if (value == null) {
|
||||
@@ -48,6 +73,9 @@ final class PropertyMap implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the int property value with a given default.
|
||||
*/
|
||||
public synchronized int getInt(String key, int defaultValue) {
|
||||
String value = get(key);
|
||||
if (value == null) {
|
||||
@@ -57,36 +85,69 @@ final class PropertyMap implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the long property value with a given default.
|
||||
*/
|
||||
public synchronized long getLong(String key, long defaultValue) {
|
||||
String value = get(key);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return Long.parseLong(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the string property value with a given default.
|
||||
*/
|
||||
public synchronized String get(String key, String defaultValue) {
|
||||
String value = map.get(key.toLowerCase());
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property value returning null if there is no value defined.
|
||||
*/
|
||||
public synchronized String get(String key) {
|
||||
return map.get(key.toLowerCase());
|
||||
}
|
||||
|
||||
synchronized void putAll(Map<String, String> keyValueMap) {
|
||||
/**
|
||||
* Put all evaluating any expressions in the values.
|
||||
*/
|
||||
public synchronized void putEvalAll(Map<String, String> keyValueMap) {
|
||||
|
||||
for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
putEval(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
synchronized String putEval(String key, String value) {
|
||||
/**
|
||||
* Put a single key value evaluating any expressions in the value.
|
||||
*/
|
||||
public synchronized String putEval(String key, String value) {
|
||||
value = PropertyExpression.eval(value, this);
|
||||
return map.put(key.toLowerCase(), value);
|
||||
}
|
||||
|
||||
synchronized String put(String key, String value) {
|
||||
/**
|
||||
* Put a single key value with no expression evaluation.
|
||||
*/
|
||||
public synchronized String put(String key, String value) {
|
||||
return map.put(key.toLowerCase(), value);
|
||||
}
|
||||
|
||||
synchronized String remove(String key) {
|
||||
/**
|
||||
* Remove an entry.
|
||||
*/
|
||||
public synchronized String remove(String key) {
|
||||
return map.remove(key.toLowerCase());
|
||||
}
|
||||
|
||||
synchronized Set<Entry<String, String>> entrySet() {
|
||||
/**
|
||||
* Return the entries.
|
||||
*/
|
||||
public synchronized Set<Entry<String, String>> entrySet() {
|
||||
return map.entrySet();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,40 +1,30 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Helper used to load the PropertyMap.
|
||||
* Helper used to load the ebean.properties into a PropertyMap.
|
||||
*/
|
||||
final class PropertyMapLoader {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PropertyMapLoader.class);
|
||||
|
||||
private static ServletContext servletContext;
|
||||
public static PropertyMap loadGlobalProperties() {
|
||||
|
||||
/**
|
||||
* Return the servlet context when in a web environment.
|
||||
*/
|
||||
public static ServletContext getServletContext() {
|
||||
return servletContext;
|
||||
}
|
||||
String fileName = System.getenv("EBEAN_PROPS_FILE");
|
||||
if (fileName == null) {
|
||||
fileName = System.getProperty("ebean.props.file");
|
||||
if (fileName == null) {
|
||||
fileName = "ebean.properties";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ServletContext for when ebean.properties is in WEB-INF in a web
|
||||
* application environment.
|
||||
*/
|
||||
public static void setServletContext(ServletContext servletContext) {
|
||||
PropertyMapLoader.servletContext = servletContext;
|
||||
return load(null, fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,15 +54,20 @@ final class PropertyMapLoader {
|
||||
* @param in
|
||||
* the InputStream of the properties file to load.
|
||||
*/
|
||||
private static PropertyMap load(PropertyMap p, InputStream in) {
|
||||
public static PropertyMap load(PropertyMap p, InputStream in) {
|
||||
|
||||
Properties props = new Properties();
|
||||
try {
|
||||
props.load(in);
|
||||
in.close();
|
||||
return load(p, props);
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static PropertyMap load(PropertyMap p, Properties props) {
|
||||
|
||||
if (p == null) {
|
||||
p = new PropertyMap();
|
||||
@@ -117,18 +112,6 @@ final class PropertyMapLoader {
|
||||
throw new NullPointerException("fileName is null?");
|
||||
}
|
||||
|
||||
if (servletContext == null) {
|
||||
logger.debug("No servletContext so not looking in WEB-INF for " + fileName);
|
||||
|
||||
} else {
|
||||
// first look in WEB-INF ...
|
||||
InputStream in = servletContext.getResourceAsStream("/WEB-INF/" + fileName);
|
||||
if (in != null) {
|
||||
logger.debug(fileName + " found in WEB-INF");
|
||||
return in;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
File f = new File(fileName);
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.avaje.ebean.PersistenceContextScope;
|
||||
import com.avaje.ebean.annotation.Encrypted;
|
||||
import com.avaje.ebean.cache.ServerCacheFactory;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.config.GlobalProperties.PropertySource;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.config.dbplatform.DbEncrypt;
|
||||
import com.avaje.ebean.event.*;
|
||||
@@ -16,6 +15,7 @@ import com.fasterxml.jackson.core.JsonFactory;
|
||||
import javax.sql.DataSource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* The configuration used for creating a EbeanServer.
|
||||
@@ -30,9 +30,9 @@ import java.util.List;
|
||||
* classes and listeners etc.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* ServerConfig c = new ServerConfig();
|
||||
* c.setName("ordh2");
|
||||
* c.setName("ordh2");
|
||||
*
|
||||
* // read the ebean.properties and load
|
||||
* // those settings into this serverConfig object
|
||||
@@ -43,18 +43,18 @@ import java.util.List;
|
||||
* c.setDdlRun(true);
|
||||
*
|
||||
* // add any classes found in the app.data package
|
||||
* c.addPackage("app.data");
|
||||
* c.addPackage("app.data");
|
||||
*
|
||||
* // add the names of Jars that contain entities
|
||||
* c.addJar("myJarContainingEntities.jar");
|
||||
* c.addJar("someOtherJarContainingEntities.jar");
|
||||
* c.addJar("myJarContainingEntities.jar");
|
||||
* c.addJar("someOtherJarContainingEntities.jar");
|
||||
*
|
||||
* // register as the 'Default' server
|
||||
* c.setDefaultServer(true);
|
||||
*
|
||||
* EbeanServer server = EbeanServerFactory.create(c);
|
||||
*
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* @see EbeanServerFactory
|
||||
*
|
||||
@@ -62,27 +62,19 @@ import java.util.List;
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class ServerConfig {
|
||||
|
||||
/**
|
||||
* The Constant DEFAULT_QUERY_BATCH_SIZE. Default: 100
|
||||
*/
|
||||
private final static int DEFAULT_QUERY_BATCH_SIZE = 100;
|
||||
|
||||
/**
|
||||
* The EbeanServer name.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
private ContainerConfig containerConfig;
|
||||
|
||||
/**
|
||||
* The resource directory.
|
||||
*/
|
||||
private String resourceDirectory;
|
||||
|
||||
/**
|
||||
* The enhance log level. Used with subclass generation.
|
||||
*/
|
||||
private int enhanceLogLevel;
|
||||
|
||||
/**
|
||||
* Set to true to register this EbeanServer with the Ebean singleton.
|
||||
*/
|
||||
@@ -111,6 +103,11 @@ public class ServerConfig {
|
||||
*/
|
||||
private List<String> searchJars = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* Class name of a classPathReader implementation.
|
||||
*/
|
||||
private String classPathReaderClassName;
|
||||
|
||||
/**
|
||||
* Config controlling the autofetch behaviour.
|
||||
*/
|
||||
@@ -136,19 +133,29 @@ public class ServerConfig {
|
||||
*/
|
||||
private int databaseSequenceBatchSize = 20;
|
||||
|
||||
private boolean persistBatching;
|
||||
/**
|
||||
* Use for transaction scoped batch mode.
|
||||
*/
|
||||
private PersistBatch persistBatch = PersistBatch.NONE;
|
||||
|
||||
/**
|
||||
* Use for per request batch mode.
|
||||
*/
|
||||
private PersistBatch persistBatchOnCascade = PersistBatch.NONE;
|
||||
|
||||
private int persistBatchSize = 20;
|
||||
|
||||
/**
|
||||
* The default batch size for lazy loading
|
||||
*/
|
||||
private int lazyLoadBatchSize = 1;
|
||||
private int lazyLoadBatchSize = 10;
|
||||
|
||||
/**
|
||||
* The query batch size.
|
||||
* The default batch size for 'query joins'.
|
||||
*/
|
||||
private int queryBatchSize = -1;
|
||||
private int queryBatchSize = 100;
|
||||
|
||||
private boolean eagerFetchLobs;
|
||||
|
||||
private boolean ddlGenerate;
|
||||
|
||||
@@ -201,7 +208,7 @@ public class ServerConfig {
|
||||
/**
|
||||
* The naming convention.
|
||||
*/
|
||||
private NamingConvention namingConvention;
|
||||
private NamingConvention namingConvention = new UnderscoreNamingConvention();
|
||||
|
||||
/**
|
||||
* Behaviour of update to include on the change properties.
|
||||
@@ -238,9 +245,9 @@ public class ServerConfig {
|
||||
|
||||
private ServerCacheManager serverCacheManager;
|
||||
|
||||
private boolean collectQueryStatsByNode;
|
||||
private boolean collectQueryStatsByNode = true;
|
||||
|
||||
private boolean collectQueryOrigins;
|
||||
private boolean collectQueryOrigins = true;
|
||||
|
||||
/**
|
||||
* The default PersistenceContextScope used if one is not explicitly set on a query.
|
||||
@@ -253,6 +260,33 @@ public class ServerConfig {
|
||||
|
||||
private boolean durationWithNanos;
|
||||
|
||||
private int maxCallStack = 5;
|
||||
|
||||
private boolean transactionRollbackOnChecked = true;
|
||||
|
||||
private boolean registerJmxMBeans = true;
|
||||
|
||||
// configuration for the background executor service (thread pool)
|
||||
|
||||
private int backgroundExecutorSchedulePoolSize = 1;
|
||||
private int backgroundExecutorCorePoolSize = 1;
|
||||
private int backgroundExecutorMaxPoolSize = 8;
|
||||
private int backgroundExecutorIdleSecs = 60;
|
||||
private int backgroundExecutorShutdownSecs = 30;
|
||||
|
||||
// defaults for the L2 bean caching
|
||||
|
||||
private int cacheWarmingDelay = 30;
|
||||
private int cacheMaxSize = 10000;
|
||||
private int cacheMaxIdleTime = 600;
|
||||
private int cacheMaxTimeToLive = 60*60*6;
|
||||
|
||||
// defaults for the L2 query caching
|
||||
|
||||
private int queryCacheMaxSize = 1000;
|
||||
private int queryCacheMaxIdleTime = 600;
|
||||
private int queryCacheMaxTimeToLive = 60*60*6;
|
||||
|
||||
/**
|
||||
* Construct a Server Configuration for programmatically creating an EbeanServer.
|
||||
*/
|
||||
@@ -306,6 +340,26 @@ public class ServerConfig {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the container / clustering configuration.
|
||||
* <p/>
|
||||
* The container holds all the EbeanServer instances and provides clustering communication
|
||||
* services to all the EbeanServer instances.
|
||||
*/
|
||||
public ContainerConfig getContainerConfig() {
|
||||
return containerConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the container / clustering configuration.
|
||||
* <p/>
|
||||
* The container holds all the EbeanServer instances and provides clustering communication
|
||||
* services to all the EbeanServer instances.
|
||||
*/
|
||||
public void setContainerConfig(ContainerConfig containerConfig) {
|
||||
this.containerConfig = containerConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this server should be registered with the Ebean singleton
|
||||
* when it is created.
|
||||
@@ -351,45 +405,74 @@ public class ServerConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if by default JDBC batching is used for persisting or deleting
|
||||
* Return the PersistBatch mode to use by default at the transaction level.
|
||||
* <p>
|
||||
* When INSERT or ALL is used then save(), delete() etc do not execute immediately but instead go into
|
||||
* a JDBC batch execute buffer that is flushed. The buffer is flushed if a query is executed, transaction ends
|
||||
* or the batch size is meet.
|
||||
* </p>
|
||||
*/
|
||||
public PersistBatch getPersistBatch() {
|
||||
return persistBatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JDBC batch mode to use at the transaction level.
|
||||
* <p>
|
||||
* When INSERT or ALL is used then save(), delete() etc do not execute immediately but instead go into
|
||||
* a JDBC batch execute buffer that is flushed. The buffer is flushed if a query is executed, transaction ends
|
||||
* or the batch size is meet.
|
||||
* </p>
|
||||
*/
|
||||
public void setPersistBatch(PersistBatch persistBatch) {
|
||||
this.persistBatch = persistBatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JDBC batch mode to use per save(), delete(), insert() or update() request.
|
||||
* <p>
|
||||
* This makes sense when a save() or delete() etc cascades and executes multiple child statements. The best caase
|
||||
* for this is when saving a master/parent bean this cascade inserts many detail/child beans.
|
||||
* </p>
|
||||
* <p>
|
||||
* This only takes effect when the persistBatch mode at the transaction level does not take effect.
|
||||
* </p>
|
||||
*/
|
||||
public PersistBatch getPersistBatchOnCascade() {
|
||||
return persistBatchOnCascade;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JDBC batch mode to use per save(), delete(), insert() or update() request.
|
||||
* <p>
|
||||
* This makes sense when a save() or delete() etc cascades and executes multiple child statements. The best caase
|
||||
* for this is when saving a master/parent bean this cascade inserts many detail/child beans.
|
||||
* </p>
|
||||
* <p>
|
||||
* This only takes effect when the persistBatch mode at the transaction level does not take effect.
|
||||
* </p>
|
||||
*/
|
||||
public void setPersistBatchOnCascade(PersistBatch persistBatchOnCascade) {
|
||||
this.persistBatchOnCascade = persistBatchOnCascade;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated, please migrate to using setPersistBatch().
|
||||
* <p>
|
||||
* Set to true if you what to use JDBC batching for persisting and deleting
|
||||
* beans.
|
||||
* </p>
|
||||
* <p>
|
||||
* With this Ebean will batch up persist requests and use the JDBC batch api.
|
||||
* This is a performance optimisation designed to reduce the network chatter.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isPersistBatching() {
|
||||
return persistBatching;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use isPersistBatching() instead.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean isUsePersistBatching() {
|
||||
return persistBatching;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if you what to use JDBC batching for persisting and deleting
|
||||
* beans.
|
||||
* <p>
|
||||
* With this Ebean will batch up persist requests and use the JDBC batch api.
|
||||
* This is a performance optimisation designed to reduce the network chatter.
|
||||
* When true this is equivalent to {@code setPersistBatch(PersistBatch.ALL)} or
|
||||
* when false to {@code setPersistBatch(PersistBatch.NONE)}
|
||||
* </p>
|
||||
*/
|
||||
public void setPersistBatching(boolean persistBatching) {
|
||||
this.persistBatching = persistBatching;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use setPersistBatching() instead.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public void setUsePersistBatching(boolean persistBatching) {
|
||||
this.persistBatching = persistBatching;
|
||||
this.persistBatch = (persistBatching) ? PersistBatch.ALL : PersistBatch.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -401,11 +484,34 @@ public class ServerConfig {
|
||||
|
||||
/**
|
||||
* Set the batch size used for JDBC batching. If unset this defaults to 20.
|
||||
* <p>
|
||||
* You can also set the batch size on the transaction.
|
||||
* </p>
|
||||
* @see com.avaje.ebean.Transaction#setBatchSize(int)
|
||||
*/
|
||||
public void setPersistBatchSize(int persistBatchSize) {
|
||||
this.persistBatchSize = persistBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the query batch size. This defaults to 100.
|
||||
*
|
||||
* @return the query batch size
|
||||
*/
|
||||
public int getQueryBatchSize() {
|
||||
return queryBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the query batch size. This defaults to 100.
|
||||
*
|
||||
* @param queryBatchSize
|
||||
* the new query batch size
|
||||
*/
|
||||
public void setQueryBatchSize(int queryBatchSize) {
|
||||
this.queryBatchSize = queryBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default batch size for lazy loading of beans and collections.
|
||||
*/
|
||||
@@ -413,25 +519,6 @@ public class ServerConfig {
|
||||
return lazyLoadBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the query batch size.
|
||||
*
|
||||
* @return the query batch size
|
||||
*/
|
||||
public int getQueryBatchSize() {
|
||||
return queryBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the query batch size.
|
||||
*
|
||||
* @param queryBatchSize
|
||||
* the new query batch size
|
||||
*/
|
||||
public void setQueryBatchSize(int queryBatchSize) {
|
||||
this.queryBatchSize = queryBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default batch size for lazy loading.
|
||||
* <p>
|
||||
@@ -439,7 +526,7 @@ public class ServerConfig {
|
||||
* invoked by default.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default value is for this is 1 (load 1 bean or collection).
|
||||
* The default value is for this is 10 (load 10 beans or collections).
|
||||
* </p>
|
||||
* <p>
|
||||
* You can explicitly control the lazy loading batch size for a given join on
|
||||
@@ -519,17 +606,231 @@ public class ServerConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the log level used for "subclassing" enhancement.
|
||||
* Return true if LOB's should default to fetch eager.
|
||||
* By default this is set to false and LOB's must be explicitly fetched.
|
||||
*/
|
||||
public int getEnhanceLogLevel() {
|
||||
return enhanceLogLevel;
|
||||
public boolean isEagerFetchLobs() {
|
||||
return eagerFetchLobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the log level used for "subclassing" enhancement.
|
||||
* Set to true if you want LOB's to be fetch eager by default.
|
||||
* By default this is set to false and LOB's must be explicitly fetched.
|
||||
*/
|
||||
public void setEnhanceLogLevel(int enhanceLogLevel) {
|
||||
this.enhanceLogLevel = enhanceLogLevel;
|
||||
public void setEagerFetchLobs(boolean eagerFetchLobs) {
|
||||
this.eagerFetchLobs = eagerFetchLobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the max call stack to use for origin location.
|
||||
*/
|
||||
public int getMaxCallStack() {
|
||||
return maxCallStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max call stack to use for origin location.
|
||||
*/
|
||||
public void setMaxCallStack(int maxCallStack) {
|
||||
this.maxCallStack = maxCallStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if transactions should rollback on checked exceptions.
|
||||
*/
|
||||
public boolean isTransactionRollbackOnChecked() {
|
||||
return transactionRollbackOnChecked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if transactions should by default rollback on checked exceptions.
|
||||
*/
|
||||
public void setTransactionRollbackOnChecked(boolean transactionRollbackOnChecked) {
|
||||
this.transactionRollbackOnChecked = transactionRollbackOnChecked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the server should register JMX MBeans.
|
||||
*/
|
||||
public boolean isRegisterJmxMBeans() {
|
||||
return registerJmxMBeans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if the server should register JMX MBeans.
|
||||
*/
|
||||
public void setRegisterJmxMBeans(boolean registerJmxMBeans) {
|
||||
this.registerJmxMBeans = registerJmxMBeans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Background executor schedule pool size. Defaults to 1.
|
||||
*/
|
||||
public int getBackgroundExecutorSchedulePoolSize() {
|
||||
return backgroundExecutorSchedulePoolSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Background executor schedule pool size.
|
||||
*/
|
||||
public void setBackgroundExecutorSchedulePoolSize(int backgroundExecutorSchedulePoolSize) {
|
||||
this.backgroundExecutorSchedulePoolSize = backgroundExecutorSchedulePoolSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Background executor core pool size.
|
||||
*/
|
||||
public int getBackgroundExecutorCorePoolSize() {
|
||||
return backgroundExecutorCorePoolSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Background executor core pool size.
|
||||
*/
|
||||
public void setBackgroundExecutorCorePoolSize(int backgroundExecutorCorePoolSize) {
|
||||
this.backgroundExecutorCorePoolSize = backgroundExecutorCorePoolSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Background executor max pool size.
|
||||
*/
|
||||
public int getBackgroundExecutorMaxPoolSize() {
|
||||
return backgroundExecutorMaxPoolSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Background executor max pool size.
|
||||
*/
|
||||
public void setBackgroundExecutorMaxPoolSize(int backgroundExecutorMaxPoolSize) {
|
||||
this.backgroundExecutorMaxPoolSize = backgroundExecutorMaxPoolSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Background executor idle seconds.
|
||||
*/
|
||||
public int getBackgroundExecutorIdleSecs() {
|
||||
return backgroundExecutorIdleSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Background executor idle seconds.
|
||||
*/
|
||||
public void setBackgroundExecutorIdleSecs(int backgroundExecutorIdleSecs) {
|
||||
this.backgroundExecutorIdleSecs = backgroundExecutorIdleSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Background executor shutdown seconds. This is the time allowed for the pool to shutdown nicely
|
||||
* before it is forced shutdown.
|
||||
*/
|
||||
public int getBackgroundExecutorShutdownSecs() {
|
||||
return backgroundExecutorShutdownSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Background executor shutdown seconds. This is the time allowed for the pool to shutdown nicely
|
||||
* before it is forced shutdown.
|
||||
*/
|
||||
public void setBackgroundExecutorShutdownSecs(int backgroundExecutorShutdownSecs) {
|
||||
this.backgroundExecutorShutdownSecs = backgroundExecutorShutdownSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cache warming delay in seconds.
|
||||
*/
|
||||
public int getCacheWarmingDelay() {
|
||||
return cacheWarmingDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cache warming delay in seconds.
|
||||
*/
|
||||
public void setCacheWarmingDelay(int cacheWarmingDelay) {
|
||||
this.cacheWarmingDelay = cacheWarmingDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the L2 cache default max size.
|
||||
*/
|
||||
public int getCacheMaxSize() {
|
||||
return cacheMaxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the L2 cache default max size.
|
||||
*/
|
||||
public void setCacheMaxSize(int cacheMaxSize) {
|
||||
this.cacheMaxSize = cacheMaxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the L2 cache default max idle time in seconds.
|
||||
*/
|
||||
public int getCacheMaxIdleTime() {
|
||||
return cacheMaxIdleTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the L2 cache default max idle time in seconds.
|
||||
*/
|
||||
public void setCacheMaxIdleTime(int cacheMaxIdleTime) {
|
||||
this.cacheMaxIdleTime = cacheMaxIdleTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the L2 cache default max time to live in seconds.
|
||||
*/
|
||||
public int getCacheMaxTimeToLive() {
|
||||
return cacheMaxTimeToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the L2 cache default max time to live in seconds.
|
||||
*/
|
||||
public void setCacheMaxTimeToLive(int cacheMaxTimeToLive) {
|
||||
this.cacheMaxTimeToLive = cacheMaxTimeToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the L2 query cache default max size.
|
||||
*/
|
||||
public int getQueryCacheMaxSize() {
|
||||
return queryCacheMaxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the L2 query cache default max size.
|
||||
*/
|
||||
public void setQueryCacheMaxSize(int queryCacheMaxSize) {
|
||||
this.queryCacheMaxSize = queryCacheMaxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the L2 query cache default max idle time in seconds.
|
||||
*/
|
||||
public int getQueryCacheMaxIdleTime() {
|
||||
return queryCacheMaxIdleTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the L2 query cache default max idle time in seconds.
|
||||
*/
|
||||
public void setQueryCacheMaxIdleTime(int queryCacheMaxIdleTime) {
|
||||
this.queryCacheMaxIdleTime = queryCacheMaxIdleTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the L2 query cache default max time to live in seconds.
|
||||
*/
|
||||
public int getQueryCacheMaxTimeToLive() {
|
||||
return queryCacheMaxTimeToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the L2 query cache default max time to live in seconds.
|
||||
*/
|
||||
public void setQueryCacheMaxTimeToLive(int queryCacheMaxTimeToLive) {
|
||||
this.queryCacheMaxTimeToLive = queryCacheMaxTimeToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1049,6 +1350,23 @@ public class ServerConfig {
|
||||
this.searchJars = searchJars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the class name of a classPathReader implementation.
|
||||
*/
|
||||
public String getClassPathReaderClassName() {
|
||||
return classPathReaderClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class name of a classPathReader implementation.
|
||||
*
|
||||
* Refer to server.util.ClassPathReader, this should really by a plugin but doing this for now
|
||||
* to be relatively compatible with current implementation.
|
||||
*/
|
||||
public void setClassPathReaderClassName(String classPathReaderClassName) {
|
||||
this.classPathReaderClassName = classPathReaderClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of classes (entities, listeners, scalarTypes etc) that should
|
||||
* be used for this server.
|
||||
@@ -1334,44 +1652,26 @@ public class ServerConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the settings from the ebean.properties file.
|
||||
* Load settings from ebean.properties.
|
||||
*/
|
||||
public void loadFromProperties() {
|
||||
ConfigPropertyMap p = new ConfigPropertyMap(name);
|
||||
loadFromProperties(PropertyMap.defaultProperties());
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the settings from the given properties
|
||||
*/
|
||||
public void loadFromProperties(Properties properties) {
|
||||
PropertiesWrapper p = new PropertiesWrapper("ebean", name, properties);
|
||||
loadSettings(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a PropertySource for this server.
|
||||
*/
|
||||
public PropertySource getPropertySource() {
|
||||
return GlobalProperties.getPropertySource(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a configuration property using a default value.
|
||||
*/
|
||||
public String getProperty(String propertyName, String defaultValue) {
|
||||
PropertySource p = new ConfigPropertyMap(name);
|
||||
return p.get(propertyName, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a configuration property.
|
||||
*/
|
||||
public String getProperty(String propertyName) {
|
||||
return getProperty(propertyName, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T createInstance(PropertySource p, Class<T> type, String key) {
|
||||
private <T> T createInstance(PropertiesWrapper p, Class<T> pluginType, String key) {
|
||||
|
||||
String classname = p.get(key, null);
|
||||
if (classname == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (T) ClassUtil.newInstance(classname);
|
||||
return classname == null ? null : (T) ClassUtil.newInstance(classname);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1381,37 +1681,39 @@ public class ServerConfig {
|
||||
*
|
||||
* @param p - The defined property source passed to load settings
|
||||
*/
|
||||
protected void loadDataSourceSettings(PropertySource p) {
|
||||
dataSourceConfig.loadSettings(p.getServerName());
|
||||
protected void loadDataSourceSettings(PropertiesWrapper p) {
|
||||
dataSourceConfig.loadSettings(p.withPrefix("datasource"));
|
||||
}
|
||||
|
||||
/**
|
||||
* This is broken out for the same reason as above - preserve existing behaviour but let it be overridden.
|
||||
*/
|
||||
protected void loadAutofetchConfig(PropertySource p) {
|
||||
protected void loadAutofetchSettings(PropertiesWrapper p) {
|
||||
autofetchConfig.loadSettings(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the configuration settings from the properties file.
|
||||
*/
|
||||
protected void loadSettings(PropertySource p) {
|
||||
protected void loadSettings(PropertiesWrapper p) {
|
||||
|
||||
namingConvention = createNamingConvention(p, namingConvention);
|
||||
if (namingConvention != null) {
|
||||
namingConvention.loadFromProperties(p);
|
||||
}
|
||||
if (autofetchConfig == null) {
|
||||
autofetchConfig = new AutofetchConfig();
|
||||
}
|
||||
|
||||
loadAutofetchConfig(p);
|
||||
loadAutofetchSettings(p);
|
||||
|
||||
if (dataSourceConfig == null) {
|
||||
dataSourceConfig = new DataSourceConfig();
|
||||
}
|
||||
|
||||
loadDataSourceSettings(p);
|
||||
|
||||
autoCommitMode = p.getBoolean("autoCommitMode", false);
|
||||
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", false);
|
||||
namingConvention = createNamingConvention(p);
|
||||
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
|
||||
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
|
||||
|
||||
databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform");
|
||||
encryptKeyManager = createInstance(p, EncryptKeyManager.class, "encryptKeyManager");
|
||||
encryptDeployManager = createInstance(p, EncryptDeployManager.class, "encryptDeployManager");
|
||||
@@ -1425,37 +1727,41 @@ public class ServerConfig {
|
||||
searchJars = getSearchJarsPackages(jarsProp);
|
||||
}
|
||||
|
||||
String packagesProp = p.get("search.packages", p.get("packages", null));
|
||||
if (packages != null) {
|
||||
String packagesProp = p.get("search.packages", p.get("packages", null));
|
||||
packages = getSearchJarsPackages(packagesProp);
|
||||
}
|
||||
|
||||
collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", true);
|
||||
collectQueryOrigins = p.getBoolean("collectQueryOrigins", true);
|
||||
collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", collectQueryStatsByNode);
|
||||
collectQueryOrigins = p.getBoolean("collectQueryOrigins", collectQueryOrigins);
|
||||
|
||||
updateChangesOnly = p.getBoolean("updateChangesOnly", true);
|
||||
updateChangesOnly = p.getBoolean("updateChangesOnly", updateChangesOnly);
|
||||
|
||||
boolean defaultDeleteMissingChildren = p.getBoolean("defaultDeleteMissingChildren", true);
|
||||
boolean defaultDeleteMissingChildren = p.getBoolean("defaultDeleteMissingChildren", updatesDeleteMissingChildren);
|
||||
updatesDeleteMissingChildren = p.getBoolean("updatesDeleteMissingChildren", defaultDeleteMissingChildren);
|
||||
|
||||
boolean batchMode = p.getBoolean("batch.mode", false);
|
||||
persistBatching = p.getBoolean("persistBatching", batchMode);
|
||||
if (p.get("batch.mode") != null || p.get("persistBatching") != null) {
|
||||
throw new IllegalArgumentException("Property 'batch.mode' or 'persistBatching' is being set but no longer used. Please change to use 'persistBatchMode'");
|
||||
}
|
||||
|
||||
int batchSize = p.getInt("batch.size", 20);
|
||||
persistBatch = p.getEnum(PersistBatch.class, "persistBatch", persistBatch);
|
||||
persistBatchOnCascade = p.getEnum(PersistBatch.class, "persistBatchOnCascade", persistBatchOnCascade);
|
||||
|
||||
int batchSize = p.getInt("batch.size", persistBatchSize);
|
||||
persistBatchSize = p.getInt("persistBatchSize", batchSize);
|
||||
|
||||
persistenceContextScope = PersistenceContextScope.valueOf(p.get("persistenceContextScope","TRANSACTION"));
|
||||
|
||||
dataSourceJndiName = p.get("dataSourceJndiName", null);
|
||||
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", 20);
|
||||
databaseBooleanTrue = p.get("databaseBooleanTrue", null);
|
||||
databaseBooleanFalse = p.get("databaseBooleanFalse", null);
|
||||
databasePlatformName = p.get("databasePlatformName", null);
|
||||
uuidStoreAsBinary = p.getBoolean("uuidStoreAsBinary", false);
|
||||
localTimeWithNanos = p.getBoolean("localTimeWithNanos", false);
|
||||
dataSourceJndiName = p.get("dataSourceJndiName", dataSourceJndiName);
|
||||
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize);
|
||||
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
|
||||
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
|
||||
databasePlatformName = p.get("databasePlatformName", databasePlatformName);
|
||||
uuidStoreAsBinary = p.getBoolean("uuidStoreAsBinary", uuidStoreAsBinary);
|
||||
localTimeWithNanos = p.getBoolean("localTimeWithNanos", localTimeWithNanos);
|
||||
|
||||
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", 1);
|
||||
queryBatchSize = p.getInt("queryBatchSize", DEFAULT_QUERY_BATCH_SIZE);
|
||||
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", lazyLoadBatchSize);
|
||||
queryBatchSize = p.getInt("queryBatchSize", queryBatchSize);
|
||||
|
||||
String jsonDateTimeFormat = p.get("jsonDateTime", null);
|
||||
if (jsonDateTimeFormat != null) {
|
||||
@@ -1464,51 +1770,35 @@ public class ServerConfig {
|
||||
jsonDateTime = JsonConfig.DateTime.MILLIS;
|
||||
}
|
||||
|
||||
ddlGenerate = p.getBoolean("ddl.generate", false);
|
||||
ddlRun = p.getBoolean("ddl.run", false);
|
||||
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
|
||||
ddlRun = p.getBoolean("ddl.run", ddlRun);
|
||||
|
||||
classes = getClasses(p);
|
||||
}
|
||||
|
||||
private NamingConvention createNamingConvention(PropertySource p) {
|
||||
private NamingConvention createNamingConvention(PropertiesWrapper properties, NamingConvention namingConvention) {
|
||||
|
||||
NamingConvention nc = createInstance(p, NamingConvention.class, "namingconvention");
|
||||
if (nc == null) {
|
||||
return null;
|
||||
}
|
||||
if (nc instanceof AbstractNamingConvention) {
|
||||
AbstractNamingConvention anc = (AbstractNamingConvention) nc;
|
||||
String v = p.get("namingConvention.useForeignKeyPrefix", null);
|
||||
if (v != null) {
|
||||
boolean useForeignKeyPrefix = Boolean.valueOf(v);
|
||||
anc.setUseForeignKeyPrefix(useForeignKeyPrefix);
|
||||
}
|
||||
|
||||
String sequenceFormat = p.get("namingConvention.sequenceFormat", null);
|
||||
if (sequenceFormat != null) {
|
||||
anc.setSequenceFormat(sequenceFormat);
|
||||
}
|
||||
}
|
||||
return nc;
|
||||
NamingConvention nc = createInstance(properties, NamingConvention.class, "namingconvention");
|
||||
return (nc != null) ? nc : namingConvention;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the list of classes from the comma delimited string.
|
||||
*
|
||||
* @param p
|
||||
* the p
|
||||
* @param properties
|
||||
* the properties
|
||||
*
|
||||
* @return the classes
|
||||
*/
|
||||
private ArrayList<Class<?>> getClasses(PropertySource p) {
|
||||
private List<Class<?>> getClasses(PropertiesWrapper properties) {
|
||||
|
||||
String classNames = p.get("classes", null);
|
||||
String classNames = properties.get("classes", null);
|
||||
if (classNames == null) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
|
||||
List<Class<?>> classes = new ArrayList<Class<?>>();
|
||||
|
||||
String[] split = classNames.split("[ ,;]");
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
@@ -1539,4 +1829,15 @@ public class ServerConfig {
|
||||
return hitList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the PersistBatch mode to use for 'batchOnCascade' taking into account if the database
|
||||
* platform supports getGeneratedKeys in batch mode.
|
||||
* <p>
|
||||
* Used to effectively turn off batchOnCascade for SQL Server - still allows explicit batch mode.
|
||||
* </p>
|
||||
*/
|
||||
public PersistBatch appliedPersistBatchOnCascade() {
|
||||
|
||||
return databasePlatform.isDisallowBatchOnCascade() ? PersistBatch.NONE : persistBatchOnCascade;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,10 @@ public class DatabasePlatform {
|
||||
*/
|
||||
protected String name = "generic";
|
||||
|
||||
protected String columnAliasPrefix = "c";
|
||||
|
||||
protected String tableAliasPlaceHolder = "${ta}";
|
||||
|
||||
/**
|
||||
* Use a BackTick ` at the beginning and end of table or column names that you
|
||||
* want to use quoted identifiers for. The backticks get converted to the
|
||||
@@ -95,7 +99,14 @@ public class DatabasePlatform {
|
||||
* findIterate() and findVisit().
|
||||
*/
|
||||
protected boolean forwardOnlyHintOnFindIterate;
|
||||
|
||||
|
||||
/**
|
||||
* Flag set for SQL Server due to lack of support of getGeneratedKeys in
|
||||
* batch mode (meaning for batch inserts you should explicitly turn off
|
||||
* getGeneratedKeys - joy).
|
||||
*/
|
||||
protected boolean disallowBatchOnCascade;
|
||||
|
||||
/**
|
||||
* Instantiates a new database platform.
|
||||
*/
|
||||
@@ -166,6 +177,34 @@ public class DatabasePlatform {
|
||||
return dbDdlSyntax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the column alias prefix.
|
||||
*/
|
||||
public String getColumnAliasPrefix() {
|
||||
return columnAliasPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the column alias prefix.
|
||||
*/
|
||||
public void setColumnAliasPrefix(String columnAliasPrefix) {
|
||||
this.columnAliasPrefix = columnAliasPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the table alias placeholder.
|
||||
*/
|
||||
public String getTableAliasPlaceHolder() {
|
||||
return tableAliasPlaceHolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the table alias placeholder.
|
||||
*/
|
||||
public void setTableAliasPlaceHolder(String tableAliasPlaceHolder) {
|
||||
this.tableAliasPlaceHolder = tableAliasPlaceHolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the close quote for quoted identifiers.
|
||||
*
|
||||
@@ -335,4 +374,15 @@ public class DatabasePlatform {
|
||||
public String getLikeClause() {
|
||||
return likeClause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the persistBatchOnCascade setting should be ignored.
|
||||
* <p>
|
||||
* This is primarily for SQL Server which does not support getGeneratedKeys with jdbc batch mode
|
||||
* so can't really be transparently used.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isDisallowBatchOnCascade() {
|
||||
return disallowBatchOnCascade;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,30 +7,35 @@ import com.avaje.ebean.config.TableName;
|
||||
*/
|
||||
public class DbDdlSyntax {
|
||||
|
||||
private boolean renderIndexForFkey = true;
|
||||
protected boolean renderIndexForFkey = true;
|
||||
|
||||
private boolean inlinePrimaryKeyConstraint = false;
|
||||
protected boolean inlinePrimaryKeyConstraint = false;
|
||||
|
||||
private boolean addOneToOneUniqueContraint = true;
|
||||
protected boolean addOneToOneUniqueContraint = true;
|
||||
|
||||
private int maxConstraintNameLength = 32;
|
||||
protected int maxConstraintNameLength = 32;
|
||||
|
||||
private int columnNameWidth = 25;
|
||||
protected int columnNameWidth = 25;
|
||||
|
||||
private String dropTableCascade;
|
||||
private String dropIfExists;
|
||||
/**
|
||||
* Flag set when we want to disable constraints on each table (rather than globally).
|
||||
*/
|
||||
protected boolean dropKeyConstraints;
|
||||
|
||||
private String newLine = "\n";
|
||||
protected String dropTableCascade;
|
||||
protected String dropIfExists;
|
||||
|
||||
private String identity = "auto_increment";
|
||||
private String identitySuffix = "";
|
||||
protected String newLine = "\n";
|
||||
|
||||
private String pkPrefix = "pk_";
|
||||
protected String identity = "auto_increment";
|
||||
protected String identitySuffix = "";
|
||||
|
||||
private String disableReferentialIntegrity;
|
||||
private String enableReferentialIntegrity;
|
||||
protected String pkPrefix = "pk_";
|
||||
|
||||
private String foreignKeySuffix;
|
||||
protected String disableReferentialIntegrity;
|
||||
protected String enableReferentialIntegrity;
|
||||
|
||||
protected String foreignKeySuffix;
|
||||
|
||||
/**
|
||||
* Return the primary key name for a given bean descriptor.
|
||||
@@ -158,6 +163,20 @@ public class DbDdlSyntax {
|
||||
this.enableReferentialIntegrity = enableReferentialIntegrity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true to is constraints are disabled on each table.
|
||||
*/
|
||||
public boolean isDropKeyConstraints() {
|
||||
return dropKeyConstraints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return some DDL to disable constraints on the given table.
|
||||
*/
|
||||
public String dropKeyConstraintPrefix(String tableName, String fkName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if indexes should be created for the foreign keys.
|
||||
*/
|
||||
@@ -188,6 +207,13 @@ public class DbDdlSyntax {
|
||||
this.dropIfExists = dropIfExists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return prefix text that goes before drop table.
|
||||
*/
|
||||
public String dropTablePrefix(String tableName) {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cascade option for the drop table command.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@@ -14,15 +13,13 @@ public class H2Platform extends DatabasePlatform {
|
||||
super();
|
||||
this.name = "h2";
|
||||
this.dbEncrypt = new H2DbEncrypt();
|
||||
this.likeClause = "like ? escape''";
|
||||
// like ? escape'' not working in the latest version H2 so just using no
|
||||
// escape clause for now noting that backslash is an escape char for like in H2
|
||||
this.likeClause = "like ?";
|
||||
|
||||
// only support getGeneratedKeys with non-batch JDBC
|
||||
// so generally use SEQUENCE instead of IDENTITY for H2
|
||||
boolean useIdentity = GlobalProperties.getBoolean("ebean.h2platform.useIdentity", false);
|
||||
|
||||
IdType idType = useIdentity ? IdType.IDENTITY : IdType.SEQUENCE;
|
||||
this.dbIdentity.setIdType(idType);
|
||||
|
||||
this.dbIdentity.setIdType(IdType.SEQUENCE);
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
this.dbIdentity.setSupportsIdentity(true);
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import java.sql.Types;
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* H2 specific platform.
|
||||
@@ -16,14 +14,7 @@ public class HsqldbPlatform extends DatabasePlatform {
|
||||
super();
|
||||
this.name = "hsqldb";
|
||||
this.dbEncrypt = new H2DbEncrypt();
|
||||
|
||||
// only support getGeneratedKeys with non-batch JDBC
|
||||
// so generally use SEQUENCE instead of IDENTITY for H2
|
||||
boolean useIdentity = GlobalProperties.getBoolean("ebean.hsqldb.useIdentity", true);
|
||||
|
||||
IdType idType = useIdentity ? IdType.IDENTITY : IdType.SEQUENCE;
|
||||
this.dbIdentity.setIdType(idType);
|
||||
|
||||
this.dbIdentity.setIdType(IdType.IDENTITY);
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
this.dbIdentity.setSupportsIdentity(true);
|
||||
|
||||
@@ -17,8 +17,12 @@ public class MsSqlServer2005Platform extends DatabasePlatform {
|
||||
public MsSqlServer2005Platform() {
|
||||
super();
|
||||
this.name = "mssqlserver2005";
|
||||
// effectively disable persistBatchOnCascade mode for SQL Server
|
||||
// due to lack of support for getGeneratedKeys in batch mode
|
||||
this.disallowBatchOnCascade = true;
|
||||
this.idInExpandedForm = true;
|
||||
this.sqlLimiter = new MsSqlServer2005SqlLimiter();
|
||||
this.dbDdlSyntax.setIdentity("identity(1,1)");
|
||||
this.dbDdlSyntax = new MsDdlSyntax();
|
||||
this.dbIdentity.setIdType(IdType.IDENTITY);
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsIdentity(true);
|
||||
@@ -28,6 +32,7 @@ public class MsSqlServer2005Platform extends DatabasePlatform {
|
||||
|
||||
dbTypeMap.put(Types.BOOLEAN, new DbType("bit default 0"));
|
||||
|
||||
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
|
||||
dbTypeMap.put(Types.BIGINT, new DbType("numeric", 19));
|
||||
dbTypeMap.put(Types.REAL, new DbType("float(16)"));
|
||||
dbTypeMap.put(Types.DOUBLE, new DbType("float(32)"));
|
||||
@@ -45,4 +50,30 @@ public class MsSqlServer2005Platform extends DatabasePlatform {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* MS SQL Server specific DDL Syntax.
|
||||
*/
|
||||
public class MsDdlSyntax extends DbDdlSyntax {
|
||||
|
||||
MsDdlSyntax() {
|
||||
this.identity = "identity(1,1)";
|
||||
this.dropKeyConstraints = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return some DDL to disable constraints on the given table.
|
||||
*/
|
||||
public String dropKeyConstraintPrefix(String tableName, String fkName) {
|
||||
return "IF OBJECT_ID('"+fkName+"', 'F') IS NOT NULL";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return prefix text that goes before drop table.
|
||||
*/
|
||||
public String dropTablePrefix(String tableName) {
|
||||
return "IF OBJECT_ID('"+tableName+"', 'U') IS NOT NULL ";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
|
||||
/**
|
||||
* Oracle encryption support.
|
||||
*
|
||||
@@ -48,12 +46,19 @@ import com.avaje.ebean.config.GlobalProperties;
|
||||
public class Oracle10DbEncrypt extends AbstractDbEncrypt {
|
||||
|
||||
/**
|
||||
* Constructs the Oracle10DbEncrypt.
|
||||
* Constructs the Oracle10DbEncrypt with default encrypt and decrypt stored procedures.
|
||||
*/
|
||||
public Oracle10DbEncrypt() {
|
||||
this("eb_encrypt", "eb_decrypt");
|
||||
}
|
||||
|
||||
String encryptfunction = GlobalProperties.get("ebean.oracle.encryptfunction", "eb_encrypt");
|
||||
String decryptfunction = GlobalProperties.get("ebean.oracle.decryptfunction", "eb_decrypt");
|
||||
/**
|
||||
* Constructs the Oracle10DbEncrypt specifying encrypt and decrypt stored procedures.
|
||||
*
|
||||
* @param encryptfunction the encrypt stored procedure
|
||||
* @param decryptfunction the decrypt stored procedure
|
||||
*/
|
||||
public Oracle10DbEncrypt(String encryptfunction, String decryptfunction) {
|
||||
|
||||
this.varcharEncryptFunction = new OraVarcharFunction(encryptfunction, decryptfunction);
|
||||
this.dateEncryptFunction = new OraDateFunction(encryptfunction, decryptfunction);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Types;
|
||||
@@ -27,12 +26,7 @@ public class Postgres8Platform extends DatabasePlatform {
|
||||
this.dbIdentity.setIdType(IdType.SEQUENCE);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
|
||||
String colAlias = GlobalProperties.get("ebean.columnAliasPrefix", null);
|
||||
if (colAlias == null) {
|
||||
// Postgres requires the "as" keyword for column alias
|
||||
GlobalProperties.put("ebean.columnAliasPrefix", "as c");
|
||||
}
|
||||
|
||||
this.columnAliasPrefix = "as c";
|
||||
this.openQuote = "\"";
|
||||
this.closeQuote = "\"";
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
@@ -38,11 +36,7 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
|
||||
String colAlias = GlobalProperties.get("ebean.columnAliasPrefix", null);
|
||||
if (colAlias == null) {
|
||||
// Postgres requires the "as" keyword for column alias
|
||||
GlobalProperties.put("ebean.columnAliasPrefix", "as c");
|
||||
}
|
||||
this.columnAliasPrefix = "as c";
|
||||
|
||||
this.openQuote = "\"";
|
||||
this.closeQuote = "\"";
|
||||
|
||||
@@ -40,7 +40,7 @@ public abstract class SequenceIdGenerator implements IdGenerator {
|
||||
|
||||
protected final BackgroundExecutor backgroundExecutor;
|
||||
|
||||
protected final ArrayList<Integer> idList = new ArrayList<Integer>(50);
|
||||
protected final ArrayList<Long> idList = new ArrayList<Long>(50);
|
||||
|
||||
protected int batchSize;
|
||||
|
||||
@@ -117,7 +117,7 @@ public abstract class SequenceIdGenerator implements IdGenerator {
|
||||
if (idList.size() == 0) {
|
||||
loadMoreIds(batchSize, t);
|
||||
}
|
||||
Integer nextId = idList.remove(0);
|
||||
Long nextId = idList.remove(0);
|
||||
|
||||
if (batchSize > 1) {
|
||||
if (idList.size() <= batchSize / 2) {
|
||||
@@ -158,7 +158,7 @@ public abstract class SequenceIdGenerator implements IdGenerator {
|
||||
|
||||
protected void loadMoreIds(final int numberToLoad, Transaction t) {
|
||||
|
||||
ArrayList<Integer> newIds = getMoreIds(numberToLoad, t);
|
||||
ArrayList<Long> newIds = getMoreIds(numberToLoad, t);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("... seq:" + seqName + " loaded:" + numberToLoad + " ids:" + newIds);
|
||||
@@ -174,11 +174,11 @@ public abstract class SequenceIdGenerator implements IdGenerator {
|
||||
/**
|
||||
* Get more Id's by executing a query and reading the Id's returned.
|
||||
*/
|
||||
protected ArrayList<Integer> getMoreIds(int loadSize, Transaction t) {
|
||||
protected ArrayList<Long> getMoreIds(int loadSize, Transaction t) {
|
||||
|
||||
String sql = getSql(loadSize);
|
||||
|
||||
ArrayList<Integer> newIds = new ArrayList<Integer>(loadSize);
|
||||
ArrayList<Long> newIds = new ArrayList<Long>(loadSize);
|
||||
|
||||
boolean useTxnConnection = t != null;
|
||||
|
||||
@@ -191,12 +191,10 @@ public abstract class SequenceIdGenerator implements IdGenerator {
|
||||
pstmt = c.prepareStatement(sql);
|
||||
rset = pstmt.executeQuery();
|
||||
while (rset.next()) {
|
||||
int val = rset.getInt(1);
|
||||
newIds.add(Integer.valueOf(val));
|
||||
newIds.add(rset.getLong(1));
|
||||
}
|
||||
if (newIds.size() == 0) {
|
||||
String m = "Always expecting more than 1 row from " + sql;
|
||||
throw new PersistenceException(m);
|
||||
throw new PersistenceException("Always expecting more than 1 row from " + sql);
|
||||
}
|
||||
|
||||
return newIds;
|
||||
|
||||
@@ -33,7 +33,7 @@ import com.avaje.ebean.config.ServerConfig;
|
||||
* </p>
|
||||
* <p>
|
||||
* A BeanPersistListener is either found automatically via class path search or
|
||||
* can be added programmatically via {@link ServerConfig#add(BeanPersistListener<?>)}}.
|
||||
* can be added programmatically via {@link ServerConfig#add(BeanPersistListener)}}.
|
||||
* </p>
|
||||
* @see ServerConfig#add(BeanPersistListener)
|
||||
*/
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<title>Ebean API</title>
|
||||
</head>
|
||||
<body BGCOLOR="#ffffff">
|
||||
Ebean Object Relational Mapping (start at <a href='com/avaje/ebean/Ebean.html'>Ebean</a>
|
||||
or <a href='com/avaje/ebean/EbeanServer.html'>EbeanServer</a>).
|
||||
Ebean Object Relational Mapping (start at
|
||||
<a href='com/avaje/ebean/EbeanServer.html'>EbeanServer</a> or <a href='com/avaje/ebean/Ebean.html'>Ebean</a>).
|
||||
|
||||
|
||||
<h3>Ebean</h3>
|
||||
@@ -21,15 +21,15 @@ For a full description of the query language refer to <a href="com/avaje/ebean/Q
|
||||
<h3>
|
||||
EXAMPLE 1: Simple fetch
|
||||
</h3>
|
||||
<pre class="code">
|
||||
<pre>{@code
|
||||
// fetch order 10
|
||||
Order order = Ebean.find(Order.class, 10);
|
||||
</pre>
|
||||
}</pre>
|
||||
|
||||
<h3>
|
||||
EXAMPLE 2: Fetch an Object with associations
|
||||
</h3>
|
||||
<pre class="code">
|
||||
<pre>{@code
|
||||
// fetch Customer 7 including their billing and shipping addresses
|
||||
Customer customer = Ebean.find(Customer.class)
|
||||
.fetch("billingAddress");
|
||||
@@ -40,19 +40,19 @@ Customer customer = Ebean.find(Customer.class)
|
||||
|
||||
Address billAddr = customer.getBillingAddress();
|
||||
Address shipAddr = customer.getShippingAddress();
|
||||
</pre>
|
||||
}</pre>
|
||||
|
||||
<h3>
|
||||
EXAMPLE 3: Fetch a list of Objects with associations
|
||||
</h3>
|
||||
<pre class="code">
|
||||
<pre>{@code
|
||||
// Note: This example shows a "Partial Object".
|
||||
// For the product objects associated with the
|
||||
// order details only the product id and name is
|
||||
// fetched (the product objects are partially populated).
|
||||
|
||||
// fetch orders for customer.id = 2
|
||||
List<Order> orderList = Ebean.find(Order.class);
|
||||
List<Order> orderList = Ebean.find(Order.class);
|
||||
.fetch("customer")
|
||||
.fetch("customer.shippingAddress")
|
||||
.fetch("details")
|
||||
@@ -72,17 +72,17 @@ Order order = orderList.get(0);
|
||||
Customer customer = order.getCustomer();
|
||||
Address shipAddr = customer.getShippingAddress();
|
||||
|
||||
List<OrderDetail> details = order.getDetails();
|
||||
List<OrderDetail> details = order.getDetails();
|
||||
OrderDetail detail = details.get(0);
|
||||
Product product = detail.getProduct();
|
||||
String productName = product.getName();
|
||||
|
||||
</pre>
|
||||
}</pre>
|
||||
|
||||
<h3>
|
||||
EXAMPLE 4: Create and save an Order
|
||||
</h3>
|
||||
<pre class="code">
|
||||
<pre>{@code
|
||||
// get a Customer reference so we don't hit the database
|
||||
Customer custRef = Ebean.getReference(Customer.class, 7);
|
||||
|
||||
@@ -107,14 +107,14 @@ orderLines.add(line);
|
||||
// NB: assumes CascadeType.PERSIST is set on the order lines association
|
||||
Ebean.save(newOrder);
|
||||
|
||||
</pre>
|
||||
}</pre>
|
||||
|
||||
<h3>
|
||||
EXAMPLE 5: Use another database
|
||||
</h3>
|
||||
<pre class="code">
|
||||
<pre>{@code
|
||||
// Get access to the Human Resources EbeanServer/Database
|
||||
EbeanServer hrServer = Ebean.getServer("HR");
|
||||
EbeanServer hrServer = Ebean.getServer("HR");
|
||||
|
||||
|
||||
// fetch contact 3 from the HR database
|
||||
@@ -125,7 +125,7 @@ contact.setStatus(Contact.Status.INACTIVE);
|
||||
|
||||
// save the contact back to the HR database
|
||||
hrServer.save(contact);
|
||||
</pre>
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -19,9 +19,6 @@ import com.avaje.ebean.Query;
|
||||
* properties and applying that to both what to fetch (ORM query) and what to
|
||||
* render (JAX-RS JSON / XML).
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*
|
||||
*/
|
||||
public class PathProperties {
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
|
||||
@@ -58,37 +59,68 @@ public interface JsonContext {
|
||||
public Object toObject(Type genericType, String json) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Write the bean or collection in JSON format to the writer with default
|
||||
* options.
|
||||
*
|
||||
* @param value the bean or collection of beans to write
|
||||
* @param writer used to write the json output to
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public void toJson(Object value, Writer writer) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* With additional options to specify JsonValueAdapter and
|
||||
* JsonWriteBeanVisitor's.
|
||||
*
|
||||
* @param value the bean or collection of beans to write
|
||||
* @param writer used to write the json output to
|
||||
* @param options additional options to control the JSON output
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public void toJson(Object value, Writer writer, JsonWriteOptions options) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Convert a bean or collection to json string using default options.
|
||||
* Return the bean or collection as JSON string.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public String toJson(Object value) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Write the bean or collection in JSON format to the writer.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public void toJson(Object value, Writer writer) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Write the bean or collection to the JsonGenerator.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public void toJson(Object value, JsonGenerator generator) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Return the bean or collection as JSON string using PathProperties.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public String toJson(Object value, PathProperties pathProperties) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Write the bean or collection as json to the writer using the PathProperties.
|
||||
*/
|
||||
public void toJson(Object value, Writer writer, PathProperties pathProperties) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Write the bean or collection to the JsonGenerator using the PathProperties.
|
||||
*/
|
||||
public void toJson(Object value, JsonGenerator generator, PathProperties pathProperties) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Deprecated in favour of using PathProperties by itself.
|
||||
* Write json to the JsonGenerator using the JsonWriteOptions.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public void toJson(Object value, JsonGenerator generator, JsonWriteOptions options) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Deprecated in favour of using PathProperties by itself.
|
||||
* With additional options.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public void toJson(Object value, Writer writer, JsonWriteOptions options) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Deprecated in favour of using PathProperties by itself.
|
||||
* Convert a bean or collection to json string.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public String toJson(Object value, JsonWriteOptions options) throws JsonIOException;
|
||||
|
||||
|
||||
@@ -3,18 +3,14 @@ package com.avaje.ebean.text.json;
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
|
||||
/**
|
||||
* Deprecated in favour of just using PathProperties.
|
||||
*
|
||||
* Provides options for customising the JSON write process.
|
||||
* <p>
|
||||
* You can explicitly state which properties to include in the JSON output for
|
||||
* the root level and each path.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
|
||||
* // output as a JSON string with pretty formatting
|
||||
* String s = json.toJson(list, true, writeOptions);
|
||||
*
|
||||
* </pre>
|
||||
* @deprecated
|
||||
*/
|
||||
public class JsonWriteOptions {
|
||||
|
||||
@@ -24,12 +20,20 @@ public class JsonWriteOptions {
|
||||
* Parse and return a PathProperties from nested string format like
|
||||
* (a,b,c(d,e),f(g)) where "c" is a path containing "d" and "e" and "f" is a
|
||||
* path containing "g" and the root path contains "a","b","c" and "f".
|
||||
*
|
||||
* @see com.avaje.ebean.text.PathProperties#parse(String)
|
||||
*/
|
||||
public static JsonWriteOptions parsePath(String pathProperties) {
|
||||
|
||||
PathProperties p = PathProperties.parse(pathProperties);
|
||||
return pathProperties(PathProperties.parse(pathProperties));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct JsonWriteOptions with the given pathProperties.
|
||||
*/
|
||||
public static JsonWriteOptions pathProperties(PathProperties pathProperties) {
|
||||
JsonWriteOptions o = new JsonWriteOptions();
|
||||
o.setPathProperties(p);
|
||||
o.setPathProperties(pathProperties);
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.api;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.avaje.ebean.TxScope;
|
||||
import com.avaje.ebean.config.PersistBatch;
|
||||
|
||||
/**
|
||||
* Used internally to handle the scoping of transactions for methods.
|
||||
@@ -10,14 +11,14 @@ import com.avaje.ebean.TxScope;
|
||||
public class ScopeTrans implements Thread.UncaughtExceptionHandler {
|
||||
|
||||
private static final int OPCODE_ATHROW = 191;
|
||||
//private static final int OPCODE_ATHROW = com.avaje.ebean.enhance.asm.Opcodes.ATHROW;
|
||||
|
||||
|
||||
private final SpiTransactionScopeManager scopeMgr;
|
||||
|
||||
/**
|
||||
* The suspended transaction (can be null).
|
||||
*/
|
||||
private final SpiTransaction suspendedTransaction;
|
||||
|
||||
/**
|
||||
* The transaction in scope (can be null).
|
||||
*/
|
||||
@@ -44,7 +45,13 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
|
||||
*/
|
||||
private final ArrayList<Class<? extends Throwable>> rollbackFor;
|
||||
|
||||
/**
|
||||
private PersistBatch restoreBatch;
|
||||
|
||||
private PersistBatch restoreBatchOnCascade;
|
||||
|
||||
private int restoreBatchSize;
|
||||
|
||||
/**
|
||||
* Flag set when a rollback has occurred.
|
||||
*/
|
||||
private boolean rolledBack;
|
||||
@@ -61,6 +68,24 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
|
||||
|
||||
this.noRollbackFor = txScope.getNoRollbackFor();
|
||||
this.rollbackFor = txScope.getRollbackFor();
|
||||
|
||||
if (transaction != null) {
|
||||
if (!created && txScope.isBatchSet() || txScope.isBatchOnCascadeSet() || txScope.isBatchSizeSet()) {
|
||||
restoreBatch = transaction.getBatch();
|
||||
restoreBatchOnCascade = transaction.getBatchOnCascade();
|
||||
restoreBatchSize = transaction.getBatchSize();
|
||||
}
|
||||
if (txScope.isBatchSet()) {
|
||||
transaction.setBatch(txScope.getBatch());
|
||||
}
|
||||
if (txScope.isBatchOnCascadeSet()) {
|
||||
transaction.setBatchOnCascade(txScope.getBatchOnCascade());
|
||||
}
|
||||
if (txScope.isBatchSizeSet()) {
|
||||
transaction.setBatchSize(txScope.getBatchSize());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,10 +122,22 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
|
||||
*/
|
||||
public void onFinally() {
|
||||
try {
|
||||
if (!rolledBack && created) {
|
||||
transaction.commit();
|
||||
if (!rolledBack) {
|
||||
if (created) {
|
||||
transaction.commit();
|
||||
} else {
|
||||
if (restoreBatch != null) {
|
||||
transaction.setBatch(restoreBatch);
|
||||
}
|
||||
if (restoreBatchOnCascade != null) {
|
||||
transaction.setBatchOnCascade(restoreBatchOnCascade);
|
||||
}
|
||||
if (restoreBatchSize > 0) {
|
||||
transaction.setBatchSize(restoreBatchSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} finally {
|
||||
if (suspendedTransaction != null){
|
||||
// put the previously suspended transaction
|
||||
|
||||
@@ -5,6 +5,8 @@ import java.util.List;
|
||||
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequest;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
|
||||
import com.avaje.ebeaninternal.server.persist.BatchControl;
|
||||
|
||||
/**
|
||||
@@ -119,6 +121,11 @@ public interface SpiTransaction extends Transaction {
|
||||
*/
|
||||
public int depth(int diff);
|
||||
|
||||
/**
|
||||
* Return the current depth.
|
||||
*/
|
||||
public int depth();
|
||||
|
||||
/**
|
||||
* Return true if this transaction was created explicitly via
|
||||
* <code>Ebean.beginTransaction()</code>.
|
||||
@@ -144,7 +151,7 @@ public interface SpiTransaction extends Transaction {
|
||||
* Return true if this request should be batched. Conversely returns false
|
||||
* if this request should be executed immediately.
|
||||
*/
|
||||
public boolean isBatchThisRequest();
|
||||
public boolean isBatchThisRequest(PersistRequest.Type type);
|
||||
|
||||
/**
|
||||
* Return the queue used to batch up persist requests.
|
||||
@@ -194,4 +201,25 @@ public interface SpiTransaction extends Transaction {
|
||||
* Return true if the manyToMany intersection should be persisted for this particular relationship direction.
|
||||
*/
|
||||
public boolean isSaveAssocManyIntersection(String intersectionTable, String beanName);
|
||||
|
||||
/**
|
||||
* Return true if batch mode got escalated for this request (and associated cascades).
|
||||
*/
|
||||
public boolean checkBatchEscalationOnCascade(PersistRequestBean<?> request);
|
||||
|
||||
/**
|
||||
* If batch mode was turned on for the request then flush the batch.
|
||||
*/
|
||||
public void flushBatchOnCascade();
|
||||
|
||||
/**
|
||||
* Potentially escalate batch mode on saving or deleting a collection.
|
||||
*/
|
||||
public void checkBatchEscalationOnCollection();
|
||||
|
||||
/**
|
||||
* Flush batch if we escalated batch mode on saving or deleting a collection.
|
||||
*/
|
||||
public void flushBatchOnCollection();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.transaction.BeanDelta;
|
||||
import com.avaje.ebeaninternal.server.transaction.DeleteByIdMap;
|
||||
import com.avaje.ebeaninternal.server.transaction.IndexInvalidate;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Holds information for a transaction. There is one TransactionEvent instance
|
||||
@@ -25,180 +17,107 @@ import org.slf4j.LoggerFactory;
|
||||
*/
|
||||
public class TransactionEvent implements Serializable {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TransactionEvent.class);
|
||||
|
||||
private static final long serialVersionUID = 7230903304106097120L;
|
||||
private static final long serialVersionUID = 7230903304106097120L;
|
||||
|
||||
/**
|
||||
* Flag indicating this is a local transaction (not from another server in
|
||||
* the cluster).
|
||||
*/
|
||||
private transient boolean local;
|
||||
/**
|
||||
* Flag indicating this is a local transaction (not from another server in
|
||||
* the cluster).
|
||||
*/
|
||||
private transient boolean local;
|
||||
|
||||
private boolean invalidateAll;
|
||||
private TransactionEventTable eventTables;
|
||||
|
||||
private TransactionEventTable eventTables;
|
||||
|
||||
private transient TransactionEventBeans eventBeans;
|
||||
private transient TransactionEventBeans eventBeans;
|
||||
|
||||
private transient List<BeanDelta> beanDeltas;
|
||||
private transient DeleteByIdMap deleteByIdMap;
|
||||
|
||||
private transient DeleteByIdMap deleteByIdMap;
|
||||
|
||||
private transient Set<IndexInvalidate> indexInvalidations;
|
||||
|
||||
private transient Set<String> pauseIndexInvalidate;
|
||||
|
||||
/**
|
||||
* Create the TransactionEvent, one per Transaction.
|
||||
*/
|
||||
public TransactionEvent() {
|
||||
this.local = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to true to invalidate all table dependent cached objects.
|
||||
*/
|
||||
public void setInvalidateAll(boolean isInvalidateAll) {
|
||||
this.invalidateAll = isInvalidateAll;
|
||||
}
|
||||
/**
|
||||
* Create the TransactionEvent, one per Transaction.
|
||||
*/
|
||||
public TransactionEvent() {
|
||||
this.local = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if all table states should be invalidated. This will cause
|
||||
* all cached objects to be invalidated.
|
||||
*/
|
||||
public boolean isInvalidateAll() {
|
||||
return invalidateAll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily pause/ignore any index invalidation for this bean type.
|
||||
*/
|
||||
public void pauseIndexInvalidate(Class<?> beanType) {
|
||||
if (pauseIndexInvalidate == null){
|
||||
pauseIndexInvalidate = new HashSet<String>();
|
||||
}
|
||||
pauseIndexInvalidate.add(beanType.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume listening for index invalidation for this bean type.
|
||||
*/
|
||||
public void resumeIndexInvalidate(Class<?> beanType) {
|
||||
if (pauseIndexInvalidate != null){
|
||||
pauseIndexInvalidate.remove(beanType.getName());
|
||||
}
|
||||
public void addDeleteById(BeanDescriptor<?> desc, Object id) {
|
||||
if (deleteByIdMap == null) {
|
||||
deleteByIdMap = new DeleteByIdMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an IndexInvalidation notices to the transaction.
|
||||
*/
|
||||
public void addIndexInvalidate(IndexInvalidate indexEvent){
|
||||
if (pauseIndexInvalidate != null && pauseIndexInvalidate.contains(indexEvent.getIndexName())){
|
||||
logger.debug("--- IGNORE Invalidate on "+indexEvent.getIndexName());
|
||||
return;
|
||||
}
|
||||
if (indexInvalidations == null){
|
||||
indexInvalidations = new HashSet<IndexInvalidate>();
|
||||
}
|
||||
indexInvalidations.add(indexEvent);
|
||||
}
|
||||
|
||||
public void addDeleteById(BeanDescriptor<?> desc, Object id){
|
||||
if (deleteByIdMap == null){
|
||||
deleteByIdMap = new DeleteByIdMap();
|
||||
}
|
||||
deleteByIdMap.add(desc, id);
|
||||
}
|
||||
|
||||
public void addDeleteByIdList(BeanDescriptor<?> desc, List<Object> idList) {
|
||||
if (deleteByIdMap == null) {
|
||||
deleteByIdMap = new DeleteByIdMap();
|
||||
}
|
||||
deleteByIdMap.addList(desc, idList);
|
||||
deleteByIdMap.add(desc, id);
|
||||
}
|
||||
|
||||
public void addDeleteByIdList(BeanDescriptor<?> desc, List<Object> idList) {
|
||||
if (deleteByIdMap == null) {
|
||||
deleteByIdMap = new DeleteByIdMap();
|
||||
}
|
||||
|
||||
public DeleteByIdMap getDeleteByIdMap() {
|
||||
return deleteByIdMap;
|
||||
deleteByIdMap.addList(desc, idList);
|
||||
}
|
||||
|
||||
public DeleteByIdMap getDeleteByIdMap() {
|
||||
return deleteByIdMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this was a local transaction. Returns false if this
|
||||
* transaction originated on another server in the cluster.
|
||||
*/
|
||||
public boolean isLocal() {
|
||||
return local;
|
||||
}
|
||||
|
||||
/**
|
||||
* For BeanListeners the requests they are interested in.
|
||||
*/
|
||||
public TransactionEventBeans getEventBeans() {
|
||||
return eventBeans;
|
||||
}
|
||||
|
||||
public TransactionEventTable getEventTables() {
|
||||
return eventTables;
|
||||
}
|
||||
|
||||
public void add(String tableName, boolean inserts, boolean updates, boolean deletes) {
|
||||
if (eventTables == null) {
|
||||
eventTables = new TransactionEventTable();
|
||||
}
|
||||
eventTables.add(tableName, inserts, updates, deletes);
|
||||
}
|
||||
|
||||
public void addBeanDelta(BeanDelta delta) {
|
||||
if (beanDeltas == null) {
|
||||
beanDeltas = new ArrayList<BeanDelta>();
|
||||
}
|
||||
beanDeltas.add(delta);
|
||||
public void add(TransactionEventTable table) {
|
||||
if (eventTables == null) {
|
||||
eventTables = new TransactionEventTable();
|
||||
}
|
||||
eventTables.add(table);
|
||||
}
|
||||
|
||||
public List<BeanDelta> getBeanDeltas() {
|
||||
return beanDeltas;
|
||||
/**
|
||||
* Add a inserted updated or deleted bean to the event.
|
||||
*/
|
||||
public void add(PersistRequestBean<?> request) {
|
||||
|
||||
if (request.isNotify()) {
|
||||
// either a BeanListener or Cache is interested
|
||||
if (eventBeans == null) {
|
||||
eventBeans = new TransactionEventBeans();
|
||||
}
|
||||
eventBeans.add(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this was a local transaction. Returns false if this
|
||||
* transaction originated on another server in the cluster.
|
||||
*/
|
||||
public boolean isLocal() {
|
||||
return local;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For BeanListeners the requests they are interested in.
|
||||
*/
|
||||
public TransactionEventBeans getEventBeans() {
|
||||
return eventBeans;
|
||||
}
|
||||
|
||||
public TransactionEventTable getEventTables() {
|
||||
return eventTables;
|
||||
}
|
||||
|
||||
public Set<IndexInvalidate> getIndexInvalidations() {
|
||||
return indexInvalidations;
|
||||
/**
|
||||
* Notify the cache of bean changes.
|
||||
* <p>
|
||||
* This returns the TransactionEventTable so that if any
|
||||
* general table changes can also be used to invalidate
|
||||
* parts of the cache.
|
||||
* </p>
|
||||
*/
|
||||
public void notifyCache() {
|
||||
if (eventBeans != null) {
|
||||
eventBeans.notifyCache();
|
||||
}
|
||||
|
||||
public void add(String tableName, boolean inserts, boolean updates, boolean deletes){
|
||||
if (eventTables == null){
|
||||
eventTables = new TransactionEventTable();
|
||||
}
|
||||
eventTables.add(tableName, inserts, updates, deletes);
|
||||
}
|
||||
|
||||
public void add(TransactionEventTable table){
|
||||
if (eventTables == null){
|
||||
eventTables = new TransactionEventTable();
|
||||
}
|
||||
eventTables.add(table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a inserted updated or deleted bean to the event.
|
||||
*/
|
||||
public void add(PersistRequestBean<?> request) {
|
||||
|
||||
if (request.isNotify(this)){
|
||||
// either a BeanListener or Cache is interested
|
||||
if (eventBeans == null) {
|
||||
eventBeans = new TransactionEventBeans();
|
||||
}
|
||||
eventBeans.add(request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the cache of bean changes.
|
||||
* <p>
|
||||
* This returns the TransactionEventTable so that if any
|
||||
* general table changes can also be used to invalidate
|
||||
* parts of the cache.
|
||||
* </p>
|
||||
*/
|
||||
public void notifyCache(){
|
||||
if (eventBeans != null){
|
||||
eventBeans.notifyCache();
|
||||
}
|
||||
if (deleteByIdMap != null) {
|
||||
deleteByIdMap.notifyCache();
|
||||
}
|
||||
}
|
||||
if (deleteByIdMap != null) {
|
||||
deleteByIdMap.notifyCache();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ public final class TransactionEventTable implements Serializable {
|
||||
private boolean update;
|
||||
private boolean delete;
|
||||
|
||||
private TableIUD(String table, boolean insert, boolean update, boolean delete){
|
||||
public TableIUD(String table, boolean insert, boolean update, boolean delete){
|
||||
this.table = table;
|
||||
this.insert = insert;
|
||||
this.update = update;
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
package com.avaje.ebeaninternal.server.autofetch;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.resource.ResourceManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
|
||||
public class AutoFetchManagerFactory {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AutoFetchManagerFactory.class);
|
||||
@@ -37,7 +35,7 @@ public class AutoFetchManagerFactory {
|
||||
|
||||
AutoFetchManager autoFetchManager = null;
|
||||
|
||||
boolean readFile = GlobalProperties.getBoolean("autofetch.readfromfile", true);
|
||||
boolean readFile = !"false".equalsIgnoreCase(System.getProperty("autofetch.readfromfile"));
|
||||
if (readFile) {
|
||||
autoFetchManager = deserializeAutoFetch(autoFetchFile);
|
||||
}
|
||||
|
||||
+14
-10
@@ -22,6 +22,8 @@ import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The manager of all the usage/query statistics as well as the tuned fetch
|
||||
@@ -29,6 +31,8 @@ import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
*/
|
||||
public class DefaultAutoFetchManager implements AutoFetchManager, Serializable {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultAutoFetchManager.class);
|
||||
|
||||
private static final long serialVersionUID = -6826119882781771722L;
|
||||
|
||||
private final String statisticsMonitor = new String();
|
||||
@@ -551,16 +555,16 @@ public class DefaultAutoFetchManager implements AutoFetchManager, Serializable {
|
||||
|
||||
Statistics stats = getQueryPointStats(origin);
|
||||
|
||||
if (logging.isTraceUsageCollection()){
|
||||
System.out.println("... NodeUsageCollector "+usageCollector);
|
||||
}
|
||||
|
||||
stats.collectUsageInfo(usageCollector);
|
||||
|
||||
if (logging.isTraceUsageCollection()){
|
||||
System.out.println("stats\n"+stats);
|
||||
}
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("... NodeUsageCollector " + usageCollector);
|
||||
}
|
||||
|
||||
stats.collectUsageInfo(usageCollector);
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("stats\n" + stats);
|
||||
}
|
||||
}
|
||||
|
||||
private Statistics getQueryPointStats(ObjectGraphOrigin originQueryPoint) {
|
||||
synchronized (statisticsMonitor) {
|
||||
|
||||
+5
-15
@@ -1,14 +1,12 @@
|
||||
package com.avaje.ebeaninternal.server.autofetch;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Handles the logging aspects for the DefaultAutoFetchListener.
|
||||
@@ -23,15 +21,12 @@ public class DefaultAutoFetchManagerLogging {
|
||||
|
||||
private final DefaultAutoFetchManager manager;
|
||||
|
||||
private final boolean traceUsageCollection;
|
||||
|
||||
private final int updateFreqInSecs;
|
||||
|
||||
public DefaultAutoFetchManagerLogging(ServerConfig serverConfig, DefaultAutoFetchManager profileListener) {
|
||||
|
||||
this.manager = profileListener;
|
||||
this.traceUsageCollection = GlobalProperties.getBoolean("ebean.autofetch.traceUsageCollection", false);
|
||||
this.updateFreqInSecs = serverConfig.getAutofetchConfig().getProfileUpdateFrequency();
|
||||
this.updateFreqInSecs = serverConfig.getAutofetchConfig().getProfileUpdateFrequency();
|
||||
}
|
||||
|
||||
public void init(SpiEbeanServer ebeanServer) {
|
||||
@@ -69,9 +64,4 @@ public class DefaultAutoFetchManagerLogging {
|
||||
String msg = tunedFetch.getLogOutput(null);
|
||||
logger.debug(msg);
|
||||
}
|
||||
|
||||
public boolean isTraceUsageCollection() {
|
||||
return traceUsageCollection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,9 +17,6 @@ import java.io.DataOutputStream;
|
||||
* be common for many Ack, Resend and Control messages to all be contained in a
|
||||
* single packet.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*
|
||||
*/
|
||||
public class BinaryMessage {
|
||||
|
||||
@@ -28,8 +25,7 @@ public class BinaryMessage {
|
||||
public static final int TYPE_TABLEIUD = 2;
|
||||
public static final int TYPE_BEANDELTA = 3;
|
||||
public static final int TYPE_BEANPATHUPDATE = 4;
|
||||
public static final int TYPE_INDEX_INVALIDATE = 6;
|
||||
public static final int TYPE_INDEX = 7;
|
||||
|
||||
public static final int TYPE_MSGACK = 8;
|
||||
public static final int TYPE_MSGRESEND = 9;
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@ package com.avaje.ebeaninternal.server.cluster;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebeaninternal.api.ClassUtil;
|
||||
import com.avaje.ebean.config.ContainerConfig;
|
||||
import com.avaje.ebeaninternal.server.cluster.mcast.McastClusterManager;
|
||||
import com.avaje.ebeaninternal.server.cluster.socket.SocketClusterBroadcast;
|
||||
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
|
||||
@@ -26,41 +25,36 @@ public class ClusterManager {
|
||||
|
||||
private boolean started;
|
||||
|
||||
public ClusterManager() {
|
||||
public ClusterManager(ContainerConfig containerConfig) {
|
||||
|
||||
String clusterType = GlobalProperties.get("ebean.cluster.type", null);
|
||||
if (clusterType == null || clusterType.trim().length() == 0) {
|
||||
// not clustering this instance
|
||||
this.broadcast = null;
|
||||
|
||||
} else {
|
||||
|
||||
try {
|
||||
if ("mcast".equalsIgnoreCase(clusterType)) {
|
||||
this.broadcast = new McastClusterManager();
|
||||
|
||||
} else if ("socket".equalsIgnoreCase(clusterType)) {
|
||||
this.broadcast = new SocketClusterBroadcast();
|
||||
|
||||
} else {
|
||||
logger.info("Clustering using [" + clusterType + "]");
|
||||
this.broadcast = (ClusterBroadcast) ClassUtil.newInstance(clusterType);
|
||||
ContainerConfig.ClusterMode mode = containerConfig.getMode();
|
||||
try {
|
||||
switch (mode) {
|
||||
case SOCKET: {
|
||||
this.broadcast = new SocketClusterBroadcast(containerConfig);
|
||||
break;
|
||||
}
|
||||
case MULTICAST: {
|
||||
this.broadcast = new McastClusterManager(containerConfig);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this.broadcast = null;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
String msg = "Error initialising ClusterManager type [" + clusterType + "]";
|
||||
logger.error(msg, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Error initialising ClusterManager type [" + mode + "]", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void registerServer(EbeanServer server) {
|
||||
synchronized (monitor) {
|
||||
serverMap.put(server.getName(), server);
|
||||
if (!started) {
|
||||
startup();
|
||||
}
|
||||
serverMap.put(server.getName(), server);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD;
|
||||
import com.avaje.ebeaninternal.server.transaction.BeanDelta;
|
||||
import com.avaje.ebeaninternal.server.transaction.BeanPersistIds;
|
||||
import com.avaje.ebeaninternal.server.transaction.IndexEvent;
|
||||
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
|
||||
|
||||
/**
|
||||
@@ -16,7 +15,6 @@ import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
|
||||
* Due to the hard limit for UDP packet sizes a RemoteTransactionEvent
|
||||
* is actually broken up into smaller messages.
|
||||
* </p>
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class PacketTransactionEvent extends Packet {
|
||||
|
||||
@@ -63,10 +61,6 @@ public class PacketTransactionEvent extends Packet {
|
||||
event.addBeanDelta(BeanDelta.readBinaryMessage(server, dataInput));
|
||||
break;
|
||||
|
||||
case BinaryMessage.TYPE_INDEX:
|
||||
event.addIndexEvent(IndexEvent.readBinaryMessage(dataInput));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new RuntimeException("Invalid Transaction msgType "+msgType);
|
||||
}
|
||||
|
||||
+41
-36
@@ -7,49 +7,54 @@ import java.util.List;
|
||||
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Mechanism to convert RemoteTransactionEvent to/from byte[] content.
|
||||
*/
|
||||
public abstract class SerialiseTransactionHelper {
|
||||
|
||||
private final PacketWriter packetWriter;
|
||||
private static final Logger logger = LoggerFactory.getLogger(SerialiseTransactionHelper.class);
|
||||
|
||||
public SerialiseTransactionHelper() {
|
||||
packetWriter = new PacketWriter(Integer.MAX_VALUE);
|
||||
private final PacketWriter packetWriter;
|
||||
|
||||
public SerialiseTransactionHelper() {
|
||||
packetWriter = new PacketWriter(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
public abstract SpiEbeanServer getEbeanServer(String serverName);
|
||||
|
||||
/**
|
||||
* Convert the RemoteTransactionEvent to byte[] content.
|
||||
*/
|
||||
public DataHolder createDataHolder(RemoteTransactionEvent transEvent) throws IOException {
|
||||
|
||||
List<Packet> packetList = packetWriter.write(transEvent);
|
||||
if (packetList.size() != 1) {
|
||||
throw new RuntimeException("Always expecting 1 Packet but got " + packetList.size());
|
||||
}
|
||||
byte[] data = packetList.get(0).getBytes();
|
||||
return new DataHolder(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the byte[] content to RemoteTransactionEvent.
|
||||
*/
|
||||
public RemoteTransactionEvent read(DataHolder dataHolder) throws IOException {
|
||||
|
||||
ByteArrayInputStream bi = new ByteArrayInputStream(dataHolder.getData());
|
||||
DataInputStream dataInput = new DataInputStream(bi);
|
||||
|
||||
Packet header = Packet.readHeader(dataInput);
|
||||
|
||||
SpiEbeanServer server = getEbeanServer(header.getServerName());
|
||||
if (server == null) {
|
||||
logger.error("server [{}] not found/registered?", header.getServerName());
|
||||
}
|
||||
|
||||
public abstract SpiEbeanServer getEbeanServer(String serverName);
|
||||
|
||||
/**
|
||||
* Convert the RemoteTransactionEvent to byte[] content.
|
||||
*/
|
||||
public DataHolder createDataHolder(RemoteTransactionEvent transEvent) throws IOException {
|
||||
|
||||
List<Packet> packetList = packetWriter.write(transEvent);
|
||||
if (packetList.size() != 1) {
|
||||
throw new RuntimeException("Always expecting 1 Packet but got " + packetList.size());
|
||||
}
|
||||
byte[] data = packetList.get(0).getBytes();
|
||||
return new DataHolder(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the byte[] content to RemoteTransactionEvent.
|
||||
*/
|
||||
public RemoteTransactionEvent read(DataHolder dataHolder) throws IOException {
|
||||
|
||||
ByteArrayInputStream bi = new ByteArrayInputStream(dataHolder.getData());
|
||||
DataInputStream dataInput = new DataInputStream(bi);
|
||||
|
||||
Packet header = Packet.readHeader(dataInput);
|
||||
|
||||
SpiEbeanServer server = getEbeanServer(header.getServerName());
|
||||
|
||||
PacketTransactionEvent tranEventPacket = PacketTransactionEvent.forRead(header, server);
|
||||
tranEventPacket.read(dataInput);
|
||||
|
||||
return tranEventPacket.getEvent();
|
||||
|
||||
}
|
||||
PacketTransactionEvent tranEventPacket = PacketTransactionEvent.forRead(header, server);
|
||||
tranEventPacket.read(dataInput);
|
||||
return tranEventPacket.getEvent();
|
||||
}
|
||||
}
|
||||
|
||||
+494
-515
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,12 @@
|
||||
*/
|
||||
package com.avaje.ebeaninternal.server.cluster.mcast;
|
||||
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.cluster.Packet;
|
||||
import com.avaje.ebeaninternal.server.cluster.PacketTransactionEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
@@ -28,13 +34,6 @@ import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.MulticastSocket;
|
||||
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.cluster.Packet;
|
||||
import com.avaje.ebeaninternal.server.cluster.PacketTransactionEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Listens for Incoming packets.
|
||||
*
|
||||
@@ -56,8 +55,6 @@ public class McastListener implements Runnable {
|
||||
|
||||
private final InetAddress group;
|
||||
|
||||
private final boolean debugIgnore;
|
||||
|
||||
private DatagramPacket pack;
|
||||
|
||||
private byte[] receiveBuffer;
|
||||
@@ -73,8 +70,6 @@ public class McastListener implements Runnable {
|
||||
int bufferSize, int timeout, String localSenderHostPort,
|
||||
boolean disableLoopback, int ttl, InetAddress mcastBindAddress) {
|
||||
|
||||
this.debugIgnore = GlobalProperties.getBoolean("ebean.debug.mcast.ignore", false);
|
||||
|
||||
this.owner = owner;
|
||||
this.packetControl = packetControl;
|
||||
this.localSenderHostPort = localSenderHostPort;
|
||||
@@ -96,7 +91,7 @@ public class McastListener implements Runnable {
|
||||
this.sock.setSoTimeout(timeout);
|
||||
|
||||
if (disableLoopback){
|
||||
sock.setLoopbackMode(disableLoopback);
|
||||
sock.setLoopbackMode(true);
|
||||
}
|
||||
|
||||
if (mcastBindAddress != null) {
|
||||
@@ -173,7 +168,7 @@ public class McastListener implements Runnable {
|
||||
String senderHostPort = senderAddr.getAddress().getHostAddress()+":"+senderAddr.getPort();
|
||||
|
||||
if (senderHostPort.equals(localSenderHostPort)){
|
||||
if (debugIgnore || logger.isDebugEnabled()){
|
||||
if (logger.isTraceEnabled()){
|
||||
logger.info("Ignoring message as sent by localSender: "+localSenderHostPort);
|
||||
}
|
||||
} else {
|
||||
@@ -195,7 +190,7 @@ public class McastListener implements Runnable {
|
||||
boolean processThisPacket = ackMsg || packetControl.isProcessPacket(senderHostPort, header.getPacketId());
|
||||
|
||||
if (!processThisPacket){
|
||||
if (debugIgnore || logger.isDebugEnabled()){
|
||||
if (logger.isTraceEnabled()){
|
||||
logger.info("Already processed packet: "+header.getPacketId()+" type:"+header.getPacketType()+" len:"+data.length);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -21,15 +21,18 @@ class RequestProcessor implements Runnable {
|
||||
private final Socket clientSocket;
|
||||
|
||||
private final SocketClusterBroadcast owner;
|
||||
|
||||
/**
|
||||
|
||||
private final String hostPort;
|
||||
|
||||
/**
|
||||
* Create including the Listener (used to lookup the Request Handler) and
|
||||
* the socket itself.
|
||||
*/
|
||||
public RequestProcessor(SocketClusterBroadcast owner, Socket clientSocket) {
|
||||
this.clientSocket = clientSocket;
|
||||
this.owner = owner;
|
||||
}
|
||||
this.hostPort = owner.getHostPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* This will parse out the command. Lookup the appropriate Handler and
|
||||
@@ -39,22 +42,20 @@ class RequestProcessor implements Runnable {
|
||||
*/
|
||||
public void run() {
|
||||
try {
|
||||
SocketConnection sc = new SocketConnection(clientSocket);
|
||||
|
||||
while(true){
|
||||
if (owner.process(sc)) {
|
||||
// got the offline message or timeout
|
||||
break;
|
||||
}
|
||||
}
|
||||
sc.disconnect();
|
||||
|
||||
} catch (IOException e) {
|
||||
logger.error(null, e);
|
||||
} catch (ClassNotFoundException e) {
|
||||
logger.error(null, e);
|
||||
logger.trace("start listening for cluster messages");
|
||||
SocketConnection sc = new SocketConnection(clientSocket);
|
||||
while (true) {
|
||||
if (owner.process(sc)) {
|
||||
// got the offline message or timeout
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.trace("disconnecting: {}", hostPort);
|
||||
sc.disconnect();
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Error listening for messages - "+owner.getHostPort(), e);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
@@ -35,6 +35,10 @@ class SocketClient {
|
||||
this.hostPort = address.getHostName()+":"+address.getPort();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return address.toString();
|
||||
}
|
||||
|
||||
public String getHostPort() {
|
||||
return hostPort;
|
||||
}
|
||||
|
||||
+217
-209
@@ -1,248 +1,256 @@
|
||||
package com.avaje.ebeaninternal.server.cluster.socket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebean.config.ContainerConfig;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.cluster.ClusterBroadcast;
|
||||
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
|
||||
import com.avaje.ebeaninternal.server.cluster.DataHolder;
|
||||
import com.avaje.ebeaninternal.server.cluster.SerialiseTransactionHelper;
|
||||
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
|
||||
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Broadcast messages across the cluster using sockets.
|
||||
* Broadcast messages across the cluster using sockets.
|
||||
*/
|
||||
public class SocketClusterBroadcast implements ClusterBroadcast {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SocketClusterBroadcast.class);
|
||||
|
||||
private final SocketClient local;
|
||||
|
||||
private final HashMap<String,SocketClient> clientMap;
|
||||
|
||||
private final SocketClusterListener listener;
|
||||
|
||||
private SocketClient[] members;
|
||||
private static final Logger logger = LoggerFactory.getLogger(SocketClusterBroadcast.class);
|
||||
|
||||
private ClusterManager clusterManager;
|
||||
private final SocketClient local;
|
||||
|
||||
private final TxnSerialiseHelper txnSerialiseHelper = new TxnSerialiseHelper();
|
||||
|
||||
private final AtomicInteger txnOutgoing = new AtomicInteger();
|
||||
private final AtomicInteger txnIncoming = new AtomicInteger();
|
||||
|
||||
|
||||
public SocketClusterBroadcast( ){
|
||||
|
||||
String localHostPort = GlobalProperties.get("ebean.cluster.local", null);
|
||||
String members = GlobalProperties.get("ebean.cluster.members", null);
|
||||
private final HashMap<String, SocketClient> clientMap;
|
||||
|
||||
logger.info("Clustering using Sockets local["+localHostPort+"] members["+members+"]");
|
||||
|
||||
this.local = new SocketClient(parseFullName(localHostPort));
|
||||
this.clientMap = new HashMap<String, SocketClient>();
|
||||
|
||||
String[] memArray = StringHelper.delimitedToArray(members, ",", false);
|
||||
for (int i = 0; i < memArray.length; i++) {
|
||||
InetSocketAddress member = parseFullName(memArray[i]);
|
||||
SocketClient client = new SocketClient(member);
|
||||
if (!local.getHostPort().equalsIgnoreCase(client.getHostPort())) {
|
||||
// don't add the local one ...
|
||||
clientMap.put(client.getHostPort(), client);
|
||||
}
|
||||
}
|
||||
|
||||
this.members = clientMap.values().toArray(new SocketClient[clientMap.size()]);
|
||||
this.listener = new SocketClusterListener(this, local.getPort());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current status of this instance.
|
||||
*/
|
||||
public SocketClusterStatus getStatus() {
|
||||
|
||||
// count of online members
|
||||
int currentGroupSize = 0;
|
||||
for (int i = 0; i < members.length; i++) {
|
||||
if (members[i].isOnline()) {
|
||||
++currentGroupSize;
|
||||
}
|
||||
}
|
||||
int txnIn = txnIncoming.get();
|
||||
int txnOut = txnOutgoing.get();
|
||||
|
||||
return new SocketClusterStatus(currentGroupSize, txnIn, txnOut);
|
||||
}
|
||||
|
||||
public void startup(ClusterManager clusterManager) {
|
||||
|
||||
this.clusterManager = clusterManager;
|
||||
try {
|
||||
listener.startListening();
|
||||
register();
|
||||
private final SocketClusterListener listener;
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
private SocketClient[] members;
|
||||
|
||||
private ClusterManager clusterManager;
|
||||
|
||||
private final TxnSerialiseHelper txnSerialiseHelper = new TxnSerialiseHelper();
|
||||
|
||||
private final AtomicInteger txnOutgoing = new AtomicInteger();
|
||||
private final AtomicInteger txnIncoming = new AtomicInteger();
|
||||
|
||||
public SocketClusterBroadcast(ContainerConfig containerConfig) {
|
||||
|
||||
ContainerConfig.SocketConfig socketConfig = containerConfig.getSocketConfig();
|
||||
|
||||
String localHostPort = socketConfig.getLocalHostPort();
|
||||
List<String> members = socketConfig.getMembers();
|
||||
|
||||
logger.info("Clustering using Sockets local[" + localHostPort + "] members[" + members + "]");
|
||||
|
||||
this.local = new SocketClient(parseFullName(localHostPort));
|
||||
this.clientMap = new HashMap<String, SocketClient>();
|
||||
|
||||
for (String memberHostPort : members) {
|
||||
InetSocketAddress member = parseFullName(memberHostPort);
|
||||
SocketClient client = new SocketClient(member);
|
||||
if (!local.getHostPort().equalsIgnoreCase(client.getHostPort())) {
|
||||
// don't add the local one ...
|
||||
clientMap.put(client.getHostPort(), client);
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
deregister();
|
||||
listener.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register with all the other members of the Cluster.
|
||||
*/
|
||||
private void register() {
|
||||
this.members = clientMap.values().toArray(new SocketClient[clientMap.size()]);
|
||||
this.listener = new SocketClusterListener(this, local.getPort(), socketConfig.getCoreThreads(), socketConfig.getMaxThreads(), socketConfig.getThreadPoolName());
|
||||
}
|
||||
|
||||
SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), true);
|
||||
|
||||
for (int i = 0; i < members.length; i++) {
|
||||
boolean online = members[i].register(h);
|
||||
|
||||
String msg = "Cluster Member ["+members[i].getHostPort()+"] online["+online+"]";
|
||||
logger.info(msg);
|
||||
}
|
||||
public String getHostPort() {
|
||||
return local.getHostPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current status of this instance.
|
||||
*/
|
||||
public SocketClusterStatus getStatus() {
|
||||
|
||||
// count of online members
|
||||
int currentGroupSize = 0;
|
||||
for (int i = 0; i < members.length; i++) {
|
||||
if (members[i].isOnline()) {
|
||||
++currentGroupSize;
|
||||
}
|
||||
}
|
||||
int txnIn = txnIncoming.get();
|
||||
int txnOut = txnOutgoing.get();
|
||||
|
||||
protected void setMemberOnline(String fullName, boolean online) throws IOException {
|
||||
synchronized (clientMap) {
|
||||
String msg = "Cluster Member ["+fullName+"] online["+online+"]";
|
||||
logger.info(msg);
|
||||
SocketClient member = clientMap.get(fullName);
|
||||
member.setOnline(online);
|
||||
}
|
||||
return new SocketClusterStatus(currentGroupSize, txnIn, txnOut);
|
||||
}
|
||||
|
||||
public void startup(ClusterManager clusterManager) {
|
||||
|
||||
this.clusterManager = clusterManager;
|
||||
try {
|
||||
listener.startListening();
|
||||
register();
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void send(SocketClient client, SocketClusterMessage msg) {
|
||||
public void shutdown() {
|
||||
deregister();
|
||||
listener.shutdown();
|
||||
}
|
||||
|
||||
try {
|
||||
// alternative would be to connect/disconnect here
|
||||
// but prefer to use keepalive
|
||||
client.send(msg);
|
||||
|
||||
} catch (Exception ex){
|
||||
logger.error("Error sending message", ex);
|
||||
try {
|
||||
client.reconnect();
|
||||
} catch (IOException e) {
|
||||
logger.error("Error trying to reconnect", ex);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Register with all the other members of the Cluster.
|
||||
*/
|
||||
private void register() {
|
||||
|
||||
SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), true);
|
||||
|
||||
for (int i = 0; i < members.length; i++) {
|
||||
boolean online = members[i].register(h);
|
||||
logger.info("Cluster Member [{}] online[{}]", members[i].getHostPort(), online);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the payload to all the members of the cluster.
|
||||
*/
|
||||
public void broadcast(RemoteTransactionEvent remoteTransEvent) {
|
||||
try {
|
||||
|
||||
txnOutgoing.incrementAndGet();
|
||||
DataHolder dataHolder = txnSerialiseHelper.createDataHolder(remoteTransEvent);
|
||||
SocketClusterMessage msg = SocketClusterMessage.transEvent(dataHolder);
|
||||
broadcast(msg);
|
||||
} catch (Exception e){
|
||||
String msg = "Error sending RemoteTransactionEvent "+remoteTransEvent+" to cluster members.";
|
||||
logger.error(msg, e);
|
||||
}
|
||||
protected void setMemberOnline(String fullName, boolean online) throws IOException {
|
||||
synchronized (clientMap) {
|
||||
logger.info("Cluster Member [{}] online[{}]", fullName, online);
|
||||
SocketClient member = clientMap.get(fullName);
|
||||
member.setOnline(online);
|
||||
}
|
||||
}
|
||||
|
||||
protected void broadcast(SocketClusterMessage msg) {
|
||||
|
||||
for (int i = 0; i < members.length; i++) {
|
||||
send(members[i], msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Leave the cluster.
|
||||
*/
|
||||
private void deregister() {
|
||||
|
||||
SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), false);
|
||||
broadcast(h);
|
||||
|
||||
for (int i = 0; i < members.length; i++) {
|
||||
members[i].disconnect();
|
||||
}
|
||||
}
|
||||
private void send(SocketClient client, SocketClusterMessage msg) {
|
||||
|
||||
/**
|
||||
* Process a Cluster message.
|
||||
*/
|
||||
protected boolean process(SocketConnection request) throws IOException, ClassNotFoundException {
|
||||
try {
|
||||
// alternative would be to connect/disconnect here but prefer to use keepalive
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("... send to member {} broadcast msg: {}", client, msg);
|
||||
}
|
||||
client.send(msg);
|
||||
|
||||
try {
|
||||
SocketClusterMessage h = (SocketClusterMessage)request.readObject();
|
||||
|
||||
if (h.isRegisterEvent()){
|
||||
setMemberOnline(h.getRegisterHost(), h.isRegister());
|
||||
|
||||
} else {
|
||||
txnIncoming.incrementAndGet();
|
||||
DataHolder dataHolder = h.getDataHolder();
|
||||
RemoteTransactionEvent transEvent = txnSerialiseHelper.read(dataHolder);
|
||||
transEvent.run();
|
||||
}
|
||||
|
||||
if (h.isRegisterEvent() && !h.isRegister()){
|
||||
// instance shutting down
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (InterruptedIOException e) {
|
||||
String msg = "Timeout waiting for message";
|
||||
logger.info(msg, e);
|
||||
try {
|
||||
request.disconnect();
|
||||
} catch (IOException ex){
|
||||
logger.info("Error disconnecting after timeout", ex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
logger.error("Error sending message", ex);
|
||||
try {
|
||||
client.reconnect();
|
||||
} catch (IOException e) {
|
||||
logger.error("Error trying to reconnect", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a host:port into a InetSocketAddress.
|
||||
*/
|
||||
private InetSocketAddress parseFullName(String hostAndPort) {
|
||||
|
||||
try {
|
||||
hostAndPort = hostAndPort.trim();
|
||||
int colonPos = hostAndPort.indexOf(":");
|
||||
if (colonPos == -1) {
|
||||
String msg = "No colon \":\" in "+hostAndPort;
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
String host = hostAndPort.substring(0, colonPos);
|
||||
String sPort = hostAndPort.substring(colonPos + 1, hostAndPort.length());
|
||||
int port = Integer.parseInt(sPort);
|
||||
|
||||
return new InetSocketAddress(host, port);
|
||||
|
||||
} catch (Exception ex){
|
||||
throw new RuntimeException("Error parsing ["+hostAndPort+"] for the form [host:port]", ex);
|
||||
}
|
||||
/**
|
||||
* Send the payload to all the members of the cluster.
|
||||
*/
|
||||
public void broadcast(RemoteTransactionEvent remoteTransEvent) {
|
||||
try {
|
||||
txnOutgoing.incrementAndGet();
|
||||
DataHolder dataHolder = txnSerialiseHelper.createDataHolder(remoteTransEvent);
|
||||
SocketClusterMessage msg = SocketClusterMessage.transEvent(dataHolder);
|
||||
broadcast(msg);
|
||||
} catch (Exception e) {
|
||||
logger.error("Error sending RemoteTransactionEvent " + remoteTransEvent + " to cluster members.", e);
|
||||
}
|
||||
|
||||
class TxnSerialiseHelper extends SerialiseTransactionHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiEbeanServer getEbeanServer(String serverName) {
|
||||
return (SpiEbeanServer)clusterManager.getServer(serverName);
|
||||
}
|
||||
protected void broadcast(SocketClusterMessage msg) {
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("... broadcast msg: "+msg);
|
||||
}
|
||||
for (int i = 0; i < members.length; i++) {
|
||||
send(members[i], msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Leave the cluster.
|
||||
*/
|
||||
private void deregister() {
|
||||
|
||||
SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), false);
|
||||
broadcast(h);
|
||||
for (int i = 0; i < members.length; i++) {
|
||||
members[i].disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an incoming Cluster message.
|
||||
*/
|
||||
protected boolean process(SocketConnection request) throws IOException, ClassNotFoundException {
|
||||
|
||||
try {
|
||||
SocketClusterMessage h = (SocketClusterMessage) request.readObject();
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("... received msg: {}", h);
|
||||
}
|
||||
|
||||
if (h.isRegisterEvent()) {
|
||||
setMemberOnline(h.getRegisterHost(), h.isRegister());
|
||||
|
||||
} else {
|
||||
txnIncoming.incrementAndGet();
|
||||
DataHolder dataHolder = h.getDataHolder();
|
||||
RemoteTransactionEvent transEvent = txnSerialiseHelper.read(dataHolder);
|
||||
transEvent.run();
|
||||
}
|
||||
|
||||
// instance shutting down
|
||||
return h.isRegisterEvent() && !h.isRegister();
|
||||
|
||||
} catch (InterruptedIOException e) {
|
||||
logger.info("Timeout waiting for message", e);
|
||||
try {
|
||||
request.disconnect();
|
||||
} catch (IOException ex) {
|
||||
logger.info("Error disconnecting after timeout", ex);
|
||||
}
|
||||
return true;
|
||||
|
||||
} catch (EOFException e) {
|
||||
logger.info("EOF disconnecting");
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
logger.info("IO Error waiting/reading message", e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a host:port into a InetSocketAddress.
|
||||
*/
|
||||
private InetSocketAddress parseFullName(String hostAndPort) {
|
||||
|
||||
try {
|
||||
hostAndPort = hostAndPort.trim();
|
||||
int colonPos = hostAndPort.indexOf(":");
|
||||
if (colonPos == -1) {
|
||||
String msg = "No colon \":\" in " + hostAndPort;
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
String host = hostAndPort.substring(0, colonPos);
|
||||
String sPort = hostAndPort.substring(colonPos + 1, hostAndPort.length());
|
||||
int port = Integer.parseInt(sPort);
|
||||
|
||||
return new InetSocketAddress(host, port);
|
||||
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Error parsing [" + hostAndPort + "] for the form [host:port]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
class TxnSerialiseHelper extends SerialiseTransactionHelper {
|
||||
|
||||
@Override
|
||||
public SpiEbeanServer getEbeanServer(String serverName) {
|
||||
return (SpiEbeanServer) clusterManager.getServer(serverName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+99
-122
@@ -6,10 +6,10 @@ import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
|
||||
import com.avaje.ebeaninternal.server.lib.DaemonThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebeaninternal.server.lib.thread.ThreadPool;
|
||||
|
||||
/**
|
||||
* Serverside multithreaded socket listener. Accepts connections and dispatches
|
||||
@@ -26,141 +26,118 @@ import com.avaje.ebeaninternal.server.lib.thread.ThreadPool;
|
||||
*/
|
||||
class SocketClusterListener implements Runnable {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SocketClusterListener.class);
|
||||
|
||||
/**
|
||||
* The port the SocketListener uses.
|
||||
*/
|
||||
private final int port;
|
||||
private static final Logger logger = LoggerFactory.getLogger(SocketClusterListener.class);
|
||||
|
||||
/**
|
||||
* The length of the socket accept timeout.
|
||||
*/
|
||||
private final int listenTimeout = 60000;
|
||||
/**
|
||||
* The server socket used to listen for requests.
|
||||
*/
|
||||
private final ServerSocket serverListenSocket;
|
||||
|
||||
/**
|
||||
* The server socket used to listen for requests.
|
||||
*/
|
||||
private final ServerSocket serverListenSocket;
|
||||
/**
|
||||
* The listening thread.
|
||||
*/
|
||||
private final Thread listenerThread;
|
||||
|
||||
/**
|
||||
* The listening thread.
|
||||
*/
|
||||
private final Thread listenerThread;
|
||||
/**
|
||||
* The pool of threads that actually do the parsing execution of requests.
|
||||
*/
|
||||
private final DaemonThreadPool threadPool;
|
||||
|
||||
/**
|
||||
* The pool of threads that actually do the parsing execution of requests.
|
||||
*/
|
||||
private final ThreadPool threadPool;
|
||||
private final SocketClusterBroadcast owner;
|
||||
|
||||
private final SocketClusterBroadcast owner;
|
||||
/**
|
||||
* shutting down flag.
|
||||
*/
|
||||
boolean doingShutdown;
|
||||
|
||||
/**
|
||||
* shutting down flag.
|
||||
*/
|
||||
boolean doingShutdown;
|
||||
/**
|
||||
* Whether the listening thread is busy assigning a request to a thread.
|
||||
*/
|
||||
boolean isActive;
|
||||
|
||||
/**
|
||||
* Whether the listening thread is busy assigning a request to a thread.
|
||||
*/
|
||||
boolean isActive;
|
||||
/**
|
||||
* Construct with a given thread pool name.
|
||||
*/
|
||||
public SocketClusterListener(SocketClusterBroadcast owner, int port, int coreThreads, int maxThreads, String poolName) {
|
||||
this.owner = owner;
|
||||
this.threadPool = new DaemonThreadPool(coreThreads, maxThreads, 60, 30, poolName);
|
||||
try {
|
||||
this.serverListenSocket = new ServerSocket(port);
|
||||
this.serverListenSocket.setSoTimeout(60000);
|
||||
this.listenerThread = new Thread(this, "EbeanClusterListener");
|
||||
|
||||
/**
|
||||
* Construct with a given thread pool name.
|
||||
*/
|
||||
public SocketClusterListener(SocketClusterBroadcast owner, int port) {
|
||||
this.owner = owner;
|
||||
this.threadPool = ThreadPool.createThreadPool("EbeanCluster");
|
||||
this.port = port;
|
||||
|
||||
try {
|
||||
this.serverListenSocket = new ServerSocket(port);
|
||||
this.serverListenSocket.setSoTimeout(listenTimeout);
|
||||
this.listenerThread = new Thread(this, "EbeanClusterListener");
|
||||
|
||||
} catch (IOException e){
|
||||
String msg = "Error starting cluster socket listener on port "+port;
|
||||
throw new RuntimeException(msg,e);
|
||||
} catch (IOException e) {
|
||||
String msg = "Error starting cluster socket listener on port " + port;
|
||||
throw new RuntimeException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening for requests.
|
||||
*/
|
||||
public void startListening() throws IOException {
|
||||
logger.trace("... startListening()");
|
||||
this.listenerThread.setDaemon(true);
|
||||
this.listenerThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown this listener.
|
||||
*/
|
||||
public void shutdown() {
|
||||
doingShutdown = true;
|
||||
try {
|
||||
if (isActive) {
|
||||
synchronized (listenerThread) {
|
||||
try {
|
||||
listenerThread.wait(1000);
|
||||
} catch (InterruptedException e) {
|
||||
// OK to ignore as expected to Interrupt for shutdown.
|
||||
}
|
||||
}
|
||||
}
|
||||
listenerThread.interrupt();
|
||||
serverListenSocket.close();
|
||||
} catch (IOException e) {
|
||||
logger.error("Error shutting down listener", e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the port the listener is using.
|
||||
*/
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
threadPool.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening for requests.
|
||||
*/
|
||||
public void startListening() throws IOException {
|
||||
this.listenerThread.setDaemon(true);
|
||||
this.listenerThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown this listener.
|
||||
*/
|
||||
public void shutdown() {
|
||||
doingShutdown = true;
|
||||
try {
|
||||
if (isActive) {
|
||||
synchronized (listenerThread) {
|
||||
try {
|
||||
listenerThread.wait(1000);
|
||||
} catch (InterruptedException e) {
|
||||
// OK to ignore as expected to Interrupt for shutdown.
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
listenerThread.interrupt();
|
||||
serverListenSocket.close();
|
||||
} catch (IOException e) {
|
||||
logger.error("Error shutting down listener", e);
|
||||
/**
|
||||
* This is a runnable and so this must be public. Don't call this externally
|
||||
* but rather call the startListening() method.
|
||||
*/
|
||||
public void run() {
|
||||
// run in loop until doingShutdown is true...
|
||||
while (!doingShutdown) {
|
||||
try {
|
||||
synchronized (listenerThread) {
|
||||
Socket clientSocket = serverListenSocket.accept();
|
||||
isActive = true;
|
||||
|
||||
Runnable request = new RequestProcessor(owner, clientSocket);
|
||||
threadPool.execute(request);
|
||||
|
||||
isActive = false;
|
||||
}
|
||||
|
||||
threadPool.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a runnable and so this must be public. Don't call this externally
|
||||
* but rather call the startListening() method.
|
||||
*/
|
||||
public void run() {
|
||||
// run in loop until doingShutdown is true...
|
||||
while (!doingShutdown) {
|
||||
try {
|
||||
synchronized (listenerThread) {
|
||||
Socket clientSocket = serverListenSocket.accept();
|
||||
|
||||
isActive = true;
|
||||
|
||||
Runnable request = new RequestProcessor(owner, clientSocket);
|
||||
threadPool.assign(request, true);
|
||||
|
||||
isActive = false;
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
if (doingShutdown) {
|
||||
String msg = "doingShutdown and accept threw:"+ e.getMessage();
|
||||
logger.info(msg);
|
||||
|
||||
} else {
|
||||
logger.error(null, e);
|
||||
}
|
||||
|
||||
} catch (InterruptedIOException e) {
|
||||
// this will happen when the server is very quiet.
|
||||
// that is, no requests
|
||||
logger.debug("Possibly expected due to accept timeout?" + e.getMessage());
|
||||
|
||||
} catch (IOException e) {
|
||||
// log it and continue in the loop...
|
||||
logger.error(null, e);
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
if (doingShutdown) {
|
||||
logger.info("doingShutdown and accept threw:" + e.getMessage());
|
||||
} else {
|
||||
logger.error("Error while listening", e);
|
||||
}
|
||||
} catch (InterruptedIOException e) {
|
||||
// this will happen when the server is very quiet.
|
||||
// that is, no requests
|
||||
logger.debug("Possibly expected due to accept timeout? {}", e.getMessage());
|
||||
|
||||
} catch (IOException e) {
|
||||
// log it and continue in the loop...
|
||||
logger.error("IOException processing cluster message", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.avaje.ebeaninternal.server.util.ClassPathSearch;
|
||||
import com.avaje.ebeaninternal.server.util.ClassPathSearchFilter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Searches for interesting classes such as Entities, Embedded and ScalarTypes.
|
||||
*/
|
||||
@@ -20,17 +20,21 @@ public class BootupClassPathSearch {
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
private final List<String> packages;
|
||||
private final List<String> jars;
|
||||
|
||||
private final List<String> jars;
|
||||
|
||||
private BootupClasses bootupClasses;
|
||||
|
||||
private final String classPathReaderClassName;
|
||||
|
||||
/**
|
||||
* Construct and search for interesting classes.
|
||||
*/
|
||||
public BootupClassPathSearch(ClassLoader classLoader, List<String> packages, List<String> jars) {
|
||||
public BootupClassPathSearch(ClassLoader classLoader, List<String> packages, List<String> jars, String classPathReaderClassName) {
|
||||
this.classLoader = (classLoader == null) ? getClass().getClassLoader() : classLoader;
|
||||
this.packages = packages;
|
||||
this.jars = jars;
|
||||
this.classPathReaderClassName = classPathReaderClassName;
|
||||
}
|
||||
|
||||
public BootupClasses getBootupClasses() {
|
||||
@@ -57,7 +61,7 @@ public class BootupClassPathSearch {
|
||||
|
||||
ClassPathSearchFilter filter = createFilter();
|
||||
|
||||
ClassPathSearch finder = new ClassPathSearch(classLoader, filter, bc);
|
||||
ClassPathSearch finder = new ClassPathSearch(classLoader, filter, bc, classPathReaderClassName);
|
||||
|
||||
finder.findClasses();
|
||||
Set<String> jars = finder.getJarHits();
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
|
||||
|
||||
/**
|
||||
* Build a ServerConfig from ebean.properties.
|
||||
*/
|
||||
public class ConfigBuilder {
|
||||
|
||||
/**
|
||||
* Create a ServerConfig and load it from ebean.properties.
|
||||
*/
|
||||
public ServerConfig build(String serverName) {
|
||||
|
||||
ServerConfig config = new ServerConfig();
|
||||
config.setName(serverName);
|
||||
|
||||
config.loadFromProperties();
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -15,19 +15,21 @@ public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
|
||||
|
||||
private final DaemonScheduleThreadPool schedulePool;
|
||||
|
||||
/**
|
||||
* Construct the default implementation of BackgroundExecutor.
|
||||
*
|
||||
* @param corePoolSize
|
||||
* the core size of the thread pool.
|
||||
* @param keepAliveSecs
|
||||
* the time in seconds idle threads are keep alive
|
||||
* @param shutdownWaitSeconds
|
||||
* the time in seconds allowed for the pool to shutdown nicely.
|
||||
* After this the pool is forced to shutdown.
|
||||
*/
|
||||
public DefaultBackgroundExecutor(int schedulePoolSize, int corePoolSize, long keepAliveSecs,int shutdownWaitSeconds, String namePrefix) {
|
||||
this.pool = new DaemonThreadPool(corePoolSize, keepAliveSecs, shutdownWaitSeconds, namePrefix);
|
||||
/**
|
||||
* Construct the default implementation of BackgroundExecutor.
|
||||
*
|
||||
* @param corePoolSize
|
||||
* the core size of the thread pool.
|
||||
* @param maximumPoolSize
|
||||
* the maximum pool size before jobs are queued
|
||||
* @param keepAliveSecs
|
||||
* the time in seconds idle threads are keep alive
|
||||
* @param shutdownWaitSeconds
|
||||
* the time in seconds allowed for the pool to shutdown nicely.
|
||||
* After this the pool is forced to shutdown.
|
||||
*/
|
||||
public DefaultBackgroundExecutor(int schedulePoolSize, int corePoolSize, int maximumPoolSize, long keepAliveSecs,int shutdownWaitSeconds, String namePrefix) {
|
||||
this.pool = new DaemonThreadPool(corePoolSize, maximumPoolSize, keepAliveSecs, shutdownWaitSeconds, namePrefix);
|
||||
this.schedulePool = new DaemonScheduleThreadPool(schedulePoolSize, shutdownWaitSeconds, namePrefix+"-periodic-");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,81 +1,27 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import javax.management.InstanceAlreadyExistsException;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectName;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.CallStack;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.bean.*;
|
||||
import com.avaje.ebean.bean.PersistenceContext.WithOption;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.config.EncryptKeyManager;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.event.BeanPersistController;
|
||||
import com.avaje.ebean.event.BeanQueryAdapter;
|
||||
import com.avaje.ebean.meta.MetaBeanInfo;
|
||||
import com.avaje.ebean.meta.MetaInfoManager;
|
||||
import com.avaje.ebean.text.csv.CsvReader;
|
||||
import com.avaje.ebean.text.json.JsonContext;
|
||||
import com.avaje.ebeaninternal.api.LoadBeanRequest;
|
||||
import com.avaje.ebeaninternal.api.LoadManyRequest;
|
||||
import com.avaje.ebeaninternal.api.ScopeTrans;
|
||||
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanPlugin;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.*;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery.Type;
|
||||
import com.avaje.ebeaninternal.api.SpiSqlQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.api.TransactionEventTable;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.ddl.DdlGenerator;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanManager;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.DNativeQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate;
|
||||
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
|
||||
import com.avaje.ebeaninternal.server.deploy.*;
|
||||
import com.avaje.ebeaninternal.server.el.ElFilter;
|
||||
import com.avaje.ebeaninternal.server.jmx.MAdminAutofetch;
|
||||
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
|
||||
import com.avaje.ebeaninternal.server.query.CQuery;
|
||||
import com.avaje.ebeaninternal.server.query.CQueryEngine;
|
||||
import com.avaje.ebeaninternal.server.query.CallableQueryIds;
|
||||
import com.avaje.ebeaninternal.server.query.CallableQueryList;
|
||||
import com.avaje.ebeaninternal.server.query.CallableQueryRowCount;
|
||||
import com.avaje.ebeaninternal.server.query.CallableSqlQueryList;
|
||||
import com.avaje.ebeaninternal.server.query.LimitOffsetPagedList;
|
||||
import com.avaje.ebeaninternal.server.query.LimitOffsetPagingQuery;
|
||||
import com.avaje.ebeaninternal.server.query.QueryFutureIds;
|
||||
import com.avaje.ebeaninternal.server.query.QueryFutureList;
|
||||
import com.avaje.ebeaninternal.server.query.QueryFutureRowCount;
|
||||
import com.avaje.ebeaninternal.server.query.SqlQueryFutureList;
|
||||
import com.avaje.ebeaninternal.server.query.*;
|
||||
import com.avaje.ebeaninternal.server.querydefn.DefaultOrmQuery;
|
||||
import com.avaje.ebeaninternal.server.querydefn.DefaultOrmUpdate;
|
||||
import com.avaje.ebeaninternal.server.querydefn.DefaultRelationalQuery;
|
||||
@@ -86,6 +32,16 @@ import com.avaje.ebeaninternal.server.transaction.TransactionManager;
|
||||
import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager;
|
||||
import com.avaje.ebeaninternal.util.ParamTypeHelper;
|
||||
import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.management.InstanceAlreadyExistsException;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectName;
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
/**
|
||||
* The default server side implementation of EbeanServer.
|
||||
@@ -226,9 +182,9 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
this.collectQueryOrigins = serverConfig.isCollectQueryOrigins();
|
||||
this.collectQueryStatsByNode = serverConfig.isCollectQueryStatsByNode();
|
||||
this.maxCallStack = GlobalProperties.getInt("ebean.maxCallStack", 5);
|
||||
this.maxCallStack = serverConfig.getMaxCallStack();
|
||||
|
||||
this.rollbackOnChecked = GlobalProperties.getBoolean("ebean.transaction.rollbackOnChecked", true);
|
||||
this.rollbackOnChecked = serverConfig.isTransactionRollbackOnChecked();
|
||||
this.transactionManager = config.getTransactionManager();
|
||||
this.transactionScopeManager = config.getTransactionScopeManager();
|
||||
|
||||
@@ -482,20 +438,6 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
return serverCacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Profile Listener.
|
||||
*/
|
||||
public AutoFetchManager getProfileListener() {
|
||||
return autoFetchManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Relational query engine.
|
||||
*/
|
||||
public RelationalQueryEngine getRelationalQueryEngine() {
|
||||
return relationalQueryEngine;
|
||||
}
|
||||
|
||||
public void refreshMany(Object parentBean, String propertyName, Transaction t) {
|
||||
|
||||
beanLoader.refreshMany(checkEntityBean(parentBean), propertyName, t);
|
||||
@@ -1072,15 +1014,10 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
public <T> SpiOrmQueryRequest<T> createQueryRequest(BeanDescriptor<T> desc, SpiQuery<T> query, Transaction t) {
|
||||
|
||||
if (desc.isAutoFetchTunable() && !query.isSqlSelect()) {
|
||||
// its a tunable query
|
||||
if (autoFetchManager.tuneQuery(query)) {
|
||||
// was automatically tuned by Autofetch
|
||||
} else {
|
||||
// use deployment FetchType.LAZY/EAGER annotations
|
||||
// to define the 'default' select clause
|
||||
query.setDefaultSelectClause();
|
||||
}
|
||||
if (desc.isAutoFetchTunable() && !query.isSqlSelect() && !autoFetchManager.tuneQuery(query)) {
|
||||
// use deployment FetchType.LAZY/EAGER annotations
|
||||
// to define the 'default' select clause
|
||||
query.setDefaultSelectClause();
|
||||
}
|
||||
|
||||
if (query.selectAllForLazyLoadProperty()) {
|
||||
@@ -1091,12 +1028,9 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
if (true) {
|
||||
// if determine cost and no origin for Autofetch
|
||||
if (query.getParentNode() == null) {
|
||||
CallStack callStack = createCallStack();
|
||||
query.setOrigin(callStack);
|
||||
}
|
||||
// if determine cost and no origin for Autofetch
|
||||
if (query.getParentNode() == null) {
|
||||
query.setOrigin(createCallStack());
|
||||
}
|
||||
|
||||
// determine extra joins required to support where clause
|
||||
@@ -1360,49 +1294,17 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
spiQuery.setFutureFetch(true);
|
||||
|
||||
// transfer the persistence content from the transaction
|
||||
if (spiQuery.getPersistenceContext() == null) {
|
||||
if (t != null) {
|
||||
spiQuery.setPersistenceContext(((SpiTransaction) t).getPersistenceContext());
|
||||
} else {
|
||||
SpiTransaction st = getCurrentServerTransaction();
|
||||
if (st != null) {
|
||||
spiQuery.setPersistenceContext(st.getPersistenceContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
// FutureList query always run in it's own persistence content
|
||||
spiQuery.setPersistenceContext(new DefaultPersistenceContext());
|
||||
|
||||
// Create a new transaction solely to execute the findList() at some future time
|
||||
Transaction newTxn = createTransaction();
|
||||
CallableQueryList<T> call = new CallableQueryList<T>(this, spiQuery, newTxn);
|
||||
QueryFutureList<T> queryFuture = new QueryFutureList<T>(call);
|
||||
backgroundExecutor.execute(queryFuture.getFutureTask());
|
||||
|
||||
return queryFuture;
|
||||
}
|
||||
|
||||
public <T> PagingList<T> findPagingList(Query<T> query, Transaction t, int pageSize) {
|
||||
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
|
||||
// we want to use a single PersistenceContext to be used
|
||||
// for all the paging queries so we make sure there is a
|
||||
// PersistenceContext on the query
|
||||
PersistenceContext pc = spiQuery.getPersistenceContext();
|
||||
if (pc == null) {
|
||||
SpiTransaction currentTransaction = getCurrentServerTransaction();
|
||||
if (currentTransaction != null) {
|
||||
pc = currentTransaction.getPersistenceContext();
|
||||
}
|
||||
if (pc == null) {
|
||||
pc = new DefaultPersistenceContext();
|
||||
}
|
||||
spiQuery.setPersistenceContext(pc);
|
||||
}
|
||||
|
||||
return new LimitOffsetPagingQuery<T>(this, spiQuery, pageSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction, int pageIndex, int pageSize) {
|
||||
|
||||
@@ -1786,6 +1688,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
TransWrapper wrap = initTransIfRequired(t);
|
||||
try {
|
||||
wrap.batchEscalateOnCollection();
|
||||
SpiTransaction trans = wrap.transaction;
|
||||
int saveCount = 0;
|
||||
while (it.hasNext()) {
|
||||
@@ -1795,7 +1698,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
}
|
||||
|
||||
wrap.commitIfCreated();
|
||||
|
||||
wrap.flushBatchOnCollection();
|
||||
return saveCount;
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
@@ -1879,6 +1782,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
TransWrapper wrap = initTransIfRequired(t);
|
||||
|
||||
try {
|
||||
wrap.batchEscalateOnCollection();
|
||||
SpiTransaction trans = wrap.transaction;
|
||||
int deleteCount = 0;
|
||||
while (it.hasNext()) {
|
||||
@@ -1888,7 +1792,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
}
|
||||
|
||||
wrap.commitIfCreated();
|
||||
|
||||
wrap.flushBatchOnCollection();
|
||||
return deleteCount;
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
@@ -1939,10 +1843,6 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
return execute(update, null);
|
||||
}
|
||||
|
||||
public <T> BeanManager<T> getBeanManager(Class<T> beanClass) {
|
||||
return beanDescriptorManager.getBeanManager(beanClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the BeanDescriptors.
|
||||
*/
|
||||
@@ -1950,13 +1850,6 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
return beanDescriptorManager.getBeanDescriptorList();
|
||||
}
|
||||
|
||||
public List<MetaBeanInfo> getMetaBeanInfoList() {
|
||||
|
||||
List<MetaBeanInfo> list = new ArrayList<MetaBeanInfo>();
|
||||
list.addAll(getBeanDescriptors());
|
||||
return list;
|
||||
}
|
||||
|
||||
public void register(BeanPersistController c) {
|
||||
List<BeanDescriptor<?>> list = beanDescriptorManager.getBeanDescriptorList();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
@@ -2081,9 +1974,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
// create the 'interesting' part of the stackTrace
|
||||
StackTraceElement[] finalTrace = new StackTraceElement[stackLength];
|
||||
for (int i = 0; i < stackLength; i++) {
|
||||
finalTrace[i] = stackTrace[i + startIndex];
|
||||
}
|
||||
System.arraycopy(stackTrace, startIndex, finalTrace, 0, stackLength);
|
||||
|
||||
if (stackLength < 1) {
|
||||
// this should not really happen
|
||||
|
||||
@@ -2,10 +2,7 @@ package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
@@ -13,6 +10,7 @@ import javax.management.MBeanServerFactory;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.avaje.ebean.config.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -21,11 +19,6 @@ import com.avaje.ebean.cache.ServerCacheFactory;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.cache.ServerCacheOptions;
|
||||
import com.avaje.ebean.common.BootupEbeanManager;
|
||||
import com.avaje.ebean.config.DataSourceConfig;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebean.config.PstmtDelegate;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.UnderscoreNamingConvention;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
@@ -38,7 +31,6 @@ import com.avaje.ebeaninternal.server.lib.ShutdownManager;
|
||||
import com.avaje.ebeaninternal.server.lib.sql.DataSourceAlert;
|
||||
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
|
||||
import com.avaje.ebeaninternal.server.lib.sql.SimpleDataSourceAlert;
|
||||
import com.avaje.ebeaninternal.server.lib.thread.ThreadPool;
|
||||
|
||||
/**
|
||||
* Default Server side implementation of ServerFactory.
|
||||
@@ -51,46 +43,18 @@ public class DefaultServerFactory implements BootupEbeanManager {
|
||||
|
||||
private final JndiDataSourceLookup jndiDataSourceFactory;
|
||||
|
||||
private final BootupClassPathSearch bootupClassSearch;
|
||||
|
||||
private final AtomicInteger serverId = new AtomicInteger(1);
|
||||
|
||||
private final XmlConfigLoader xmlConfigLoader;
|
||||
public DefaultServerFactory(ContainerConfig containerConfig) {
|
||||
|
||||
private final XmlConfig xmlConfig;
|
||||
|
||||
public DefaultServerFactory() {
|
||||
|
||||
this.clusterManager = new ClusterManager();
|
||||
this.clusterManager = new ClusterManager(containerConfig);
|
||||
this.jndiDataSourceFactory = new JndiDataSourceLookup();
|
||||
|
||||
List<String> packages = getSearchJarsPackages(GlobalProperties.get("ebean.search.packages", null));
|
||||
List<String> jars = getSearchJarsPackages(GlobalProperties.get("ebean.search.jars", null));
|
||||
|
||||
this.bootupClassSearch = new BootupClassPathSearch(null, packages, jars);
|
||||
this.xmlConfigLoader = new XmlConfigLoader(null);
|
||||
|
||||
this.xmlConfig = xmlConfigLoader.load();
|
||||
|
||||
// register so that we can shutdown any Ebean wide
|
||||
// resources such as clustering
|
||||
ShutdownManager.registerServerFactory(this);
|
||||
}
|
||||
|
||||
private List<String> getSearchJarsPackages(String searchPackages) {
|
||||
|
||||
List<String> hitList = new ArrayList<String>();
|
||||
|
||||
if (searchPackages != null) {
|
||||
|
||||
String[] entries = searchPackages.split("[ ,;]");
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
hitList.add(entries[i].trim());
|
||||
}
|
||||
}
|
||||
return hitList;
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
clusterManager.shutdown();
|
||||
}
|
||||
@@ -100,35 +64,26 @@ public class DefaultServerFactory implements BootupEbeanManager {
|
||||
*/
|
||||
public SpiEbeanServer createServer(String name) {
|
||||
|
||||
ConfigBuilder b = new ConfigBuilder();
|
||||
ServerConfig config = b.build(name);
|
||||
ServerConfig config = new ServerConfig();
|
||||
config.setName(name);
|
||||
|
||||
Properties prop = PropertyMap.defaultProperties();
|
||||
config.loadFromProperties(prop);
|
||||
|
||||
return createServer(config);
|
||||
}
|
||||
|
||||
private SpiBackgroundExecutor createBackgroundExecutor(ServerConfig serverConfig, int uniqueServerId) {
|
||||
private SpiBackgroundExecutor createBackgroundExecutor(ServerConfig serverConfig) {
|
||||
|
||||
String namePrefix = "Ebean-" + serverConfig.getName();
|
||||
String namePrefix = "ebean-" + serverConfig.getName();
|
||||
|
||||
// the size of the pool for executing periodic tasks (such as cache flushing)
|
||||
int schedulePoolSize = GlobalProperties.getInt("backgroundExecutor.schedulePoolsize", 1);
|
||||
int schedulePoolSize = serverConfig.getBackgroundExecutorSchedulePoolSize();
|
||||
int corePoolSize = serverConfig.getBackgroundExecutorCorePoolSize();
|
||||
int maxPoolSize = serverConfig.getBackgroundExecutorMaxPoolSize();
|
||||
int idleSecs = serverConfig.getBackgroundExecutorIdleSecs();
|
||||
int shutdownSecs = serverConfig.getBackgroundExecutorShutdownSecs();
|
||||
|
||||
// the side of the main pool for immediate background task execution
|
||||
int minPoolSize = GlobalProperties.getInt("backgroundExecutor.minPoolSize", 0);
|
||||
int poolSize = GlobalProperties.getInt("backgroundExecutor.poolsize", 20);
|
||||
int maxPoolSize = GlobalProperties.getInt("backgroundExecutor.maxPoolSize", poolSize);
|
||||
|
||||
int idleSecs = GlobalProperties.getInt("backgroundExecutor.idlesecs", 120);
|
||||
int shutdownSecs = GlobalProperties.getInt("backgroundExecutor.shutdownSecs", 30);
|
||||
|
||||
boolean useTrad = GlobalProperties.getBoolean("backgroundExecutor.traditional", true);
|
||||
if (useTrad) {
|
||||
// this pool will use Idle seconds to maintain the thread count between min and max
|
||||
ThreadPool pool = new ThreadPool(namePrefix, true, null, minPoolSize, maxPoolSize, idleSecs*1000);
|
||||
return new TraditionalBackgroundExecutor(pool, schedulePoolSize, shutdownSecs, namePrefix);
|
||||
} else {
|
||||
return new DefaultBackgroundExecutor(schedulePoolSize, maxPoolSize, idleSecs, shutdownSecs, namePrefix);
|
||||
}
|
||||
return new DefaultBackgroundExecutor(schedulePoolSize, corePoolSize, maxPoolSize, idleSecs, shutdownSecs, namePrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,8 +118,7 @@ public class DefaultServerFactory implements BootupEbeanManager {
|
||||
pstmtDelegate = getOraclePstmtDelegate(serverConfig.getDataSource());
|
||||
}
|
||||
if (pstmtDelegate != null) {
|
||||
// We can support JDBC batching with Oracle
|
||||
// via OraclePreparedStatement
|
||||
// We can support JDBC batching with Oracle via OraclePreparedStatement
|
||||
pstmtBatch = new OraclePstmtBatch(pstmtDelegate);
|
||||
}
|
||||
if (pstmtBatch == null) {
|
||||
@@ -180,27 +134,30 @@ public class DefaultServerFactory implements BootupEbeanManager {
|
||||
ServerCacheManager cacheManager = getCacheManager(serverConfig);
|
||||
|
||||
int uniqueServerId = serverId.incrementAndGet();
|
||||
SpiBackgroundExecutor bgExecutor = createBackgroundExecutor(serverConfig, uniqueServerId);
|
||||
SpiBackgroundExecutor bgExecutor = createBackgroundExecutor(serverConfig);
|
||||
|
||||
InternalConfiguration c = new InternalConfiguration(xmlConfig, clusterManager, cacheManager, bgExecutor, serverConfig, bootupClasses,
|
||||
pstmtBatch);
|
||||
XmlConfigLoader xmlConfigLoader = new XmlConfigLoader(null);
|
||||
XmlConfig xmlConfig = xmlConfigLoader.load();
|
||||
|
||||
InternalConfiguration c = new InternalConfiguration(xmlConfig, clusterManager, cacheManager, bgExecutor, serverConfig, bootupClasses, pstmtBatch);
|
||||
|
||||
DefaultServer server = new DefaultServer(c, cacheManager);
|
||||
|
||||
cacheManager.init(server);
|
||||
|
||||
MBeanServer mbeanServer;
|
||||
ArrayList<?> list = MBeanServerFactory.findMBeanServer(null);
|
||||
if (list.size() == 0) {
|
||||
// probably not running in a server
|
||||
mbeanServer = MBeanServerFactory.createMBeanServer();
|
||||
} else {
|
||||
// use the first MBeanServer
|
||||
mbeanServer = (MBeanServer) list.get(0);
|
||||
if (serverConfig.isRegisterJmxMBeans()) {
|
||||
MBeanServer mbeanServer;
|
||||
ArrayList<?> list = MBeanServerFactory.findMBeanServer(null);
|
||||
if (list.size() == 0) {
|
||||
// probably not running in a server
|
||||
mbeanServer = MBeanServerFactory.createMBeanServer();
|
||||
} else {
|
||||
// use the first MBeanServer
|
||||
mbeanServer = (MBeanServer) list.get(0);
|
||||
}
|
||||
server.registerMBeans(mbeanServer, uniqueServerId);
|
||||
}
|
||||
|
||||
server.registerMBeans(mbeanServer, uniqueServerId);
|
||||
|
||||
// generate and run DDL if required
|
||||
// if there are any other tasks requiring action in their plugins, do them as well
|
||||
server.executePlugins(online);
|
||||
@@ -215,9 +172,7 @@ public class DefaultServerFactory implements BootupEbeanManager {
|
||||
}
|
||||
|
||||
// warm the cache in 30 seconds
|
||||
int delaySecs = GlobalProperties.getInt("ebean.cacheWarmingDelay", 30);
|
||||
long sleepMillis = 1000 * delaySecs;
|
||||
|
||||
long sleepMillis = 1000 * serverConfig.getCacheWarmingDelay();
|
||||
if (sleepMillis > 0) {
|
||||
Timer t = new Timer("EbeanCacheWarmer", true);
|
||||
t.schedule(new CacheWarmer(server), sleepMillis);
|
||||
@@ -252,19 +207,15 @@ public class DefaultServerFactory implements BootupEbeanManager {
|
||||
|
||||
// reasonable default settings are for a cache per bean type
|
||||
ServerCacheOptions beanOptions = new ServerCacheOptions();
|
||||
beanOptions.setMaxSize(GlobalProperties.getInt("cache.maxSize", 1000));
|
||||
// maxIdleTime 10 minutes
|
||||
beanOptions.setMaxIdleSecs(GlobalProperties.getInt("cache.maxIdleTime", 60 * 10));
|
||||
// maxTimeToLive 6 hrs
|
||||
beanOptions.setMaxSecsToLive(GlobalProperties.getInt("cache.maxTimeToLive", 60 * 60 * 6));
|
||||
beanOptions.setMaxSize(serverConfig.getCacheMaxSize());
|
||||
beanOptions.setMaxIdleSecs(serverConfig.getCacheMaxIdleTime());
|
||||
beanOptions.setMaxSecsToLive(serverConfig.getCacheMaxTimeToLive());
|
||||
|
||||
// reasonable default settings for the query cache per bean type
|
||||
ServerCacheOptions queryOptions = new ServerCacheOptions();
|
||||
queryOptions.setMaxSize(GlobalProperties.getInt("querycache.maxSize", 100));
|
||||
// maxIdleTime 10 minutes
|
||||
queryOptions.setMaxIdleSecs(GlobalProperties.getInt("querycache.maxIdleTime", 60 * 10));
|
||||
// maxTimeToLive 6 hours
|
||||
queryOptions.setMaxSecsToLive(GlobalProperties.getInt("querycache.maxTimeToLive", 60 * 60 * 6));
|
||||
queryOptions.setMaxSize(serverConfig.getQueryCacheMaxSize());
|
||||
queryOptions.setMaxIdleSecs(serverConfig.getQueryCacheMaxIdleTime());
|
||||
queryOptions.setMaxSecsToLive(serverConfig.getQueryCacheMaxTimeToLive());
|
||||
|
||||
ServerCacheFactory cacheFactory = serverConfig.getServerCacheFactory();
|
||||
if (cacheFactory == null) {
|
||||
@@ -303,17 +254,8 @@ public class DefaultServerFactory implements BootupEbeanManager {
|
||||
return new BootupClasses(serverConfig.getClasses());
|
||||
}
|
||||
|
||||
List<String> jars = serverConfig.getJars();
|
||||
List<String> packages = serverConfig.getPackages();
|
||||
|
||||
if ((packages != null && !packages.isEmpty()) || (jars != null && !jars.isEmpty())) {
|
||||
// filter by package name
|
||||
BootupClassPathSearch search = new BootupClassPathSearch(null, packages, jars);
|
||||
return search.getBootupClasses();
|
||||
}
|
||||
|
||||
// just use classes we can find via class path search
|
||||
return bootupClassSearch.getBootupClasses().createCopy();
|
||||
BootupClassPathSearch search = new BootupClassPathSearch(null, serverConfig.getPackages(), serverConfig.getJars(), serverConfig.getClassPathReaderClassName());
|
||||
return search.getBootupClasses();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,22 +265,6 @@ public class DefaultServerFactory implements BootupEbeanManager {
|
||||
if (config.getNamingConvention() == null) {
|
||||
UnderscoreNamingConvention nc = new UnderscoreNamingConvention();
|
||||
config.setNamingConvention(nc);
|
||||
|
||||
String v = config.getProperty("namingConvention.useForeignKeyPrefix");
|
||||
if (v != null) {
|
||||
boolean useForeignKeyPrefix = Boolean.valueOf(v);
|
||||
nc.setUseForeignKeyPrefix(useForeignKeyPrefix);
|
||||
}
|
||||
|
||||
String sequenceFormat = config.getProperty("namingConvention.sequenceFormat");
|
||||
if (sequenceFormat != null) {
|
||||
nc.setSequenceFormat(sequenceFormat);
|
||||
}
|
||||
|
||||
String schema = config.getProperty("namingConvention.schema");
|
||||
if (schema != null) {
|
||||
nc.setSchema(schema);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,7 +277,6 @@ public class DefaultServerFactory implements BootupEbeanManager {
|
||||
if (dbPlatform == null) {
|
||||
|
||||
DatabasePlatformFactory factory = new DatabasePlatformFactory();
|
||||
|
||||
DatabasePlatform db = factory.create(config);
|
||||
config.setDatabasePlatform(db);
|
||||
logger.info("DatabasePlatform name:" + config.getName() + " platform:" + db.getName());
|
||||
@@ -370,7 +295,7 @@ public class DefaultServerFactory implements BootupEbeanManager {
|
||||
|
||||
private DataSource getDataSourceFromConfig(ServerConfig config) {
|
||||
|
||||
DataSource ds = null;
|
||||
DataSource ds;
|
||||
|
||||
if (config.getDataSourceJndiName() != null) {
|
||||
ds = jndiDataSourceFactory.lookup(config.getDataSourceJndiName());
|
||||
|
||||
@@ -1,266 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.ExpressionFactory;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.config.ExternalTransactionManager;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.text.json.JsonContext;
|
||||
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManagerFactory;
|
||||
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import com.avaje.ebeaninternal.server.deploy.DeployOrmXml;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
|
||||
import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory;
|
||||
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
|
||||
import com.avaje.ebeaninternal.server.persist.Binder;
|
||||
import com.avaje.ebeaninternal.server.persist.DefaultPersister;
|
||||
import com.avaje.ebeaninternal.server.query.CQueryEngine;
|
||||
import com.avaje.ebeaninternal.server.query.DefaultOrmQueryEngine;
|
||||
import com.avaje.ebeaninternal.server.query.DefaultRelationalQueryEngine;
|
||||
import com.avaje.ebeaninternal.server.resource.ResourceManager;
|
||||
import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory;
|
||||
import com.avaje.ebeaninternal.server.text.json.DJsonContext;
|
||||
<<<<<<< HEAD
|
||||
import com.avaje.ebeaninternal.server.text.json.DefaultJsonValueAdapter;
|
||||
import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager;
|
||||
=======
|
||||
>>>>>>> json-refactor
|
||||
import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager;
|
||||
import com.avaje.ebeaninternal.server.transaction.ExternalTransactionScopeManager;
|
||||
import com.avaje.ebeaninternal.server.transaction.JtaTransactionManager;
|
||||
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
|
||||
import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager;
|
||||
import com.avaje.ebeaninternal.server.type.DefaultTypeManager;
|
||||
import com.avaje.ebeaninternal.server.type.TypeManager;
|
||||
|
||||
/**
|
||||
* Used to extend the ServerConfig with additional objects used to configure and
|
||||
* construct an EbeanServer.
|
||||
*/
|
||||
public class InternalConfiguration {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(InternalConfiguration.class);
|
||||
|
||||
private final ServerConfig serverConfig;
|
||||
|
||||
private final BootupClasses bootupClasses;
|
||||
|
||||
private final DeployInherit deployInherit;
|
||||
|
||||
private final ResourceManager resourceManager;
|
||||
|
||||
private final DeployOrmXml deployOrmXml;
|
||||
|
||||
private final TypeManager typeManager;
|
||||
|
||||
private final Binder binder;
|
||||
|
||||
private final DeployCreateProperties deployCreateProperties;
|
||||
|
||||
private final DeployUtil deployUtil;
|
||||
|
||||
private final BeanDescriptorManager beanDescriptorManager;
|
||||
|
||||
private final TransactionManager transactionManager;
|
||||
|
||||
private final TransactionScopeManager transactionScopeManager;
|
||||
|
||||
private final CQueryEngine cQueryEngine;
|
||||
|
||||
private final ClusterManager clusterManager;
|
||||
|
||||
private final ServerCacheManager cacheManager;
|
||||
|
||||
private final ExpressionFactory expressionFactory;
|
||||
|
||||
private final SpiBackgroundExecutor backgroundExecutor;
|
||||
|
||||
private final PstmtBatch pstmtBatch;
|
||||
|
||||
private final XmlConfig xmlConfig;
|
||||
|
||||
public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager,
|
||||
ServerCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor,
|
||||
ServerConfig serverConfig, BootupClasses bootupClasses, PstmtBatch pstmtBatch) {
|
||||
|
||||
this.xmlConfig = xmlConfig;
|
||||
this.pstmtBatch = pstmtBatch;
|
||||
this.clusterManager = clusterManager;
|
||||
this.backgroundExecutor = backgroundExecutor;
|
||||
this.cacheManager = cacheManager;
|
||||
this.serverConfig = serverConfig;
|
||||
this.bootupClasses = bootupClasses;
|
||||
this.expressionFactory = new DefaultExpressionFactory();
|
||||
|
||||
this.typeManager = new DefaultTypeManager(serverConfig, bootupClasses);
|
||||
this.binder = new Binder(typeManager);
|
||||
|
||||
this.resourceManager = ResourceManagerFactory.createResourceManager(serverConfig);
|
||||
this.deployOrmXml = new DeployOrmXml(resourceManager.getResourceSource());
|
||||
this.deployInherit = new DeployInherit(bootupClasses);
|
||||
|
||||
this.deployCreateProperties = new DeployCreateProperties(typeManager);
|
||||
this.deployUtil = new DeployUtil(typeManager, serverConfig);
|
||||
|
||||
this.beanDescriptorManager = new BeanDescriptorManager(this);
|
||||
beanDescriptorManager.deploy();
|
||||
|
||||
this.transactionManager = createTransactionManager();
|
||||
|
||||
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder);
|
||||
|
||||
ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager();
|
||||
if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) {
|
||||
externalTransactionManager = new JtaTransactionManager();
|
||||
}
|
||||
if (externalTransactionManager != null) {
|
||||
externalTransactionManager.setTransactionManager(transactionManager);
|
||||
this.transactionScopeManager = new ExternalTransactionScopeManager(transactionManager, externalTransactionManager);
|
||||
logger.info("Using Transaction Manager [" + externalTransactionManager.getClass() + "]");
|
||||
} else {
|
||||
this.transactionScopeManager = new DefaultTransactionScopeManager(transactionManager);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the TransactionManager taking into account autoCommit mode.
|
||||
*/
|
||||
private TransactionManager createTransactionManager() {
|
||||
|
||||
if (isAutoCommitMode()) {
|
||||
return new AutoCommitTransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
|
||||
}
|
||||
|
||||
return new TransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if autoCommit mode is on.
|
||||
*/
|
||||
private boolean isAutoCommitMode() {
|
||||
if (serverConfig.isAutoCommitMode()) {
|
||||
// explicitly set
|
||||
return true;
|
||||
}
|
||||
DataSource dataSource = serverConfig.getDataSource();
|
||||
if (dataSource instanceof DataSourcePool && ((DataSourcePool)dataSource).getAutoCommit()) {
|
||||
// We know the DataSourcePool is using autoCommit
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public JsonContext createJsonContext(SpiEbeanServer server) {
|
||||
|
||||
return new DJsonContext(server);
|
||||
}
|
||||
|
||||
public XmlConfig getXmlConfig() {
|
||||
return xmlConfig;
|
||||
}
|
||||
|
||||
public AutoFetchManager createAutoFetchManager(SpiEbeanServer server) {
|
||||
return AutoFetchManagerFactory.create(server, serverConfig, resourceManager);
|
||||
}
|
||||
|
||||
public RelationalQueryEngine createRelationalQueryEngine() {
|
||||
return new DefaultRelationalQueryEngine(binder, serverConfig.getDatabaseBooleanTrue());
|
||||
}
|
||||
|
||||
public OrmQueryEngine createOrmQueryEngine() {
|
||||
return new DefaultOrmQueryEngine(beanDescriptorManager, cQueryEngine);
|
||||
}
|
||||
|
||||
public Persister createPersister(SpiEbeanServer server) {
|
||||
return new DefaultPersister(server, binder, beanDescriptorManager, pstmtBatch);
|
||||
}
|
||||
|
||||
public PstmtBatch getPstmtBatch() {
|
||||
return pstmtBatch;
|
||||
}
|
||||
|
||||
public ServerCacheManager getCacheManager() {
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
public BootupClasses getBootupClasses() {
|
||||
return bootupClasses;
|
||||
}
|
||||
|
||||
public DatabasePlatform getDatabasePlatform() {
|
||||
return serverConfig.getDatabasePlatform();
|
||||
}
|
||||
|
||||
public ServerConfig getServerConfig() {
|
||||
return serverConfig;
|
||||
}
|
||||
|
||||
public ExpressionFactory getExpressionFactory() {
|
||||
return expressionFactory;
|
||||
}
|
||||
|
||||
public TypeManager getTypeManager() {
|
||||
return typeManager;
|
||||
}
|
||||
|
||||
public Binder getBinder() {
|
||||
return binder;
|
||||
}
|
||||
|
||||
public BeanDescriptorManager getBeanDescriptorManager() {
|
||||
return beanDescriptorManager;
|
||||
}
|
||||
|
||||
public DeployInherit getDeployInherit() {
|
||||
return deployInherit;
|
||||
}
|
||||
|
||||
public ResourceManager getResourceManager() {
|
||||
return resourceManager;
|
||||
}
|
||||
|
||||
public DeployOrmXml getDeployOrmXml() {
|
||||
return deployOrmXml;
|
||||
}
|
||||
|
||||
public DeployCreateProperties getDeployCreateProperties() {
|
||||
return deployCreateProperties;
|
||||
}
|
||||
|
||||
public DeployUtil getDeployUtil() {
|
||||
return deployUtil;
|
||||
}
|
||||
|
||||
public TransactionManager getTransactionManager() {
|
||||
return transactionManager;
|
||||
}
|
||||
|
||||
public TransactionScopeManager getTransactionScopeManager() {
|
||||
return transactionScopeManager;
|
||||
}
|
||||
|
||||
public CQueryEngine getCQueryEngine() {
|
||||
return cQueryEngine;
|
||||
}
|
||||
|
||||
public ClusterManager getClusterManager() {
|
||||
return clusterManager;
|
||||
}
|
||||
|
||||
public SpiBackgroundExecutor getBackgroundExecutor() {
|
||||
return backgroundExecutor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,9 +6,6 @@ import javax.naming.NamingException;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
|
||||
|
||||
/**
|
||||
* Helper to lookup a DataSource from JNDI.
|
||||
*/
|
||||
@@ -16,8 +13,6 @@ public class JndiDataSourceLookup {
|
||||
|
||||
private static final String DEFAULT_PREFIX = "java:comp/env/jdbc/";
|
||||
|
||||
String jndiPrefix = GlobalProperties.get("ebean.datasource.jndi.prefix", DEFAULT_PREFIX);
|
||||
|
||||
public JndiDataSourceLookup() {
|
||||
}
|
||||
|
||||
@@ -32,7 +27,7 @@ public class JndiDataSourceLookup {
|
||||
try {
|
||||
|
||||
if (!jndiName.startsWith("java:")){
|
||||
jndiName = jndiPrefix + jndiName;
|
||||
jndiName = DEFAULT_PREFIX + jndiName;
|
||||
}
|
||||
|
||||
Context ctx = new InitialContext();
|
||||
|
||||
@@ -13,7 +13,7 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
|
||||
|
||||
public enum Type {
|
||||
DETERMINE, INSERT, UPDATE, DELETE, ORMUPDATE, UPDATESQL, CALLABLESQL
|
||||
};
|
||||
}
|
||||
|
||||
protected boolean persistCascade;
|
||||
|
||||
@@ -24,7 +24,7 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
|
||||
|
||||
protected final PersistExecute persistExecute;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Used by CallableSqlRequest and UpdateSqlRequest.
|
||||
*/
|
||||
public PersistRequest(SpiEbeanServer server, SpiTransaction t, PersistExecute persistExecute) {
|
||||
@@ -41,25 +41,33 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
|
||||
* Execute the request right now.
|
||||
*/
|
||||
public abstract int executeNow();
|
||||
|
||||
public PstmtBatch getPstmtBatch() {
|
||||
return ebeanServer.getPstmtBatch();
|
||||
}
|
||||
|
||||
public boolean isLogSql() {
|
||||
return transaction.isLogSql();
|
||||
}
|
||||
|
||||
public boolean isLogSummary() {
|
||||
return transaction.isLogSummary();
|
||||
}
|
||||
|
||||
|
||||
public PstmtBatch getPstmtBatch() {
|
||||
return ebeanServer.getPstmtBatch();
|
||||
}
|
||||
|
||||
public boolean isLogSql() {
|
||||
return transaction.isLogSql();
|
||||
}
|
||||
|
||||
public boolean isLogSummary() {
|
||||
return transaction.isLogSummary();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if this persist request should use JDBC batch.
|
||||
*/
|
||||
public boolean isBatchThisRequest() {
|
||||
return transaction.isBatchThisRequest(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the Callable statement.
|
||||
* Execute the statement.
|
||||
*/
|
||||
public int executeStatement() {
|
||||
|
||||
boolean batch = transaction.isBatchThisRequest();
|
||||
boolean batch = isBatchThisRequest();
|
||||
|
||||
int rows;
|
||||
BatchControl control = transaction.getBatchControl();
|
||||
@@ -69,15 +77,15 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
|
||||
} else if (batch) {
|
||||
// need to create the BatchControl
|
||||
control = persistExecute.createBatchControl(transaction);
|
||||
rows = control.executeStatementOrBatch(this, batch);
|
||||
rows = control.executeStatementOrBatch(this, true);
|
||||
} else {
|
||||
rows = executeNow();
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
public void initTransIfRequired() {
|
||||
|
||||
public void initTransIfRequired() {
|
||||
createImplicitTransIfRequired(false);
|
||||
persistCascade = transaction.isPersistCascade();
|
||||
}
|
||||
|
||||
@@ -97,6 +97,21 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
*/
|
||||
private Set<String> updatedProperties;
|
||||
|
||||
/**
|
||||
* Flag set when request is added to JDBC batch.
|
||||
*/
|
||||
private boolean batched;
|
||||
|
||||
/**
|
||||
* Flag set when batchOnCascade to avoid using batch on the top bean.
|
||||
*/
|
||||
private boolean skipBatchForTopLevel;
|
||||
|
||||
/**
|
||||
* Flag set when batch mode is turned on for a persist cascade.
|
||||
*/
|
||||
private boolean batchOnCascadeSet;
|
||||
|
||||
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
|
||||
PersistExecute persistExecute, PersistRequest.Type type, boolean saveRecurse) {
|
||||
|
||||
@@ -127,6 +142,54 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
this.dirty = intercept.isDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the transaction and also check for batch on cascade escalation.
|
||||
*/
|
||||
public void initTransIfRequiredWithBatchCascade() {
|
||||
createImplicitTransIfRequired(false);
|
||||
if (transaction.checkBatchEscalationOnCascade(this)) {
|
||||
// we escalated to use batch mode so flush when done
|
||||
// but if createdTransaction then commit will flush it
|
||||
batchOnCascadeSet = !createdTransaction;
|
||||
}
|
||||
persistCascade = transaction.isPersistCascade();
|
||||
}
|
||||
|
||||
/**
|
||||
* If using batch on cascade flush if required.
|
||||
*/
|
||||
public void flushBatchOnCascade() {
|
||||
if (batchOnCascadeSet) {
|
||||
// we escalated to batch mode for request so flush
|
||||
transaction.flushBatchOnCascade();
|
||||
batchOnCascadeSet = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true is this request was added to the JDBC batch.
|
||||
*/
|
||||
public boolean isBatched() {
|
||||
return batched;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set when request is added to the JDBC batch.
|
||||
*/
|
||||
public void setBatched() {
|
||||
batched = true;
|
||||
}
|
||||
|
||||
|
||||
public void setSkipBatchForTopLevel() {
|
||||
skipBatchForTopLevel = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBatchThisRequest() {
|
||||
return !skipBatchForTopLevel && super.isBatchThisRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is an insert request.
|
||||
*/
|
||||
@@ -149,7 +212,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
return intercept.getDirtyValues();
|
||||
}
|
||||
|
||||
public boolean isNotify(TransactionEvent txnEvent) {
|
||||
public boolean isNotify() {
|
||||
this.notifyCache = beanDescriptor.isCacheNotify();
|
||||
return notifyCache || isNotifyPersistListener();
|
||||
}
|
||||
@@ -234,7 +297,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
if (id != null) {
|
||||
hc += id.hashCode();
|
||||
}
|
||||
beanHash = Integer.valueOf(hc);
|
||||
beanHash = new Integer(hc);
|
||||
}
|
||||
return beanHash;
|
||||
}
|
||||
@@ -397,7 +460,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
@Override
|
||||
public int executeOrQueue() {
|
||||
|
||||
boolean batch = transaction.isBatchThisRequest();
|
||||
boolean batch = isBatchThisRequest();
|
||||
|
||||
BatchControl control = transaction.getBatchControl();
|
||||
if (control != null) {
|
||||
@@ -405,7 +468,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
if (batch) {
|
||||
control = persistExecute.createBatchControl(transaction);
|
||||
return control.executeOrQueue(this, batch);
|
||||
return control.executeOrQueue(this, true);
|
||||
|
||||
} else {
|
||||
return executeNow();
|
||||
@@ -438,10 +501,15 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
String m = Message.msg("persist.conc2", "" + rowCount);
|
||||
throw new OptimisticLockException(m, null, bean);
|
||||
}
|
||||
if (type == Type.DELETE) {
|
||||
postDelete();
|
||||
}
|
||||
}
|
||||
|
||||
public void postDelete() {
|
||||
|
||||
/**
|
||||
* Aggressive L1 and L2 cache cleanup for deletes.
|
||||
*/
|
||||
private void postDelete() {
|
||||
// Delete the bean from the PersistenceContent
|
||||
transaction.getPersistenceContext().clear(beanDescriptor.getBeanType(), idValue);
|
||||
// Delete from cache early even if transaction fails
|
||||
@@ -457,10 +525,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
controllerPost();
|
||||
}
|
||||
|
||||
if (intercept != null) {
|
||||
// if bean persisted again then should result in an update
|
||||
intercept.setLoaded();
|
||||
}
|
||||
// if bean persisted again then should result in an update
|
||||
intercept.setLoaded();
|
||||
|
||||
addEvent();
|
||||
|
||||
@@ -528,9 +594,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
if (concurrencyMode.equals(ConcurrencyMode.VERSION)) {
|
||||
// check the version property was loaded
|
||||
BeanProperty prop = beanDescriptor.getVersionProperty();
|
||||
if (prop != null && intercept.isLoadedProperty(prop.getPropertyIndex())) {
|
||||
// OK to use version property
|
||||
} else {
|
||||
if (prop == null || !intercept.isLoadedProperty(prop.getPropertyIndex())) {
|
||||
concurrencyMode = ConcurrencyMode.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
|
||||
/**
|
||||
* Listens for webserver server starting and stopping events.
|
||||
@@ -19,8 +14,6 @@ import org.slf4j.LoggerFactory;
|
||||
*/
|
||||
public class ServletContextListener implements javax.servlet.ServletContextListener {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ServletContextListener.class);
|
||||
|
||||
/**
|
||||
* The servlet container is stopping.
|
||||
*/
|
||||
@@ -29,29 +22,10 @@ public class ServletContextListener implements javax.servlet.ServletContextListe
|
||||
}
|
||||
|
||||
/**
|
||||
* The servlet container is starting.
|
||||
* <p>
|
||||
* Initialise the properties file using SystemProperties.initWebapp();
|
||||
* and start Ebean.
|
||||
* </p>
|
||||
* Do nothing on startup.
|
||||
*/
|
||||
public void contextInitialized(ServletContextEvent event) {
|
||||
|
||||
try {
|
||||
ServletContext servletContext = event.getServletContext();
|
||||
GlobalProperties.setServletContext(servletContext);
|
||||
|
||||
if (servletContext != null) {
|
||||
String servletRealPath = servletContext.getRealPath("");
|
||||
GlobalProperties.put("servlet.realpath", servletRealPath);
|
||||
logger.info("servlet.realpath=[" + servletRealPath + "]");
|
||||
}
|
||||
|
||||
Ebean.getServer(null);
|
||||
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import com.avaje.ebeaninternal.server.lib.DaemonScheduleThreadPool;
|
||||
import com.avaje.ebeaninternal.server.lib.thread.ThreadPool;
|
||||
|
||||
/**
|
||||
* BackgroundExecutor using my traditional ThreadPool that will grow and trim.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class TraditionalBackgroundExecutor implements SpiBackgroundExecutor {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(TraditionalBackgroundExecutor.class);
|
||||
|
||||
private final ThreadPool pool;
|
||||
|
||||
private final DaemonScheduleThreadPool schedulePool;
|
||||
|
||||
/**
|
||||
* Construct the default implementation of BackgroundExecutor.
|
||||
*/
|
||||
public TraditionalBackgroundExecutor(ThreadPool pool, int schedulePoolSize, int shutdownWaitSeconds, String namePrefix) {
|
||||
this.pool = pool;
|
||||
this.schedulePool = new DaemonScheduleThreadPool(schedulePoolSize, shutdownWaitSeconds, namePrefix+"-periodic-");
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a Runnable using a background thread.
|
||||
*/
|
||||
public void execute(Runnable r) {
|
||||
pool.assign(r, true);
|
||||
}
|
||||
|
||||
public void executePeriodically(Runnable r, long delay, TimeUnit unit) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Registering for executePeriodically {} delay:{} {}",r, delay, unit);
|
||||
}
|
||||
schedulePool.scheduleWithFixedDelay(r, delay, delay, unit);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Shutting down");
|
||||
}
|
||||
pool.shutdown();
|
||||
schedulePool.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,16 @@ final class TransWrapper {
|
||||
wasCreated = created;
|
||||
}
|
||||
|
||||
void batchEscalateOnCollection() {
|
||||
transaction.checkBatchEscalationOnCollection();
|
||||
}
|
||||
|
||||
void flushBatchOnCollection() {
|
||||
if (!wasCreated) {
|
||||
transaction.flushBatchOnCollection();
|
||||
}
|
||||
}
|
||||
|
||||
void commitIfCreated() {
|
||||
if (wasCreated){
|
||||
transaction.commit();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.ddl;
|
||||
|
||||
import com.avaje.ebean.config.dbplatform.DbDdlSyntax;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
@@ -16,23 +17,23 @@ public class AddForeignKeysVisitor extends AbstractBeanVisitor {
|
||||
|
||||
final FkeyPropertyVisitor pv;
|
||||
|
||||
public AddForeignKeysVisitor(DdlGenContext ctx) {
|
||||
public AddForeignKeysVisitor(boolean addMode, DdlGenContext ctx) {
|
||||
this.ctx = ctx;
|
||||
this.pv = new FkeyPropertyVisitor(this, ctx);
|
||||
this.pv = new FkeyPropertyVisitor(addMode, this, ctx);
|
||||
}
|
||||
|
||||
public boolean visitBean(BeanDescriptor<?> descriptor) {
|
||||
if (!descriptor.isInheritanceRoot()){
|
||||
// ignore/skip if not a top level BeanDescriptor
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void visitBeanEnd(BeanDescriptor<?> descriptor) {
|
||||
|
||||
visitInheritanceProperties(descriptor, pv);
|
||||
public boolean visitBean(BeanDescriptor<?> descriptor) {
|
||||
if (!descriptor.isInheritanceRoot()) {
|
||||
// ignore/skip if not a top level BeanDescriptor
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void visitBeanEnd(BeanDescriptor<?> descriptor) {
|
||||
|
||||
visitInheritanceProperties(descriptor, pv);
|
||||
}
|
||||
|
||||
public void visitBegin() {
|
||||
}
|
||||
@@ -48,11 +49,17 @@ public class AddForeignKeysVisitor extends AbstractBeanVisitor {
|
||||
|
||||
public static class FkeyPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
|
||||
/**
|
||||
* Set to false when generating drop foreign key statements.
|
||||
*/
|
||||
final boolean addMode;
|
||||
|
||||
final DdlGenContext ctx;
|
||||
|
||||
final AddForeignKeysVisitor parent;
|
||||
|
||||
public FkeyPropertyVisitor(AddForeignKeysVisitor parent, DdlGenContext ctx) {
|
||||
public FkeyPropertyVisitor(boolean addMode, AddForeignKeysVisitor parent, DdlGenContext ctx) {
|
||||
this.addMode = addMode;
|
||||
this.parent = parent;
|
||||
this.ctx = ctx;
|
||||
}
|
||||
@@ -69,51 +76,59 @@ public class AddForeignKeysVisitor extends AbstractBeanVisitor {
|
||||
// Alter table o_address add Foreign Key (country_code) references o_country (code) on delete restrict on update restrict;
|
||||
|
||||
String baseTable = p.getBeanDescriptor().getBaseTable();
|
||||
|
||||
TableJoin tableJoin = p.getTableJoin();
|
||||
|
||||
TableJoinColumn[] columns = tableJoin.columns();
|
||||
|
||||
|
||||
String tableName = p.getBeanDescriptor().getBaseTable();
|
||||
String fkName = ctx.getDdlSyntax().getForeignKeyName(tableName, p.getName(), ctx.incrementFkCount());
|
||||
|
||||
ctx.write("alter table ").write(baseTable).write(" add ");
|
||||
DbDdlSyntax ddlSyntax = ctx.getDdlSyntax();
|
||||
String fkName = ddlSyntax.getForeignKeyName(tableName, p.getName(), ctx.incrementFkCount());
|
||||
|
||||
if (!addMode) {
|
||||
// look to generate drop foreign key statement
|
||||
String dropKeyConstraintPrefix = ddlSyntax.dropKeyConstraintPrefix(tableName, fkName);
|
||||
if (dropKeyConstraintPrefix != null && !dropKeyConstraintPrefix.isEmpty()) {
|
||||
ctx.write(dropKeyConstraintPrefix).write(" ");
|
||||
}
|
||||
}
|
||||
|
||||
ctx.write("alter table ").write(baseTable).write(addOrDrop());
|
||||
if (fkName != null) {
|
||||
ctx.write("constraint ").write(fkName).write(" ");
|
||||
}
|
||||
ctx.write("foreign key (");
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
if (i > 0){
|
||||
ctx.write(",");
|
||||
}
|
||||
ctx.write(columns[i].getLocalDbColumn());
|
||||
}
|
||||
ctx.write(")");
|
||||
if (addMode) {
|
||||
ctx.write("foreign key (");
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
if (i > 0) {
|
||||
ctx.write(",");
|
||||
}
|
||||
ctx.write(columns[i].getLocalDbColumn());
|
||||
}
|
||||
ctx.write(")");
|
||||
|
||||
ctx.write(" references ");
|
||||
ctx.write(tableJoin.getTable());
|
||||
ctx.write(" (");
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
if (i > 0){
|
||||
ctx.write(",");
|
||||
}
|
||||
ctx.write(columns[i].getForeignDbColumn());
|
||||
}
|
||||
ctx.write(")");
|
||||
ctx.write(" references ");
|
||||
ctx.write(tableJoin.getTable());
|
||||
ctx.write(" (");
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
if (i > 0) {
|
||||
ctx.write(",");
|
||||
}
|
||||
ctx.write(columns[i].getForeignDbColumn());
|
||||
}
|
||||
ctx.write(")");
|
||||
|
||||
String fkeySuffix = ctx.getDdlSyntax().getForeignKeySuffix();
|
||||
if (fkeySuffix != null){
|
||||
ctx.write(" ").write(fkeySuffix);
|
||||
}
|
||||
String fkeySuffix = ctx.getDdlSyntax().getForeignKeySuffix();
|
||||
if (fkeySuffix != null) {
|
||||
ctx.write(" ").write(fkeySuffix);
|
||||
}
|
||||
}
|
||||
ctx.write(";").writeNewLine();
|
||||
|
||||
if (ctx.getDdlSyntax().isRenderIndexForFkey()){
|
||||
if (addMode && ddlSyntax.isRenderIndexForFkey()){
|
||||
|
||||
//create index idx_fk_o_address_ctry on o_address(country_code);
|
||||
ctx.write("create index ");
|
||||
|
||||
String idxName = ctx.getDdlSyntax().getIndexName(tableName, p.getName(), ctx.incrementIxCount());
|
||||
String idxName = ddlSyntax.getIndexName(tableName, p.getName(), ctx.incrementIxCount());
|
||||
if (idxName != null){
|
||||
ctx.write(idxName);
|
||||
}
|
||||
@@ -129,21 +144,27 @@ public class AddForeignKeysVisitor extends AbstractBeanVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitScalar(BeanProperty p) {
|
||||
// not interested
|
||||
}
|
||||
/**
|
||||
* Return 'add' or 'drop' of foreign key.
|
||||
*/
|
||||
protected String addOrDrop() {
|
||||
return addMode ? " add " : " drop " ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCompound(BeanPropertyCompound p) {
|
||||
// not interested
|
||||
}
|
||||
@Override
|
||||
public void visitScalar(BeanProperty p) {
|
||||
// not interested
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) {
|
||||
// not interested
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
public void visitCompound(BeanPropertyCompound p) {
|
||||
// not interested
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) {
|
||||
// not interested
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -126,6 +126,13 @@ public class DdlGenerator implements SpiEbeanPlugin {
|
||||
|
||||
DdlGenContext ctx = createContext();
|
||||
|
||||
if (ctx.getDdlSyntax().isDropKeyConstraints()) {
|
||||
// generate drop foreign key constraint statements (sql server joy)
|
||||
AddForeignKeysVisitor fkeys = new AddForeignKeysVisitor(false, ctx);
|
||||
VisitorUtil.visit(server, fkeys);
|
||||
ctx.writeNewLine();
|
||||
}
|
||||
|
||||
DropTableVisitor drop = new DropTableVisitor(ctx);
|
||||
VisitorUtil.visit(server, drop);
|
||||
|
||||
@@ -146,7 +153,7 @@ public class DdlGenerator implements SpiEbeanPlugin {
|
||||
CreateSequenceVisitor createSequence = new CreateSequenceVisitor(ctx);
|
||||
VisitorUtil.visit(server, createSequence);
|
||||
|
||||
AddForeignKeysVisitor fkeys = new AddForeignKeysVisitor(ctx);
|
||||
AddForeignKeysVisitor fkeys = new AddForeignKeysVisitor(true, ctx);
|
||||
VisitorUtil.visit(server, fkeys);
|
||||
|
||||
CreateIndexVisitor indexes = new CreateIndexVisitor(ctx);
|
||||
@@ -275,7 +282,6 @@ public class DdlGenerator implements SpiEbeanPlugin {
|
||||
} catch (Exception e) {
|
||||
if (expectErrors) {
|
||||
logger.info(" ... ignoring error executing " + getSummary(stmt) + " error: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
} else {
|
||||
String msg = "Error executing stmt[" + stmt + "] error[" + e.getMessage() + "]";
|
||||
throw new RuntimeException(msg, e);
|
||||
|
||||
@@ -24,6 +24,7 @@ public class DropTableVisitor implements BeanVisitor {
|
||||
}
|
||||
|
||||
protected void writeDropTable(String tableName){
|
||||
ctx.write(ddlSyntax.dropTablePrefix(tableName));
|
||||
ctx.write("drop table ");
|
||||
if (ddlSyntax.getDropIfExists() != null){
|
||||
ctx.write(ddlSyntax.getDropIfExists()).write(" ");
|
||||
|
||||
@@ -1,24 +1,5 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.SqlUpdate;
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.annotation.ConcurrencyMode;
|
||||
@@ -35,11 +16,7 @@ import com.avaje.ebean.event.BeanPersistListener;
|
||||
import com.avaje.ebean.event.BeanQueryAdapter;
|
||||
import com.avaje.ebean.meta.MetaBeanInfo;
|
||||
import com.avaje.ebean.meta.MetaQueryPlanStatistic;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlan;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.api.SpiUpdatePlan;
|
||||
import com.avaje.ebeaninternal.api.*;
|
||||
import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD;
|
||||
import com.avaje.ebeaninternal.server.cache.CachedBeanData;
|
||||
import com.avaje.ebeaninternal.server.cache.CachedManyIds;
|
||||
@@ -50,13 +27,7 @@ import com.avaje.ebeaninternal.server.core.PersistRequestBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyLists;
|
||||
import com.avaje.ebeaninternal.server.el.ElComparator;
|
||||
import com.avaje.ebeaninternal.server.el.ElComparatorCompound;
|
||||
import com.avaje.ebeaninternal.server.el.ElComparatorProperty;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
import com.avaje.ebeaninternal.server.persist.DmlUtil;
|
||||
import com.avaje.ebeaninternal.server.el.*;
|
||||
import com.avaje.ebeaninternal.server.query.CQueryPlan;
|
||||
import com.avaje.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
@@ -68,6 +39,15 @@ import com.avaje.ebeaninternal.util.SortByClause;
|
||||
import com.avaje.ebeaninternal.util.SortByClause.Property;
|
||||
import com.avaje.ebeaninternal.util.SortByClauseParser;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Describes Beans including their deployment information.
|
||||
@@ -1036,6 +1016,14 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a raw expression for 'where parent id in ...' clause.
|
||||
*/
|
||||
public String getParentIdInExpr(int parentIdSize, String rawWhere) {
|
||||
String inClause = idBinder.getIdInValueExpr(parentIdSize);
|
||||
return idBinder.isIdInExpandedForm() ? inClause : rawWhere + inClause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the IdBinder which is helpful for handling the various types of Id.
|
||||
*/
|
||||
|
||||
@@ -1,25 +1,5 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.Transient;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.Model;
|
||||
import com.avaje.ebean.RawSql;
|
||||
@@ -29,7 +9,6 @@ import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.config.EncryptKey;
|
||||
import com.avaje.ebean.config.EncryptKeyManager;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebean.config.NamingConvention;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.config.dbplatform.DbIdentity;
|
||||
@@ -38,35 +17,31 @@ import com.avaje.ebean.config.dbplatform.IdType;
|
||||
import com.avaje.ebean.event.BeanFinder;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.TransactionEventTable;
|
||||
import com.avaje.ebeaninternal.server.core.BootupClasses;
|
||||
import com.avaje.ebeaninternal.server.core.InternString;
|
||||
import com.avaje.ebeaninternal.server.core.InternalConfiguration;
|
||||
import com.avaje.ebeaninternal.server.core.Message;
|
||||
import com.avaje.ebeaninternal.server.core.XmlConfig;
|
||||
import com.avaje.ebeaninternal.server.core.*;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
|
||||
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
|
||||
import com.avaje.ebeaninternal.server.deploy.id.IdBinderEmbedded;
|
||||
import com.avaje.ebeaninternal.server.deploy.id.IdBinderFactory;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.DeployBeanInfo;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.ReadAnnotations;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.TransientProperties;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.*;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.*;
|
||||
import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator;
|
||||
import com.avaje.ebeaninternal.server.lib.util.Dnode;
|
||||
import com.avaje.ebeaninternal.server.properties.BeanPropertiesReader;
|
||||
import com.avaje.ebeaninternal.server.properties.BeanPropertyInfo;
|
||||
import com.avaje.ebeaninternal.server.properties.BeanPropertyInfoFactory;
|
||||
import com.avaje.ebeaninternal.server.properties.BeanPropertiesReader;
|
||||
import com.avaje.ebeaninternal.server.properties.EnhanceBeanPropertyInfoFactory;
|
||||
import com.avaje.ebeaninternal.server.type.TypeManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.Transient;
|
||||
import javax.sql.DataSource;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Creates BeanDescriptors.
|
||||
@@ -153,6 +128,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
private final BeanLifecycleAdapterFactory beanLifecycleAdapterFactory;
|
||||
|
||||
private final boolean eagerFetchLobs;
|
||||
|
||||
/**
|
||||
* Create for a given database dbConfig.
|
||||
*/
|
||||
@@ -167,6 +144,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
this.encryptKeyManager = config.getServerConfig().getEncryptKeyManager();
|
||||
this.databasePlatform = config.getServerConfig().getDatabasePlatform();
|
||||
this.idBinderFactory = new IdBinderFactory(databasePlatform.isIdInExpandedForm());
|
||||
this.eagerFetchLobs = config.getServerConfig().isEagerFetchLobs();
|
||||
|
||||
this.bootupClasses = config.getBootupClasses();
|
||||
this.createProperties = config.getDeployCreateProperties();
|
||||
@@ -397,12 +375,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
String msg = "SERIOUS ERROR: The hashCode() and equals() methods *MUST* be implemented ";
|
||||
msg += "on Embedded bean " + idType + " as it is used as an Id for " + beanType;
|
||||
|
||||
if (GlobalProperties.getBoolean("ebean.strict", true)) {
|
||||
throw new PersistenceException(msg, source);
|
||||
} else {
|
||||
logger.error(msg, source);
|
||||
}
|
||||
throw new PersistenceException(msg, source);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1017,7 +990,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
DeployBeanInfo<T> info = new DeployBeanInfo<T>(deployUtil, desc);
|
||||
|
||||
readAnnotations.readInitial(info);
|
||||
readAnnotations.readInitial(info, eagerFetchLobs);
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
@@ -225,6 +225,10 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
return createElPropertyValue(propName, remainder, chain, propertyDeploy);
|
||||
}
|
||||
|
||||
public void buildSelectExpressionChain(String prefix, List<String> selectChain) {
|
||||
// do not add to the selectChain at the top level of the Many bean
|
||||
}
|
||||
|
||||
public SqlUpdate deleteByParentId(Object parentId, List<Object> parentIdist) {
|
||||
if (parentId != null) {
|
||||
return deleteByParentId(parentId);
|
||||
@@ -278,9 +282,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
query.setIncludeTableJoin(inverseJoin);
|
||||
}
|
||||
String rawWhere = deriveWhereParentIdSql(true, tableAlias);
|
||||
String inClause = descriptor.getIdBinder().getIdInValueExpr(parentIds.size());
|
||||
|
||||
String expr = rawWhere + inClause;
|
||||
String expr = descriptor.getParentIdInExpr(parentIds.size(), rawWhere);
|
||||
|
||||
// Flatten the bind values if needed (embeddedId)
|
||||
List<Object> bindValues = getBindParentIds(parentIds);
|
||||
|
||||
@@ -287,7 +287,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if this a OneToOne property. Otherwise assumed ManyToOne.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebeaninternal.server.core.InternString;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin;
|
||||
@@ -99,15 +98,11 @@ public class BeanTable {
|
||||
}
|
||||
|
||||
if (complexKey){
|
||||
// check to see if we want prefixes by default with complex keys
|
||||
boolean usePrefixOnComplex = GlobalProperties.getBoolean("ebean.prefixComplexKeys", false);
|
||||
if (!usePrefixOnComplex){
|
||||
// just to copy the column name rather than prefix with the foreignKeyPrefix.
|
||||
// I think that with complex keys this is the more common approach.
|
||||
String msg = "On table["+baseTable+"] foreign key column ["+lc+"]";
|
||||
logger.debug(msg);
|
||||
fk = lc;
|
||||
}
|
||||
// just to copy the column name rather than prefix with the foreignKeyPrefix.
|
||||
// I think that with complex keys this is the more common approach.
|
||||
String msg = "On table["+baseTable+"] foreign key column ["+lc+"]";
|
||||
logger.debug(msg);
|
||||
fk = lc;
|
||||
}
|
||||
|
||||
DeployTableJoinColumn joinCol = new DeployTableJoinColumn(lc, fk);
|
||||
|
||||
@@ -25,6 +25,11 @@ public interface IdBinder {
|
||||
*/
|
||||
public void initialise();
|
||||
|
||||
/**
|
||||
* Return true if this is a compound key and must use expanded and or form.
|
||||
*/
|
||||
public boolean isIdInExpandedForm();
|
||||
|
||||
/**
|
||||
* Write the Id value to binary DataOuput.
|
||||
*/
|
||||
|
||||
@@ -43,17 +43,21 @@ public final class IdBinderEmbedded implements IdBinder {
|
||||
this.idInValueSql = idInExpandedForm ? idInExpanded() : idInCompressed();
|
||||
}
|
||||
|
||||
public boolean isIdInExpandedForm() {
|
||||
return idInExpandedForm;
|
||||
}
|
||||
|
||||
private String idInExpanded() {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
StringBuilder sb = new StringBuilder(30);
|
||||
sb.append("(");
|
||||
for (int i = 0; i < props.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(" and ");
|
||||
}
|
||||
sb.append(embIdProperty.getName());
|
||||
sb.append(idDesc.getBaseTableAlias());
|
||||
sb.append(".");
|
||||
sb.append(props[i].getName());
|
||||
sb.append(props[i].getDbColumn());
|
||||
sb.append("=?");
|
||||
}
|
||||
sb.append(")");
|
||||
|
||||
@@ -30,6 +30,10 @@ public final class IdBinderEmpty implements IdBinder {
|
||||
|
||||
}
|
||||
|
||||
public boolean isIdInExpandedForm() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getOrderBy(String pathPrefix, boolean ascending) {
|
||||
return pathPrefix;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,11 @@ public final class IdBinderSimple implements IdBinder {
|
||||
public void initialise(){
|
||||
// do nothing
|
||||
}
|
||||
|
||||
|
||||
public boolean isIdInExpandedForm() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getOrderBy(String pathPrefix, boolean ascending) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -1,21 +1,11 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import java.sql.Types;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import com.avaje.ebean.annotation.*;
|
||||
import com.avaje.ebean.config.EncryptDeploy;
|
||||
import com.avaje.ebean.config.EncryptDeploy.Mode;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebean.config.dbplatform.DbEncrypt;
|
||||
import com.avaje.ebean.config.dbplatform.DbEncryptFunction;
|
||||
import com.avaje.ebean.config.dbplatform.IdType;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
|
||||
@@ -23,12 +13,14 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound;
|
||||
import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator;
|
||||
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
|
||||
import com.avaje.ebeaninternal.server.type.CtCompoundType;
|
||||
import com.avaje.ebeaninternal.server.type.DataEncryptSupport;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarTypeBytesBase;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarTypeBytesEncrypted;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarTypeEncryptedWrapper;
|
||||
import com.avaje.ebeaninternal.server.type.*;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.sql.Types;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Read the field level deployment annotations.
|
||||
@@ -42,10 +34,10 @@ public class AnnotationFields extends AnnotationParser {
|
||||
|
||||
private GeneratedPropertyFactory generatedPropFactory = new GeneratedPropertyFactory();
|
||||
|
||||
public AnnotationFields(DeployBeanInfo<?> info) {
|
||||
public AnnotationFields(DeployBeanInfo<?> info, boolean eagerFetchLobs) {
|
||||
super(info);
|
||||
|
||||
if (GlobalProperties.getBoolean("ebean.lobEagerFetch", false)) {
|
||||
if (eagerFetchLobs) {
|
||||
defaultLobFetchType = FetchType.EAGER;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@ public class ReadAnnotations {
|
||||
* to resolve the relationships etc.
|
||||
* </p>
|
||||
*/
|
||||
public void readInitial(DeployBeanInfo<?> info){
|
||||
public void readInitial(DeployBeanInfo<?> info, boolean eagerFetchLobs){
|
||||
|
||||
try {
|
||||
new AnnotationClass(info).parse();
|
||||
new AnnotationFields(info).parse();
|
||||
new AnnotationFields(info, eagerFetchLobs).parse();
|
||||
|
||||
} catch (RuntimeException e){
|
||||
String msg = "Error reading annotations for "+info;
|
||||
|
||||
@@ -59,6 +59,7 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
this.exprList = new DefaultExpressionList<T>(query, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
|
||||
List<SpiExpression> list = exprList.internalList();
|
||||
@@ -79,17 +80,20 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Junction<T> add(Expression item) {
|
||||
SpiExpression i = (SpiExpression) item;
|
||||
exprList.add(i);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Junction<T> addAll(ExpressionList<T> addList) {
|
||||
exprList.addAll(addList);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
List<SpiExpression> list = exprList.internalList();
|
||||
@@ -100,6 +104,7 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
List<SpiExpression> list = exprList.internalList();
|
||||
@@ -122,6 +127,7 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
/**
|
||||
* Based on Junction type and all the expression contained.
|
||||
*/
|
||||
@Override
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(JunctionExpression.class).add(joinType);
|
||||
List<SpiExpression> list = exprList.internalList();
|
||||
@@ -130,6 +136,7 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
builder.add(JunctionExpression.class).add(joinType);
|
||||
List<SpiExpression> list = exprList.internalList();
|
||||
@@ -138,6 +145,7 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
int hc = JunctionExpression.class.getName().hashCode();
|
||||
|
||||
@@ -149,50 +157,62 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> endJunction() {
|
||||
return exprList.endJunction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> allEq(Map<String, Object> propertyMap) {
|
||||
return exprList.allEq(propertyMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> and(Expression expOne, Expression expTwo) {
|
||||
return exprList.and(expOne, expTwo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> between(String propertyName, Object value1, Object value2) {
|
||||
return exprList.between(propertyName, value1, value2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> betweenProperties(String lowProperty, String highProperty, Object value) {
|
||||
return exprList.betweenProperties(lowProperty, highProperty, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Junction<T> conjunction() {
|
||||
return exprList.conjunction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> contains(String propertyName, String value) {
|
||||
return exprList.contains(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Junction<T> disjunction() {
|
||||
return exprList.disjunction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> endsWith(String propertyName, String value) {
|
||||
return exprList.endsWith(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> eq(String propertyName, Object value) {
|
||||
return exprList.eq(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> exampleLike(Object example) {
|
||||
return exprList.exampleLike(example);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> filterMany(String prop) {
|
||||
throw new RuntimeException("filterMany not allowed on Junction expression list");
|
||||
}
|
||||
@@ -202,18 +222,22 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
return exprList.apply(pathProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FutureIds<T> findFutureIds() {
|
||||
return exprList.findFutureIds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FutureList<T> findFutureList() {
|
||||
return exprList.findFutureList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FutureRowCount<T> findFutureRowCount() {
|
||||
return exprList.findFutureRowCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> findIds() {
|
||||
return exprList.findIds();
|
||||
}
|
||||
@@ -228,195 +252,237 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
exprList.findEachWhile(consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findVisit(QueryResultVisitor<T> visitor) {
|
||||
exprList.findVisit(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryIterator<T> findIterate() {
|
||||
return exprList.findIterate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> findList() {
|
||||
return exprList.findList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<?, T> findMap() {
|
||||
return exprList.findMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K> Map<K, T> findMap(String keyProperty, Class<K> keyType) {
|
||||
return exprList.findMap(keyProperty, keyType);
|
||||
}
|
||||
|
||||
public PagingList<T> findPagingList(int pageSize) {
|
||||
return exprList.findPagingList(pageSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PagedList<T> findPagedList(int pageIndex, int pageSize) {
|
||||
return exprList.findPagedList(pageIndex, pageSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findRowCount() {
|
||||
return exprList.findRowCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<T> findSet() {
|
||||
return exprList.findSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T findUnique() {
|
||||
return exprList.findUnique();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> ge(String propertyName, Object value) {
|
||||
return exprList.ge(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> gt(String propertyName, Object value) {
|
||||
return exprList.gt(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> having() {
|
||||
throw new RuntimeException("having() not allowed on Junction expression list");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> icontains(String propertyName, String value) {
|
||||
return exprList.icontains(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> idEq(Object value) {
|
||||
return exprList.idEq(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> idIn(List<?> idValues) {
|
||||
return exprList.idIn(idValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> iendsWith(String propertyName, String value) {
|
||||
return exprList.iendsWith(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> ieq(String propertyName, String value) {
|
||||
return exprList.ieq(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> iexampleLike(Object example) {
|
||||
return exprList.iexampleLike(example);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> ilike(String propertyName, String value) {
|
||||
return exprList.ilike(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> in(String propertyName, Collection<?> values) {
|
||||
return exprList.in(propertyName, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> in(String propertyName, Object... values) {
|
||||
return exprList.in(propertyName, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> in(String propertyName, com.avaje.ebean.Query<?> subQuery) {
|
||||
return exprList.in(propertyName, subQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> isNotNull(String propertyName) {
|
||||
return exprList.isNotNull(propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> isNull(String propertyName) {
|
||||
return exprList.isNull(propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> istartsWith(String propertyName, String value) {
|
||||
return exprList.istartsWith(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> le(String propertyName, Object value) {
|
||||
return exprList.le(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> like(String propertyName, String value) {
|
||||
return exprList.like(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> lt(String propertyName, Object value) {
|
||||
return exprList.lt(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> ne(String propertyName, Object value) {
|
||||
return exprList.ne(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> not(Expression exp) {
|
||||
return exprList.not(exp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> or(Expression expOne, Expression expTwo) {
|
||||
return exprList.or(expOne, expTwo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrderBy<T> order() {
|
||||
return exprList.order();
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.avaje.ebean.Query<T> order(String orderByClause) {
|
||||
return exprList.order(orderByClause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrderBy<T> orderBy() {
|
||||
return exprList.orderBy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.avaje.ebean.Query<T> orderBy(String orderBy) {
|
||||
return exprList.orderBy(orderBy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.avaje.ebean.Query<T> query() {
|
||||
return exprList.query();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> raw(String raw, Object value) {
|
||||
return exprList.raw(raw, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> raw(String raw, Object[] values) {
|
||||
return exprList.raw(raw, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> raw(String raw) {
|
||||
return exprList.raw(raw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.avaje.ebean.Query<T> select(String properties) {
|
||||
return exprList.select(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.avaje.ebean.Query<T> setFirstRow(int firstRow) {
|
||||
return exprList.setFirstRow(firstRow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.avaje.ebean.Query<T> setMapKey(String mapKey) {
|
||||
return exprList.setMapKey(mapKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.avaje.ebean.Query<T> setMaxRows(int maxRows) {
|
||||
return exprList.setMaxRows(maxRows);
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.avaje.ebean.Query<T> setOrderBy(String orderBy) {
|
||||
return exprList.setOrderBy(orderBy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.avaje.ebean.Query<T> setUseCache(boolean useCache) {
|
||||
return exprList.setUseCache(useCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> startsWith(String propertyName, String value) {
|
||||
return exprList.startsWith(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> where() {
|
||||
return exprList.where();
|
||||
}
|
||||
|
||||
@@ -6,18 +6,15 @@ import com.avaje.ebean.config.PstmtDelegate;
|
||||
import com.avaje.ebeaninternal.server.lib.sql.ExtendedPreparedStatement;
|
||||
|
||||
/**
|
||||
* Implementation of PstmtDelegate from Ebean's own
|
||||
* DataSource.
|
||||
*
|
||||
* @author rbygrave
|
||||
* Implementation of PstmtDelegate from Ebean's own DataSource.
|
||||
*/
|
||||
public class StandardPstmtDelegate implements PstmtDelegate {
|
||||
|
||||
/**
|
||||
* Unwrap the PreparedStatement from Ebean's DataSource implementation.
|
||||
*/
|
||||
public PreparedStatement unwrap(PreparedStatement pstmt) {
|
||||
|
||||
return ((ExtendedPreparedStatement)pstmt).getDelegate();
|
||||
}
|
||||
/**
|
||||
* Unwrap the PreparedStatement from Ebean's DataSource implementation.
|
||||
*/
|
||||
public PreparedStatement unwrap(PreparedStatement pstmt) {
|
||||
|
||||
return ((ExtendedPreparedStatement) pstmt).getDelegate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,9 @@ public final class DaemonThreadPool extends ThreadPoolExecutor {
|
||||
* the time in seconds allowed for the pool to shutdown nicely. After
|
||||
* this the pool is forced to shutdown.
|
||||
*/
|
||||
public DaemonThreadPool(int coreSize, long keepAliveSecs, int shutdownWaitSeconds, String namePrefix) {
|
||||
public DaemonThreadPool(int coreSize, int maximumPoolSize, long keepAliveSecs, int shutdownWaitSeconds, String namePrefix) {
|
||||
|
||||
super(coreSize, coreSize, keepAliveSecs, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new DaemonThreadFactory(namePrefix));
|
||||
super(coreSize, maximumPoolSize, keepAliveSecs, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new DaemonThreadFactory(namePrefix));
|
||||
allowCoreThreadTimeOut(true);
|
||||
this.shutdownWaitSeconds = shutdownWaitSeconds;
|
||||
this.namePrefix = namePrefix;
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package com.avaje.ebeaninternal.server.lib;
|
||||
|
||||
import com.avaje.ebean.common.BootupEbeanManager;
|
||||
import com.avaje.ebeaninternal.api.ClassUtil;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Driver;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
@@ -7,14 +13,6 @@ import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.common.BootupEbeanManager;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebeaninternal.api.ClassUtil;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
|
||||
/**
|
||||
* Manages the shutdown of the JVM Runtime.
|
||||
* <p>
|
||||
@@ -33,12 +31,9 @@ public final class ShutdownManager {
|
||||
|
||||
static BootupEbeanManager serverFactory;
|
||||
|
||||
static boolean whyShutdown;
|
||||
|
||||
static {
|
||||
// Register the Shutdown hook
|
||||
registerShutdownHook();
|
||||
whyShutdown = GlobalProperties.getBoolean("debug.shutdown.why",false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,19 +113,11 @@ public final class ShutdownManager {
|
||||
logger.debug("Shutting down");
|
||||
}
|
||||
|
||||
if (whyShutdown) {
|
||||
try {
|
||||
throw new RuntimeException("debug.shutdown.why=true ...");
|
||||
} catch (Throwable e) {
|
||||
logger.warn("Stacktrace showing why shutdown was fired", e);
|
||||
}
|
||||
}
|
||||
|
||||
stopping = true;
|
||||
|
||||
deregisterShutdownHook();
|
||||
|
||||
String shutdownRunner = GlobalProperties.get("system.shutdown.runnable", null);
|
||||
String shutdownRunner = System.getProperty("ebean.shutdown.runnable");
|
||||
if (shutdownRunner != null) {
|
||||
try {
|
||||
// A custom runnable executed at the start of shutdown
|
||||
@@ -157,7 +144,7 @@ public final class ShutdownManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (GlobalProperties.getBoolean("datasource.deregisterAllDrivers", false)) {
|
||||
if ("true".equalsIgnoreCase(System.getProperty("ebean.datasource.deregisterAllDrivers", "false"))) {
|
||||
deregisterAllJdbcDrivers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package com.avaje.ebeaninternal.server.lib.sql;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebeaninternal.server.lib.util.MailEvent;
|
||||
import com.avaje.ebeaninternal.server.lib.util.MailListener;
|
||||
import com.avaje.ebeaninternal.server.lib.util.MailMessage;
|
||||
import com.avaje.ebeaninternal.server.lib.util.MailSender;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A simple smtp email alert that sends a email message on dataSourceDown and
|
||||
@@ -23,7 +21,11 @@ public class SimpleDataSourceAlert implements DataSourceAlert, MailListener {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SimpleDataSourceAlert.class);
|
||||
|
||||
// boolean sendInBackGround = true;
|
||||
private static String alertMailServerName = System.getProperty("ebean.datasource.alert.mailserver");
|
||||
|
||||
private static String fromUser = System.getProperty("ebean.datasource.alert.fromUser");
|
||||
private static String fromEmail = System.getProperty("ebean.datasource.alert.fromEmail");
|
||||
private static String toEmail = System.getProperty("ebean.datasource.alert.toEmail");
|
||||
|
||||
/**
|
||||
* Create a SimpleAlerter.
|
||||
@@ -79,15 +81,10 @@ public class SimpleDataSourceAlert implements DataSourceAlert, MailListener {
|
||||
|
||||
private void sendMessage(String subject, String msg) {
|
||||
|
||||
String mailServerName = GlobalProperties.get("datasource.alert.mailserver", null);
|
||||
if (mailServerName == null) {
|
||||
if (alertMailServerName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String fromUser = GlobalProperties.get("datasource.alert.fromuser", null);
|
||||
String fromEmail = GlobalProperties.get("datasource.alert.fromemail", null);
|
||||
String toEmail = GlobalProperties.get("datasource.alert.toemail", null);
|
||||
|
||||
MailMessage data = new MailMessage();
|
||||
data.setSender(fromUser, fromEmail);
|
||||
data.addBodyLine(msg);
|
||||
@@ -95,15 +92,15 @@ public class SimpleDataSourceAlert implements DataSourceAlert, MailListener {
|
||||
|
||||
String[] toList = toEmail.split(",");
|
||||
if (toList.length == 0) {
|
||||
throw new RuntimeException("alert.toemail has not been set?");
|
||||
logger.error("alert.toemail has not been set?");
|
||||
} else {
|
||||
for (int i = 0; i < toList.length; i++) {
|
||||
data.addRecipient(null, toList[i].trim());
|
||||
}
|
||||
MailSender sender = new MailSender(alertMailServerName);
|
||||
sender.setMailListener(this);
|
||||
sender.sendInBackground(data);
|
||||
}
|
||||
for (int i = 0; i < toList.length; i++) {
|
||||
data.addRecipient(null, toList[i].trim());
|
||||
}
|
||||
|
||||
MailSender sender = new MailSender(mailServerName);
|
||||
sender.setMailListener(this);
|
||||
sender.sendInBackground(data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,278 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.lib.thread;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A thread that belongs to a ThreadPool. It will return to the Threadpool when
|
||||
* it has finished its assigned task.
|
||||
*/
|
||||
public class PooledThread implements Runnable {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PooledThread.class);
|
||||
|
||||
/**
|
||||
* Flag to indicate that the thread was interrupted.
|
||||
*/
|
||||
private boolean wasInterrupted;
|
||||
|
||||
/**
|
||||
* The time the thread was last used.
|
||||
*/
|
||||
private long lastUsedTime;
|
||||
|
||||
/**
|
||||
* The work to run
|
||||
*/
|
||||
private Work work;
|
||||
|
||||
/**
|
||||
* Set to indicate the thread is stopping.
|
||||
*/
|
||||
private boolean isStopping;
|
||||
|
||||
/**
|
||||
* Set when the thread has stopped.
|
||||
*/
|
||||
private boolean isStopped;
|
||||
/**
|
||||
* The background thread.
|
||||
*/
|
||||
private Thread thread;
|
||||
|
||||
/**
|
||||
* The pool this worker belongs to.
|
||||
*/
|
||||
private ThreadPool threadPool;
|
||||
|
||||
/**
|
||||
* The name of the Thread
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* The thread synchronization object.
|
||||
*/
|
||||
private Object threadMonitor = new Object();
|
||||
|
||||
/**
|
||||
* The monitor for work notification.
|
||||
*/
|
||||
private Object workMonitor = new Object();
|
||||
|
||||
/**
|
||||
* Total number of work performed.
|
||||
*/
|
||||
private int totalWorkCount;
|
||||
|
||||
/**
|
||||
* Total work execution time.
|
||||
*/
|
||||
private long totalWorkExecutionTime;
|
||||
|
||||
/**
|
||||
* Create the PooledThread.
|
||||
*/
|
||||
protected PooledThread(ThreadPool threadPool, String name, boolean isDaemon, Integer threadPriority) {
|
||||
|
||||
this.name = name;
|
||||
this.threadPool = threadPool;
|
||||
this.lastUsedTime = System.currentTimeMillis();
|
||||
|
||||
thread = new Thread(this, name);
|
||||
thread.setDaemon(isDaemon);
|
||||
|
||||
if (threadPriority != null) {
|
||||
thread.setPriority(threadPriority.intValue());
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
protected void start() {
|
||||
thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign work to this thread. The thread will notify the listener when it has
|
||||
* finished the work.
|
||||
*/
|
||||
public boolean assignWork(Work work) {
|
||||
synchronized (workMonitor) {
|
||||
this.work = work;
|
||||
workMonitor.notifyAll();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* process any assigned work until stopped or interrupted.
|
||||
*/
|
||||
public void run() {
|
||||
// process assigned work until we receive a shutdown signal
|
||||
synchronized (workMonitor) {
|
||||
while (!isStopping) {
|
||||
try {
|
||||
if (work == null) {
|
||||
workMonitor.wait();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
doTheWork();
|
||||
}
|
||||
}
|
||||
|
||||
// Tell stop() we have shut ourselves down successfully
|
||||
synchronized (threadMonitor) {
|
||||
threadMonitor.notifyAll();
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("PooledThread [" + getName() + "] finished ");
|
||||
}
|
||||
isStopped = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually do the work and gather the appropriate measures.
|
||||
*/
|
||||
private void doTheWork() {
|
||||
if (isStopping) {
|
||||
return;
|
||||
}
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
if (work == null) {
|
||||
// probably shutting down the thread
|
||||
|
||||
} else {
|
||||
try {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("start work "+work);
|
||||
}
|
||||
|
||||
work.setStartTime(startTime);
|
||||
work.getRunnable().run();
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("finished work "+work);
|
||||
}
|
||||
|
||||
} catch (Throwable ex) {
|
||||
logger.error(null, ex);
|
||||
|
||||
if (wasInterrupted) {
|
||||
this.isStopping = true;
|
||||
threadPool.removeThread(this);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("PooledThread [" + name + "] removed due to interrupt");
|
||||
}
|
||||
try {
|
||||
thread.interrupt();
|
||||
} catch (Exception e) {
|
||||
logger.error("Error interrupting PooledThead[" + name + "]", e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
lastUsedTime = System.currentTimeMillis();
|
||||
totalWorkCount++;
|
||||
totalWorkExecutionTime = totalWorkExecutionTime + lastUsedTime - startTime;
|
||||
this.work = null;
|
||||
threadPool.returnThread(this);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to interrupt the thread.
|
||||
* <p>
|
||||
* If the Thread was interrupted then it will be removed from the pool.
|
||||
* </p>
|
||||
*/
|
||||
public void interrupt() {
|
||||
|
||||
// set a flag so doTheWork knows that it was interrupted
|
||||
// and removes rather than returns
|
||||
wasInterrupted = true;
|
||||
try {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("interrupt()");
|
||||
}
|
||||
thread.interrupt();
|
||||
|
||||
} catch (SecurityException ex) {
|
||||
wasInterrupted = false;
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the thread has finished.
|
||||
*/
|
||||
public boolean isStopped() {
|
||||
return isStopped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the thread relatively nicely. It will wait a maximum of 10 seconds for
|
||||
* it to complete any existing work.
|
||||
*/
|
||||
protected void stop() {
|
||||
isStopping = true;
|
||||
|
||||
synchronized (threadMonitor) {
|
||||
|
||||
assignWork(null);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("stopping thread ["+name+"]");
|
||||
}
|
||||
try {
|
||||
threadMonitor.wait(10000);
|
||||
} catch (InterruptedException e) {
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
thread = null;
|
||||
threadPool.removeThread(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* return the name of the thread.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently executing work, otherwise null.
|
||||
*/
|
||||
public Work getWork() {
|
||||
return work;
|
||||
}
|
||||
|
||||
/**
|
||||
* The total number of jobs this thread has run.
|
||||
*/
|
||||
public int getTotalWorkCount() {
|
||||
return totalWorkCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* The total time for performing all assigned work.
|
||||
*/
|
||||
public long getTotalWorkExecutionTime() {
|
||||
return totalWorkExecutionTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time this thread was last used.
|
||||
*/
|
||||
public long getLastUsedTime() {
|
||||
return lastUsedTime;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,502 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.lib.thread;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* This is a pool of threads which can be assigned work.
|
||||
* <p>
|
||||
* The Pool will automatically grow as required up to its maximum pool size. The
|
||||
* Pool will be automatically shrink by trimming threads that have been idle for
|
||||
* some time.
|
||||
* </p>
|
||||
*/
|
||||
public class ThreadPool {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ThreadPool.class);
|
||||
|
||||
/**
|
||||
* The max idle time used to trim idle threads from the pool.
|
||||
*/
|
||||
private long maxIdleTime;
|
||||
|
||||
/**
|
||||
* The name of the pool
|
||||
*/
|
||||
private String poolName;
|
||||
|
||||
/**
|
||||
* The initial pool size.
|
||||
*/
|
||||
private int minSize;
|
||||
|
||||
/**
|
||||
* Whether or not the threads are going to be Daemon threads.
|
||||
*/
|
||||
private boolean isDaemon;
|
||||
|
||||
/**
|
||||
* The priority or the threads. Can be null, in which case the threads have
|
||||
* the default priority.
|
||||
*/
|
||||
private Integer threadPriority;
|
||||
|
||||
/**
|
||||
* Incrementing int for thread name. NB: currentThreadCount will go up and
|
||||
* down as the pool grows and shrinks.
|
||||
*/
|
||||
private int uniqueThreadID;
|
||||
|
||||
/**
|
||||
* The maximum number of threads to grow to. Hitting this limit will have
|
||||
* performance ramifications.
|
||||
*/
|
||||
private int maxSize = 100;
|
||||
|
||||
/**
|
||||
* Flag that the pool should terminate all the threads and stop.
|
||||
*/
|
||||
private boolean stopThePool;
|
||||
|
||||
/**
|
||||
* Flag to indicate that the pool is being shutdown.
|
||||
*/
|
||||
private boolean isStopping;
|
||||
|
||||
/**
|
||||
* List of PooledThread that are free for work.
|
||||
*/
|
||||
private Vector<PooledThread> freeList = new Vector<PooledThread>();
|
||||
|
||||
/**
|
||||
* List of PooledThread that are busy.
|
||||
*/
|
||||
private Vector<PooledThread> busyList = new Vector<PooledThread>();
|
||||
|
||||
/**
|
||||
* List holding queued work.
|
||||
*/
|
||||
private Vector<Work> workOverflowQueue = new Vector<Work>();
|
||||
|
||||
/**
|
||||
* Create and return a ThreadPool reading configuration from GlobalProperties.
|
||||
*/
|
||||
public static ThreadPool createThreadPool(String poolName) {
|
||||
|
||||
int min = GlobalProperties.getInt("threadpool." + poolName + ".min", 0);
|
||||
int max = GlobalProperties.getInt("threadpool." + poolName + ".max", 100);
|
||||
|
||||
int defaultIdleTime = GlobalProperties.getInt("threadpool.idletime", 60);
|
||||
long idle = 1000 * GlobalProperties.getInt("threadpool." + poolName + ".idletime", defaultIdleTime);
|
||||
|
||||
Integer priority = null;
|
||||
String threadPriority = GlobalProperties.get("threadpool." + poolName + ".priority", null);
|
||||
if (threadPriority != null) {
|
||||
priority = new Integer(threadPriority);
|
||||
}
|
||||
|
||||
return new ThreadPool(poolName, true, priority, min, max, idle);
|
||||
}
|
||||
|
||||
public ThreadPool(String poolName, boolean isDaemon, Integer threadPriority, int minSize, int maxSize, long maxIdleMillis) {
|
||||
this(poolName, isDaemon, threadPriority);
|
||||
this.minSize = minSize;
|
||||
this.maxSize = maxSize;
|
||||
this.maxIdleTime = maxIdleMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the ThreadPool.
|
||||
*/
|
||||
public ThreadPool(String poolName, boolean isDaemon, Integer threadPriority) {
|
||||
|
||||
this.poolName = poolName;
|
||||
this.isDaemon = isDaemon;
|
||||
this.threadPriority = threadPriority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the pool is shutting down.
|
||||
*/
|
||||
public boolean isStopping() {
|
||||
synchronized (freeList) {
|
||||
return isStopping;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the thread pool.
|
||||
*/
|
||||
public String getName() {
|
||||
return poolName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum size the pool should try to maintain.
|
||||
*/
|
||||
public void setMinSize(int minSize) {
|
||||
if (minSize > 0) {
|
||||
if (minSize > maxSize) {
|
||||
this.maxSize = minSize;
|
||||
}
|
||||
this.minSize = minSize;
|
||||
maintainPoolSize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum size the pool should maintain.
|
||||
*/
|
||||
public int getMinSize() {
|
||||
return minSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum size the pool should grow to.
|
||||
*/
|
||||
public void setMaxSize(int maxSize) {
|
||||
if (maxSize > 0) {
|
||||
if (minSize > maxSize) {
|
||||
minSize = maxSize;
|
||||
}
|
||||
this.maxSize = maxSize;
|
||||
maintainPoolSize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum size this pool can grow to.
|
||||
*/
|
||||
public int getMaxSize() {
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total number of busy and free threads in the pool.
|
||||
*/
|
||||
public int size() {
|
||||
return busyList.size() + freeList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of currently busy threads.
|
||||
*/
|
||||
public int getBusyCount() {
|
||||
return busyList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a task to the thread pool, specifing the options to wait or queue
|
||||
* the task if the pool is fully busy and can't grow.
|
||||
* <p>
|
||||
* When the pool is fully busy...
|
||||
* </p>
|
||||
* <p>
|
||||
* addToQueue=true -> work is added to queue, returns false<br>
|
||||
* addToQueue=false -> work is not done or queued, returns false<br>
|
||||
* </p>
|
||||
*
|
||||
* @param work
|
||||
* the runnable work to do.
|
||||
* @param addToQueueIfFull
|
||||
* If the pool is maxed out and this is true then it queues the
|
||||
* Runnable.
|
||||
*/
|
||||
public boolean assign(Runnable work, boolean addToQueueIfFull) {
|
||||
|
||||
if (stopThePool) {
|
||||
throw new RuntimeException("Pool is stopping... no more work please.");
|
||||
}
|
||||
|
||||
Work runWork = new Work(work);
|
||||
|
||||
// get the next available thread in the pool (block)
|
||||
PooledThread thread = getNextAvailableThread();
|
||||
if (thread != null) {
|
||||
// assign the work to that thread
|
||||
busyList.add(thread);
|
||||
thread.assignWork(runWork);
|
||||
return true;
|
||||
|
||||
} else {
|
||||
if (addToQueueIfFull) {
|
||||
runWork.setEnterQueueTime(System.currentTimeMillis());
|
||||
workOverflowQueue.add(runWork);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the thread from the pool. The thread should be stopped before it is
|
||||
* removed.
|
||||
*/
|
||||
protected void removeThread(PooledThread thread) {
|
||||
synchronized (freeList) {
|
||||
busyList.remove(thread);
|
||||
freeList.remove(thread);
|
||||
freeList.notify();
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("PooledThread stopped [" + getName() + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fired when a Thread from the pool has finished, and can be put back into
|
||||
* the pool.
|
||||
*/
|
||||
protected void returnThread(PooledThread thread) {
|
||||
|
||||
synchronized (freeList) {
|
||||
|
||||
// deregister from the busyList
|
||||
busyList.remove(thread);
|
||||
|
||||
if (!workOverflowQueue.isEmpty()) {
|
||||
// get the first bit of work off the queue
|
||||
Work queuedWork = (Work) workOverflowQueue.remove(0);
|
||||
|
||||
// work out the queue time and counts etc
|
||||
queuedWork.setExitQueueTime(System.currentTimeMillis());
|
||||
busyList.add(thread);
|
||||
thread.assignWork(queuedWork);
|
||||
|
||||
} else {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("returnThread - add to freeList [" + thread + "]");
|
||||
}
|
||||
// put the thread back onto the available list
|
||||
freeList.add(thread);
|
||||
// tell shutdown() one has returned
|
||||
freeList.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next available thread. Block until thread is available. NB: The
|
||||
* dispatcher is blocked but work can still be assigned to the dispatcher in a
|
||||
* non-blocking way
|
||||
*/
|
||||
private PooledThread getNextAvailableThread() {
|
||||
|
||||
synchronized (freeList) {
|
||||
if (!freeList.isEmpty()) {
|
||||
return (PooledThread) freeList.remove(0);
|
||||
}
|
||||
if (size() < maxSize) {
|
||||
return growPool(true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an Iterator of PooledThread that are currently running. You should
|
||||
* only use this for display. Use the getPooledThread() or interrupt() methods
|
||||
* to interrupt a particular thread.
|
||||
*
|
||||
* @return an Iterator of busy PooledThread's.
|
||||
*/
|
||||
public Iterator<PooledThread> getBusyThreads() {
|
||||
synchronized (freeList) {
|
||||
return busyList.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the threadpool stopping all the threads. This will wait until any
|
||||
* busy threads have finished their assigned work.
|
||||
*/
|
||||
public void shutdown() {
|
||||
|
||||
synchronized (freeList) {
|
||||
if (isStopping) {
|
||||
logger.debug("already shutting down");
|
||||
}
|
||||
isStopping = true;
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("ThreadPool [" + poolName + "] Shutting down; threadCount[" + size()+ "] busyCount[" + getBusyCount() + "]");
|
||||
}
|
||||
stopThePool = true;
|
||||
|
||||
while (!freeList.isEmpty()) {
|
||||
PooledThread thread = (PooledThread) freeList.remove(0);
|
||||
thread.stop();
|
||||
}
|
||||
|
||||
try {
|
||||
while (getBusyCount() > 0) {
|
||||
|
||||
String msg = "ThreadPool [" + poolName + "] has [" + getBusyCount()+ "] busy threads, waiting for those to finish.";
|
||||
logger.info(msg);
|
||||
|
||||
Iterator<PooledThread> busyThreads = getBusyThreads();
|
||||
while (busyThreads.hasNext()) {
|
||||
PooledThread busyThread = (PooledThread) busyThreads.next();
|
||||
String busymsg = "Busy thread [" + busyThread.getName() + "] work["+ busyThread.getWork() + "]";
|
||||
logger.info(busymsg);
|
||||
}
|
||||
|
||||
freeList.wait();
|
||||
PooledThread thread = (PooledThread) freeList.remove(0);
|
||||
logger.debug("wait finished on thread[" + thread.getName() + "]");
|
||||
if (thread != null) {
|
||||
thread.stop();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("Error during threadpool shutdown", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim or grow the pool leaving at least min free.
|
||||
*/
|
||||
protected void maintainPoolSize() {
|
||||
synchronized (freeList) {
|
||||
if (isStopping) {
|
||||
// don't bother as the pool is shutting down
|
||||
return;
|
||||
}
|
||||
|
||||
int numToStop = size() - minSize;
|
||||
if (numToStop > 0) {
|
||||
// should trim idle threads as we are over the minSize
|
||||
long usedAfter = System.currentTimeMillis() - maxIdleTime;
|
||||
ArrayList<PooledThread> stopList = new ArrayList<PooledThread>();
|
||||
Iterator<PooledThread> it = freeList.iterator();
|
||||
while (it.hasNext() && numToStop > 0) {
|
||||
PooledThread thread = (PooledThread) it.next();
|
||||
if (thread.getLastUsedTime() < usedAfter) {
|
||||
stopList.add(thread);
|
||||
numToStop--;
|
||||
}
|
||||
}
|
||||
Iterator<PooledThread> stopIt = stopList.iterator();
|
||||
while (stopIt.hasNext()) {
|
||||
PooledThread thread = (PooledThread) stopIt.next();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("trimming pool - stopping thread "+thread);
|
||||
}
|
||||
thread.stop();
|
||||
}
|
||||
}
|
||||
int numToAdd = minSize - size();
|
||||
if (numToAdd > 0) {
|
||||
// should add some more to the pool
|
||||
for (int i = 0; i < numToAdd; i++) {
|
||||
growPool(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupt a named thread that is currently busy.
|
||||
* <p>
|
||||
* Returns the thread that was interrupted or null if the thread was not
|
||||
* found. If the thread was interrupted then it will automatically be stopped
|
||||
* and removed from the pool.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that it may take some time to actually interrupt the thread so an
|
||||
* immediate test to see if the thread stopped will probably be wrong.
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* ThreadPool test = ThreadPoolManager.getThreadPool("test");
|
||||
* PooledThread pt = test.interrupt("test.1");
|
||||
* if (pt == null) {
|
||||
* // the thread was not found, perhaps finished?
|
||||
* } else {
|
||||
* // give interrupt a little time to execute
|
||||
* Thread.sleep(1000);
|
||||
* boolean hasStopped = pt.isStopped();
|
||||
* //..
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* </p>
|
||||
*
|
||||
* @return the thread that was interrupted
|
||||
*/
|
||||
public PooledThread interrupt(String threadName) {
|
||||
PooledThread thread = getBusyThread(threadName);
|
||||
if (thread != null) {
|
||||
thread.interrupt();
|
||||
return thread;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a thread using its name from the busy list. Returns null if the thread
|
||||
* is not found in the busy list.
|
||||
*/
|
||||
public PooledThread getBusyThread(String threadName) {
|
||||
synchronized (freeList) {
|
||||
Iterator<PooledThread> it = getBusyThreads();
|
||||
while (it.hasNext()) {
|
||||
PooledThread pt = (PooledThread) it.next();
|
||||
if (pt.getName().equals(threadName)) {
|
||||
return pt;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Grow the pool with the option of either putting it on the available list,
|
||||
* or returning it.
|
||||
*/
|
||||
private PooledThread growPool(boolean andReturn) {
|
||||
|
||||
synchronized (freeList) {
|
||||
|
||||
String threadName = poolName + "." + uniqueThreadID++;
|
||||
PooledThread bgw = new PooledThread(this, threadName, isDaemon, threadPriority);
|
||||
bgw.start();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("ThreadPool grow created [" + threadName + "] size[" + size() + "]");
|
||||
}
|
||||
if (andReturn) {
|
||||
return bgw;
|
||||
} else {
|
||||
freeList.add(bgw);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum amount of time in millis that Threads can be idle before
|
||||
* they are trimmed.
|
||||
*/
|
||||
public long getMaxIdleTime() {
|
||||
return maxIdleTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maxiumium amount of time in millis that Threads can be idle before
|
||||
* they are trimed.
|
||||
*/
|
||||
public void setMaxIdleTime(long maxIdleTime) {
|
||||
this.maxIdleTime = maxIdleTime;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.lib.thread;
|
||||
|
||||
|
||||
/**
|
||||
* Used internally by the ThreadPool to wrap a Runnable that is
|
||||
* going to be run.
|
||||
*
|
||||
* <p>Used to maintains some useful times about the Runnable in terms of when
|
||||
* it was queued and then eventually run.</p>
|
||||
*/
|
||||
public class Work {
|
||||
|
||||
/**
|
||||
* Create a Runnable Work.
|
||||
*/
|
||||
public Work(Runnable runnable) {
|
||||
this.runnable = runnable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associated Runnable object.
|
||||
*/
|
||||
public Runnable getRunnable() {
|
||||
return runnable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time this work actually started.
|
||||
*/
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time this work actually started.
|
||||
*/
|
||||
public void setStartTime(long startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time this entered the queue.
|
||||
*/
|
||||
public long getEnterQueueTime() {
|
||||
return enterQueueTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time this entered the queue.
|
||||
*/
|
||||
public void setEnterQueueTime(long enterQueueTime) {
|
||||
this.enterQueueTime = enterQueueTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time this left the queue.
|
||||
*/
|
||||
public long getExitQueueTime() {
|
||||
return exitQueueTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time this work left the queue.
|
||||
*/
|
||||
public void setExitQueueTime(long exitQueueTime) {
|
||||
this.exitQueueTime = exitQueueTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as getDescription().
|
||||
*/
|
||||
public String toString() {
|
||||
return getDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a description of this work.
|
||||
*/
|
||||
public String getDescription() {
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append("Work[");
|
||||
if (runnable != null){
|
||||
sb.append(runnable.toString());
|
||||
}
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private Runnable runnable;
|
||||
|
||||
private long exitQueueTime;
|
||||
private long enterQueueTime;
|
||||
private long startTime;
|
||||
|
||||
};
|
||||
@@ -1,12 +0,0 @@
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
|
||||
<TITLE>AvajeLib</TITLE>
|
||||
</HEAD>
|
||||
<Body BGCOLOR="#ffffff">
|
||||
ThreadPool service.
|
||||
<P>
|
||||
Service providing thread pooling for executing background tasks.
|
||||
</P>
|
||||
</Body>
|
||||
</HTML>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user