mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
112
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4306ce1ab | ||
|
|
444b656780 | ||
|
|
fcbba23441 | ||
|
|
18961047a1 | ||
|
|
34347fc3e1 | ||
|
|
189e0a2ea9 | ||
|
|
182e027521 | ||
|
|
f85fa2c21d | ||
|
|
5a5f479568 | ||
|
|
9ca2486c9c | ||
|
|
656b239925 | ||
|
|
5d46904341 | ||
|
|
f0dd069ed2 | ||
|
|
5bd454bdc0 | ||
|
|
eeebaf9a08 | ||
|
|
636a148509 | ||
|
|
170cd6051e | ||
|
|
522fc2384c | ||
|
|
6f214590aa | ||
|
|
0014ea3501 | ||
|
|
db3140512f | ||
|
|
8bfa331c61 | ||
|
|
456a1d8608 | ||
|
|
695d3374a8 | ||
|
|
5488d054bf | ||
|
|
4d72273dc3 | ||
|
|
cb9a5d6709 | ||
|
|
1b8aa682c6 | ||
|
|
ef39bfcf14 | ||
|
|
749f8d0d45 | ||
|
|
2fd221bbb5 | ||
|
|
ee96c4afa6 | ||
|
|
87a948223c | ||
|
|
acca803a80 | ||
|
|
470b27efe1 | ||
|
|
f287205421 | ||
|
|
6bd8c2bd05 | ||
|
|
05dcdf162b | ||
|
|
e97e77e2e0 | ||
|
|
e685e2f4ac | ||
|
|
c6019bf3c5 | ||
|
|
1a9a4c957c | ||
|
|
bd57a3b9c8 | ||
|
|
21a71c3a4f | ||
|
|
f66439a62e | ||
|
|
73452a5f68 | ||
|
|
5f4789f9ba | ||
|
|
eaac874ac0 | ||
|
|
4900a460d1 | ||
|
|
d16f33d26b | ||
|
|
664f08fd6f | ||
|
|
39416e933e | ||
|
|
74b118e8f0 | ||
|
|
43853b0357 | ||
|
|
f62bc9a35f | ||
|
|
9d8fe97de0 | ||
|
|
b896e55af1 | ||
|
|
bd75f2cf77 | ||
|
|
4f33fe087e | ||
|
|
ccf94bf1a4 | ||
|
|
0acabf820e | ||
|
|
1330fe8fb6 | ||
|
|
09ea5217dc | ||
|
|
93304949e8 | ||
|
|
1a292275d8 | ||
|
|
236fb5541b | ||
|
|
1cafea20c6 | ||
|
|
f71eac5f1e | ||
|
|
3f9c3c1db5 | ||
|
|
b4df899314 | ||
|
|
9c7f62e8b1 | ||
|
|
bf8574833a | ||
|
|
6f1bb5a9d7 | ||
|
|
2f7b4e3a24 | ||
|
|
44d95dc06c | ||
|
|
6f579ba890 | ||
|
|
cd1772fa05 | ||
|
|
1fa7a0a45d | ||
|
|
01548da0c5 | ||
|
|
a9ff65f7b5 | ||
|
|
7d23c93411 | ||
|
|
243864fef7 | ||
|
|
c0b83b939d | ||
|
|
c176e4f05e | ||
|
|
c3b26104f3 | ||
|
|
4f2f9a2e99 | ||
|
|
4bbbad9b3b | ||
|
|
488b856470 | ||
|
|
7cd71559b6 | ||
|
|
0c373f99c6 | ||
|
|
a32ca484c5 | ||
|
|
9ad4931c12 | ||
|
|
251076e30d | ||
|
|
93d3638f6c | ||
|
|
0d3b1c625f | ||
|
|
2de57f7b35 | ||
|
|
60a1a4bb5b | ||
|
|
d2f532ad3c | ||
|
|
26a8af8c5b | ||
|
|
d8ac8e1694 | ||
|
|
7e6a6e3875 | ||
|
|
703ee59792 | ||
|
|
464cb38b8b | ||
|
|
eb61246254 | ||
|
|
a4dda42650 | ||
|
|
fbfa593bdb | ||
|
|
a3dc2a55f4 | ||
|
|
2f97c5c36e | ||
|
|
bb2479c9e7 | ||
|
|
e79a5d4332 | ||
|
|
513a61503e | ||
|
|
925036925c |
@@ -6,3 +6,4 @@
|
||||
target/
|
||||
logs/
|
||||
log/
|
||||
/mydb.db
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
avaje-ebeanorm-server
|
||||
=====================
|
||||
|
||||
The internal server implementation of EbeanORM API
|
||||
The internal server implementation of EbeanORM API
|
||||
|
||||
Maven Dependency
|
||||
----------------
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>3.2.5</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<version>3.3.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>avaje-ebeanorm</name>
|
||||
@@ -84,12 +84,33 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-agentloader</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-agent</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>1.3.153</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.7.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>postgresql</groupId>
|
||||
@@ -108,7 +129,7 @@
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>5.1.15</version>
|
||||
<version>5.1.27</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -119,6 +140,13 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
@@ -126,6 +154,13 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -135,7 +170,7 @@
|
||||
<plugin>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<version>3.2.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>main</id>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
@@ -57,6 +56,9 @@ final class DRawSqlColumnsParser {
|
||||
split = tmp.toArray(new String[tmp.size()]);
|
||||
}
|
||||
|
||||
if (split.length == 0) {
|
||||
throw new PersistenceException("Huh? Not expecting length=0 when parsing column " + colInfo);
|
||||
}
|
||||
if (split.length == 1) {
|
||||
// default to column the same name as the property
|
||||
return new ColumnMapping.Column(indexPos++, split[0], null);
|
||||
@@ -64,17 +66,18 @@ final class DRawSqlColumnsParser {
|
||||
if (split.length == 2) {
|
||||
return new ColumnMapping.Column(indexPos++, split[0], split[1]);
|
||||
}
|
||||
if (split.length == 3) {
|
||||
if (!split[1].equalsIgnoreCase("as")) {
|
||||
String msg = "Expecting AS keyword parsing column " + colInfo;
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
return new ColumnMapping.Column(indexPos++, split[0], split[2]);
|
||||
// Ok, we now expect/require the AS keyword and it should be the
|
||||
// second to last word in the colInfo content
|
||||
if (!split[split.length - 2].equalsIgnoreCase("as")) {
|
||||
throw new PersistenceException("Expecting AS keyword as second to last word when parsing column " + colInfo);
|
||||
}
|
||||
|
||||
String msg = "Expecting Max 3 words parsing column " + colInfo + ". Got "
|
||||
+ Arrays.toString(split);
|
||||
throw new PersistenceException(msg);
|
||||
// build back the 'column formula' that precedes the AS keyword
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(split[0]);
|
||||
for (int i = 1; i < split.length-2; i++) {
|
||||
sb.append(" ").append(split[i]);
|
||||
}
|
||||
return new ColumnMapping.Column(indexPos++, sb.toString(), split[split.length - 1]);
|
||||
}
|
||||
|
||||
private int nextComma() {
|
||||
|
||||
@@ -11,6 +11,7 @@ import javax.persistence.OptimisticLockException;
|
||||
import com.avaje.ebean.annotation.CacheStrategy;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.meta.MetaInfoManager;
|
||||
import com.avaje.ebean.text.csv.CsvReader;
|
||||
import com.avaje.ebean.text.json.JsonContext;
|
||||
|
||||
@@ -117,6 +118,13 @@ public interface EbeanServer {
|
||||
*/
|
||||
public ExpressionFactory getExpressionFactory();
|
||||
|
||||
/**
|
||||
* Return the MetaInfoManager which is used to get meta data from the EbeanServer
|
||||
* such as query execution statistics.
|
||||
*/
|
||||
public MetaInfoManager getMetaInfoManager();
|
||||
|
||||
|
||||
/**
|
||||
* Return the BeanState for a given entity bean.
|
||||
* <p>
|
||||
|
||||
@@ -73,6 +73,14 @@ public class Expr {
|
||||
return Ebean.getExpressionFactory().between(propertyName, value1, value2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Between - value between two given properties.
|
||||
*/
|
||||
public static Expression between(String lowProperty, String highProperty, Object value) {
|
||||
|
||||
return Ebean.getExpressionFactory().betweenProperties(lowProperty, highProperty, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Greater Than - property greater than the given value.
|
||||
*/
|
||||
|
||||
@@ -36,11 +36,6 @@ import java.util.Map;
|
||||
*/
|
||||
public interface ExpressionFactory {
|
||||
|
||||
/**
|
||||
* Return the language for this expression factory.
|
||||
*/
|
||||
public String getLang();
|
||||
|
||||
/**
|
||||
* Equal To - property equal to the given value.
|
||||
*/
|
||||
|
||||
@@ -260,9 +260,11 @@ public interface ExpressionList<T> extends Serializable {
|
||||
public Query<T> setMapKey(String mapKey);
|
||||
|
||||
/**
|
||||
* Please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}.
|
||||
* Set a QueryListener for bean by bean processing.
|
||||
*
|
||||
* @see Query#setListener(QueryListener)
|
||||
* @deprecated Migrate to {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
|
||||
*/
|
||||
public Query<T> setListener(QueryListener<T> queryListener);
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.io.Serializable;
|
||||
*
|
||||
* <pre class="code">
|
||||
* // Normal fetch join results in a single SQL query
|
||||
* List<Order> list = Ebean.find(Order.class).join("details").findList();
|
||||
* List<Order> list = Ebean.find(Order.class).fetch("details").findList();
|
||||
*
|
||||
* // Find Orders join details using a single SQL query
|
||||
* </pre>
|
||||
@@ -51,8 +51,8 @@ import java.io.Serializable;
|
||||
* // This will use 3 SQL queries to build this object graph
|
||||
* List<Order> list =
|
||||
* Ebean.find(Order.class)
|
||||
* .fetch("details", new JoinConfig().query())
|
||||
* .fetch("customer", new JoinConfig().query(5))
|
||||
* .fetch("details", new FetchConfig().query())
|
||||
* .fetch("customer", new FetchConfig().queryFirst(5))
|
||||
* .findList();
|
||||
*
|
||||
* // query 1) find order
|
||||
@@ -70,7 +70,7 @@ import java.io.Serializable;
|
||||
* .select("status, shipDate")
|
||||
* .fetch("details", "quantity, price", new FetchConfig().query())
|
||||
* .fetch("details.product", "sku, name")
|
||||
* .fetch("customer", "name", new FetchConfig().query(10))
|
||||
* .fetch("customer", "name", new FetchConfig().queryFirst(5))
|
||||
* .fetch("customer.contacts")
|
||||
* .fetch("customer.shippingAddress")
|
||||
* .findList();
|
||||
@@ -96,13 +96,13 @@ import java.io.Serializable;
|
||||
* <pre class="code">
|
||||
* List<Order> list =
|
||||
* Ebean.find(Order.class)
|
||||
* .fetch("customer", new FetchConfig().query(3).lazy(10))
|
||||
* .fetch("customer", new FetchConfig().query(10).lazy(5))
|
||||
* .findList();
|
||||
*
|
||||
* // query 1) find order
|
||||
* // query 2) find customer where id in (?,?,?) // first 3 customers
|
||||
* // query 2) find customer where id in (?,?,?,?,?,?,?,?,?,?) // first 10 customers
|
||||
* // .. then if lazy loading of customers is invoked
|
||||
* // .. use a batch size of 10 to load the customers
|
||||
* // .. use a batch size of 5 to load the customers
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
@@ -128,8 +128,8 @@ import java.io.Serializable;
|
||||
* // .. use a batch size of 5 to load the customers
|
||||
*
|
||||
* find customer (name)
|
||||
* fetch contact (contactName, phone, email)
|
||||
* fetch shippingAddress (*)
|
||||
* fetch customer.contacts (contactName, phone, email)
|
||||
* fetch customer.shippingAddress (*)
|
||||
* where id in (?,?,?,?,?)
|
||||
*
|
||||
* </pre>
|
||||
@@ -159,6 +159,7 @@ public class FetchConfig implements Serializable {
|
||||
*/
|
||||
public FetchConfig lazy() {
|
||||
this.lazyBatchSize = 0;
|
||||
this.queryAll = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -170,11 +171,12 @@ public class FetchConfig implements Serializable {
|
||||
*/
|
||||
public FetchConfig lazy(int lazyBatchSize) {
|
||||
this.lazyBatchSize = lazyBatchSize;
|
||||
this.queryAll = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that this path should be loaded as a separate query (rather than as
|
||||
* Eagerly fetch the beans in this path as a separate query (rather than as
|
||||
* part of the main query).
|
||||
* <p>
|
||||
* This will use the default batch size for separate query which is 100.
|
||||
@@ -187,14 +189,15 @@ public class FetchConfig implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that this path should be loaded as a separate query (rather than as
|
||||
* Eagerly fetch the beans in this path as a separate query (rather than as
|
||||
* part of the main query).
|
||||
* <p>
|
||||
* The queryBatchSize is the number of parent id's that this separate query
|
||||
* will load per batch.
|
||||
* </p>
|
||||
* <p>
|
||||
* This will load all beans on this path eagerly.
|
||||
* This will load all beans on this path eagerly unless a {@link #lazy(int)}
|
||||
* is also used.
|
||||
* </p>
|
||||
*
|
||||
* @param queryBatchSize
|
||||
@@ -202,12 +205,14 @@ public class FetchConfig implements Serializable {
|
||||
*/
|
||||
public FetchConfig query(int queryBatchSize) {
|
||||
this.queryBatchSize = queryBatchSize;
|
||||
this.queryAll = true;
|
||||
// queryAll true as long as a lazy batch size has not already been set
|
||||
this.queryAll = (lazyBatchSize == -1);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #query(int)} but only fetches the first batch.
|
||||
* Eagerly fetch the first batch of beans on this path.
|
||||
* This is similar to {@link #query(int)} but only fetches the first batch.
|
||||
* <p>
|
||||
* If there are more parent beans than the batch size then they will not be
|
||||
* loaded eagerly but instead use lazy loading.
|
||||
@@ -228,7 +233,7 @@ public class FetchConfig implements Serializable {
|
||||
public int getLazyBatchSize() {
|
||||
return lazyBatchSize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the batch size for separate query load.
|
||||
*/
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* The transaction log level.
|
||||
* <p>
|
||||
* This is used to define how much Ebean should log such as generated SQL.
|
||||
* </p>
|
||||
*/
|
||||
public enum LogLevel {
|
||||
|
||||
/**
|
||||
* No logging.
|
||||
*/
|
||||
NONE,
|
||||
|
||||
/**
|
||||
* Log only a summary level.
|
||||
*/
|
||||
SUMMARY,
|
||||
|
||||
/**
|
||||
* Log generated SQL/DML and binding variables.
|
||||
*/
|
||||
SQL
|
||||
}
|
||||
@@ -15,8 +15,6 @@ import java.util.List;
|
||||
* Typically you will not construct an OrderBy yourself but use one that exists
|
||||
* on the Query object.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public final class OrderBy<T> implements Serializable {
|
||||
|
||||
@@ -24,7 +22,7 @@ public final class OrderBy<T> implements Serializable {
|
||||
|
||||
private transient Query<T> query;
|
||||
|
||||
private List<Property> list;
|
||||
private final List<Property> list;
|
||||
|
||||
/**
|
||||
* Create an empty OrderBy with no associated query.
|
||||
@@ -32,6 +30,10 @@ public final class OrderBy<T> implements Serializable {
|
||||
public OrderBy() {
|
||||
this.list = new ArrayList<Property>(2);
|
||||
}
|
||||
|
||||
private OrderBy(List<Property> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an orderBy parsing the order by clause.
|
||||
@@ -81,6 +83,17 @@ public final class OrderBy<T> implements Serializable {
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this OrderBy with the path trimmed.
|
||||
*/
|
||||
public OrderBy<T> copyWithTrim(String path) {
|
||||
List<Property> newList = new ArrayList<Property>(list.size());
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
newList.add(list.get(i).copyWithTrim(path));
|
||||
}
|
||||
return new OrderBy<T>(newList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the properties for this OrderBy.
|
||||
*/
|
||||
@@ -153,31 +166,23 @@ public final class OrderBy<T> implements Serializable {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof OrderBy<?>) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
OrderBy<?> other = (OrderBy<?>) obj;
|
||||
return hashCode() == other.hashCode();
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hash();
|
||||
if (!(obj instanceof OrderBy<?>)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OrderBy<?> e = (OrderBy<?>) obj;
|
||||
return e.list.equals(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash value for this OrderBy. This can be to determine logical
|
||||
* equality for OrderBy clauses.
|
||||
*/
|
||||
public int hash() {
|
||||
int hc = OrderBy.class.getName().hashCode();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
hc = hc * 31 + list.get(i).hash();
|
||||
}
|
||||
return hc;
|
||||
public int hashCode() {
|
||||
return list.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,11 +201,31 @@ public final class OrderBy<T> implements Serializable {
|
||||
this.ascending = ascending;
|
||||
}
|
||||
|
||||
protected int hash() {
|
||||
/**
|
||||
* Return a copy of this Property with the path trimmed.
|
||||
*/
|
||||
public Property copyWithTrim(String path) {
|
||||
return new Property(property.substring(path.length() + 1), ascending);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = property.hashCode();
|
||||
hc = hc * 31 + (ascending ? 0 : 1);
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof Property)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Property e = (Property) obj;
|
||||
return e.ascending == ascending
|
||||
&& e.property.equals(property);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return toStringFormat();
|
||||
|
||||
@@ -269,26 +269,6 @@ import java.util.Set;
|
||||
*/
|
||||
public interface Query<T> extends Serializable {
|
||||
|
||||
/**
|
||||
* How this query should use (or not) the Lucene Index if one is defined for
|
||||
* the bean type.
|
||||
*/
|
||||
public enum UseIndex {
|
||||
NO, DEFAULT, YES_IDS, YES_OBJECTS
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly specify how this query should use a Lucene Index if one is
|
||||
* defined for this bean type.
|
||||
*/
|
||||
public Query<T> setUseIndex(UseIndex useIndex);
|
||||
|
||||
/**
|
||||
* Return the setting for how this query should use a Lucene Index if one is
|
||||
* defined for this bean type.
|
||||
*/
|
||||
public UseIndex getUseIndex();
|
||||
|
||||
/**
|
||||
* Return the RawSql that was set to use for this query.
|
||||
*/
|
||||
@@ -684,7 +664,10 @@ public interface Query<T> extends Serializable {
|
||||
public Query<T> setParameter(int position, Object value);
|
||||
|
||||
/**
|
||||
* Please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
|
||||
* <p>
|
||||
* Set a listener to process the query on a row by row basis.
|
||||
* </p>
|
||||
* <p>
|
||||
* Use this when you want to process a large query and do not want to hold the
|
||||
* entire query result in memory.
|
||||
@@ -706,6 +689,7 @@ public interface Query<T> extends Serializable {
|
||||
* // list (emptyList) will be empty ...
|
||||
* List<Order> emtyList = query.findList();
|
||||
* </pre>
|
||||
* @deprecated Deprecated in favor of {@link #findIterate()} and {@link #findVisit(QueryResultVisitor)}
|
||||
*/
|
||||
public Query<T> setListener(QueryListener<T> queryListener);
|
||||
|
||||
@@ -1068,16 +1052,14 @@ public interface Query<T> extends Serializable {
|
||||
*/
|
||||
public String getGeneratedSql();
|
||||
|
||||
/**
|
||||
* Return the total hits matched for a lucene text search query.
|
||||
*/
|
||||
public int getTotalHits();
|
||||
|
||||
/**
|
||||
* executed the select with "for update" which should lock the record
|
||||
* "on read"
|
||||
*/
|
||||
public Query<T> setForUpdate(boolean forUpdate);
|
||||
|
||||
/**
|
||||
* Return true if this query has forUpdate set.
|
||||
*/
|
||||
public boolean isForUpdate();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* Deprecated, please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
|
||||
* <p>
|
||||
* Provides a mechanism for processing a query one bean at a time.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is useful when the query will return a large number of results and you
|
||||
* want to process the beans one at a time rather than whole all of the beans in
|
||||
@@ -23,6 +26,8 @@ package com.avaje.ebean;
|
||||
*
|
||||
* @param <T>
|
||||
* the type of entity bean
|
||||
* @deprecated Please migrate to using {@link #findIterate()} or
|
||||
* {@link #findVisit(QueryResultVisitor)}
|
||||
*/
|
||||
public interface QueryListener<T> {
|
||||
|
||||
|
||||
@@ -3,12 +3,13 @@ package com.avaje.ebean;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.RollbackException;
|
||||
import java.io.Closeable;
|
||||
import java.sql.Connection;
|
||||
|
||||
/**
|
||||
* The Transaction object. Typically representing a JDBC or JTA transaction.
|
||||
*/
|
||||
public interface Transaction {
|
||||
public interface Transaction extends Closeable {
|
||||
|
||||
/**
|
||||
* Read Committed transaction isolation. Same as
|
||||
@@ -34,13 +35,6 @@ public interface Transaction {
|
||||
*/
|
||||
public static final int SERIALIZABLE = java.sql.Connection.TRANSACTION_SERIALIZABLE;
|
||||
|
||||
// /**
|
||||
// * You can call this after the transaction commit to wait for any changes to
|
||||
// * Lucene indexes to be made and committed. Note that generally this could
|
||||
// * be some time so it is not expected that you call this method generally.
|
||||
// */
|
||||
// public void waitForIndexUpdates();
|
||||
|
||||
/**
|
||||
* Return true if this transaction is read only.
|
||||
*/
|
||||
@@ -51,29 +45,6 @@ public interface Transaction {
|
||||
*/
|
||||
public void setReadOnly(boolean readOnly);
|
||||
|
||||
// /**
|
||||
// * Log a comment to the transaction log.
|
||||
// */
|
||||
// public void log(String msg);
|
||||
|
||||
/**
|
||||
* Set the logLevel to use for this transaction.
|
||||
*/
|
||||
public void setLogLevel(LogLevel logLevel);
|
||||
|
||||
/**
|
||||
* Return the logLevel this transaction is using.
|
||||
*/
|
||||
public LogLevel getLogLevel();
|
||||
|
||||
/**
|
||||
* Deprecated in favour of using {@link #setLogLevel} Set this to false to
|
||||
* disable logging for this transaction.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public void setLoggingOn(boolean isLoggingOn);
|
||||
|
||||
/**
|
||||
* Commit the transaction.
|
||||
*/
|
||||
|
||||
@@ -6,7 +6,6 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebean.Query.UseIndex;
|
||||
|
||||
/**
|
||||
* Specify the default cache use specific entity type.
|
||||
@@ -56,9 +55,4 @@ public @interface CacheStrategy {
|
||||
*/
|
||||
String warmingQuery() default "";
|
||||
|
||||
/**
|
||||
* Default setting for using a text index if it has been defined on this bean
|
||||
* type.
|
||||
*/
|
||||
UseIndex useIndex() default UseIndex.DEFAULT;
|
||||
};
|
||||
|
||||
@@ -145,14 +145,19 @@ public interface BeanCollection<E> extends Serializable {
|
||||
public boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Returns the underlying details as an iterator.
|
||||
* <p>
|
||||
* Note that for maps this returns the entrySet as we need the keys of the
|
||||
* map.
|
||||
* </p>
|
||||
* Returns the underlying collection of beans from the Set, Map or List.
|
||||
*/
|
||||
public Collection<E> getActualDetails();
|
||||
|
||||
/**
|
||||
* Returns the underlying entries so for Maps this is a collection of
|
||||
* Map.Entry.
|
||||
* <p>
|
||||
* For maps this returns the entrySet as we need the keys of the map.
|
||||
* </p>
|
||||
*/
|
||||
public Collection<?> getActualEntries();
|
||||
|
||||
/**
|
||||
* Set to true if maxRows was hit and there are actually more rows available.
|
||||
* <p>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebean.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Represent the call stack (stack trace elements).
|
||||
@@ -34,6 +35,25 @@ public final class CallStack implements Serializable {
|
||||
}
|
||||
this.pathHash = enc(hc);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 0;
|
||||
for (int i = 0; i < callStack.length; i++) {
|
||||
hc = 31 * hc + callStack[i].hashCode();
|
||||
}
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof CallStack)) {
|
||||
return false;
|
||||
}
|
||||
CallStack e = (CallStack) obj;
|
||||
return Arrays.equals(callStack, e.callStack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first element of the call stack.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebean.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Identifies a unique node of an object graph.
|
||||
@@ -64,6 +65,25 @@ public final class ObjectGraphNode implements Serializable {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "origin:" + originQueryPoint + " " + ":" + path + ":" + path;
|
||||
return "origin:" + originQueryPoint + " path[" + path+"]";
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 31 * originQueryPoint.hashCode();
|
||||
hc = 31 * hc + Objects.hashCode(path);
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ObjectGraphNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectGraphNode e = (ObjectGraphNode) obj;
|
||||
return Objects.equals(e.path, path)
|
||||
&& e.originQueryPoint.equals(originQueryPoint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,17 +14,20 @@ import java.io.Serializable;
|
||||
*/
|
||||
public final class ObjectGraphOrigin implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 410937765287968707L;
|
||||
private static final long serialVersionUID = 410937765287968708L;
|
||||
|
||||
private final CallStack callStack;
|
||||
|
||||
private final String key;
|
||||
|
||||
private final String beanType;
|
||||
|
||||
private final int queryHash;
|
||||
|
||||
private final String key;
|
||||
|
||||
public ObjectGraphOrigin(int queryHash, CallStack callStack, String beanType) {
|
||||
this.callStack = callStack;
|
||||
this.beanType = beanType;
|
||||
this.queryHash = queryHash;
|
||||
this.key = callStack.getOriginKey(queryHash);
|
||||
}
|
||||
|
||||
@@ -55,7 +58,27 @@ public final class ObjectGraphOrigin implements Serializable {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return key + " " + beanType + " " + callStack.getFirstStackTraceElement();
|
||||
return "key["+ key + "] type[" + beanType + "] " + callStack.getFirstStackTraceElement()+" ";
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 31 * callStack.hashCode();
|
||||
hc = 31 * hc + beanType.hashCode();
|
||||
hc = 31 * hc + queryHash;
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ObjectGraphOrigin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectGraphOrigin e = (ObjectGraphOrigin) obj;
|
||||
return e.queryHash == queryHash
|
||||
&& e.beanType.equals(beanType)
|
||||
&& e.callStack.equals(callStack);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,13 @@ public interface PersistenceContext {
|
||||
*/
|
||||
public Object get(Class<?> beanType, Object uid);
|
||||
|
||||
/**
|
||||
* Get the bean from the persistence context also checked to see if it had
|
||||
* been previously deleted (if so then you also can't hit the L2 cache to
|
||||
* fetch the bean for this particular persistence context).
|
||||
*/
|
||||
public WithOption getWithOption(Class<?> beanType, Object uid);
|
||||
|
||||
/**
|
||||
* Clear all the references.
|
||||
*/
|
||||
@@ -44,9 +51,60 @@ public interface PersistenceContext {
|
||||
*/
|
||||
public void clear(Class<?> beanType, Object uid);
|
||||
|
||||
/**
|
||||
* Clear the reference as a result of an entity being deleted.
|
||||
*/
|
||||
public void deleted(Class<?> beanType, Object id);
|
||||
|
||||
/**
|
||||
* Return the number of beans of the given type in the persistence context.
|
||||
*/
|
||||
public int size(Class<?> beanType);
|
||||
|
||||
/**
|
||||
* Wrapper on a bean to also indicate if a bean has been deleted.
|
||||
* <p>
|
||||
* If a bean has been deleted then for the same persistence context is should
|
||||
* not be able to be fetched from persistence context or L2 cache.
|
||||
* </p>
|
||||
*/
|
||||
public static class WithOption {
|
||||
|
||||
/**
|
||||
* The bean was previously deleted from this persistence context (can't hit
|
||||
* L2 cache).
|
||||
*/
|
||||
public static WithOption DELETED = new WithOption(true);
|
||||
|
||||
private final boolean deleted;
|
||||
private final Object bean;
|
||||
|
||||
private WithOption(boolean deleted) {
|
||||
this.deleted = true;
|
||||
this.bean = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The bean exists in the persistence context (and not been previously deleted).
|
||||
*/
|
||||
public WithOption(Object bean) {
|
||||
this.deleted = false;
|
||||
this.bean = bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the bean was deleted. This means you can't hit the L2
|
||||
* cache.
|
||||
*/
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean (from the persistence context).
|
||||
*/
|
||||
public Object getBean() {
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,9 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void internalAdd(Object bean) {
|
||||
if (list == null) {
|
||||
list = new ArrayList<E>();
|
||||
}
|
||||
list.add((E) bean);
|
||||
}
|
||||
|
||||
@@ -106,6 +109,11 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
public Collection<E> getActualDetails() {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<?> getActualEntries() {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying list.
|
||||
|
||||
@@ -37,6 +37,14 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
super(ebeanServer, ownerBean, propertyName);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void internalPut(Object key, Object bean) {
|
||||
if (map == null) {
|
||||
map = new LinkedHashMap<K, E>();
|
||||
}
|
||||
map.put((K)key, (E)bean);
|
||||
}
|
||||
|
||||
public void internalAdd(Object bean) {
|
||||
throw new RuntimeException("Not allowed for map");
|
||||
}
|
||||
@@ -104,14 +112,23 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map entrySet iterator.
|
||||
* Returns the collection of beans (map values).
|
||||
*/
|
||||
public Collection<E> getActualDetails() {
|
||||
return map.values();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the map entrySet.
|
||||
* <p>
|
||||
* This is because the key values may need to be set against the details (so
|
||||
* they don't need to be set twice).
|
||||
* </p>
|
||||
*/
|
||||
public Collection<E> getActualDetails() {
|
||||
return map.values();
|
||||
public Collection<?> getActualEntries() {
|
||||
return map.entrySet();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,8 +12,7 @@ import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
/**
|
||||
* Set capable of lazy loading.
|
||||
*/
|
||||
public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E>,
|
||||
BeanCollectionAdd {
|
||||
public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E>, BeanCollectionAdd {
|
||||
|
||||
/**
|
||||
* The underlying Set implementation.
|
||||
@@ -45,6 +44,9 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void internalAdd(Object bean) {
|
||||
if (set == null) {
|
||||
set = new LinkedHashSet<E>();
|
||||
}
|
||||
set.add((E) bean);
|
||||
}
|
||||
|
||||
@@ -113,6 +115,11 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<?> getActualEntries() {
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying set.
|
||||
*/
|
||||
|
||||
@@ -36,25 +36,31 @@ public class DataSourceConfig {
|
||||
|
||||
private int heartbeatFreqSecs = 30;
|
||||
|
||||
private int heartbeatTimeoutSeconds = 3;
|
||||
|
||||
private boolean captureStackTrace;
|
||||
|
||||
private int maxStackTraceSize = 5;
|
||||
|
||||
private int leakTimeMinutes = 30;
|
||||
|
||||
private int maxInactiveTimeSecs = 900;
|
||||
private int maxInactiveTimeSecs = 720;
|
||||
|
||||
private int maxAgeMinutes = 0;
|
||||
|
||||
private int trimPoolFreqSecs = 59;
|
||||
|
||||
private int pstmtCacheSize = 20;
|
||||
|
||||
private int cstmtCacheSize = 20;
|
||||
|
||||
private int waitTimeoutMillis = 1000;
|
||||
|
||||
|
||||
private String poolListener;
|
||||
|
||||
private boolean offline;
|
||||
|
||||
Map<String, String> customProperties;
|
||||
|
||||
protected Map<String, String> customProperties;
|
||||
|
||||
/**
|
||||
* Return the connection URL.
|
||||
@@ -194,6 +200,20 @@ public class DataSourceConfig {
|
||||
public void setHeartbeatFreqSecs(int heartbeatFreqSecs) {
|
||||
this.heartbeatFreqSecs = heartbeatFreqSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the heart beat timeout in seconds.
|
||||
*/
|
||||
public int getHeartbeatTimeoutSeconds() {
|
||||
return heartbeatTimeoutSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the heart beat timeout in seconds.
|
||||
*/
|
||||
public void setHeartbeatTimeoutSeconds(int heartbeatTimeoutSeconds) {
|
||||
this.heartbeatTimeoutSeconds = heartbeatTimeoutSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a stack trace should be captured when obtaining a connection
|
||||
@@ -309,6 +329,23 @@ public class DataSourceConfig {
|
||||
return maxInactiveTimeSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum age a connection is allowed to be before it is closed.
|
||||
* <p>
|
||||
* This can be used to close really old connections.
|
||||
* </p>
|
||||
*/
|
||||
public int getMaxAgeMinutes() {
|
||||
return maxAgeMinutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum age a connection can be in minutes.
|
||||
*/
|
||||
public void setMaxAgeMinutes(int maxAgeMinutes) {
|
||||
this.maxAgeMinutes = maxAgeMinutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time in seconds a connection can be idle after which it can be
|
||||
* trimmed from the pool.
|
||||
@@ -321,6 +358,25 @@ public class DataSourceConfig {
|
||||
this.maxInactiveTimeSecs = maxInactiveTimeSecs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the minimum time gap between pool trim checks.
|
||||
* <p>
|
||||
* This defaults to 59 seconds meaning that the pool trim check will run every
|
||||
* minute assuming the heart beat check runs every 30 seconds.
|
||||
* </p>
|
||||
*/
|
||||
public int getTrimPoolFreqSecs() {
|
||||
return trimPoolFreqSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum trim gap between pool trim checks.
|
||||
*/
|
||||
public void setTrimPoolFreqSecs(int trimPoolFreqSecs) {
|
||||
this.trimPoolFreqSecs = trimPoolFreqSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pool listener.
|
||||
*/
|
||||
@@ -359,7 +415,7 @@ public class DataSourceConfig {
|
||||
public void setOffline(boolean offline) {
|
||||
this.offline = offline;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a map of custom properties for the jdbc driver connection.
|
||||
*/
|
||||
@@ -388,18 +444,18 @@ public class DataSourceConfig {
|
||||
this.username = properties.get(prefix + "username", null);
|
||||
this.password = properties.get(prefix + "password", null);
|
||||
|
||||
String v;
|
||||
String dbDriver = properties.get(prefix + "databaseDriver", null);
|
||||
this.driver = properties.get(prefix + "driver", dbDriver);
|
||||
|
||||
v = properties.get(prefix + "databaseDriver", null);
|
||||
this.driver = properties.get(prefix + "driver", v);
|
||||
|
||||
v = properties.get(prefix + "databaseUrl", null);
|
||||
this.url = properties.get(prefix + "url", v);
|
||||
String dbUrl = properties.get(prefix + "databaseUrl", null);
|
||||
this.url = properties.get(prefix + "url", dbUrl);
|
||||
|
||||
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", 900);
|
||||
this.maxInactiveTimeSecs = properties.getInt(prefix + "maxInactiveTimeSecs", 720);
|
||||
this.trimPoolFreqSecs = properties.getInt(prefix + "trimPoolFreqSecs", 59);
|
||||
this.maxAgeMinutes = properties.getInt(prefix + "maxAgeMinutes", 0);
|
||||
|
||||
this.minConnections = properties.getInt(prefix + "minConnections", 0);
|
||||
this.maxConnections = properties.getInt(prefix + "maxConnections", 20);
|
||||
@@ -409,6 +465,7 @@ public class DataSourceConfig {
|
||||
this.waitTimeoutMillis = properties.getInt(prefix + "waitTimeout", 1000);
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import java.util.List;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.avaje.ebean.EbeanServerFactory;
|
||||
import com.avaje.ebean.LogLevel;
|
||||
import com.avaje.ebean.annotation.Encrypted;
|
||||
import com.avaje.ebean.cache.ServerCacheFactory;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
@@ -19,6 +18,7 @@ import com.avaje.ebean.event.BeanQueryAdapter;
|
||||
import com.avaje.ebean.event.BulkTableEventListener;
|
||||
import com.avaje.ebean.event.ServerConfigStartup;
|
||||
import com.avaje.ebean.event.TransactionEventListener;
|
||||
import com.avaje.ebean.meta.MetaInfoManager;
|
||||
import com.avaje.ebean.util.ClassUtil;
|
||||
|
||||
/**
|
||||
@@ -140,10 +140,6 @@ public class ServerConfig {
|
||||
|
||||
private boolean ddlRun;
|
||||
|
||||
private boolean debugSql;
|
||||
|
||||
private boolean debugLazyLoad;
|
||||
|
||||
private boolean useJtaTransactionManager;
|
||||
|
||||
/**
|
||||
@@ -151,22 +147,6 @@ public class ServerConfig {
|
||||
*/
|
||||
private ExternalTransactionManager externalTransactionManager;
|
||||
|
||||
/**
|
||||
* Set to true to log using java.util.logging and otherwise uses ebean
|
||||
* transaction loggers.
|
||||
*/
|
||||
private boolean loggingToJavaLogger;
|
||||
|
||||
/**
|
||||
* The directory transaction logs go (when loggingToJavaLogger is false).
|
||||
*/
|
||||
private String loggingDirectory = "logs";
|
||||
|
||||
/**
|
||||
* The overall transaction logging level.
|
||||
*/
|
||||
private LogLevel loggingLevel = LogLevel.NONE;
|
||||
|
||||
/**
|
||||
* Used to unwrap PreparedStatements to perform JDBC Driver specific functions
|
||||
*/
|
||||
@@ -212,6 +192,10 @@ public class ServerConfig {
|
||||
|
||||
private ServerCacheManager serverCacheManager;
|
||||
|
||||
private boolean collectQueryStatsByNode;
|
||||
|
||||
private boolean collectQueryOrigins;
|
||||
|
||||
/**
|
||||
* Construct a Server Configuration for programmatically creating an
|
||||
* EbeanServer.
|
||||
@@ -780,139 +764,6 @@ public class ServerConfig {
|
||||
this.dbEncrypt = dbEncrypt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true to get the generated SQL queries output to the console.
|
||||
* <p>
|
||||
* To get the SQL and bind variables for insert update delete statements you
|
||||
* should use transaction logging.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isDebugSql() {
|
||||
return debugSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to get the generated SQL queries output to the console.
|
||||
* <p>
|
||||
* To get the SQL and bind variables for insert update delete statements you
|
||||
* should use transaction logging.
|
||||
* </p>
|
||||
*/
|
||||
public void setDebugSql(boolean debugSql) {
|
||||
this.debugSql = debugSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is debug logging on lazy loading events.
|
||||
*/
|
||||
public boolean isDebugLazyLoad() {
|
||||
return debugLazyLoad;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to get debug logging on lazy loading events.
|
||||
*/
|
||||
public void setDebugLazyLoad(boolean debugLazyLoad) {
|
||||
this.debugLazyLoad = debugLazyLoad;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default transaction logging level.
|
||||
* <p>
|
||||
* The logging level can be changed on a per transaction basis.
|
||||
* </p>
|
||||
*/
|
||||
public LogLevel getLoggingLevel() {
|
||||
return loggingLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default transaction logging level.
|
||||
* <p>
|
||||
* The logging level can be changed on a per transaction basis.
|
||||
* </p>
|
||||
*/
|
||||
public void setLoggingLevel(LogLevel logLevel) {
|
||||
this.loggingLevel = logLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the directory where transaction logs go.
|
||||
*/
|
||||
public String getLoggingDirectory() {
|
||||
return loggingDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the transaction log directory substituting any expressions such as
|
||||
* ${catalina.base} etc.
|
||||
*/
|
||||
public String getLoggingDirectoryWithEval() {
|
||||
return GlobalProperties.evaluateExpressions(loggingDirectory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the directory that the transaction logs go in.
|
||||
* <p>
|
||||
* This will not be used if the transaction logging is going to java util
|
||||
* logging (via {@link #setLoggingToJavaLogger(boolean)}).
|
||||
* </p>
|
||||
* <p>
|
||||
* This can contain expressions like ${catalina.base} with environment
|
||||
* variables, java system properties and entries in ebean.properties.
|
||||
* </p>
|
||||
* <p>
|
||||
* e.g. ${catalina.base}/logs/trans
|
||||
* </p>
|
||||
*
|
||||
* @param loggingDirectory
|
||||
* the transaction log directory
|
||||
*/
|
||||
public void setLoggingDirectory(String loggingDirectory) {
|
||||
this.loggingDirectory = loggingDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if you want to use a java.util.logging.Logger to log
|
||||
* transaction statements, bind values etc.
|
||||
* <p>
|
||||
* If this is false then the default transaction logger is used which logs the
|
||||
* transaction details to separate transaction log files.
|
||||
* </p>
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isLoggingToJavaLogger() {
|
||||
return loggingToJavaLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to true if you want transaction logging to use a
|
||||
* java.util.logging.Logger to log the statements and bind variables etc
|
||||
* rather than the default one which creates separate transaction log files.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setLoggingToJavaLogger(boolean transactionLogToJavaLogger) {
|
||||
this.loggingToJavaLogger = transactionLogToJavaLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated - please use isTransactionLogToJavaLogger();
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean isUseJuliTransactionLogger() {
|
||||
return isLoggingToJavaLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated - please use setTransactionLogToJavaLogger();
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public void setUseJuliTransactionLogger(boolean transactionLogToJavaLogger) {
|
||||
setLoggingToJavaLogger(transactionLogToJavaLogger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to run the DDL generation on startup.
|
||||
*/
|
||||
@@ -1080,6 +931,51 @@ public class ServerConfig {
|
||||
public void setUpdateChangesOnly(boolean updateChangesOnly) {
|
||||
this.updateChangesOnly = updateChangesOnly;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the ebeanServer should collection query statistics by ObjectGraphNode.
|
||||
*/
|
||||
public boolean isCollectQueryStatsByNode() {
|
||||
return collectQueryStatsByNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to collection query execution statistics by ObjectGraphNode.
|
||||
* <p>
|
||||
* These statistics can be used to highlight code/query 'origin points' that result in lots of lazy loading.
|
||||
* </p>
|
||||
* <p>
|
||||
* It is considered safe/fine to have this set to true for production.
|
||||
* </p>
|
||||
* <p>
|
||||
* This information can be later retrieved via {@link MetaInfoManager}.
|
||||
* </p>
|
||||
* @see MetaInfoManager
|
||||
*/
|
||||
public void setCollectQueryStatsByNode(boolean collectQueryStatsByNode) {
|
||||
this.collectQueryStatsByNode = collectQueryStatsByNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if query plans should also collect their 'origins'. This means for a given query plan you
|
||||
* can identify the code/origin points where this query resulted from including lazy loading origins.
|
||||
*/
|
||||
public boolean isCollectQueryOrigins() {
|
||||
return collectQueryOrigins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if query plans should collect their 'origin' points. This means for a given query plan you
|
||||
* can identify the code/origin points where this query resulted from including lazy loading origins.
|
||||
* <p>
|
||||
* This information can be later retrieved via {@link MetaInfoManager}.
|
||||
* </p>
|
||||
* @see MetaInfoManager
|
||||
*/
|
||||
public void setCollectQueryOrigins(boolean collectQueryOrigins) {
|
||||
this.collectQueryOrigins = collectQueryOrigins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resource directory.
|
||||
@@ -1337,6 +1233,9 @@ public class ServerConfig {
|
||||
packages = getSearchJarsPackages(packagesProp);
|
||||
}
|
||||
|
||||
collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", true);
|
||||
collectQueryOrigins = p.getBoolean("collectQueryOrigins", true);
|
||||
|
||||
updateChangesOnly = p.getBoolean("updateChangesOnly", true);
|
||||
|
||||
boolean batchMode = p.getBoolean("batch.mode", false);
|
||||
@@ -1356,32 +1255,10 @@ public class ServerConfig {
|
||||
|
||||
ddlGenerate = p.getBoolean("ddl.generate", false);
|
||||
ddlRun = p.getBoolean("ddl.run", false);
|
||||
debugSql = p.getBoolean("debug.sql", false);
|
||||
debugLazyLoad = p.getBoolean("debug.lazyload", false);
|
||||
|
||||
loggingLevel = getLogLevelValue(p);
|
||||
|
||||
String s = p.get("useJuliTransactionLogger", null);
|
||||
s = p.get("loggingToJavaLogger", s);
|
||||
loggingToJavaLogger = "true".equalsIgnoreCase(s);
|
||||
|
||||
s = p.get("log.directory", "logs");
|
||||
loggingDirectory = p.get("logging.directory", s);
|
||||
|
||||
classes = getClasses(p);
|
||||
}
|
||||
|
||||
private LogLevel getLogLevelValue(PropertySource p) {
|
||||
// logging.level preferred but others parameters will work
|
||||
String logValue = p.get("logging", "NONE");
|
||||
logValue = p.get("log.level", logValue);
|
||||
logValue = p.get("logging.level", logValue);
|
||||
if (logValue.trim().equalsIgnoreCase("ALL")) {
|
||||
logValue = "SQL";
|
||||
}
|
||||
return Enum.valueOf(LogLevel.class, logValue.toUpperCase());
|
||||
}
|
||||
|
||||
private NamingConvention createNamingConvention(PropertySource p) {
|
||||
|
||||
NamingConvention nc = createInstance(p, NamingConvention.class, "namingconvention");
|
||||
|
||||
@@ -17,29 +17,16 @@ public class DB2Platform extends DatabasePlatform {
|
||||
|
||||
// only support getGeneratedKeys with non-batch JDBC
|
||||
// so generally use SEQUENCE instead for H2
|
||||
this.sqlLimiter = new Db2SqlLimiter();
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setIdType(IdType.IDENTITY);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
|
||||
this.openQuote = "\"";
|
||||
this.closeQuote = "\"";
|
||||
|
||||
booleanDbType = Types.INTEGER;
|
||||
dbTypeMap.put(Types.BOOLEAN, new DbType("smallint default 0"));
|
||||
dbTypeMap.put(Types.INTEGER, new DbType("integer"));
|
||||
dbTypeMap.put(Types.BIGINT, new DbType("bigint"));
|
||||
dbTypeMap.put(Types.REAL, new DbType("float"));
|
||||
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
|
||||
dbTypeMap.put(Types.SMALLINT, new DbType("smallint"));
|
||||
booleanDbType = Types.BOOLEAN;
|
||||
dbTypeMap.put(Types.REAL, new DbType("real"));
|
||||
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
|
||||
dbTypeMap.put(Types.DECIMAL, new DbType("decimal", 15));
|
||||
|
||||
this.dbDdlSyntax.setIdentity("generated by default as identity");
|
||||
|
||||
// this.dbDdlSyntax.setDropIfExists("if exists");
|
||||
// this.dbDdlSyntax.setDisableReferentialIntegrity("SET REFERENTIAL_INTEGRITY FALSE");
|
||||
// this.dbDdlSyntax.setEnableReferentialIntegrity("SET REFERENTIAL_INTEGRITY TRUE");
|
||||
// this.dbDdlSyntax.setForeignKeySuffix("on delete restrict on update restrict");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -63,6 +63,12 @@ public class DatabasePlatform {
|
||||
|
||||
protected boolean selectCountWithAlias;
|
||||
|
||||
/**
|
||||
* If set then use the FORWARD ONLY hint when creating ResultSets for
|
||||
* findIterate() and findVisit().
|
||||
*/
|
||||
protected boolean forwardOnlyHintOnFindIterate;
|
||||
|
||||
/**
|
||||
* Instantiates a new database platform.
|
||||
*/
|
||||
@@ -199,6 +205,24 @@ public class DatabasePlatform {
|
||||
return idInExpandedForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the ResultSet TYPE_FORWARD_ONLY Hint should be used on
|
||||
* findIterate() and findVisit() PreparedStatements.
|
||||
* <p>
|
||||
* This specifically is required for MySql when processing large results.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isForwardOnlyHintOnFindIterate() {
|
||||
return forwardOnlyHintOnFindIterate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if the ResultSet TYPE_FORWARD_ONLY Hint should be used by default on findIterate PreparedStatements.
|
||||
*/
|
||||
public void setForwardOnlyHintOnFindIterate(boolean forwardOnlyHintOnFindIterate) {
|
||||
this.forwardOnlyHintOnFindIterate = forwardOnlyHintOnFindIterate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DB identity/sequence features for this platform.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
public class Db2SqlLimiter implements SqlLimiter {
|
||||
|
||||
@Override
|
||||
public SqlLimitResponse limit(SqlLimitRequest request) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(512);
|
||||
sb.append("select ");
|
||||
sb.append(request.getDbSql());
|
||||
|
||||
int maxRows = request.getMaxRows();
|
||||
if (maxRows > 0) {
|
||||
sb.append(" ").append(NEW_LINE).append("FETCH FIRST ").append(maxRows).append(" ROWS ONLY");
|
||||
}
|
||||
|
||||
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
|
||||
return new SqlLimitResponse(sql, false);
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,10 @@ public class DbDdlSyntax {
|
||||
private String dropTableCascade;
|
||||
private String dropIfExists;
|
||||
|
||||
private String newLine = "\r\n";
|
||||
private String newLine = "\n";
|
||||
|
||||
private String identity = "auto_increment";
|
||||
private String identitySuffix = "";
|
||||
|
||||
private String pkPrefix = "pk_";
|
||||
|
||||
@@ -56,6 +57,24 @@ public class DbDdlSyntax {
|
||||
this.identity = identity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Typically returns empty string but for SQLite and perhaps others
|
||||
* the Identity/AutoIncrement clause comes after the primary key clause.
|
||||
*/
|
||||
public String getIdentitySuffix() {
|
||||
return identitySuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the identity clause that would appear after the primary key clause.
|
||||
* <p>
|
||||
* Only used for SQLite at this stage.
|
||||
* </p>
|
||||
*/
|
||||
public void setIdentitySuffix(String identitySuffix) {
|
||||
this.identitySuffix = identitySuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the width for padding whitespace after column names.
|
||||
*/
|
||||
|
||||
@@ -31,13 +31,12 @@ public class LimitOffsetSqlLimiter implements SqlLimiter {
|
||||
maxRows = maxRows + 1;
|
||||
}
|
||||
|
||||
sb.append(" ").append(NEW_LINE).append(LIMIT).append(" ");
|
||||
if (maxRows > 0) {
|
||||
sb.append(maxRows);
|
||||
}
|
||||
if (firstRow > 0) {
|
||||
sb.append(" ").append(OFFSET).append(" ");
|
||||
sb.append(firstRow);
|
||||
if (maxRows > 0 || firstRow > 0) {
|
||||
sb.append(" ").append(NEW_LINE).append(LIMIT).append(" ").append(maxRows);
|
||||
if (firstRow > 0) {
|
||||
sb.append(" ").append(OFFSET).append(" ");
|
||||
sb.append(firstRow);
|
||||
}
|
||||
}
|
||||
|
||||
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
|
||||
|
||||
@@ -31,6 +31,7 @@ public class MySqlPlatform extends DatabasePlatform {
|
||||
this.openQuote = "`";
|
||||
this.closeQuote = "`";
|
||||
|
||||
this.forwardOnlyHintOnFindIterate = true;
|
||||
this.booleanDbType = Types.BIT;
|
||||
|
||||
dbTypeMap.put(Types.BIT, new DbType("tinyint(1) default 0"));
|
||||
|
||||
@@ -22,9 +22,15 @@ public class SQLitePlatform extends DatabasePlatform {
|
||||
|
||||
dbTypeMap.put(Types.BIT, new DbType("int default 0"));
|
||||
dbTypeMap.put(Types.BOOLEAN, new DbType("int default 0"));
|
||||
|
||||
dbTypeMap.put(Types.BIGINT, new DbType("integer"));
|
||||
dbTypeMap.put(Types.SMALLINT, new DbType("integer"));
|
||||
|
||||
dbDdlSyntax.setInlinePrimaryKeyConstraint(true);
|
||||
dbDdlSyntax.setIdentity("AUTOINCREMENT");
|
||||
|
||||
// AutoIncrement goes the primary key declaration
|
||||
dbDdlSyntax.setIdentity("");
|
||||
dbDdlSyntax.setIdentitySuffix("AUTOINCREMENT");
|
||||
|
||||
dbDdlSyntax.setDisableReferentialIntegrity("PRAGMA foreign_keys = OFF");
|
||||
dbDdlSyntax.setEnableReferentialIntegrity("PRAGMA foreign_keys = ON");
|
||||
}
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
|
||||
/**
|
||||
* Statistics collected by AutoFetch profiling.
|
||||
*/
|
||||
@Entity
|
||||
public class MetaAutoFetchStatistic implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -6640406753257176803L;
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
private ObjectGraphOrigin origin;
|
||||
|
||||
private String beanType;
|
||||
|
||||
private int counter;
|
||||
|
||||
@Transient
|
||||
private List<QueryStats> queryStats;
|
||||
|
||||
@Transient
|
||||
private List<NodeUsageStats> nodeUsageStats;
|
||||
|
||||
public MetaAutoFetchStatistic() {
|
||||
}
|
||||
|
||||
public MetaAutoFetchStatistic(ObjectGraphOrigin origin, int counter, List<QueryStats> queryStats,
|
||||
List<NodeUsageStats> nodeUsageStats) {
|
||||
|
||||
this.origin = origin;
|
||||
this.beanType = origin == null ? null : origin.getBeanType();
|
||||
this.id = origin == null ? null : origin.getKey();
|
||||
this.counter = counter;
|
||||
this.queryStats = queryStats;
|
||||
this.nodeUsageStats = nodeUsageStats;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the query point key.
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean type.
|
||||
*/
|
||||
public String getBeanType() {
|
||||
return beanType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query point.
|
||||
*/
|
||||
public ObjectGraphOrigin getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of profiled queries the statistics is based on.
|
||||
*/
|
||||
public int getCounter() {
|
||||
return counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query execution statistics.
|
||||
*/
|
||||
public List<QueryStats> getQueryStats() {
|
||||
return queryStats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the node usage statistics.
|
||||
*/
|
||||
public List<NodeUsageStats> getNodeUsageStats() {
|
||||
return nodeUsageStats;
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME: This will likely be deprecated in favour of a separate object graph
|
||||
* cost.
|
||||
*/
|
||||
public static class QueryStats implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -5517935732867671387L;
|
||||
|
||||
private final String path;
|
||||
|
||||
private final int exeCount;
|
||||
|
||||
private final int totalBeanLoaded;
|
||||
|
||||
private final int totalMicros;
|
||||
|
||||
public QueryStats(String path, int exeCount, int totalBeanLoaded, int totalMicros) {
|
||||
this.path = path;
|
||||
this.exeCount = exeCount;
|
||||
this.totalBeanLoaded = totalBeanLoaded;
|
||||
this.totalMicros = totalMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path. This is empty string for the origin query and otherwise
|
||||
* the path for the associated lazy loading queries.
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of queries executed.
|
||||
*/
|
||||
public int getExeCount() {
|
||||
return exeCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* The total number of beans loaded by the query.
|
||||
*/
|
||||
public int getTotalBeanLoaded() {
|
||||
return totalBeanLoaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* The total time in microseconds of the queries.
|
||||
*/
|
||||
public int getTotalMicros() {
|
||||
return totalMicros;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
long avgMicros = exeCount == 0 ? 0 : totalMicros / exeCount;
|
||||
|
||||
return "queryExe path[" + path + "] count[" + exeCount + "] totalBeansLoaded["
|
||||
+ totalBeanLoaded + "] avgMicros[" + avgMicros + "] totalMicros[" + totalMicros
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects usages statistics for a given node in the object graph.
|
||||
*/
|
||||
public static class NodeUsageStats implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1786787832374844739L;
|
||||
|
||||
private final String path;
|
||||
|
||||
private final int profileCount;
|
||||
|
||||
private final int profileUsedCount;
|
||||
|
||||
private final String[] usedProperties;
|
||||
|
||||
public NodeUsageStats(String path, int profileCount, int profileUsedCount,
|
||||
String[] usedProperties) {
|
||||
this.path = path == null ? "" : path;
|
||||
this.profileCount = profileCount;
|
||||
this.profileUsedCount = profileUsedCount;
|
||||
this.usedProperties = usedProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path. This is empty string for the origin and otherwise the
|
||||
* path for the associated nodes.
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of profiled beans for this node.
|
||||
*/
|
||||
public int getProfileCount() {
|
||||
return profileCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of profiled beans that where actually used for this node.
|
||||
* <p>
|
||||
* The difference between profiled and used could show uneven traversal of
|
||||
* the object graph. UI paging through results means the traversal for the
|
||||
* first x beans can be much higher than the last x beans.
|
||||
* </p>
|
||||
*/
|
||||
public int getProfileUsedCount() {
|
||||
return profileUsedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* The properties used at this node.
|
||||
*/
|
||||
public String[] getUsedProperties() {
|
||||
return usedProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the properties as a Set rather than an Array.
|
||||
*/
|
||||
public Set<String> getUsedPropertiesSet() {
|
||||
LinkedHashSet<String> s = new LinkedHashSet<String>();
|
||||
for (int i = 0; i < usedProperties.length; i++) {
|
||||
s.add(usedProperties[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "path[" + path + "] profileCount[" + profileCount + "] used[" + profileUsedCount
|
||||
+ "] props" + Arrays.toString(usedProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
|
||||
/**
|
||||
* "Tuned fetch" information used by AutoFetch.
|
||||
* <p>
|
||||
* Note that the queryPoint is effectively the Id field for this bean.
|
||||
* </p>
|
||||
* <p>
|
||||
* The queryPoint identifies both the query and call stack.
|
||||
* </p>
|
||||
*/
|
||||
@Entity
|
||||
public class MetaAutoFetchTunedQueryInfo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 3119991928889170215L;
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
private String beanType;
|
||||
|
||||
/**
|
||||
* The profile query point (call stack and query).
|
||||
*/
|
||||
private ObjectGraphOrigin origin;
|
||||
|
||||
/**
|
||||
* The tuned query details with joins and properties.
|
||||
*/
|
||||
private String tunedDetail;
|
||||
|
||||
/**
|
||||
* The number of times profiling has been collected for this query point.
|
||||
*/
|
||||
private int profileCount;
|
||||
|
||||
/**
|
||||
* The number of queries tuned by this info.
|
||||
*/
|
||||
private int tunedCount;
|
||||
|
||||
private long lastTuneTime;
|
||||
|
||||
public MetaAutoFetchTunedQueryInfo() {
|
||||
|
||||
}
|
||||
|
||||
public MetaAutoFetchTunedQueryInfo(final ObjectGraphOrigin origin, String tunedDetail,
|
||||
int profileCount, int tunedCount, long lastTuneTime) {
|
||||
|
||||
this.origin = origin;
|
||||
this.beanType = origin == null ? null : origin.getBeanType();
|
||||
this.id = origin == null ? null : origin.getKey();
|
||||
this.tunedDetail = tunedDetail;
|
||||
this.profileCount = profileCount;
|
||||
this.tunedCount = tunedCount;
|
||||
this.lastTuneTime = lastTuneTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query point key.
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of bean this is tuned for.
|
||||
*/
|
||||
public String getBeanType() {
|
||||
return beanType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query point.
|
||||
*/
|
||||
public ObjectGraphOrigin getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* The tuned query detail in string form.
|
||||
*/
|
||||
public String getTunedDetail() {
|
||||
return tunedDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of profiled queries the tuned query is based on.
|
||||
*/
|
||||
public int getProfileCount() {
|
||||
return profileCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of queries tuned.
|
||||
*/
|
||||
public int getTunedCount() {
|
||||
return tunedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time of the last tune (that changed the query).
|
||||
*/
|
||||
public long getLastTuneTime() {
|
||||
return lastTuneTime;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "origin[" + origin + "] query[" + tunedDetail + "] profileCount[" + profileCount + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface MetaBeanInfo {
|
||||
|
||||
/**
|
||||
* Collect the current query plan statistics return the non-empty statistics.
|
||||
*/
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset);
|
||||
|
||||
/**
|
||||
* Collect the current query plan statistics return all the statistics (include query plans that haven't had query executions).
|
||||
*/
|
||||
public List<MetaQueryPlanStatistic> collectAllQueryPlanStatistics(boolean reset);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides access to the meta data in EbeanServer such as query execution statistics.
|
||||
*/
|
||||
public interface MetaInfoManager {
|
||||
|
||||
/**
|
||||
* Return the MetaBeanInfo for a bean type.
|
||||
*/
|
||||
public MetaBeanInfo getMetaBeanInfo(Class<?> beanClass);
|
||||
|
||||
/**
|
||||
* Return all the MetaBeanInfo.
|
||||
*/
|
||||
public List<MetaBeanInfo> getMetaBeanInfoList();
|
||||
|
||||
/**
|
||||
* Collect and return the query plan statistics for all the beans.
|
||||
* <p>
|
||||
* Note that this excludes the query plan statistics where there has been no
|
||||
* executions (since the last collection with reset).
|
||||
* </p>
|
||||
*/
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset);
|
||||
|
||||
/**
|
||||
* Collect and return the ObjectGraphNode statistics.
|
||||
* <p>
|
||||
* These show query executions based on an origin point and relative path.
|
||||
* This is used to look at the amount of lazy loading occurring for a given
|
||||
* query origin point and highlight potential for tuning a query.
|
||||
* </p>
|
||||
*
|
||||
* @param reset
|
||||
* Set to true to reset the underlying statistics after collection.
|
||||
*/
|
||||
public List<MetaObjectGraphNodeStats> collectNodeStatistics(boolean reset);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
|
||||
/**
|
||||
* Statistics for query execution based on object graph origin and paths.
|
||||
* <p>
|
||||
* These statistics can be used to identify origin queries that result in lots
|
||||
* of lazy loading.
|
||||
* </p>
|
||||
*
|
||||
* @see MetaInfoManager#collectNodeStatistics(boolean)
|
||||
*/
|
||||
public interface MetaObjectGraphNodeStats {
|
||||
|
||||
/**
|
||||
* Return the ObjectGraphNode which has the origin point and relative path.
|
||||
*/
|
||||
public ObjectGraphNode getNode();
|
||||
|
||||
/**
|
||||
* Return the startTime of statistics collection.
|
||||
*/
|
||||
public long getStartTime();
|
||||
|
||||
/**
|
||||
* Return the total count of queries executed for this node.
|
||||
*/
|
||||
public long getCount();
|
||||
|
||||
/**
|
||||
* Return the total time of queries executed for this node.
|
||||
*/
|
||||
public long getTotalTime();
|
||||
|
||||
/**
|
||||
* Return the total beans loaded by queries for this node.
|
||||
*/
|
||||
public long getTotalBeans();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
|
||||
/**
|
||||
* Holds a query 'origin' point and count for the number of queries executed for
|
||||
* this 'origin'.
|
||||
* <p>
|
||||
* This basically points to the bit of original code and query that results in
|
||||
* this query directly or via lazy loading.
|
||||
* </p>
|
||||
*
|
||||
* @see MetaQueryPlanStatistic
|
||||
* @see MetaInfoManager#collectQueryPlanStatistics(boolean)
|
||||
*/
|
||||
public interface MetaQueryPlanOriginCount {
|
||||
|
||||
/**
|
||||
* The 'origin' and path which this query belongs to.
|
||||
* <p>
|
||||
* For lazy loading queries this points to the original query and associated
|
||||
* navigation path that resulted in this query being executed.
|
||||
* </p>
|
||||
*/
|
||||
public ObjectGraphNode getObjectGraphNode();
|
||||
|
||||
/**
|
||||
* The number of times a query was fired for this node since the counter was
|
||||
* last reset.
|
||||
*/
|
||||
public long getCount();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Query execution statistics Meta data.
|
||||
*
|
||||
* @see MetaInfoManager#collectQueryPlanStatistics(boolean)
|
||||
*/
|
||||
public interface MetaQueryPlanStatistic {
|
||||
|
||||
/**
|
||||
* Return the bean type this query plan is for.
|
||||
*/
|
||||
public Class<?> getBeanType();
|
||||
|
||||
/**
|
||||
* Return true if this query plan was tuned by Autofetch.
|
||||
*/
|
||||
public boolean isAutofetchTuned();
|
||||
|
||||
/**
|
||||
* Return a string representation of the query plan hash.
|
||||
*/
|
||||
public String getQueryPlanHash();
|
||||
|
||||
/**
|
||||
* Return the sql executed.
|
||||
*/
|
||||
public String getSql();
|
||||
|
||||
/**
|
||||
* Return the total number of queries executed.
|
||||
*/
|
||||
public long getExecutionCount();
|
||||
|
||||
/**
|
||||
* Return the total number of beans loaded by the queries.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public long getTotalLoadedBeans();
|
||||
|
||||
/**
|
||||
* Return the total time taken by executions of this query.
|
||||
*/
|
||||
public long getTotalTimeMicros();
|
||||
|
||||
/**
|
||||
* Return the max execution time for this query.
|
||||
*/
|
||||
public long getMaxTimeMicros();
|
||||
|
||||
/**
|
||||
* Return the time collection started (or was last reset).
|
||||
*/
|
||||
public long getCollectionStart();
|
||||
|
||||
/**
|
||||
* Return the time of the last query executed using this plan.
|
||||
*/
|
||||
public long getLastQueryTime();
|
||||
|
||||
/**
|
||||
* Return the average query execution time in microseconds.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public long getAvgTimeMicros();
|
||||
|
||||
/**
|
||||
* Return the average number of bean loaded per query.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public long getAvgLoadedBeans();
|
||||
|
||||
/**
|
||||
* Return the 'origin' points and paths that resulted in the query being
|
||||
* executed and the associated number of times the query was executed via that
|
||||
* path.
|
||||
* <p>
|
||||
* This includes direct and lazy loading paths.
|
||||
* </p>
|
||||
*/
|
||||
public List<MetaQueryPlanOriginCount> getOrigins();
|
||||
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
|
||||
/**
|
||||
* Query execution statistics Meta data.
|
||||
*/
|
||||
@Entity
|
||||
public class MetaQueryStatistic implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -8746524372894472583L;
|
||||
|
||||
boolean autofetchTuned;
|
||||
|
||||
String beanType;
|
||||
|
||||
/**
|
||||
* The original query plan hash (calculated prior to autofetch tuning).
|
||||
*/
|
||||
int origQueryPlanHash;
|
||||
|
||||
/**
|
||||
* The final query plan hash (calculated after to autofetch tuning).
|
||||
*/
|
||||
int finalQueryPlanHash;
|
||||
|
||||
String sql;
|
||||
|
||||
int executionCount;
|
||||
|
||||
int totalLoadedBeans;
|
||||
|
||||
int totalTimeMicros;
|
||||
|
||||
long collectionStart;
|
||||
|
||||
long lastQueryTime;
|
||||
|
||||
int avgTimeMicros;
|
||||
|
||||
int avgLoadedBeans;
|
||||
|
||||
public MetaQueryStatistic() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MetaQueryStatistic.
|
||||
*/
|
||||
public MetaQueryStatistic(boolean autofetchTuned, String beanType, int plan, String sql,
|
||||
int executionCount, int totalLoadedBeans, int totalTimeMicros, long collectionStart,
|
||||
long lastQueryTime) {
|
||||
|
||||
this.autofetchTuned = autofetchTuned;
|
||||
this.beanType = beanType;
|
||||
this.finalQueryPlanHash = plan;
|
||||
this.sql = sql;
|
||||
this.executionCount = executionCount;
|
||||
this.totalLoadedBeans = totalLoadedBeans;
|
||||
this.totalTimeMicros = totalTimeMicros;
|
||||
this.collectionStart = collectionStart;
|
||||
|
||||
this.lastQueryTime = lastQueryTime;
|
||||
this.avgTimeMicros = executionCount == 0 ? 0 : totalTimeMicros / executionCount;
|
||||
this.avgLoadedBeans = executionCount == 0 ? 0 : totalLoadedBeans / executionCount;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "type=" + beanType + " tuned:" + autofetchTuned + " origHash=" + origQueryPlanHash
|
||||
+ " count=" + executionCount + " avgMicros=" + getAvgTimeMicros();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this query plan was built for Autofetch tuned queries.
|
||||
*/
|
||||
public boolean isAutofetchTuned() {
|
||||
return autofetchTuned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the original query plan hash (calculated prior to autofetch tuning).
|
||||
* <p>
|
||||
* This will return 0 if there is no autofetch profiling or tuning on this
|
||||
* query.
|
||||
* </p>
|
||||
*/
|
||||
public int getOrigQueryPlanHash() {
|
||||
return origQueryPlanHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the queryPlanHash value. This is unique for a given query plan.
|
||||
*/
|
||||
public int getFinalQueryPlanHash() {
|
||||
return finalQueryPlanHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean type.
|
||||
*/
|
||||
public String getBeanType() {
|
||||
return beanType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sql executed.
|
||||
*/
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total number of queries executed.
|
||||
*/
|
||||
public int getExecutionCount() {
|
||||
return executionCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total number of beans loaded by the queries.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public int getTotalLoadedBeans() {
|
||||
return totalLoadedBeans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of times this query was executed.
|
||||
*/
|
||||
public int getTotalTimeMicros() {
|
||||
return totalTimeMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time collection started.
|
||||
*/
|
||||
public long getCollectionStart() {
|
||||
return collectionStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time of the last query executed using this plan.
|
||||
*/
|
||||
public long getLastQueryTime() {
|
||||
return lastQueryTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the average query execution time in microseconds.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public int getAvgTimeMicros() {
|
||||
return avgTimeMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the average number of bean loaded per query.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public int getAvgLoadedBeans() {
|
||||
return avgLoadedBeans;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Meta data that can be retrieved for the EbeanServer.
|
||||
*/
|
||||
package com.avaje.ebean.meta;
|
||||
@@ -1,17 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Entity Beans for getting "Meta" data from Ebean</title>
|
||||
</head>
|
||||
<body>
|
||||
Entity Beans for getting "Meta" data from Ebean
|
||||
<p>
|
||||
You can query these entity beans to get "meta" data from Ebean.
|
||||
This includes things like query execution statistics.
|
||||
</p>
|
||||
<pre class="code">
|
||||
// fetch the meta data that controls autoFetch query tuning
|
||||
Query<MetaAutoFetchTunedFetch> query = Ebean.createQuery(MetaAutoFetchTunedFetch.class);
|
||||
List<MetaAutoFetchTunedFetch> list = query.findList();
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,8 +3,7 @@ package com.avaje.ebeaninternal.api;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -14,22 +13,16 @@ import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
|
||||
/**
|
||||
* Parameters used for binding to a statement.
|
||||
* <p>
|
||||
* Used by FindByNativeSql and UpdateSql to support ordered and named
|
||||
* parameters. Note that you can use either ordered OR named parameters.
|
||||
* Supports ordered or named parameters.
|
||||
* </p>
|
||||
*/
|
||||
public class BindParams implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 4541081933302086285L;
|
||||
|
||||
private ArrayList<Param> positionedParameters = new ArrayList<Param>();
|
||||
private List<Param> positionedParameters = new ArrayList<Param>();
|
||||
|
||||
private HashMap<String, Param> namedParameters = new HashMap<String, Param>();
|
||||
|
||||
/**
|
||||
* Need to create a hash when binding collection values (for in clauses).
|
||||
*/
|
||||
private int queryPlanHash = 1;
|
||||
private Map<String, Param> namedParameters = new LinkedHashMap<String, Param>();
|
||||
|
||||
/**
|
||||
* This is the sql. For named parameters this is the sql after the named
|
||||
@@ -38,6 +31,40 @@ public class BindParams implements Serializable {
|
||||
*/
|
||||
private String preparedSql;
|
||||
|
||||
public BindParams() {
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
int hc = namedParameters.hashCode();
|
||||
for (int i = 0; i < positionedParameters.size(); i++) {
|
||||
hc = hc * 31 + positionedParameters.get(i).hashCode();
|
||||
}
|
||||
return hc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hash that should be included with the query plan.
|
||||
* <p>
|
||||
* This is to handle binding collections to in clauses. The number of values
|
||||
* in the collection effects the query (number of bind values) and so must be
|
||||
* taken into account when calculating the query hash.
|
||||
* </p>
|
||||
*/
|
||||
public void buildQueryPlanHash(HashQueryPlanBuilder builder) {
|
||||
int hc = 31;
|
||||
for (Param param : positionedParameters) {
|
||||
hc = hc * 31 + param.queryBindCount();
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Param> entry : namedParameters.entrySet()) {
|
||||
hc = hc * 31 + entry.getKey().hashCode();
|
||||
hc = hc * 31 + entry.getValue().queryBindCount();
|
||||
}
|
||||
|
||||
int bindCount = positionedParameters.size() + namedParameters.size();
|
||||
builder.add(hc).bind(bindCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a deep copy of the BindParams.
|
||||
*/
|
||||
@@ -46,45 +73,12 @@ public class BindParams implements Serializable {
|
||||
for (Param p : positionedParameters) {
|
||||
copy.positionedParameters.add(p.copy());
|
||||
}
|
||||
Iterator<Entry<String, Param>> it = namedParameters.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<String, Param> entry = (Map.Entry<String, Param>) it.next();
|
||||
copy.namedParameters.put(entry.getKey(), entry.getValue().copy());
|
||||
for (Entry<String, Param> entry : namedParameters.entrySet()) {
|
||||
copy.namedParameters.put(entry.getKey(), entry.getValue().copy());
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
int hc = namedParameters.hashCode();
|
||||
for (int i = 0; i < positionedParameters.size(); i++) {
|
||||
hc = hc * 31 + positionedParameters.get(i).hashCode();
|
||||
}
|
||||
return hc;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = getClass().hashCode();
|
||||
hc = hc * 31 + namedParameters.hashCode();
|
||||
for (int i = 0; i < positionedParameters.size(); i++) {
|
||||
hc = hc * 31 + positionedParameters.get(i).hashCode();
|
||||
}
|
||||
hc = hc * 31 + (preparedSql == null ? 0 : preparedSql.hashCode());
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == null) {
|
||||
return false;
|
||||
}
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (o instanceof BindParams) {
|
||||
return hashCode() == o.hashCode();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are no bind parameters.
|
||||
*/
|
||||
@@ -131,10 +125,8 @@ public class BindParams implements Serializable {
|
||||
* Set an In Out parameter using position.
|
||||
*/
|
||||
public void setParameter(int position, Object value, int outType) {
|
||||
|
||||
addToQueryPlanHash(String.valueOf(position), value);
|
||||
|
||||
Param p = getParam(position);
|
||||
Param p = getParam(position);
|
||||
p.setInValue(value);
|
||||
p.setOutType(outType);
|
||||
}
|
||||
@@ -144,10 +136,8 @@ public class BindParams implements Serializable {
|
||||
* must use setNullParameter.
|
||||
*/
|
||||
public void setParameter(int position, Object value) {
|
||||
|
||||
addToQueryPlanHash(String.valueOf(position), value);
|
||||
|
||||
Param p = getParam(position);
|
||||
Param p = getParam(position);
|
||||
p.setInValue(value);
|
||||
}
|
||||
|
||||
@@ -182,10 +172,8 @@ public class BindParams implements Serializable {
|
||||
* Set a named In Out parameter.
|
||||
*/
|
||||
public void setParameter(String name, Object value, int outType) {
|
||||
|
||||
addToQueryPlanHash(name, value);
|
||||
|
||||
Param p = getParam(name);
|
||||
Param p = getParam(name);
|
||||
p.setInValue(value);
|
||||
p.setOutType(outType);
|
||||
}
|
||||
@@ -203,48 +191,22 @@ public class BindParams implements Serializable {
|
||||
*/
|
||||
public Param setParameter(String name, Object value) {
|
||||
|
||||
addToQueryPlanHash(name, value);
|
||||
|
||||
Param p = getParam(name);
|
||||
Param p = getParam(name);
|
||||
p.setInValue(value);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* For binding collections calculate a hash to be used for the query plan.
|
||||
*/
|
||||
private void addToQueryPlanHash(String name, Object value){
|
||||
if (value != null){
|
||||
if (value instanceof Collection<?>){
|
||||
queryPlanHash = queryPlanHash * 31 + name.hashCode();
|
||||
queryPlanHash = queryPlanHash * 31 + ((Collection<?>)value).size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hash that should be included with the query plan.
|
||||
* <p>
|
||||
* This is to handle binding collections to in clauses. The number
|
||||
* of values in the collection effects the query (number of bind values)
|
||||
* and so must be taken into account when calculating the query hash.
|
||||
* </p>
|
||||
*/
|
||||
public int getQueryPlanHash() {
|
||||
return queryPlanHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an encryption key as a bind value.
|
||||
* <p>
|
||||
* Needs special treatment as the value should not be included in a log.
|
||||
* </p>
|
||||
*/
|
||||
public Param setEncryptionKey(String name, Object value) {
|
||||
Param p = getParam(name);
|
||||
p.setEncryptionKey(value);
|
||||
return p;
|
||||
}
|
||||
/**
|
||||
* Set an encryption key as a bind value.
|
||||
* <p>
|
||||
* Needs special treatment as the value should not be included in a log.
|
||||
* </p>
|
||||
*/
|
||||
public Param setEncryptionKey(String name, Object value) {
|
||||
Param p = getParam(name);
|
||||
p.setEncryptionKey(value);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the named parameter as an Out parameter.
|
||||
@@ -300,9 +262,9 @@ public class BindParams implements Serializable {
|
||||
*/
|
||||
public static final class OrderedList {
|
||||
|
||||
final List<Param> paramList;
|
||||
private final List<Param> paramList;
|
||||
|
||||
final StringBuilder preparedSql;
|
||||
private final StringBuilder preparedSql;
|
||||
|
||||
public OrderedList() {
|
||||
this(new ArrayList<Param>());
|
||||
@@ -373,6 +335,16 @@ public class BindParams implements Serializable {
|
||||
public Param() {
|
||||
}
|
||||
|
||||
public int queryBindCount() {
|
||||
if (inValue == null) {
|
||||
return 0;
|
||||
}
|
||||
if (inValue instanceof Collection<?>){
|
||||
return ((Collection<?>)inValue).size();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a deep copy of the Param.
|
||||
*/
|
||||
@@ -448,14 +420,14 @@ public class BindParams implements Serializable {
|
||||
this.isInParam = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an encryption key (which can not be logged).
|
||||
*/
|
||||
public void setEncryptionKey(Object in) {
|
||||
this.inValue = in;
|
||||
this.isInParam = true;
|
||||
this.encryptionKey = true;
|
||||
}
|
||||
/**
|
||||
* Set an encryption key (which can not be logged).
|
||||
*/
|
||||
public void setEncryptionKey(Object in) {
|
||||
this.inValue = in;
|
||||
this.isInParam = true;
|
||||
this.encryptionKey = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that the In parameter is NULL and the specific type that it
|
||||
@@ -506,12 +478,12 @@ public class BindParams implements Serializable {
|
||||
this.textLocation = textLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true do not include this value in a transaction log.
|
||||
*/
|
||||
public boolean isEncryptionKey() {
|
||||
return encryptionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true do not include this value in a transaction log.
|
||||
*/
|
||||
public boolean isEncryptionKey() {
|
||||
return encryptionKey;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* A hash key for a query including both the query plan and bind values.
|
||||
*/
|
||||
public class HashQuery {
|
||||
|
||||
private final HashQueryPlan planHash;
|
||||
|
||||
private final int bindHash;
|
||||
|
||||
/**
|
||||
* Create the HashQuery.
|
||||
*/
|
||||
public HashQuery(HashQueryPlan planHash, int bindHash) {
|
||||
this.planHash = planHash;
|
||||
this.bindHash = bindHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query plan hash.
|
||||
*/
|
||||
public HashQueryPlan getPlanHash() {
|
||||
return planHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bind values hash.
|
||||
*/
|
||||
public int getBindHash() {
|
||||
return bindHash;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 31 * planHash.hashCode();
|
||||
hc = 31 * hc + bindHash;
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof HashQuery)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HashQuery e = (HashQuery) obj;
|
||||
return e.bindHash == bindHash && e.planHash.equals(planHash);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A hash for a query plan.
|
||||
*/
|
||||
public class HashQueryPlan {
|
||||
|
||||
private final String rawSql;
|
||||
|
||||
private final int planHash;
|
||||
|
||||
private final int bindCount;
|
||||
|
||||
public HashQueryPlan(String rawSql, int planHash, int bindCount) {
|
||||
this.rawSql = rawSql;
|
||||
this.planHash = planHash;
|
||||
this.bindCount = bindCount;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return planHash+":"+bindCount+(rawSql != null ? ":r" : "");
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = planHash;
|
||||
hc = hc * 31 + bindCount;
|
||||
hc = hc * 31 + Objects.hashCode(rawSql);
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof HashQueryPlan)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HashQueryPlan e = (HashQueryPlan) obj;
|
||||
return e.planHash == planHash
|
||||
&& e.bindCount == bindCount
|
||||
&& Objects.equals(e.rawSql, rawSql);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Used to build HashQueryPlan instances.
|
||||
*/
|
||||
public class HashQueryPlanBuilder {
|
||||
|
||||
private int planHash;
|
||||
|
||||
private int bindCount;
|
||||
|
||||
private String rawSql;
|
||||
|
||||
public HashQueryPlanBuilder() {
|
||||
this.planHash = 31;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return planHash+":"+bindCount+(rawSql != null ? ":r" : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(Class<?> cls) {
|
||||
planHash = planHash * 31 + cls.getName().hashCode();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an object to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(Object object) {
|
||||
planHash = planHash * 31 + Objects.hashCode(object);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an integer to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(int hashValue) {
|
||||
planHash = planHash * 31 + (hashValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a boolean to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(boolean booleanValue) {
|
||||
planHash = planHash * 31 + (booleanValue ? 31 : 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a number to the bind count for the hash.
|
||||
*/
|
||||
public HashQueryPlanBuilder bind(int extraBindCount) {
|
||||
bindCount += extraBindCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add raw sql to the hash.
|
||||
*/
|
||||
public HashQueryPlanBuilder addRawSql(String rawSql) {
|
||||
this.rawSql = rawSql;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and return the calculated HashQueryPlan.
|
||||
*/
|
||||
public HashQueryPlan build() {
|
||||
return new HashQueryPlan(rawSql, planHash, bindCount);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
/**
|
||||
* A buffer of beans for batch lazy loading and secondary query loading.
|
||||
*/
|
||||
public interface LoadBeanBuffer {
|
||||
|
||||
public List<EntityBeanIntercept> getBatch();
|
||||
|
||||
public BeanDescriptor<?> getBeanDescriptor();
|
||||
|
||||
public PersistenceContext getPersistenceContext();
|
||||
|
||||
public String getFullPath();
|
||||
|
||||
public void configureQuery(SpiQuery<?> query, String lazyLoadProperty);
|
||||
|
||||
}
|
||||
@@ -1,39 +1,10 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
/**
|
||||
* Controls the loading of ManyToOne and OneToOne relationships.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public interface LoadBeanContext extends LoadSecondaryQuery {
|
||||
|
||||
/**
|
||||
* Configure the query to load beans for this node/path.
|
||||
*/
|
||||
public void configureQuery(SpiQuery<?> query, String lazyLoadProperty);
|
||||
|
||||
/**
|
||||
* Return the full path of this node from the root object.
|
||||
*/
|
||||
public String getFullPath();
|
||||
|
||||
/**
|
||||
* Return the persistence context used for all queries
|
||||
* related to this object graph.
|
||||
*/
|
||||
public PersistenceContext getPersistenceContext();
|
||||
|
||||
/**
|
||||
* Return the BeanDescriptor for beans for this node.
|
||||
*/
|
||||
public BeanDescriptor<?> getBeanDescriptor();
|
||||
|
||||
/**
|
||||
* Return the batchSize used for lazy loading beans.
|
||||
*/
|
||||
public int getBatchSize();
|
||||
|
||||
}
|
||||
|
||||
@@ -9,55 +9,52 @@ import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
* Request for loading ManyToOne and OneToOne relationships.
|
||||
*/
|
||||
public class LoadBeanRequest extends LoadRequest {
|
||||
|
||||
private final List<EntityBeanIntercept> batch;
|
||||
|
||||
private final LoadBeanContext loadContext;
|
||||
|
||||
private final String lazyLoadProperty;
|
||||
|
||||
private final boolean loadCache;
|
||||
|
||||
public LoadBeanRequest(LoadBeanContext loadContext, List<EntityBeanIntercept> batch,
|
||||
Transaction transaction, int batchSize, boolean lazy, String lazyLoadProperty, boolean loadCache) {
|
||||
|
||||
super(transaction, batchSize, lazy);
|
||||
this.loadContext = loadContext;
|
||||
this.batch = batch;
|
||||
this.lazyLoadProperty = lazyLoadProperty;
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
|
||||
public boolean isLoadCache() {
|
||||
return loadCache;
|
||||
}
|
||||
private final List<EntityBeanIntercept> batch;
|
||||
|
||||
public String getDescription() {
|
||||
String fullPath = loadContext.getFullPath();
|
||||
String s = "path:" + fullPath + " batch:" + batchSize + " actual:"
|
||||
+ batch.size();
|
||||
return s;
|
||||
}
|
||||
private final LoadBeanBuffer LoadBuffer;
|
||||
|
||||
/**
|
||||
* Return the batch of beans to actually load.
|
||||
*/
|
||||
public List<EntityBeanIntercept> getBatch() {
|
||||
return batch;
|
||||
}
|
||||
private final String lazyLoadProperty;
|
||||
|
||||
/**
|
||||
* Return the load context.
|
||||
*/
|
||||
public LoadBeanContext getLoadContext() {
|
||||
return loadContext;
|
||||
}
|
||||
private final boolean loadCache;
|
||||
|
||||
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, Transaction transaction, boolean lazy, String lazyLoadProperty,
|
||||
boolean loadCache) {
|
||||
|
||||
super(transaction, lazy);
|
||||
this.LoadBuffer = LoadBuffer;
|
||||
this.batch = LoadBuffer.getBatch();
|
||||
this.lazyLoadProperty = lazyLoadProperty;
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
|
||||
public boolean isLoadCache() {
|
||||
return loadCache;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return "path:" + LoadBuffer.getFullPath() + " batch:" + batch.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch of beans to actually load.
|
||||
*/
|
||||
public List<EntityBeanIntercept> getBatch() {
|
||||
return batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the load context.
|
||||
*/
|
||||
public LoadBeanBuffer getLoadContext() {
|
||||
return LoadBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property that invoked the lazy loading.
|
||||
*/
|
||||
public String getLazyLoadProperty() {
|
||||
return lazyLoadProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property that invoked the lazy loading.
|
||||
*/
|
||||
public String getLazyLoadProperty() {
|
||||
return lazyLoadProperty;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
|
||||
/**
|
||||
* A buffer of bean collections for batch lazy loading and secondary query loading.
|
||||
*/
|
||||
public interface LoadManyBuffer {
|
||||
|
||||
public List<BeanCollection<?>> getBatch();
|
||||
|
||||
public BeanPropertyAssocMany<?> getBeanProperty();
|
||||
|
||||
public ObjectGraphNode getObjectGraphNode();
|
||||
|
||||
public BeanDescriptor<?> getBeanDescriptor();
|
||||
|
||||
public PersistenceContext getPersistenceContext();
|
||||
|
||||
public String getFullPath();
|
||||
|
||||
public void configureQuery(SpiQuery<?> query);
|
||||
|
||||
}
|
||||
@@ -1,54 +1,9 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
|
||||
/**
|
||||
* Controls the loading of OneToMany and ManyToMany relationships.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public interface LoadManyContext extends LoadSecondaryQuery {
|
||||
|
||||
/**
|
||||
* Configure the query to load beans for this node/path.
|
||||
*/
|
||||
public void configureQuery(SpiQuery<?> query);
|
||||
|
||||
/**
|
||||
* Return the full path of this node from the root object.
|
||||
*/
|
||||
public String getFullPath();
|
||||
|
||||
/**
|
||||
* Return the node location for this node/path.
|
||||
*/
|
||||
public ObjectGraphNode getObjectGraphNode();
|
||||
|
||||
|
||||
/**
|
||||
* Return the persistence context used for all queries
|
||||
* related to this object graph.
|
||||
*/
|
||||
public PersistenceContext getPersistenceContext();
|
||||
|
||||
/**
|
||||
* Return the batchSize used for lazy loading beans.
|
||||
*/
|
||||
public int getBatchSize();
|
||||
|
||||
/**
|
||||
* Return the BeanDescriptor for beans for this node.
|
||||
*/
|
||||
public BeanDescriptor<?> getBeanDescriptor();
|
||||
|
||||
/**
|
||||
* Return the associated Many bean property.
|
||||
*/
|
||||
public BeanPropertyAssocMany<?> getBeanProperty();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -13,28 +13,24 @@ public class LoadManyRequest extends LoadRequest {
|
||||
|
||||
private final List<BeanCollection<?>> batch;
|
||||
|
||||
private final LoadManyContext loadContext;
|
||||
private final LoadManyBuffer loadContext;
|
||||
|
||||
private final boolean onlyIds;
|
||||
|
||||
private final boolean loadCache;
|
||||
|
||||
public LoadManyRequest(LoadManyContext loadContext,
|
||||
List<BeanCollection<?>> batch, Transaction transaction,
|
||||
int batchSize, boolean lazy, boolean onlyIds, boolean loadCache) {
|
||||
public LoadManyRequest(LoadManyBuffer loadContext, Transaction transaction, int batchSize, boolean lazy,
|
||||
boolean onlyIds, boolean loadCache) {
|
||||
|
||||
super(transaction, batchSize, lazy);
|
||||
super(transaction, lazy);
|
||||
this.loadContext = loadContext;
|
||||
this.batch = batch;
|
||||
this.batch = loadContext.getBatch();
|
||||
this.onlyIds = onlyIds;
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
String fullPath = loadContext.getFullPath();
|
||||
String s = "path:" + fullPath + " batch:" + batchSize + " actual:"
|
||||
+ batch.size();
|
||||
return s;
|
||||
return "path:" + loadContext.getFullPath() + " size:"+ batch.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +43,7 @@ public class LoadManyRequest extends LoadRequest {
|
||||
/**
|
||||
* Return the load context.
|
||||
*/
|
||||
public LoadManyContext getLoadContext() {
|
||||
public LoadManyBuffer getLoadContext() {
|
||||
return loadContext;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,18 +9,14 @@ public abstract class LoadRequest {
|
||||
|
||||
protected final boolean lazy;
|
||||
|
||||
protected final int batchSize;
|
||||
|
||||
protected final Transaction transaction;
|
||||
|
||||
public LoadRequest(Transaction transaction, int batchSize, boolean lazy) {
|
||||
public LoadRequest(Transaction transaction, boolean lazy) {
|
||||
|
||||
this.transaction = transaction;
|
||||
this.batchSize = batchSize;
|
||||
this.lazy = lazy;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if this is a lazy load and false if it is a secondary query.
|
||||
*/
|
||||
@@ -28,13 +24,6 @@ public abstract class LoadRequest {
|
||||
return lazy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the requested batch size.
|
||||
*/
|
||||
public int getBatchSize() {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the transaction to use if this is a secondary query.
|
||||
* <p>
|
||||
|
||||
@@ -19,6 +19,10 @@ public class ManyWhereJoins implements Serializable {
|
||||
|
||||
private final TreeSet<String> joins = new TreeSet<String>();
|
||||
|
||||
private StringBuilder formulaProperties = new StringBuilder();
|
||||
|
||||
private boolean formulaWithJoin;
|
||||
|
||||
/**
|
||||
* Add a many where join.
|
||||
*/
|
||||
@@ -73,4 +77,36 @@ public class ManyWhereJoins implements Serializable {
|
||||
return joins;
|
||||
}
|
||||
|
||||
/**
|
||||
* In findRowCount query found a formula property with a join clause so building a select clause
|
||||
* specifically for the findRowCount query.
|
||||
*/
|
||||
public void addFormulaWithJoin(String propertyName) {
|
||||
if (formulaWithJoin) {
|
||||
formulaProperties.append(",");
|
||||
} else {
|
||||
formulaProperties = new StringBuilder();
|
||||
formulaWithJoin = true;
|
||||
}
|
||||
formulaProperties.append(propertyName);
|
||||
}
|
||||
|
||||
public boolean isHasMany() {
|
||||
return formulaWithJoin || !joins.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the findRowCount query just needs the id property in the select clause.
|
||||
*/
|
||||
public boolean isSelectId() {
|
||||
return !formulaWithJoin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the formula properties to build the select clause for a findRowCount query.
|
||||
*/
|
||||
public String getFormulaProperties() {
|
||||
return formulaProperties.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.avaje.ebean.TxScope;
|
||||
@@ -45,9 +44,6 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
|
||||
*/
|
||||
private final ArrayList<Class<? extends Throwable>> rollbackFor;
|
||||
|
||||
|
||||
private final UncaughtExceptionHandler originalUncaughtHandler;
|
||||
|
||||
/**
|
||||
* Flag set when a rollback has occurred.
|
||||
*/
|
||||
@@ -65,11 +61,6 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
|
||||
|
||||
this.noRollbackFor = txScope.getNoRollbackFor();
|
||||
this.rollbackFor = txScope.getRollbackFor();
|
||||
|
||||
Thread t = Thread.currentThread();
|
||||
originalUncaughtHandler = t.getUncaughtExceptionHandler();
|
||||
|
||||
t.setUncaughtExceptionHandler(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,13 +72,8 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
|
||||
// rollback transaction if required
|
||||
caughtThrowable(e);
|
||||
|
||||
// reinstate suspended transaction and
|
||||
// original uncaughtExceptionHandler if required
|
||||
// reinstate suspended transaction
|
||||
onFinally();
|
||||
|
||||
if (originalUncaughtHandler != null){
|
||||
originalUncaughtHandler.uncaughtException(thread, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,11 +96,7 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
|
||||
* Also reinstate the suspended transaction if there was one.
|
||||
*/
|
||||
public void onFinally() {
|
||||
try {
|
||||
if (originalUncaughtHandler != null){
|
||||
Thread.currentThread().setUncaughtExceptionHandler(originalUncaughtHandler);
|
||||
}
|
||||
|
||||
try {
|
||||
if (!rolledBack && created) {
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.avaje.ebean.TxScope;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.BeanLoader;
|
||||
import com.avaje.ebean.bean.CallStack;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.core.PstmtBatch;
|
||||
@@ -29,6 +30,8 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
*/
|
||||
public void shutdownManaged();
|
||||
|
||||
public boolean isCollectQueryOrigins();
|
||||
|
||||
/**
|
||||
* Return true if DeleteMissingChildren defaults to true for stateless
|
||||
* updates.
|
||||
@@ -187,4 +190,9 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
*/
|
||||
public boolean isSupportedType(java.lang.reflect.Type genericType);
|
||||
|
||||
/**
|
||||
* Collect query statistics by ObjectGraphNode. Used for Lazy loading reporting.
|
||||
*/
|
||||
public void collectQueryStats(ObjectGraphNode objectGraphNode, long loadedBeanCount, long timeMicros);
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public interface SpiExpression extends Expression {
|
||||
* from an AutoFetch perspective and get different tuning.
|
||||
* </p>
|
||||
*/
|
||||
public int queryAutoFetchHash();
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Calculate a hash value for the expression.
|
||||
@@ -37,7 +37,7 @@ public interface SpiExpression extends Expression {
|
||||
* case the query execution plan can be reused.
|
||||
* </p>
|
||||
*/
|
||||
public int queryPlanHash(BeanQueryRequest<?> request);
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Return the hash value for the values that will be bound.
|
||||
|
||||
@@ -8,6 +8,6 @@ public interface SpiExpressionFactory extends ExpressionFactory {
|
||||
/**
|
||||
* Create another expression factory with a given sub path.
|
||||
*/
|
||||
public ExpressionFactory createExpressionFactory(FilterExprPath prefix);
|
||||
public ExpressionFactory createExpressionFactory();//FilterExprPath prefix);
|
||||
|
||||
}
|
||||
|
||||
@@ -18,24 +18,23 @@ public interface SpiExpressionList<T> extends ExpressionList<T> {
|
||||
*/
|
||||
public List<SpiExpression> getUnderlyingList();
|
||||
|
||||
/**
|
||||
* Trim the path for filterMany() expressions.
|
||||
*/
|
||||
public void trimPath(int prefixTrim);
|
||||
/**
|
||||
* Return a copy of the ExpressionList with the path trimmed for filterMany() expressions.
|
||||
*/
|
||||
public SpiExpressionList<?> trimPath(int prefixTrim);
|
||||
|
||||
/**
|
||||
* Restore the ExpressionFactory after deserialisation.
|
||||
*/
|
||||
public void setExpressionFactory(ExpressionFactory expr);
|
||||
|
||||
/**
|
||||
* Process "Many" properties populating ManyWhereJoins.
|
||||
* <p>
|
||||
* Predicates on Many properties require an extra independent
|
||||
* join clause.
|
||||
* </p>
|
||||
*/
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoins);
|
||||
/**
|
||||
* Process "Many" properties populating ManyWhereJoins.
|
||||
* <p>
|
||||
* Predicates on Many properties require an extra independent join clause.
|
||||
* </p>
|
||||
*/
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoins);
|
||||
|
||||
/**
|
||||
* Return true if this list is empty.
|
||||
@@ -62,10 +61,10 @@ public interface SpiExpressionList<T> extends ExpressionList<T> {
|
||||
*/
|
||||
public ArrayList<Object> buildBindValues(SpiExpressionRequest request);
|
||||
|
||||
/**
|
||||
* Calculate a hash based on the expressions but excluding the actual bind
|
||||
* values.
|
||||
*/
|
||||
public int queryPlanHash(BeanQueryRequest<?> request);
|
||||
/**
|
||||
* Calculate a hash based on the expressions but excluding the actual bind
|
||||
* values.
|
||||
*/
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder);
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
import com.avaje.ebeaninternal.server.query.CancelableQuery;
|
||||
import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
|
||||
@@ -79,11 +80,6 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
SUBQUERY
|
||||
}
|
||||
|
||||
/**
|
||||
* Set total hits when querying against lucene.
|
||||
*/
|
||||
public void setTotalHits(int totalHits);
|
||||
|
||||
/**
|
||||
* Return true if select all properties was used to ensure the property
|
||||
@@ -153,11 +149,23 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
public String getLoadMode();
|
||||
|
||||
/**
|
||||
* This becomes a lazy loading query for a many relationship.
|
||||
*/
|
||||
public void setLazyLoadForParents(List<Object> parentIds, BeanPropertyAssocMany<?> many);
|
||||
|
||||
/**
|
||||
* Return the lazy loading 'many' property.
|
||||
*/
|
||||
public BeanPropertyAssocMany<?> getLazyLoadForParentsProperty();
|
||||
|
||||
/**
|
||||
* Return the list of parent Id's for lazy loading.
|
||||
*/
|
||||
public List<Object> getLazyLoadForParentIds();
|
||||
|
||||
/**
|
||||
* Set the load mode (+lazy or +query) and the load description.
|
||||
*
|
||||
* @param loadMode
|
||||
* @param loadDescription
|
||||
*/
|
||||
public void setLoadDescription(String loadMode, String loadDescription);
|
||||
|
||||
@@ -341,7 +349,7 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
* tuning/modifying the query.
|
||||
* </p>
|
||||
*/
|
||||
public int queryAutofetchHash();
|
||||
public HashQueryPlan queryAutofetchHash(HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Identifies queries that are the same bar the bind variables.
|
||||
@@ -354,7 +362,7 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
* Excludes the actual bind values (as they don't effect the query plan).
|
||||
* </p>
|
||||
*/
|
||||
public int queryPlanHash(BeanQueryRequest<?> request);
|
||||
public HashQueryPlan queryPlanHash(BeanQueryRequest<?> request);
|
||||
|
||||
/**
|
||||
* Calculate a hash based on the bind values used in the query.
|
||||
@@ -368,7 +376,7 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
/**
|
||||
* Identifies queries that are exactly the same including bind variables.
|
||||
*/
|
||||
public int queryHash();
|
||||
public HashQuery queryHash();
|
||||
|
||||
/**
|
||||
* Return true if this is a query based on a SqlSelect rather than
|
||||
|
||||
@@ -224,7 +224,7 @@ public interface AutoFetchManager extends NodeUsageListener {
|
||||
* @param micros
|
||||
* the query executing time in microseconds
|
||||
*/
|
||||
public void collectQueryInfo(ObjectGraphNode node, int beans, int micros);
|
||||
public void collectQueryInfo(ObjectGraphNode node, long beans, long micros);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,6 +61,7 @@ public class AutoFetchManagerFactory {
|
||||
FileInputStream fi = new FileInputStream(autoFetchFile);
|
||||
ObjectInputStream ois = new ObjectInputStream(fi);
|
||||
AutoFetchManager profListener = (AutoFetchManager) ois.readObject();
|
||||
ois.close();
|
||||
|
||||
logger.info("AutoFetch deserialized from file ["+autoFetchFile.getAbsolutePath()+"]");
|
||||
|
||||
|
||||
@@ -527,7 +527,7 @@ public class DefaultAutoFetchManager implements AutoFetchManager, Serializable {
|
||||
* query in which case the parentNode will be null, or a lazy loading query
|
||||
* resulting from traversal of the object graph.
|
||||
*/
|
||||
public void collectQueryInfo(ObjectGraphNode node, int beans, int micros) {
|
||||
public void collectQueryInfo(ObjectGraphNode node, long beans, long micros) {
|
||||
|
||||
if (node != null){
|
||||
ObjectGraphOrigin origin = node.getOriginQueryPoint();
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
package com.avaje.ebeaninternal.server.autofetch;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.avaje.ebean.bean.NodeUsageCollector;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
import com.avaje.ebean.meta.MetaAutoFetchStatistic;
|
||||
import com.avaje.ebean.meta.MetaAutoFetchStatistic.NodeUsageStats;
|
||||
import com.avaje.ebean.meta.MetaAutoFetchStatistic.QueryStats;
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import com.avaje.ebean.text.PathProperties.Props;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
@@ -53,30 +48,6 @@ public class Statistics implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
public MetaAutoFetchStatistic createPublicMeta() {
|
||||
|
||||
synchronized (monitor) {
|
||||
|
||||
StatisticsQuery[] sourceQueryStats = queryStatsMap.values().toArray(new StatisticsQuery[queryStatsMap.size()]);
|
||||
List<QueryStats> destQueryStats = new ArrayList<QueryStats>(sourceQueryStats.length);
|
||||
|
||||
// copy the query statistics
|
||||
for (int i = 0; i < sourceQueryStats.length; i++) {
|
||||
destQueryStats.add(sourceQueryStats[i].createPublicMeta());
|
||||
}
|
||||
|
||||
StatisticsNodeUsage[] sourceNodeUsage = nodeUsageMap.values().toArray(new StatisticsNodeUsage[nodeUsageMap.size()]);
|
||||
List<NodeUsageStats> destNodeUsage = new ArrayList<NodeUsageStats>(sourceNodeUsage.length);
|
||||
|
||||
// copy the node usage statistics
|
||||
for (int i = 0; i < sourceNodeUsage.length; i++) {
|
||||
destNodeUsage.add(sourceNodeUsage[i].createPublicMeta());
|
||||
}
|
||||
|
||||
return new MetaAutoFetchStatistic(origin, counter, destQueryStats, destNodeUsage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of times the root query has executed.
|
||||
* <p>
|
||||
@@ -127,7 +98,7 @@ public class Statistics implements Serializable {
|
||||
}
|
||||
|
||||
|
||||
public void collectQueryInfo(ObjectGraphNode node, int beansLoaded, int micros) {
|
||||
public void collectQueryInfo(ObjectGraphNode node, long beansLoaded, long micros) {
|
||||
|
||||
synchronized (monitor) {
|
||||
String key = node.getPath();
|
||||
|
||||
@@ -5,16 +5,16 @@ import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.bean.NodeUsageCollector;
|
||||
import com.avaje.ebean.meta.MetaAutoFetchStatistic.NodeUsageStats;
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Collects usages statistics for a given node in the object graph.
|
||||
@@ -44,13 +44,6 @@ public class StatisticsNodeUsage implements Serializable {
|
||||
this.queryTuningAddVersion = queryTuningAddVersion;
|
||||
}
|
||||
|
||||
public NodeUsageStats createPublicMeta() {
|
||||
synchronized(monitor){
|
||||
String[] usedProps = aggregateUsed.toArray(new String[aggregateUsed.size()]);
|
||||
return new NodeUsageStats(path, profileCount, profileUsedCount, usedProps);
|
||||
}
|
||||
}
|
||||
|
||||
public void buildTunedFetch(PathProperties pathProps, BeanDescriptor<?> rootDesc) {
|
||||
|
||||
synchronized(monitor){
|
||||
|
||||
@@ -2,8 +2,6 @@ package com.avaje.ebeaninternal.server.autofetch;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.avaje.ebean.meta.MetaAutoFetchStatistic.QueryStats;
|
||||
|
||||
/**
|
||||
* Used to accumulate query execution statistics.
|
||||
*/
|
||||
@@ -13,21 +11,17 @@ public class StatisticsQuery implements Serializable {
|
||||
|
||||
private final String path;
|
||||
|
||||
private int exeCount;
|
||||
private long exeCount;
|
||||
|
||||
private int totalBeanLoaded;
|
||||
private long totalBeanLoaded;
|
||||
|
||||
private int totalMicros;
|
||||
private long totalMicros;
|
||||
|
||||
public StatisticsQuery(String path){
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public QueryStats createPublicMeta() {
|
||||
return new QueryStats(path, exeCount, totalBeanLoaded, totalMicros);
|
||||
}
|
||||
|
||||
public void add(int beansLoaded, int micros) {
|
||||
public void add(long beansLoaded, long micros) {
|
||||
exeCount++;
|
||||
totalBeanLoaded += beansLoaded;
|
||||
totalMicros += micros;
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.avaje.ebeaninternal.server.autofetch;
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
import com.avaje.ebean.meta.MetaAutoFetchTunedQueryInfo;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
|
||||
@@ -73,13 +72,6 @@ public class TunedQueryInfo implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of this tuned fetch data for public consumption.
|
||||
*/
|
||||
public MetaAutoFetchTunedQueryInfo createPublicMeta() {
|
||||
return new MetaAutoFetchTunedQueryInfo(origin, tunedDetail.toString(), profileCount, tunedCount, lastTuneTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of times profiling has been collected for this query
|
||||
* point.
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.bean;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.common.BeanList;
|
||||
import com.avaje.ebean.event.BeanFinder;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebean.meta.MetaAutoFetchStatistic;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.autofetch.Statistics;
|
||||
|
||||
/**
|
||||
* Bean Finder for MetaAutoFetchStatistic.
|
||||
* <p>
|
||||
* This gets the meta data from the AutoFetchManager and creates a copy of that
|
||||
* data to give back to the caller in the form of MetaAutoFetchStatistic beans.
|
||||
* </p>
|
||||
*/
|
||||
public class BFAutoFetchStatisticFinder implements BeanFinder<MetaAutoFetchStatistic> {
|
||||
|
||||
|
||||
public MetaAutoFetchStatistic find(BeanQueryRequest<MetaAutoFetchStatistic> request) {
|
||||
SpiQuery<MetaAutoFetchStatistic> query = (SpiQuery<MetaAutoFetchStatistic>)request.getQuery();
|
||||
try {
|
||||
String queryPointKey = (String) query.getId();
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) request.getEbeanServer();
|
||||
AutoFetchManager manager = server.getAutoFetchManager();
|
||||
|
||||
Statistics stats = manager.getStatistics(queryPointKey);
|
||||
if (stats != null) {
|
||||
return stats.createPublicMeta();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only returns Lists at this stage.
|
||||
*/
|
||||
public BeanCollection<MetaAutoFetchStatistic> findMany(BeanQueryRequest<MetaAutoFetchStatistic> request) {
|
||||
|
||||
SpiQuery.Type queryType = ((SpiQuery<?>)request.getQuery()).getType();
|
||||
if (!queryType.equals(SpiQuery.Type.LIST)) {
|
||||
throw new PersistenceException("Only findList() supported at this stage.");
|
||||
}
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) request.getEbeanServer();
|
||||
AutoFetchManager manager = server.getAutoFetchManager();
|
||||
|
||||
BeanList<MetaAutoFetchStatistic> list = new BeanList<MetaAutoFetchStatistic>();
|
||||
|
||||
Iterator<Statistics> it = manager.iterateStatistics();
|
||||
while (it.hasNext()) {
|
||||
Statistics stats = it.next();
|
||||
// create a copy for public use
|
||||
list.add(stats.createPublicMeta());
|
||||
}
|
||||
|
||||
String orderBy = request.getQuery().order().toStringFormat();
|
||||
if (orderBy == null){
|
||||
orderBy = "beanType";
|
||||
}
|
||||
server.sort(list, orderBy);
|
||||
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.bean;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.common.BeanList;
|
||||
import com.avaje.ebean.event.BeanFinder;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebean.meta.MetaAutoFetchTunedQueryInfo;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.autofetch.TunedQueryInfo;
|
||||
|
||||
/**
|
||||
* BeanFinder for MetaAutoFetchTunedFetch.
|
||||
*/
|
||||
public class BFAutoFetchTunedFetchFinder implements BeanFinder<MetaAutoFetchTunedQueryInfo> {
|
||||
|
||||
|
||||
public MetaAutoFetchTunedQueryInfo find(BeanQueryRequest<MetaAutoFetchTunedQueryInfo> request) {
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>)request.getQuery();
|
||||
try {
|
||||
String queryPointKey = (String)query.getId();
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) request.getEbeanServer();
|
||||
AutoFetchManager manager = server.getAutoFetchManager();
|
||||
|
||||
TunedQueryInfo tunedFetch = manager.getTunedQueryInfo(queryPointKey);
|
||||
if (tunedFetch != null){
|
||||
return tunedFetch.createPublicMeta();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (Exception e){
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only returns Lists at this stage.
|
||||
*/
|
||||
public BeanCollection<MetaAutoFetchTunedQueryInfo> findMany(BeanQueryRequest<MetaAutoFetchTunedQueryInfo> request) {
|
||||
|
||||
SpiQuery.Type queryType = ((SpiQuery<?>)request.getQuery()).getType();
|
||||
if (!queryType.equals(SpiQuery.Type.LIST)){
|
||||
throw new PersistenceException("Only findList() supported at this stage.");
|
||||
}
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) request.getEbeanServer();
|
||||
AutoFetchManager manager = server.getAutoFetchManager();
|
||||
|
||||
BeanList<MetaAutoFetchTunedQueryInfo> list = new BeanList<MetaAutoFetchTunedQueryInfo>();
|
||||
|
||||
Iterator<TunedQueryInfo> it = manager.iterateTunedQueryInfo();
|
||||
while (it.hasNext()) {
|
||||
TunedQueryInfo tunedFetch = it.next();
|
||||
// create a copy for public use
|
||||
list.add(tunedFetch.createPublicMeta());
|
||||
}
|
||||
|
||||
String orderBy = request.getQuery().order().toStringFormat();
|
||||
if (orderBy == null){
|
||||
orderBy = "beanType, origQueryPlanHash";
|
||||
}
|
||||
server.sort(list, orderBy);
|
||||
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.bean;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.common.BeanList;
|
||||
import com.avaje.ebean.event.BeanFinder;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebean.meta.MetaQueryStatistic;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.query.CQueryPlan;
|
||||
|
||||
/**
|
||||
* BeanFinder for MetaQueryStatistic.
|
||||
*/
|
||||
public class BFQueryStatisticFinder implements BeanFinder<MetaQueryStatistic> {
|
||||
|
||||
|
||||
public MetaQueryStatistic find(BeanQueryRequest<MetaQueryStatistic> request) {
|
||||
throw new RuntimeException("Not Supported yet");
|
||||
}
|
||||
|
||||
/**
|
||||
* Only returns Lists at this stage.
|
||||
*/
|
||||
public BeanCollection<MetaQueryStatistic> findMany(BeanQueryRequest<MetaQueryStatistic> request) {
|
||||
|
||||
SpiQuery.Type queryType = ((SpiQuery<?>)request.getQuery()).getType();
|
||||
if (!queryType.equals(SpiQuery.Type.LIST)){
|
||||
throw new PersistenceException("Only findList() supported at this stage.");
|
||||
}
|
||||
|
||||
BeanList<MetaQueryStatistic> list = new BeanList<MetaQueryStatistic>();
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) request.getEbeanServer();
|
||||
build(list, server);
|
||||
|
||||
String orderBy = request.getQuery().order().toStringFormat();
|
||||
if (orderBy == null){
|
||||
orderBy = "beanType, origQueryPlanHash, autofetchTuned";
|
||||
}
|
||||
server.sort(list, orderBy);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private void build(List<MetaQueryStatistic> list, SpiEbeanServer server) {
|
||||
|
||||
for (BeanDescriptor<?> desc : server.getBeanDescriptors()) {
|
||||
desc.clearQueryStatistics();
|
||||
build(list, desc);
|
||||
}
|
||||
}
|
||||
|
||||
private void build(List<MetaQueryStatistic> list, BeanDescriptor<?> desc) {
|
||||
|
||||
Iterator<CQueryPlan> it = desc.queryPlans();
|
||||
while (it.hasNext()) {
|
||||
CQueryPlan queryPlan = (CQueryPlan) it.next();
|
||||
list.add(queryPlan.createMetaQueryStatistic(desc.getFullName()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>BeanFinders, BeanControllers etc for "meta" beans</title>
|
||||
</head>
|
||||
<body>
|
||||
BeanFinders, BeanControllers etc for "meta" beans
|
||||
</body>
|
||||
</html>
|
||||
@@ -2,8 +2,12 @@ package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.LogLevel;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
|
||||
@@ -12,6 +16,8 @@ import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
*/
|
||||
public abstract class BeanRequest {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(BeanRequest.class);
|
||||
|
||||
/**
|
||||
* The server processing the request.
|
||||
*/
|
||||
@@ -85,7 +91,14 @@ public abstract class BeanRequest {
|
||||
*/
|
||||
public void rollbackTransIfRequired() {
|
||||
if (createdTransaction) {
|
||||
transaction.rollback();
|
||||
try {
|
||||
transaction.rollback();
|
||||
} catch (PersistenceException e) {
|
||||
// Just log this and carry on. A previous exception has been
|
||||
// thrown and if this rollback throws exception it likely means
|
||||
// that the connection is broken (and the datasource and db will cleanup)
|
||||
log.error("Error trying to rollack a transaction (after a prior exception thrown)", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +110,10 @@ public abstract class BeanRequest {
|
||||
return ebeanServer;
|
||||
}
|
||||
|
||||
public SpiEbeanServer getServer() {
|
||||
return ebeanServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Transaction associated with this request.
|
||||
*/
|
||||
@@ -115,13 +132,13 @@ public abstract class BeanRequest {
|
||||
* Return true if SQL should be logged for this transaction.
|
||||
*/
|
||||
public boolean isLogSql() {
|
||||
return transaction.getLogLevel().ordinal() >= LogLevel.SQL.ordinal();
|
||||
return transaction.isLogSql();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if SUMMARY information should be logged for this transaction.
|
||||
*/
|
||||
public boolean isLogSummary() {
|
||||
return transaction.getLogLevel().ordinal() >= LogLevel.SUMMARY.ordinal();
|
||||
return transaction.isLogSummary();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,16 +82,22 @@ public class BootupClassPathSearch {
|
||||
ClassPathSearchFilter filter = new ClassPathSearchFilter();
|
||||
filter.addDefaultExcludePackages();
|
||||
|
||||
if (packages != null) {
|
||||
if (packages != null && packages.size() > 0) {
|
||||
for (String packageName : packages) {
|
||||
filter.includePackage(packageName);
|
||||
}
|
||||
|
||||
// if they specified include packages, they don't want by default to include everything
|
||||
filter.setDefaultPackageMatch(false);
|
||||
}
|
||||
|
||||
if (jars != null) {
|
||||
if (jars != null && jars.size() > 0) {
|
||||
for (String jarName : jars) {
|
||||
filter.includeJar(jarName);
|
||||
}
|
||||
|
||||
// if they specified jars to specifically include, they don't want everything included
|
||||
filter.setDefaultJarMatch(false);
|
||||
}
|
||||
|
||||
return filter;
|
||||
|
||||
@@ -8,8 +8,9 @@ import java.util.List;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.config.CompoundType;
|
||||
import com.avaje.ebean.config.ScalarTypeConverter;
|
||||
@@ -22,8 +23,6 @@ import com.avaje.ebean.event.ServerConfigStartup;
|
||||
import com.avaje.ebean.event.TransactionEventListener;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
import com.avaje.ebeaninternal.server.util.ClassPathSearchMatcher;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Interesting classes for a EbeanServer such as Embeddable, Entity,
|
||||
@@ -33,8 +32,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BootupClasses.class);
|
||||
|
||||
private ArrayList<Class<?>> xmlBeanList = new ArrayList<Class<?>>();
|
||||
|
||||
private ArrayList<Class<?>> embeddableList = new ArrayList<Class<?>>();
|
||||
|
||||
private ArrayList<Class<?>> entityList = new ArrayList<Class<?>>();
|
||||
@@ -55,8 +52,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
|
||||
|
||||
private ArrayList<Class<?>> beanQueryAdapterList = new ArrayList<Class<?>>();
|
||||
|
||||
private ArrayList<Class<?>> luceneIndexList = new ArrayList<Class<?>>();
|
||||
|
||||
private ArrayList<Class<?>> serverConfigStartupList = new ArrayList<Class<?>>();
|
||||
private ArrayList<ServerConfigStartup> serverConfigStartupInstances = new ArrayList<ServerConfigStartup>();
|
||||
|
||||
@@ -75,7 +70,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
|
||||
}
|
||||
|
||||
private BootupClasses(BootupClasses parent) {
|
||||
this.xmlBeanList.addAll(parent.xmlBeanList);
|
||||
this.embeddableList.addAll(parent.embeddableList);
|
||||
this.entityList.addAll(parent.entityList);
|
||||
this.scalarTypeList.addAll(parent.scalarTypeList);
|
||||
@@ -86,7 +80,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
|
||||
this.beanFinderList.addAll(parent.beanFinderList);
|
||||
this.beanListenerList.addAll(parent.beanListenerList);
|
||||
this.beanQueryAdapterList.addAll(parent.beanQueryAdapterList);
|
||||
this.luceneIndexList.addAll(parent.luceneIndexList);
|
||||
this.serverConfigStartupList.addAll(parent.serverConfigStartupList);
|
||||
}
|
||||
|
||||
@@ -304,13 +297,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
|
||||
return beanListenerList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of XML Beans.
|
||||
*/
|
||||
public ArrayList<Class<?>> getXmlBeanList() {
|
||||
return xmlBeanList;
|
||||
}
|
||||
|
||||
public void add(Iterator<Class<?>> it) {
|
||||
while (it.hasNext()) {
|
||||
Class<?> clazz = it.next();
|
||||
@@ -325,11 +311,7 @@ public class BootupClasses implements ClassPathSearchMatcher {
|
||||
|
||||
} else if (isEntity(cls)) {
|
||||
entityList.add(cls);
|
||||
|
||||
} else if (isXmlBean(cls)){
|
||||
entityList.add(cls);
|
||||
//xmlBeanList.add(cls);
|
||||
|
||||
|
||||
} else if (isInterestingInterface(cls)) {
|
||||
return true;
|
||||
|
||||
@@ -419,18 +401,4 @@ public class BootupClasses implements ClassPathSearchMatcher {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isXmlBean(Class<?> cls) {
|
||||
|
||||
Annotation ann = cls.getAnnotation(XmlRootElement.class);
|
||||
if (ann != null) {
|
||||
return true;
|
||||
}
|
||||
ann = cls.getAnnotation(XmlType.class);
|
||||
if (ann != null) {
|
||||
// Only looking for Beans and not Enums
|
||||
return !cls.isEnum();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import com.avaje.ebean.meta.*;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebeaninternal.server.util.LongAdder;
|
||||
|
||||
/**
|
||||
* Helper to collect the query execution statistics for a given node.
|
||||
*/
|
||||
public class CObjectGraphNodeStatistics {
|
||||
|
||||
private final ObjectGraphNode node;
|
||||
|
||||
private final LongAdder count = new LongAdder();
|
||||
|
||||
private final LongAdder totalTime = new LongAdder();
|
||||
|
||||
private final LongAdder totalBeans = new LongAdder();
|
||||
|
||||
private final AtomicLong startTime = new AtomicLong(System.currentTimeMillis());
|
||||
|
||||
public CObjectGraphNodeStatistics(ObjectGraphNode node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public void add(long beanCount, long exeMicros) {
|
||||
count.increment();
|
||||
totalTime.add(exeMicros);
|
||||
totalBeans.add(beanCount);
|
||||
}
|
||||
|
||||
public MetaObjectGraphNodeStats get(boolean reset) {
|
||||
if (reset) {
|
||||
return new Snapshot(node, startTime.getAndSet(System.currentTimeMillis()), count.sumThenReset(),
|
||||
totalTime.sumThenReset(), totalBeans.sumThenReset());
|
||||
} else {
|
||||
return new Snapshot(node, startTime.get(), count.sum(), totalTime.sum(), totalBeans.sum());
|
||||
}
|
||||
}
|
||||
|
||||
private static class Snapshot implements MetaObjectGraphNodeStats {
|
||||
|
||||
private final ObjectGraphNode node;
|
||||
private final long startTime;
|
||||
private final long count;
|
||||
private final long totalTime;
|
||||
private final long totalBeans;
|
||||
|
||||
public Snapshot(ObjectGraphNode node, long startTime, long count, long totalTime, long totalBeans) {
|
||||
this.node = node;
|
||||
this.startTime = startTime;
|
||||
this.count = count;
|
||||
this.totalTime = totalTime;
|
||||
this.totalBeans = totalBeans;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return node + " count[" + count + "] time[" + totalTime + "] beans[" + totalBeans + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectGraphNode getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalTime() {
|
||||
return totalTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalBeans() {
|
||||
return totalBeans;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,17 +8,7 @@ import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.config.dbplatform.H2Platform;
|
||||
import com.avaje.ebean.config.dbplatform.HsqldbPlatform;
|
||||
import com.avaje.ebean.config.dbplatform.MsSqlServer2000Platform;
|
||||
import com.avaje.ebean.config.dbplatform.MsSqlServer2005Platform;
|
||||
import com.avaje.ebean.config.dbplatform.MySqlPlatform;
|
||||
import com.avaje.ebean.config.dbplatform.Oracle10Platform;
|
||||
import com.avaje.ebean.config.dbplatform.Oracle9Platform;
|
||||
import com.avaje.ebean.config.dbplatform.PostgresPlatform;
|
||||
import com.avaje.ebean.config.dbplatform.SQLitePlatform;
|
||||
import com.avaje.ebean.config.dbplatform.SqlAnywherePlatform;
|
||||
import com.avaje.ebean.config.dbplatform.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -84,7 +74,9 @@ public class DatabasePlatformFactory {
|
||||
if (dbName.equals("sqlanywhere")) {
|
||||
return new SqlAnywherePlatform();
|
||||
}
|
||||
|
||||
if (dbName.equals("db2")) {
|
||||
return new DB2Platform();
|
||||
}
|
||||
if (dbName.equals("mysql")) {
|
||||
return new MySqlPlatform();
|
||||
}
|
||||
@@ -132,41 +124,43 @@ public class DatabasePlatformFactory {
|
||||
|
||||
int majorVersion = metaData.getDatabaseMajorVersion();
|
||||
|
||||
if (dbProductName.indexOf("oracle") > -1) {
|
||||
if (dbProductName.contains("oracle")) {
|
||||
if (majorVersion > 9) {
|
||||
return new Oracle10Platform();
|
||||
} else {
|
||||
return new Oracle9Platform();
|
||||
}
|
||||
}
|
||||
if (dbProductName.indexOf("microsoft") > -1) {
|
||||
else if (dbProductName.contains("microsoft")) {
|
||||
if (majorVersion > 8) {
|
||||
return new MsSqlServer2005Platform();
|
||||
} else {
|
||||
return new MsSqlServer2000Platform();
|
||||
}
|
||||
}
|
||||
|
||||
if (dbProductName.indexOf("mysql") > -1) {
|
||||
else if (dbProductName.contains("mysql")) {
|
||||
return new MySqlPlatform();
|
||||
}
|
||||
if (dbProductName.indexOf("h2") > -1) {
|
||||
else if (dbProductName.contains("h2")) {
|
||||
return new H2Platform();
|
||||
}
|
||||
if (dbProductName.indexOf("hsql database engine") > -1) {
|
||||
else if (dbProductName.contains("hsql database engine")) {
|
||||
return new HsqldbPlatform();
|
||||
}
|
||||
if (dbProductName.indexOf("postgres") > -1) {
|
||||
else if (dbProductName.contains("postgres")) {
|
||||
return new PostgresPlatform();
|
||||
}
|
||||
if (dbProductName.indexOf("sqlite") > -1) {
|
||||
else if (dbProductName.contains("sqlite")) {
|
||||
return new SQLitePlatform();
|
||||
}
|
||||
if (dbProductName.indexOf("sql anywhere") > -1) {
|
||||
else if (dbProductName.contains("db2")) {
|
||||
return new DB2Platform();
|
||||
}
|
||||
else if (dbProductName.contains("sql anywhere")) {
|
||||
return new SqlAnywherePlatform();
|
||||
}
|
||||
|
||||
// use the standard one
|
||||
|
||||
// use the standard one
|
||||
return new DatabasePlatform();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
|
||||
/**
|
||||
* Helper used in producing debug output on lazy loading.
|
||||
*/
|
||||
public class DebugLazyLoad {
|
||||
|
||||
private final String[] ignoreList;
|
||||
|
||||
private final boolean debug;
|
||||
|
||||
public DebugLazyLoad(boolean lazyLoadDebug) {
|
||||
ignoreList = buildLazyLoadIgnoreList();
|
||||
debug = lazyLoadDebug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if debugging is on.
|
||||
*/
|
||||
public boolean isDebug() {
|
||||
return debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the StackTraceElement that is believed to be the line of code that
|
||||
* triggered the lazy loading.
|
||||
* <p>
|
||||
* This is determined by going up the stack trace ignoring all the sections
|
||||
* of java and Ebean code etc until you find code not in the ignore list -
|
||||
* this is assumed to be your application code that triggered the lazy
|
||||
* loading (it could be a third party layer such as a web template).
|
||||
* </p>
|
||||
*/
|
||||
public StackTraceElement getStackTraceElement(Class<?> beanType) {
|
||||
|
||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||
for (int i = 0; i < stackTrace.length; i++) {
|
||||
if (isStackLine(stackTrace[i], beanType)) {
|
||||
return stackTrace[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the code is expected to be application code.
|
||||
*/
|
||||
private boolean isStackLine(StackTraceElement element, Class<?> beanType) {
|
||||
|
||||
String stackClass = element.getClassName();
|
||||
|
||||
if (isBeanClass(beanType, stackClass)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ignoreList.length; i++) {
|
||||
if (stackClass.startsWith(ignoreList[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recurse up the inheritance checking to see if the stackClass is
|
||||
* this beanType (or a parent type).
|
||||
*/
|
||||
private boolean isBeanClass(Class<?> beanType, String stackClass) {
|
||||
if (stackClass.startsWith(beanType.getName())) {
|
||||
return true;
|
||||
}
|
||||
Class<?> superCls = beanType.getSuperclass();
|
||||
if (superCls.equals(Object.class)){
|
||||
return false;
|
||||
} else {
|
||||
return isBeanClass(superCls, stackClass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build list of prefixes used to find the application code that triggered
|
||||
* the lazy loading.
|
||||
*/
|
||||
private String[] buildLazyLoadIgnoreList() {
|
||||
|
||||
List<String> ignore = new ArrayList<String>();
|
||||
|
||||
// code that should be ignored when searching
|
||||
// the stack trace elements
|
||||
ignore.add("com.avaje.ebean");
|
||||
ignore.add("java");
|
||||
ignore.add("sun.reflect");
|
||||
ignore.add("org.codehaus.groovy.runtime.");
|
||||
|
||||
String extraIgnore = GlobalProperties.get("debug.lazyload.ignore", null);
|
||||
if (extraIgnore != null) {
|
||||
String[] split = extraIgnore.split(",");
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
ignore.add(split[i].trim());
|
||||
}
|
||||
}
|
||||
|
||||
return ignore.toArray(new String[ignore.size()]);
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,10 @@ 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.ebeaninternal.api.LoadBeanContext;
|
||||
import com.avaje.ebeaninternal.api.LoadBeanBuffer;
|
||||
import com.avaje.ebeaninternal.api.LoadBeanRequest;
|
||||
import com.avaje.ebeaninternal.api.LoadManyContext;
|
||||
import com.avaje.ebeaninternal.api.LoadManyRequest;
|
||||
import com.avaje.ebeaninternal.api.LoadManyBuffer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
@@ -27,8 +27,6 @@ import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
|
||||
/**
|
||||
* Helper to handle lazy loading and refreshing of beans.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class DefaultBeanLoader {
|
||||
|
||||
@@ -50,32 +48,29 @@ public class DefaultBeanLoader {
|
||||
* re-use the query plan cache and get DB statement re-use.
|
||||
* </p>
|
||||
*/
|
||||
private int getBatchSize(int batchListSize, int requestedBatchSize) {
|
||||
if (batchListSize == requestedBatchSize) {
|
||||
return batchListSize;
|
||||
}
|
||||
if (batchListSize == 1) {
|
||||
private int getBatchSize(int batchSize) {
|
||||
|
||||
if (batchSize == 1) {
|
||||
// there is only one bean/collection to load
|
||||
return 1;
|
||||
}
|
||||
if (requestedBatchSize <= 5) {
|
||||
if (batchSize <= 5) {
|
||||
// anything less than 5 becomes 5
|
||||
return 5;
|
||||
}
|
||||
if (batchListSize <= 10 || requestedBatchSize <= 10) {
|
||||
// 10 or less to load
|
||||
// ... or we wanted a batch size between 6 and 10
|
||||
if (batchSize <= 10) {
|
||||
return 10;
|
||||
}
|
||||
if (batchListSize <= 20 || requestedBatchSize <= 20) {
|
||||
// 20 or less to load
|
||||
// ... or we wanted a batch size between 11 and 20
|
||||
if (batchSize <= 20) {
|
||||
return 20;
|
||||
}
|
||||
if (batchListSize <= 50) {
|
||||
if (batchSize <= 50) {
|
||||
return 50;
|
||||
}
|
||||
return requestedBatchSize;
|
||||
if (batchSize <= 100) {
|
||||
return 100;
|
||||
}
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
public void refreshMany(Object parentBean, String propertyName) {
|
||||
@@ -86,9 +81,9 @@ public class DefaultBeanLoader {
|
||||
|
||||
List<BeanCollection<?>> batch = loadRequest.getBatch();
|
||||
|
||||
int batchSize = getBatchSize(batch.size(), loadRequest.getBatchSize());
|
||||
int batchSize = getBatchSize(batch.size());
|
||||
|
||||
LoadManyContext ctx = loadRequest.getLoadContext();
|
||||
LoadManyBuffer ctx = loadRequest.getLoadContext();
|
||||
BeanPropertyAssocMany<?> many = ctx.getBeanProperty();
|
||||
|
||||
PersistenceContext pc = ctx.getPersistenceContext();
|
||||
@@ -111,20 +106,12 @@ public class DefaultBeanLoader {
|
||||
|
||||
BeanDescriptor<?> desc = ctx.getBeanDescriptor();
|
||||
|
||||
String idProperty = desc.getIdBinder().getIdProperty();
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(many.getTargetType());
|
||||
|
||||
query.setLazyLoadForParents(idList, many);
|
||||
many.addWhereParentIdIn(query, idList);
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(desc.getBeanType());
|
||||
query.setMode(Mode.LAZYLOAD_MANY);
|
||||
query.setLazyLoadManyPath(many.getName());
|
||||
query.setPersistenceContext(pc);
|
||||
query.select(idProperty);
|
||||
query.fetch(many.getName());
|
||||
|
||||
if (idList.size() == 1) {
|
||||
query.where().idEq(idList.get(0));
|
||||
} else {
|
||||
query.where().idIn(idList);
|
||||
}
|
||||
|
||||
String mode = loadRequest.isLazy() ? "+lazy" : "+query";
|
||||
query.setLoadDescription(mode, loadRequest.getDescription());
|
||||
@@ -134,7 +121,7 @@ public class DefaultBeanLoader {
|
||||
|
||||
if (loadRequest.isOnlyIds()) {
|
||||
// override to just select the Id values
|
||||
query.fetch(many.getName(), many.getTargetIdProperty());
|
||||
query.select(many.getTargetIdProperty());
|
||||
}
|
||||
|
||||
server.findList(query, loadRequest.getTransaction());
|
||||
@@ -154,14 +141,14 @@ public class DefaultBeanLoader {
|
||||
}
|
||||
}
|
||||
|
||||
public void loadMany(BeanCollection<?> bc, LoadManyContext ctx, boolean onlyIds) {
|
||||
public void loadMany(BeanCollection<?> bc, boolean onlyIds) {
|
||||
|
||||
Object parentBean = bc.getOwnerBean();
|
||||
String propertyName = bc.getPropertyName();
|
||||
|
||||
ObjectGraphNode node = ctx == null ? null : ctx.getObjectGraphNode();
|
||||
//ObjectGraphNode node = ctx == null ? null : ctx.getObjectGraphNode();
|
||||
|
||||
loadManyInternal(parentBean, propertyName, null, false, node, onlyIds);
|
||||
loadManyInternal(parentBean, propertyName, null, false, null, onlyIds);
|
||||
}
|
||||
|
||||
public void refreshMany(Object parentBean, String propertyName, Transaction t) {
|
||||
@@ -170,17 +157,15 @@ public class DefaultBeanLoader {
|
||||
|
||||
private void loadManyInternal(Object parentBean, String propertyName, Transaction t, boolean refresh, ObjectGraphNode node, boolean onlyIds) {
|
||||
|
||||
EntityBeanIntercept ebi = null;
|
||||
PersistenceContext pc = null;
|
||||
BeanCollection<?> beanCollection = null;
|
||||
ExpressionList<?> filterMany = null;
|
||||
|
||||
ebi = ((EntityBean) parentBean)._ebean_getIntercept();
|
||||
pc = ebi.getPersistenceContext();
|
||||
EntityBeanIntercept ebi = ((EntityBean) parentBean)._ebean_getIntercept();
|
||||
PersistenceContext pc = ebi.getPersistenceContext();
|
||||
|
||||
BeanDescriptor<?> parentDesc = server.getBeanDescriptor(parentBean.getClass());
|
||||
BeanPropertyAssocMany<?> many = (BeanPropertyAssocMany<?>) parentDesc.getBeanProperty(propertyName);
|
||||
|
||||
BeanCollection<?> beanCollection = null;
|
||||
ExpressionList<?> filterMany = null;
|
||||
|
||||
Object currentValue = many.getValue(parentBean);
|
||||
if (currentValue instanceof BeanCollection<?>) {
|
||||
beanCollection = (BeanCollection<?>) currentValue;
|
||||
@@ -270,9 +255,9 @@ public class DefaultBeanLoader {
|
||||
throw new RuntimeException("Nothing in batch?");
|
||||
}
|
||||
|
||||
int batchSize = getBatchSize(batch.size(), loadRequest.getBatchSize());
|
||||
int batchSize = getBatchSize(batch.size());
|
||||
|
||||
LoadBeanContext ctx = loadRequest.getLoadContext();
|
||||
LoadBeanBuffer ctx = loadRequest.getLoadContext();
|
||||
BeanDescriptor<?> desc = ctx.getBeanDescriptor();
|
||||
|
||||
Class<?> beanType = desc.getBeanType();
|
||||
@@ -349,7 +334,6 @@ public class DefaultBeanLoader {
|
||||
ebis[i].setLazyLoadFailure();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void refresh(Object bean) {
|
||||
@@ -362,7 +346,6 @@ public class DefaultBeanLoader {
|
||||
|
||||
private void refreshBeanInternal(Object bean, SpiQuery.Mode mode) {
|
||||
|
||||
|
||||
EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept();;
|
||||
PersistenceContext pc = ebi.getPersistenceContext();
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.meta.MetaBeanInfo;
|
||||
import com.avaje.ebean.meta.MetaQueryPlanStatistic;
|
||||
import com.avaje.ebean.meta.MetaInfoManager;
|
||||
import com.avaje.ebean.meta.MetaObjectGraphNodeStats;
|
||||
|
||||
/**
|
||||
* DefaultServer based implementation of MetaInfoManager.
|
||||
*/
|
||||
public class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
|
||||
private final DefaultServer server;
|
||||
|
||||
public DefaultMetaInfoManager(DefaultServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaBeanInfo getMetaBeanInfo(Class<?> beanClass) {
|
||||
return server.getBeanDescriptor(beanClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaBeanInfo> getMetaBeanInfoList() {
|
||||
|
||||
return new ArrayList<MetaBeanInfo>(server.getBeanDescriptors());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset) {
|
||||
|
||||
List<MetaQueryPlanStatistic> list = new ArrayList<MetaQueryPlanStatistic>();
|
||||
|
||||
for (MetaBeanInfo metaBeanInfo : getMetaBeanInfoList()) {
|
||||
list.addAll(metaBeanInfo.collectQueryPlanStatistics(reset));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<MetaObjectGraphNodeStats> collectNodeStatistics(boolean reset) {
|
||||
|
||||
List<MetaObjectGraphNodeStats> list = new ArrayList<MetaObjectGraphNodeStats>();
|
||||
|
||||
for (CObjectGraphNodeStatistics nodeStatistics : server.objectGraphStats.values()) {
|
||||
MetaObjectGraphNodeStats nodeStats = nodeStatistics.get(reset);
|
||||
if (nodeStats.getCount() > 0) {
|
||||
// Only collection non-empty statistics
|
||||
list.add(nodeStats);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,7 @@ 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;
|
||||
@@ -49,13 +50,18 @@ 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.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.ebean.text.json.JsonElement;
|
||||
@@ -154,7 +160,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
private final CQueryEngine cqueryEngine;
|
||||
|
||||
@Deprecated
|
||||
//@Deprecated
|
||||
private DdlGenerator ddlGenerator;
|
||||
|
||||
private final ExpressionFactory expressionFactory;
|
||||
@@ -167,6 +173,8 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
private final JsonContext jsonContext;
|
||||
|
||||
private final MetaInfoManager metaInfoManager;
|
||||
|
||||
/**
|
||||
* The MBean name used to register Ebean.
|
||||
*/
|
||||
@@ -187,38 +195,56 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
*/
|
||||
private int lazyLoadBatchSize;
|
||||
|
||||
/** The query batch size */
|
||||
/**
|
||||
* The query batch size
|
||||
*/
|
||||
private int queryBatchSize;
|
||||
|
||||
/**
|
||||
* JDBC driver specific handling for JDBC batch execution.
|
||||
*/
|
||||
private PstmtBatch pstmtBatch;
|
||||
|
||||
|
||||
/**
|
||||
* holds plugins (e.g. ddl generator) detected by the service loader
|
||||
*/
|
||||
private List<SpiEbeanPlugin> ebeanPlugins;
|
||||
|
||||
private final boolean collectQueryOrigins;
|
||||
|
||||
private final boolean collectQueryStatsByNode;
|
||||
|
||||
/**
|
||||
* Cache used to collect statistics based on ObjectGraphNode (used to highlight lazy loading origin points).
|
||||
*/
|
||||
protected final ConcurrentHashMap<ObjectGraphNode, CObjectGraphNodeStatistics> objectGraphStats;
|
||||
|
||||
/**
|
||||
* Create the DefaultServer.
|
||||
*/
|
||||
public DefaultServer(InternalConfiguration config, ServerCacheManager cache) {
|
||||
|
||||
ServerConfig serverConfig = config.getServerConfig();
|
||||
|
||||
this.objectGraphStats = new ConcurrentHashMap<ObjectGraphNode, CObjectGraphNodeStatistics>();
|
||||
this.metaInfoManager = new DefaultMetaInfoManager(this);
|
||||
this.serverCacheManager = cache;
|
||||
this.pstmtBatch = config.getPstmtBatch();
|
||||
this.databasePlatform = config.getDatabasePlatform();
|
||||
this.backgroundExecutor = config.getBackgroundExecutor();
|
||||
this.serverName = config.getServerConfig().getName();
|
||||
this.lazyLoadBatchSize = config.getServerConfig().getLazyLoadBatchSize();
|
||||
this.queryBatchSize = config.getServerConfig().getQueryBatchSize();
|
||||
|
||||
this.serverName = serverConfig.getName();
|
||||
this.lazyLoadBatchSize = serverConfig.getLazyLoadBatchSize();
|
||||
this.queryBatchSize = serverConfig.getQueryBatchSize();
|
||||
this.cqueryEngine = config.getCQueryEngine();
|
||||
this.expressionFactory = config.getExpressionFactory();
|
||||
this.encryptKeyManager = config.getServerConfig().getEncryptKeyManager();
|
||||
this.encryptKeyManager = serverConfig.getEncryptKeyManager();
|
||||
|
||||
this.beanDescriptorManager = config.getBeanDescriptorManager();
|
||||
beanDescriptorManager.setEbeanServer(this);
|
||||
|
||||
this.collectQueryOrigins = serverConfig.isCollectQueryOrigins();
|
||||
this.collectQueryStatsByNode = serverConfig.isCollectQueryStatsByNode();
|
||||
this.maxCallStack = GlobalProperties.getInt("ebean.maxCallStack", 5);
|
||||
|
||||
this.defaultUpdateNullProperties = "true"
|
||||
@@ -276,6 +302,11 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCollectQueryOrigins() {
|
||||
return collectQueryOrigins;
|
||||
}
|
||||
|
||||
public boolean isDefaultDeleteMissingChildren() {
|
||||
return defaultDeleteMissingChildren;
|
||||
}
|
||||
@@ -295,6 +326,11 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
public DatabasePlatform getDatabasePlatform() {
|
||||
return databasePlatform;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaInfoManager getMetaInfoManager() {
|
||||
return metaInfoManager;
|
||||
}
|
||||
|
||||
public BackgroundExecutor getBackgroundExecutor() {
|
||||
return backgroundExecutor;
|
||||
@@ -454,7 +490,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a query. Only valid for ORM queries (not LDAP)
|
||||
* Compile a query. Only valid for ORM queries.
|
||||
*/
|
||||
public <T> CQuery<T> compileQuery(Query<T> query, Transaction t) {
|
||||
SpiOrmQueryRequest<T> qr = createQueryRequest(Type.SUBQUERY, query, t);
|
||||
@@ -501,7 +537,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
public void loadMany(BeanCollection<?> bc, boolean onlyIds) {
|
||||
|
||||
beanLoader.loadMany(bc, null, onlyIds);
|
||||
beanLoader.loadMany(bc, onlyIds);
|
||||
}
|
||||
|
||||
public void refresh(Object bean) {
|
||||
@@ -1136,9 +1172,14 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
// first look in the persistence context
|
||||
context = t.getPersistenceContext();
|
||||
if (context != null) {
|
||||
Object o = context.get(beanDescriptor.getBeanType(), query.getId());
|
||||
WithOption o = context.getWithOption(beanDescriptor.getBeanType(), query.getId());
|
||||
if (o != null) {
|
||||
return (T) o;
|
||||
if (o.isDeleted()) {
|
||||
// Bean was previously deleted in the same transaction / persistence context
|
||||
return null;
|
||||
}
|
||||
// Return the entity bean instance from the persistence context
|
||||
return (T) o.getBean();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1148,6 +1189,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Hit the L2 bean cache
|
||||
Object cachedBean = beanDescriptor.cacheGetBean(query.getId(), query.isReadOnly());
|
||||
if (cachedBean != null) {
|
||||
if (context == null) {
|
||||
@@ -1156,7 +1198,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
}
|
||||
context.put(query.getId(), cachedBean);
|
||||
|
||||
DLoadContext loadContext = new DLoadContext(this, beanDescriptor, query.isReadOnly(), false, null, false);
|
||||
DLoadContext loadContext = new DLoadContext(this, beanDescriptor, query.isReadOnly(), query);
|
||||
loadContext.setPersistenceContext(context);
|
||||
|
||||
EntityBeanIntercept ebi = ((EntityBean) cachedBean)._ebean_getIntercept();
|
||||
@@ -1340,10 +1382,9 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
Transaction newTxn = createTransaction();
|
||||
|
||||
CallableQueryRowCount<T> call = new CallableQueryRowCount<T>(this, copy, newTxn);
|
||||
FutureTask<Integer> futureTask = new FutureTask<Integer>(call);
|
||||
|
||||
QueryFutureRowCount<T> queryFuture = new QueryFutureRowCount<T>(copy, futureTask);
|
||||
backgroundExecutor.execute(futureTask);
|
||||
QueryFutureRowCount<T> queryFuture = new QueryFutureRowCount<T>(call);
|
||||
backgroundExecutor.execute(queryFuture.getFutureTask());
|
||||
|
||||
return queryFuture;
|
||||
}
|
||||
@@ -1362,11 +1403,9 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
Transaction newTxn = createTransaction();
|
||||
|
||||
CallableQueryIds<T> call = new CallableQueryIds<T>(this, copy, newTxn);
|
||||
FutureTask<List<Object>> futureTask = new FutureTask<List<Object>>(call);
|
||||
QueryFutureIds<T> queryFuture = new QueryFutureIds<T>(call);
|
||||
|
||||
QueryFutureIds<T> queryFuture = new QueryFutureIds<T>(copy, futureTask);
|
||||
|
||||
backgroundExecutor.execute(futureTask);
|
||||
backgroundExecutor.execute(queryFuture.getFutureTask());
|
||||
|
||||
return queryFuture;
|
||||
}
|
||||
@@ -1376,6 +1415,7 @@ 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());
|
||||
@@ -1387,14 +1427,13 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new transaction solely to execute the findList() at some future time
|
||||
Transaction newTxn = createTransaction();
|
||||
CallableQueryList<T> call = new CallableQueryList<T>(this, query, newTxn);
|
||||
CallableQueryList<T> call = new CallableQueryList<T>(this, spiQuery, newTxn);
|
||||
QueryFutureList<T> queryFuture = new QueryFutureList<T>(call);
|
||||
backgroundExecutor.execute(queryFuture.getFutureTask());
|
||||
|
||||
FutureTask<List<T>> futureTask = new FutureTask<List<T>>(call);
|
||||
|
||||
backgroundExecutor.execute(futureTask);
|
||||
|
||||
return new QueryFutureList<T>(query, futureTask);
|
||||
return queryFuture;
|
||||
}
|
||||
|
||||
public <T> PagingList<T> findPagingList(Query<T> query, Transaction t, int pageSize) {
|
||||
@@ -1921,6 +1960,13 @@ 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++) {
|
||||
@@ -2070,4 +2116,20 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
return jsonContext;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void collectQueryStats(ObjectGraphNode node, long loadedBeanCount, long timeMicros) {
|
||||
|
||||
if (collectQueryStatsByNode) {
|
||||
CObjectGraphNodeStatistics nodeStatistics = objectGraphStats.get(node);
|
||||
if (nodeStatistics == null) {
|
||||
// race condition here but I actually don't care too much if we miss a
|
||||
// few early statistics - especially when the server is warming up etc
|
||||
nodeStatistics = new CObjectGraphNodeStatistics(node);
|
||||
objectGraphStats.put(node, nodeStatistics);
|
||||
}
|
||||
nodeStatistics.add(loadedBeanCount, timeMicros);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -353,6 +353,11 @@ public class DefaultServerFactory implements BootupEbeanManager {
|
||||
if (sequenceFormat != null) {
|
||||
nc.setSequenceFormat(sequenceFormat);
|
||||
}
|
||||
|
||||
String schema = config.getProperty("namingConvention.schema");
|
||||
if (schema != null) {
|
||||
nc.setSchema(schema);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,32 +415,10 @@ public class DefaultServerFactory implements BootupEbeanManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (dsConfig.getHeartbeatSql() == null) {
|
||||
// use default heartbeatSql from the DatabasePlatform
|
||||
String heartbeatSql = getHeartbeatSql(dsConfig.getDriver());
|
||||
dsConfig.setHeartbeatSql(heartbeatSql);
|
||||
}
|
||||
|
||||
DataSourceAlert notify = new SimpleDataSourceAlert();
|
||||
return new DataSourcePool(notify, config.getName(), dsConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a heartbeatSql depending on the jdbc driver name.
|
||||
*/
|
||||
private String getHeartbeatSql(String driver) {
|
||||
if (driver != null) {
|
||||
String d = driver.toLowerCase();
|
||||
if (d.contains("oracle")) {
|
||||
return "select 'x' from dual";
|
||||
}
|
||||
if (d.contains(".h2.") || d.contains(".mysql.") || d.contains("postgre")) {
|
||||
return "select 1";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the autoCommit and Transaction Isolation levels of the DataSource.
|
||||
* <p>
|
||||
|
||||
@@ -69,8 +69,6 @@ public class InternalConfiguration {
|
||||
|
||||
private final BeanDescriptorManager beanDescriptorManager;
|
||||
|
||||
private final DebugLazyLoad debugLazyLoad;
|
||||
|
||||
private final TransactionManager transactionManager;
|
||||
|
||||
private final TransactionScopeManager transactionScopeManager;
|
||||
@@ -115,23 +113,18 @@ public class InternalConfiguration {
|
||||
this.beanDescriptorManager = new BeanDescriptorManager(this);
|
||||
beanDescriptorManager.deploy();
|
||||
|
||||
this.debugLazyLoad = new DebugLazyLoad(serverConfig.isDebugLazyLoad());
|
||||
|
||||
this.transactionManager = new TransactionManager(clusterManager, backgroundExecutor,
|
||||
serverConfig, beanDescriptorManager, this.getBootupClasses());
|
||||
|
||||
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder,
|
||||
backgroundExecutor);
|
||||
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder, backgroundExecutor);
|
||||
|
||||
ExternalTransactionManager externalTransactionManager = serverConfig
|
||||
.getExternalTransactionManager();
|
||||
ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager();
|
||||
if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) {
|
||||
externalTransactionManager = new JtaTransactionManager();
|
||||
}
|
||||
if (externalTransactionManager != null) {
|
||||
externalTransactionManager.setTransactionManager(transactionManager);
|
||||
this.transactionScopeManager = new ExternalTransactionScopeManager(transactionManager,
|
||||
externalTransactionManager);
|
||||
this.transactionScopeManager = new ExternalTransactionScopeManager(transactionManager, externalTransactionManager);
|
||||
logger.info("Using Transaction Manager [" + externalTransactionManager.getClass() + "]");
|
||||
} else {
|
||||
this.transactionScopeManager = new DefaultTransactionScopeManager(transactionManager);
|
||||
@@ -170,7 +163,6 @@ public class InternalConfiguration {
|
||||
}
|
||||
|
||||
public Persister createPersister(SpiEbeanServer server) {
|
||||
|
||||
return new DefaultPersister(server, binder, beanDescriptorManager, pstmtBatch);
|
||||
}
|
||||
|
||||
@@ -246,10 +238,6 @@ public class InternalConfiguration {
|
||||
return clusterManager;
|
||||
}
|
||||
|
||||
public DebugLazyLoad getDebugLazyLoad() {
|
||||
return debugLazyLoad;
|
||||
}
|
||||
|
||||
public SpiBackgroundExecutor getBackgroundExecutor() {
|
||||
return backgroundExecutor;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.event.BeanFinder;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.BeanIdList;
|
||||
import com.avaje.ebeaninternal.api.HashQuery;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlan;
|
||||
import com.avaje.ebeaninternal.api.LoadContext;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
@@ -49,9 +51,9 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
|
||||
private PersistenceContext persistenceContext;
|
||||
|
||||
private Integer cacheKey;
|
||||
private HashQuery cacheKey;
|
||||
|
||||
private int queryPlanHash;
|
||||
private HashQueryPlan queryPlanHash;
|
||||
|
||||
/**
|
||||
* Flag set if background fetching taking place. In this case the transaction
|
||||
@@ -78,10 +80,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
graphContext.registerSecondaryQueries(query);
|
||||
}
|
||||
|
||||
public void setTotalHits(int totalHits) {
|
||||
query.setTotalHits(totalHits);
|
||||
}
|
||||
|
||||
public void executeSecondaryQueries(int defaultQueryBatch) {
|
||||
graphContext.executeSecondaryQueries(this, defaultQueryBatch);
|
||||
}
|
||||
@@ -332,7 +330,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
* with just the bind variables changing.
|
||||
* </p>
|
||||
*/
|
||||
public int getQueryPlanHash() {
|
||||
public HashQueryPlan getQueryPlanHash() {
|
||||
return queryPlanHash;
|
||||
}
|
||||
|
||||
@@ -356,14 +354,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
return null;
|
||||
}
|
||||
|
||||
if (query.getType() == null) {
|
||||
// the query plan and bind values must be the same
|
||||
cacheKey = Integer.valueOf(query.queryHash());
|
||||
|
||||
} else {
|
||||
// additionally the return type (List/Set/Map) must be the same
|
||||
cacheKey = Integer.valueOf(31 * query.queryHash() + query.getType().hashCode());
|
||||
}
|
||||
cacheKey = query.queryHash();
|
||||
|
||||
// TODO: Sort out returning BeanCollection from L2 cache
|
||||
return null;
|
||||
@@ -389,4 +380,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
}
|
||||
}
|
||||
|
||||
public void flushPersistenceContextOnIterate() {
|
||||
beanDescriptor.flushPersistenceContextOnIterate(persistenceContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -176,23 +176,26 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
return beanPersistListener != null;
|
||||
}
|
||||
|
||||
public void notifyCache() {
|
||||
if (notifyCache) {
|
||||
switch (type) {
|
||||
case INSERT:
|
||||
beanDescriptor.cacheInsert(idValue, this);
|
||||
break;
|
||||
case UPDATE:
|
||||
beanDescriptor.cacheUpdate(idValue, this);
|
||||
break;
|
||||
case DELETE:
|
||||
beanDescriptor.cacheDelete(idValue, this);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid type "+type);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Notify/Update the local L2 cache after the transaction has successfully committed.
|
||||
*/
|
||||
public void notifyCache() {
|
||||
if (notifyCache) {
|
||||
switch (type) {
|
||||
case INSERT:
|
||||
beanDescriptor.cacheInsert(idValue, this);
|
||||
break;
|
||||
case UPDATE:
|
||||
beanDescriptor.cacheUpdate(idValue, this);
|
||||
break;
|
||||
case DELETE:
|
||||
// Bean deleted from cache early via postDelete()
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid type " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addToPersistMap(BeanPersistIdMap beanPersistMap) {
|
||||
|
||||
@@ -510,6 +513,14 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
}
|
||||
}
|
||||
|
||||
public void postDelete() {
|
||||
|
||||
// Delete the bean from the PersistenceContent
|
||||
transaction.getPersistenceContext().clear(beanDescriptor.getBeanType(), idValue);
|
||||
// Delete from cache early even if transaction fails
|
||||
beanDescriptor.cacheDelete(idValue, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post processing.
|
||||
*/
|
||||
|
||||
@@ -1,173 +1,71 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebeaninternal.api.ClassUtil;
|
||||
import com.avaje.ebeaninternal.server.lib.util.Dnode;
|
||||
import com.avaje.ebeaninternal.server.lib.util.DnodeReader;
|
||||
import com.avaje.ebeaninternal.server.util.ClassPathReader;
|
||||
import com.avaje.ebeaninternal.server.util.DefaultClassPathReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Used to read the orm.xml and ebean-orm.xml configuration files.
|
||||
*
|
||||
*
|
||||
* @author rbygrave
|
||||
* @author Richard Vowles - http://plus.google.com/RichardVowles
|
||||
*/
|
||||
public class XmlConfigLoader {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(XmlConfigLoader.class);
|
||||
|
||||
private final ClassPathReader classPathReader;
|
||||
private static final Logger logger = LoggerFactory.getLogger(XmlConfigLoader.class);
|
||||
|
||||
private final Object[] classPaths;
|
||||
|
||||
|
||||
public XmlConfigLoader(ClassLoader classLoader){
|
||||
|
||||
if (classLoader == null) {
|
||||
classLoader = getClass().getClassLoader();
|
||||
}
|
||||
|
||||
String cn = GlobalProperties.get("ebean.classpathreader", null);
|
||||
if (cn != null){
|
||||
// use a user defined classPathReader
|
||||
logger.info("Using ["+cn+"] to read the searchable class path");
|
||||
this.classPathReader = (ClassPathReader)ClassUtil.newInstance(cn, this.getClass());
|
||||
} else {
|
||||
this.classPathReader = new DefaultClassPathReader();
|
||||
}
|
||||
|
||||
this.classPaths = classPathReader.readPath(classLoader);
|
||||
}
|
||||
|
||||
public XmlConfig load() {
|
||||
List<Dnode> ormXml = search("META-INF/orm.xml");
|
||||
List<Dnode> ebeanOrmXml = search("META-INF/ebean-orm.xml");
|
||||
|
||||
return new XmlConfig(ormXml, ebeanOrmXml);
|
||||
}
|
||||
|
||||
public List<Dnode> search(String searchFor) {
|
||||
|
||||
ArrayList<Dnode> xmlList = new ArrayList<Dnode>();
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
String charsetName = Charset.defaultCharset().name();
|
||||
public XmlConfigLoader(ClassLoader classLoader) {
|
||||
|
||||
for (int h = 0; h < classPaths.length; h++) {
|
||||
if (classLoader == null) {
|
||||
classLoader = getClass().getClassLoader();
|
||||
}
|
||||
|
||||
try {
|
||||
// for each class path ...
|
||||
File classPath;
|
||||
if (URL.class.isInstance(classPaths[h])) {
|
||||
classPath = new File(((URL) classPaths[h]).getFile());
|
||||
} else {
|
||||
classPath = new File(classPaths[h].toString());
|
||||
}
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
// URL Decode the path replacing %20 to space characters.
|
||||
String path = URLDecoder.decode(classPath.getAbsolutePath(), charsetName);
|
||||
public XmlConfig load() {
|
||||
List<Dnode> ormXml = search("META-INF/orm.xml");
|
||||
List<Dnode> ebeanOrmXml = search("META-INF/ebean-orm.xml");
|
||||
|
||||
classPath = new File(path);
|
||||
return new XmlConfig(ormXml, ebeanOrmXml);
|
||||
}
|
||||
|
||||
if (classPath.isDirectory()) {
|
||||
checkDir(searchFor, xmlList, classPath);
|
||||
public List<Dnode> search(String resourceName) {
|
||||
ArrayList<Dnode> xmlList = new ArrayList<Dnode>();
|
||||
|
||||
} else if (classPath.getName().endsWith(".jar") || classPath.getName().endsWith(".war")) {
|
||||
checkJar(searchFor, xmlList, classPath);
|
||||
|
||||
} else {
|
||||
// this is not expected
|
||||
String msg = "Not a Jar or Directory? " + classPath.getAbsolutePath();
|
||||
logger.error(msg);
|
||||
}
|
||||
try {
|
||||
Enumeration<URL> resources = classLoader.getResources(resourceName);
|
||||
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
while (resources.hasMoreElements()) {
|
||||
URL url = resources.nextElement();
|
||||
|
||||
return xmlList;
|
||||
|
||||
}
|
||||
InputStream is = url.openStream();
|
||||
processInputStream(xmlList, is);
|
||||
is.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Unable to find resources {}", resourceName);
|
||||
}
|
||||
|
||||
private void processInputStream(ArrayList<Dnode> xmlList, InputStream is) throws IOException {
|
||||
|
||||
DnodeReader reader = new DnodeReader();
|
||||
Dnode xmlDoc = reader.parseXml(is);
|
||||
is.close();
|
||||
|
||||
xmlList.add(xmlDoc);
|
||||
}
|
||||
|
||||
private void checkFile(String searchFor, ArrayList<Dnode> xmlList, File dir) throws IOException {
|
||||
return xmlList;
|
||||
}
|
||||
|
||||
File f = new File(dir, searchFor);
|
||||
if (f.exists()){
|
||||
FileInputStream fis = new FileInputStream(f);
|
||||
BufferedInputStream is = new BufferedInputStream(fis);
|
||||
processInputStream(xmlList, is);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDir(String searchFor, ArrayList<Dnode> xmlList, File dir) throws IOException {
|
||||
private void processInputStream(ArrayList<Dnode> xmlList, InputStream is) throws IOException {
|
||||
|
||||
checkFile(searchFor, xmlList, dir);
|
||||
|
||||
if (dir.getPath().endsWith("classes")) {
|
||||
// see if this is part of webapp and look for META-INF/searchFor
|
||||
// relative to the WEB-INF/classes directory
|
||||
File parent = dir.getParentFile();
|
||||
if (parent != null && parent.getPath().endsWith("WEB-INF")){
|
||||
parent = parent.getParentFile();
|
||||
if (parent != null){
|
||||
File metaInf = new File(parent, "META-INF");
|
||||
if (metaInf.exists()){
|
||||
checkFile(searchFor, xmlList, metaInf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkJar(String searchFor, ArrayList<Dnode> xmlList, File classPath) throws IOException {
|
||||
|
||||
String fileName = classPath.getName();
|
||||
if (fileName.toLowerCase().startsWith("surefire")){
|
||||
return;
|
||||
}
|
||||
JarFile module = null;
|
||||
try {
|
||||
module = new JarFile(classPath);
|
||||
ZipEntry entry = module.getEntry(searchFor);
|
||||
if (entry != null){
|
||||
InputStream is = module.getInputStream(entry);
|
||||
processInputStream(xmlList, is);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.info("Unable to check jar file "+fileName+" for ebean-orm.xml");
|
||||
} finally {
|
||||
if (module != null){
|
||||
module.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
DnodeReader reader = new DnodeReader();
|
||||
Dnode xmlDoc = reader.parseXml(is);
|
||||
is.close();
|
||||
|
||||
|
||||
xmlList.add(xmlDoc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,136 +21,130 @@ import org.slf4j.LoggerFactory;
|
||||
public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CreateTableColumnVisitor.class);
|
||||
|
||||
private final DdlGenContext ctx;
|
||||
|
||||
private final DbDdlSyntax ddl;
|
||||
private final DdlGenContext ctx;
|
||||
|
||||
private final CreateTableVisitor parent;
|
||||
private final DbDdlSyntax ddl;
|
||||
|
||||
public CreateTableColumnVisitor(CreateTableVisitor parent, DdlGenContext ctx) {
|
||||
this.parent = parent;
|
||||
this.ctx = ctx;
|
||||
this.ddl = ctx.getDdlSyntax();
|
||||
}
|
||||
private final CreateTableVisitor parent;
|
||||
|
||||
public CreateTableColumnVisitor(CreateTableVisitor parent, DdlGenContext ctx) {
|
||||
this.parent = parent;
|
||||
this.ctx = ctx;
|
||||
this.ddl = ctx.getDdlSyntax();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMany(BeanPropertyAssocMany<?> p) {
|
||||
if (p.isManyToMany()){
|
||||
if (p.getMappedBy() != null) {
|
||||
// only create on other 'owning' side
|
||||
|
||||
} else {
|
||||
TableJoin intersectionTableJoin = p.getIntersectionTableJoin();
|
||||
|
||||
// check if the intersection table has already been created
|
||||
String intTable = intersectionTableJoin.getTable();
|
||||
if (ctx.isProcessIntersectionTable(intTable)){
|
||||
// build the create table and fkey constraints
|
||||
// putting the DDL into ctx for later output as we are
|
||||
// in the middle of rendering the create table DDL
|
||||
new CreateIntersectionTable(ctx, p).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void visitMany(BeanPropertyAssocMany<?> p) {
|
||||
if (p.isManyToMany()) {
|
||||
if (p.getMappedBy() != null) {
|
||||
// only create on other 'owning' side
|
||||
|
||||
|
||||
public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) {
|
||||
visitScalar(p);
|
||||
}
|
||||
} else {
|
||||
TableJoin intersectionTableJoin = p.getIntersectionTableJoin();
|
||||
|
||||
|
||||
public void visitCompound(BeanPropertyCompound p) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
|
||||
|
||||
visitScalar(p);
|
||||
}
|
||||
|
||||
private StringBuilder createUniqueConstraintBuffer(String table, String column) {
|
||||
|
||||
String uqConstraintName = "uq_"+ table+ "_"+column;
|
||||
|
||||
if (uqConstraintName.length() > ddl.getMaxConstraintNameLength()){
|
||||
uqConstraintName = uqConstraintName.substring(0, ddl.getMaxConstraintNameLength());
|
||||
// check if the intersection table has already been created
|
||||
String intTable = intersectionTableJoin.getTable();
|
||||
if (ctx.isProcessIntersectionTable(intTable)) {
|
||||
// build the create table and fkey constraints
|
||||
// putting the DDL into ctx for later output as we are
|
||||
// in the middle of rendering the create table DDL
|
||||
new CreateIntersectionTable(ctx, p).build();
|
||||
}
|
||||
|
||||
uqConstraintName = ctx.removeQuotes(uqConstraintName);
|
||||
uqConstraintName = StringHelper.replaceString(uqConstraintName, " ", "_");
|
||||
|
||||
StringBuilder constraintExpr = new StringBuilder();
|
||||
constraintExpr.append("constraint ")
|
||||
.append(uqConstraintName)
|
||||
.append(" unique (");
|
||||
|
||||
return constraintExpr;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOneImported(BeanPropertyAssocOne<?> p) {
|
||||
}
|
||||
|
||||
public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) {
|
||||
visitScalar(p);
|
||||
}
|
||||
|
||||
ImportedId importedId = p.getImportedId();
|
||||
public void visitCompound(BeanPropertyCompound p) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
TableJoinColumn[] columns = p.getTableJoin().columns();
|
||||
if (columns.length == 0){
|
||||
String msg = "No join columns for "+p.getFullBeanName();
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
|
||||
StringBuilder constraintExpr = createUniqueConstraintBuffer(p.getBeanDescriptor().getBaseTable(), columns[0].getLocalDbColumn());
|
||||
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
|
||||
String dbCol = columns[i].getLocalDbColumn();
|
||||
@Override
|
||||
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
|
||||
|
||||
if (i > 0){
|
||||
constraintExpr.append(", ");
|
||||
}
|
||||
constraintExpr.append(dbCol);
|
||||
|
||||
if (parent.isDbColumnWritten(dbCol)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
parent.writeColumnName(dbCol, p);
|
||||
visitScalar(p);
|
||||
}
|
||||
|
||||
BeanProperty importedProperty = importedId.findMatchImport(dbCol);
|
||||
if (importedProperty != null) {
|
||||
private StringBuilder createUniqueConstraintBuffer(String table, String column) {
|
||||
|
||||
String columnDefn = ctx.getColumnDefn(importedProperty);
|
||||
ctx.write(columnDefn);
|
||||
String uqConstraintName = "uq_" + table + "_" + column;
|
||||
|
||||
} else {
|
||||
throw new RuntimeException("Imported BeanProperty not found?");
|
||||
}
|
||||
if (uqConstraintName.length() > ddl.getMaxConstraintNameLength()) {
|
||||
uqConstraintName = uqConstraintName.substring(0, ddl.getMaxConstraintNameLength());
|
||||
}
|
||||
|
||||
if (!p.isNullable()) {
|
||||
ctx.write(" not null");
|
||||
}
|
||||
ctx.write(",").writeNewLine();
|
||||
}
|
||||
constraintExpr.append(")");
|
||||
|
||||
if (p.isOneToOne()){
|
||||
if (ddl.isAddOneToOneUniqueContraint()){
|
||||
parent.addUniqueConstraint( constraintExpr.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
uqConstraintName = ctx.removeQuotes(uqConstraintName);
|
||||
uqConstraintName = StringHelper.replaceString(uqConstraintName, " ", "_");
|
||||
|
||||
StringBuilder constraintExpr = new StringBuilder();
|
||||
constraintExpr.append("constraint ").append(uqConstraintName).append(" unique (");
|
||||
|
||||
return constraintExpr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOneImported(BeanPropertyAssocOne<?> p) {
|
||||
|
||||
ImportedId importedId = p.getImportedId();
|
||||
|
||||
TableJoinColumn[] columns = p.getTableJoin().columns();
|
||||
if (columns.length == 0) {
|
||||
String msg = "No join columns for " + p.getFullBeanName();
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
|
||||
StringBuilder constraintExpr = createUniqueConstraintBuffer(p.getBeanDescriptor().getBaseTable(), columns[0].getLocalDbColumn());
|
||||
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
|
||||
String dbCol = columns[i].getLocalDbColumn();
|
||||
|
||||
if (i > 0) {
|
||||
constraintExpr.append(", ");
|
||||
}
|
||||
constraintExpr.append(dbCol);
|
||||
|
||||
if (parent.isDbColumnWritten(dbCol)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
parent.writeColumnName(dbCol, p);
|
||||
|
||||
BeanProperty importedProperty = importedId.findMatchImport(dbCol);
|
||||
if (importedProperty != null) {
|
||||
|
||||
String columnDefn = ctx.getColumnDefn(importedProperty);
|
||||
ctx.write(columnDefn);
|
||||
|
||||
} else {
|
||||
throw new RuntimeException("Imported BeanProperty not found?");
|
||||
}
|
||||
|
||||
if (!p.isNullable()) {
|
||||
ctx.write(" not null");
|
||||
}
|
||||
ctx.write(",").writeNewLine();
|
||||
}
|
||||
constraintExpr.append(")");
|
||||
|
||||
if (p.isOneToOne()) {
|
||||
if (ddl.isAddOneToOneUniqueContraint()) {
|
||||
parent.addUniqueConstraint(constraintExpr.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitScalar(BeanProperty p) {
|
||||
|
||||
if (p.isSecondaryTable()) {
|
||||
return;
|
||||
}
|
||||
if (p.isSecondaryTable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (parent.isDbColumnWritten(p.getDbColumn())) {
|
||||
return;
|
||||
@@ -161,7 +155,8 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
|
||||
String columnDefn = ctx.getColumnDefn(p);
|
||||
ctx.write(columnDefn);
|
||||
|
||||
if (isIdentity(p)) {
|
||||
boolean identity = isIdentity(p);
|
||||
if (identity) {
|
||||
writeIdentity();
|
||||
}
|
||||
|
||||
@@ -172,23 +167,25 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
|
||||
ctx.write(" not null");
|
||||
}
|
||||
|
||||
if (p.isUnique() && !p.isId()){
|
||||
parent.addUniqueConstraint(createUniqueConstraint(p));
|
||||
}
|
||||
if (identity) {
|
||||
writeIdentitySuffix();
|
||||
}
|
||||
|
||||
if (p.isUnique() && !p.isId()) {
|
||||
parent.addUniqueConstraint(createUniqueConstraint(p));
|
||||
}
|
||||
|
||||
parent.addCheckConstraint(p);
|
||||
|
||||
ctx.write(",").writeNewLine();
|
||||
}
|
||||
|
||||
private String createUniqueConstraint(BeanProperty p) {
|
||||
|
||||
StringBuilder expr = createUniqueConstraintBuffer(p.getBeanDescriptor().getBaseTable(), p.getDbColumn());
|
||||
|
||||
expr.append(p.getDbColumn()).append(")");
|
||||
|
||||
return expr.toString();
|
||||
}
|
||||
private String createUniqueConstraint(BeanProperty p) {
|
||||
|
||||
StringBuilder expr = createUniqueConstraintBuffer(p.getBeanDescriptor().getBaseTable(), p.getDbColumn());
|
||||
|
||||
expr.append(p.getDbColumn()).append(")");
|
||||
return expr.toString();
|
||||
}
|
||||
|
||||
protected void writeIdentity() {
|
||||
String identity = ddl.getIdentity();
|
||||
@@ -197,27 +194,33 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isIdentity(BeanProperty p) {
|
||||
|
||||
if (p.isId()) {
|
||||
try {
|
||||
IdType idType = p.getBeanDescriptor().getIdType();
|
||||
|
||||
if (idType.equals(IdType.IDENTITY)){
|
||||
|
||||
int jdbcType = p.getScalarType().getJdbcType();
|
||||
if (jdbcType == Types.INTEGER || jdbcType == Types.BIGINT || jdbcType == Types.SMALLINT) {
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e){
|
||||
String msg = "Error determining identity on property "+p.getFullBeanName();
|
||||
logger.error(msg, e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
protected void writeIdentitySuffix() {
|
||||
String identity = ddl.getIdentitySuffix();
|
||||
if (identity != null && identity.length() > 0) {
|
||||
ctx.write(" ").write(identity);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isIdentity(BeanProperty p) {
|
||||
|
||||
if (p.isId()) {
|
||||
try {
|
||||
IdType idType = p.getBeanDescriptor().getIdType();
|
||||
|
||||
if (idType.equals(IdType.IDENTITY)) {
|
||||
|
||||
int jdbcType = p.getScalarType().getJdbcType();
|
||||
if (jdbcType == Types.INTEGER || jdbcType == Types.BIGINT || jdbcType == Types.SMALLINT) {
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
String msg = "Error determining identity on property " + p.getFullBeanName();
|
||||
logger.error(msg, e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,75 +31,75 @@ import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
public class DdlGenerator implements SpiEbeanPlugin {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DdlGenerator.class);
|
||||
|
||||
private SpiEbeanServer server;
|
||||
|
||||
private DatabasePlatform dbPlatform;
|
||||
private SpiEbeanServer server;
|
||||
|
||||
private int summaryLength = 80;
|
||||
private DatabasePlatform dbPlatform;
|
||||
|
||||
private boolean generateDdl;
|
||||
private boolean runDdl;
|
||||
private int summaryLength = 80;
|
||||
|
||||
private String dropContent;
|
||||
private String createContent;
|
||||
private boolean generateDdl;
|
||||
private boolean runDdl;
|
||||
|
||||
private NamingConvention namingConvention;
|
||||
private String dropContent;
|
||||
private String createContent;
|
||||
|
||||
public void setup(SpiEbeanServer server, DatabasePlatform dbPlatform, ServerConfig serverConfig) {
|
||||
this.server = server;
|
||||
this.dbPlatform = dbPlatform;
|
||||
this.generateDdl = serverConfig.isDdlGenerate();
|
||||
this.runDdl = serverConfig.isDdlRun();
|
||||
this.namingConvention = serverConfig.getNamingConvention();
|
||||
}
|
||||
private NamingConvention namingConvention;
|
||||
|
||||
/**
|
||||
* Generate the DDL and then run the DDL based on property settings (ebean.ddl.generate and ebean.ddl.run etc).
|
||||
*/
|
||||
public void execute(boolean online) {
|
||||
generateDdl();
|
||||
if (online){
|
||||
runDdl();
|
||||
}
|
||||
}
|
||||
public void setup(SpiEbeanServer server, DatabasePlatform dbPlatform, ServerConfig serverConfig) {
|
||||
this.server = server;
|
||||
this.dbPlatform = dbPlatform;
|
||||
this.generateDdl = serverConfig.isDdlGenerate();
|
||||
this.runDdl = serverConfig.isDdlRun();
|
||||
this.namingConvention = serverConfig.getNamingConvention();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the DDL drop and create scripts if the properties have been set.
|
||||
*/
|
||||
public void generateDdl() {
|
||||
if (generateDdl) {
|
||||
writeDrop(getDropFileName());
|
||||
writeCreate(getCreateFileName());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Generate the DDL and then run the DDL based on property settings
|
||||
* (ebean.ddl.generate and ebean.ddl.run etc).
|
||||
*/
|
||||
public void execute(boolean online) {
|
||||
generateDdl();
|
||||
if (online) {
|
||||
runDdl();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the DDL drop and DDL create scripts if properties have been set.
|
||||
*/
|
||||
public void runDdl() {
|
||||
/**
|
||||
* Generate the DDL drop and create scripts if the properties have been set.
|
||||
*/
|
||||
public void generateDdl() {
|
||||
if (generateDdl) {
|
||||
writeDrop(getDropFileName());
|
||||
writeCreate(getCreateFileName());
|
||||
}
|
||||
}
|
||||
|
||||
if (runDdl) {
|
||||
try {
|
||||
if (dropContent == null){
|
||||
dropContent = readFile(getDropFileName());
|
||||
}
|
||||
if (createContent == null){
|
||||
createContent = readFile(getCreateFileName());
|
||||
}
|
||||
runScript(true, dropContent);
|
||||
runScript(false, createContent);
|
||||
/**
|
||||
* Run the DDL drop and DDL create scripts if properties have been set.
|
||||
*/
|
||||
public void runDdl() {
|
||||
|
||||
} catch (IOException e){
|
||||
String msg = "Error reading drop/create script from file system";
|
||||
throw new RuntimeException(msg, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (runDdl) {
|
||||
try {
|
||||
if (dropContent == null) {
|
||||
dropContent = readFile(getDropFileName());
|
||||
}
|
||||
if (createContent == null) {
|
||||
createContent = readFile(getCreateFileName());
|
||||
}
|
||||
runScript(true, dropContent);
|
||||
runScript(false, createContent);
|
||||
|
||||
} catch (IOException e) {
|
||||
String msg = "Error reading drop/create script from file system";
|
||||
throw new RuntimeException(msg, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeDrop(String dropFile) {
|
||||
|
||||
// String c = generateDropDdl();
|
||||
try {
|
||||
String c = generateDropDdl();
|
||||
writeFile(dropFile, c);
|
||||
@@ -110,224 +110,230 @@ public class DdlGenerator implements SpiEbeanPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeCreate(String createFile) {
|
||||
protected void writeCreate(String createFile) {
|
||||
|
||||
// String c = generateCreateDdl();
|
||||
try {
|
||||
String c = generateCreateDdl();
|
||||
writeFile(createFile, c);
|
||||
try {
|
||||
String c = generateCreateDdl();
|
||||
writeFile(createFile, c);
|
||||
|
||||
} catch (IOException e) {
|
||||
String msg = "Error generating Create DDL";
|
||||
throw new PersistenceException(msg, e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
String msg = "Error generating Create DDL";
|
||||
throw new PersistenceException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
public String generateDropDdl() {
|
||||
public String generateDropDdl() {
|
||||
|
||||
DdlGenContext ctx = createContext();
|
||||
DdlGenContext ctx = createContext();
|
||||
|
||||
DropTableVisitor drop = new DropTableVisitor(ctx);
|
||||
VisitorUtil.visit(server, drop);
|
||||
DropTableVisitor drop = new DropTableVisitor(ctx);
|
||||
VisitorUtil.visit(server, drop);
|
||||
|
||||
DropSequenceVisitor dropSequence = new DropSequenceVisitor(ctx);
|
||||
VisitorUtil.visit(server, dropSequence);
|
||||
DropSequenceVisitor dropSequence = new DropSequenceVisitor(ctx);
|
||||
VisitorUtil.visit(server, dropSequence);
|
||||
|
||||
ctx.flush();
|
||||
dropContent = ctx.getContent();
|
||||
return dropContent;
|
||||
}
|
||||
ctx.flush();
|
||||
dropContent = ctx.getContent();
|
||||
return dropContent;
|
||||
}
|
||||
|
||||
public String generateCreateDdl() {
|
||||
public String generateCreateDdl() {
|
||||
|
||||
DdlGenContext ctx = createContext();
|
||||
CreateTableVisitor create = new CreateTableVisitor(ctx);
|
||||
VisitorUtil.visit(server, create);
|
||||
DdlGenContext ctx = createContext();
|
||||
CreateTableVisitor create = new CreateTableVisitor(ctx);
|
||||
VisitorUtil.visit(server, create);
|
||||
|
||||
CreateSequenceVisitor createSequence = new CreateSequenceVisitor(ctx);
|
||||
VisitorUtil.visit(server, createSequence);
|
||||
CreateSequenceVisitor createSequence = new CreateSequenceVisitor(ctx);
|
||||
VisitorUtil.visit(server, createSequence);
|
||||
|
||||
AddForeignKeysVisitor fkeys = new AddForeignKeysVisitor(ctx);
|
||||
VisitorUtil.visit(server, fkeys);
|
||||
AddForeignKeysVisitor fkeys = new AddForeignKeysVisitor(ctx);
|
||||
VisitorUtil.visit(server, fkeys);
|
||||
|
||||
ctx.flush();
|
||||
createContent = ctx.getContent();
|
||||
return createContent;
|
||||
}
|
||||
ctx.flush();
|
||||
createContent = ctx.getContent();
|
||||
return createContent;
|
||||
}
|
||||
|
||||
protected String getDropFileName() {
|
||||
return server.getName() + "-drop.sql";
|
||||
}
|
||||
protected String getDropFileName() {
|
||||
return server.getName() + "-drop.sql";
|
||||
}
|
||||
|
||||
protected String getCreateFileName() {
|
||||
return server.getName() + "-create.sql";
|
||||
}
|
||||
protected String getCreateFileName() {
|
||||
return server.getName() + "-create.sql";
|
||||
}
|
||||
|
||||
protected DdlGenContext createContext() {
|
||||
return new DdlGenContext(dbPlatform, namingConvention);
|
||||
}
|
||||
protected DdlGenContext createContext() {
|
||||
return new DdlGenContext(dbPlatform, namingConvention);
|
||||
}
|
||||
|
||||
protected void writeFile(String fileName, String fileContent) throws IOException {
|
||||
protected void writeFile(String fileName, String fileContent) throws IOException {
|
||||
|
||||
File f = new File(fileName);
|
||||
File f = new File(fileName);
|
||||
|
||||
FileWriter fw = new FileWriter(f);
|
||||
try {
|
||||
fw.write(fileContent);
|
||||
fw.flush();
|
||||
} finally {
|
||||
fw.close();
|
||||
}
|
||||
}
|
||||
FileWriter fw = new FileWriter(f);
|
||||
try {
|
||||
fw.write(fileContent);
|
||||
fw.flush();
|
||||
} finally {
|
||||
fw.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected String readFile(String fileName) throws IOException {
|
||||
protected String readFile(String fileName) throws IOException {
|
||||
|
||||
File f = new File(fileName);
|
||||
if (!f.exists()){
|
||||
return null;
|
||||
}
|
||||
File f = new File(fileName);
|
||||
if (!f.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder buf = new StringBuilder();
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
||||
FileReader fr = new FileReader(f);
|
||||
LineNumberReader lr = new LineNumberReader(fr);
|
||||
try {
|
||||
String s = null;
|
||||
while ((s = lr.readLine()) != null){
|
||||
buf.append(s).append("\n");
|
||||
}
|
||||
} finally {
|
||||
lr.close();
|
||||
}
|
||||
FileReader fr = new FileReader(f);
|
||||
LineNumberReader lr = new LineNumberReader(fr);
|
||||
try {
|
||||
String s = null;
|
||||
while ((s = lr.readLine()) != null) {
|
||||
buf.append(s).append("\n");
|
||||
}
|
||||
} finally {
|
||||
lr.close();
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all the DDL statements in the script.
|
||||
*/
|
||||
public void runScript(boolean expectErrors, String content) {
|
||||
/**
|
||||
* Execute all the DDL statements in the script.
|
||||
*/
|
||||
public void runScript(boolean expectErrors, String content) {
|
||||
|
||||
StringReader sr = new StringReader(content);
|
||||
List<String> statements = parseStatements(sr);
|
||||
StringReader sr = new StringReader(content);
|
||||
List<String> statements = parseStatements(sr);
|
||||
|
||||
Transaction t = server.createTransaction();
|
||||
try {
|
||||
Connection connection = t.getConnection();
|
||||
Transaction t = server.createTransaction();
|
||||
try {
|
||||
Connection connection = t.getConnection();
|
||||
|
||||
logger.info("Running DDL");
|
||||
logger.info("Running DDL");
|
||||
|
||||
runStatements(expectErrors, statements, connection);
|
||||
runStatements(expectErrors, statements, connection);
|
||||
|
||||
logger.info("Running DDL Complete");
|
||||
logger.info("Running DDL Complete");
|
||||
|
||||
t.commit();
|
||||
t.commit();
|
||||
|
||||
} catch (Exception e){
|
||||
String msg = "Error: "+e.getMessage();
|
||||
throw new PersistenceException(msg, e);
|
||||
} finally {
|
||||
t.end();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
String msg = "Error: " + e.getMessage();
|
||||
throw new PersistenceException(msg, e);
|
||||
} finally {
|
||||
t.end();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the list of statements.
|
||||
*/
|
||||
private void runStatements(boolean expectErrors, List<String> statements, Connection c) {
|
||||
/**
|
||||
* Execute the list of statements.
|
||||
*/
|
||||
private void runStatements(boolean expectErrors, List<String> statements, Connection c) {
|
||||
List<String> noDuplicates = new ArrayList<String>();
|
||||
|
||||
for (int i = 0; i < statements.size(); i++) {
|
||||
String xOfy = (i + 1) + " of " + statements.size();
|
||||
runStatement(expectErrors, xOfy, statements.get(i), c);
|
||||
}
|
||||
}
|
||||
for (String statement : statements) {
|
||||
if (!noDuplicates.contains(statement)) {
|
||||
noDuplicates.add(statement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the statement.
|
||||
*/
|
||||
private void runStatement(boolean expectErrors, String oneOf, String stmt, Connection c) {
|
||||
for (int i = 0; i < noDuplicates.size(); i++) {
|
||||
String xOfy = (i + 1) + " of " + noDuplicates.size();
|
||||
runStatement(expectErrors, xOfy, noDuplicates.get(i), c);
|
||||
}
|
||||
}
|
||||
|
||||
PreparedStatement pstmt = null;
|
||||
try {
|
||||
/**
|
||||
* Execute the statement.
|
||||
*/
|
||||
private void runStatement(boolean expectErrors, String oneOf, String stmt, Connection c) {
|
||||
|
||||
// trim and remove trailing ; or /
|
||||
stmt = stmt.trim();
|
||||
if (stmt.endsWith(";")){
|
||||
stmt = stmt.substring(0, stmt.length()-1);
|
||||
} else if (stmt.endsWith("/")) {
|
||||
stmt = stmt.substring(0, stmt.length()-1);
|
||||
}
|
||||
PreparedStatement pstmt = null;
|
||||
try {
|
||||
|
||||
logger.trace("executing "+oneOf+" "+ getSummary(stmt));
|
||||
// trim and remove trailing ; or /
|
||||
stmt = stmt.trim();
|
||||
if (stmt.endsWith(";")) {
|
||||
stmt = stmt.substring(0, stmt.length() - 1);
|
||||
} else if (stmt.endsWith("/")) {
|
||||
stmt = stmt.substring(0, stmt.length() - 1);
|
||||
}
|
||||
|
||||
pstmt = c.prepareStatement(stmt);
|
||||
pstmt.execute();
|
||||
logger.trace("executing " + oneOf + " " + getSummary(stmt));
|
||||
|
||||
} 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);
|
||||
}
|
||||
} finally {
|
||||
if (pstmt != null){
|
||||
try {
|
||||
pstmt.close();
|
||||
} catch (SQLException e){
|
||||
logger.error("Error closing pstmt", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pstmt = c.prepareStatement(stmt);
|
||||
pstmt.execute();
|
||||
|
||||
/**
|
||||
* Break up the sql in reader into a list of statements using the semi-colon
|
||||
* character;
|
||||
*/
|
||||
protected List<String> parseStatements(StringReader reader) {
|
||||
} 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);
|
||||
}
|
||||
} finally {
|
||||
if (pstmt != null) {
|
||||
try {
|
||||
pstmt.close();
|
||||
} catch (SQLException e) {
|
||||
logger.error("Error closing pstmt", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
BufferedReader br = new BufferedReader(reader);
|
||||
/**
|
||||
* Break up the sql in reader into a list of statements using the semi-colon
|
||||
* character;
|
||||
*/
|
||||
protected List<String> parseStatements(StringReader reader) {
|
||||
|
||||
ArrayList<String> statements = new ArrayList<String>();
|
||||
try {
|
||||
BufferedReader br = new BufferedReader(reader);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String s;
|
||||
while ((s = br.readLine()) != null) {
|
||||
s = s.trim();
|
||||
int semiPos = s.indexOf(';');
|
||||
if (semiPos == -1) {
|
||||
sb.append(s).append(" ");
|
||||
ArrayList<String> statements = new ArrayList<String>();
|
||||
|
||||
} else if (semiPos == s.length()-1) {
|
||||
// semicolon at end of line
|
||||
sb.append(s);
|
||||
statements.add(sb.toString().trim());
|
||||
sb = new StringBuilder();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String s;
|
||||
while ((s = br.readLine()) != null) {
|
||||
s = s.trim();
|
||||
int semiPos = s.indexOf(';');
|
||||
if (semiPos == -1) {
|
||||
sb.append(s).append(" ");
|
||||
|
||||
} else {
|
||||
// semicolon in middle of line
|
||||
String preSemi = s.substring(0, semiPos);
|
||||
sb.append(preSemi);
|
||||
statements.add(sb.toString().trim());
|
||||
sb = new StringBuilder();
|
||||
sb.append(s.substring(semiPos+1));
|
||||
} else if (semiPos == s.length() - 1) {
|
||||
// semicolon at end of line
|
||||
sb.append(s);
|
||||
statements.add(sb.toString().trim());
|
||||
sb = new StringBuilder();
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// semicolon in middle of line
|
||||
String preSemi = s.substring(0, semiPos);
|
||||
sb.append(preSemi);
|
||||
statements.add(sb.toString().trim());
|
||||
sb = new StringBuilder();
|
||||
sb.append(s.substring(semiPos + 1));
|
||||
|
||||
return statements;
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getSummary(String s){
|
||||
if (s.length() > summaryLength){
|
||||
return s.substring(0, summaryLength).trim()+"...";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
return statements;
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getSummary(String s) {
|
||||
if (s.length() > summaryLength) {
|
||||
return s.substring(0, summaryLength).trim() + "...";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,10 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebean.Query.UseIndex;
|
||||
import com.avaje.ebean.SqlUpdate;
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.annotation.ConcurrencyMode;
|
||||
@@ -33,8 +35,11 @@ import com.avaje.ebean.event.BeanFinder;
|
||||
import com.avaje.ebean.event.BeanPersistController;
|
||||
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.ebean.text.TextException;
|
||||
import com.avaje.ebean.text.json.JsonWriteBeanVisitor;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlan;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiUpdatePlan;
|
||||
@@ -59,6 +64,7 @@ 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.query.CQueryPlan;
|
||||
import com.avaje.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import com.avaje.ebeaninternal.server.reflect.BeanReflect;
|
||||
@@ -71,19 +77,17 @@ import com.avaje.ebeaninternal.server.type.TypeManager;
|
||||
import com.avaje.ebeaninternal.util.SortByClause;
|
||||
import com.avaje.ebeaninternal.util.SortByClause.Property;
|
||||
import com.avaje.ebeaninternal.util.SortByClauseParser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Describes Beans including their deployment information.
|
||||
*/
|
||||
public class BeanDescriptor<T> {
|
||||
public class BeanDescriptor<T> implements MetaBeanInfo {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanDescriptor.class);
|
||||
|
||||
private final ConcurrentHashMap<Integer, SpiUpdatePlan> updatePlanCache = new ConcurrentHashMap<Integer, SpiUpdatePlan>();
|
||||
|
||||
private final ConcurrentHashMap<Integer, CQueryPlan> queryPlanCache = new ConcurrentHashMap<Integer, CQueryPlan>();
|
||||
private final ConcurrentHashMap<HashQueryPlan, CQueryPlan> queryPlanCache = new ConcurrentHashMap<HashQueryPlan, CQueryPlan>();
|
||||
|
||||
private final ConcurrentHashMap<String, ElPropertyValue> elGetCache = new ConcurrentHashMap<String, ElPropertyValue>();
|
||||
|
||||
@@ -92,7 +96,7 @@ public class BeanDescriptor<T> {
|
||||
private final ConcurrentHashMap<String, BeanFkeyProperty> fkeyMap = new ConcurrentHashMap<String, BeanFkeyProperty>();
|
||||
|
||||
public enum EntityType {
|
||||
ORM, EMBEDDED, SQL, META, XMLELEMENT
|
||||
ORM, EMBEDDED, SQL
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +105,7 @@ public class BeanDescriptor<T> {
|
||||
private final String serverName;
|
||||
|
||||
/**
|
||||
* Set to true if this is a LDAP domain object.
|
||||
* The nature/type of this bean.
|
||||
*/
|
||||
private final EntityType entityType;
|
||||
|
||||
@@ -341,12 +345,9 @@ public class BeanDescriptor<T> {
|
||||
|
||||
private final String defaultSelectClause;
|
||||
private final Set<String> defaultSelectClauseSet;
|
||||
private final String[] defaultSelectDbArray;
|
||||
|
||||
private final String descriptorId;
|
||||
|
||||
private final UseIndex useIndex;
|
||||
|
||||
private SpiEbeanServer ebeanServer;
|
||||
|
||||
private ServerCache beanCache;
|
||||
@@ -367,7 +368,6 @@ public class BeanDescriptor<T> {
|
||||
this.fullName = InternString.intern(deploy.getFullName());
|
||||
this.descriptorId = descriptorId;
|
||||
|
||||
this.useIndex = deploy.getUseIndex();
|
||||
this.typeManager = typeManager;
|
||||
this.beanType = deploy.getBeanType();
|
||||
this.factoryType = deploy.getFactoryType();
|
||||
@@ -385,7 +385,6 @@ public class BeanDescriptor<T> {
|
||||
|
||||
this.defaultSelectClause = deploy.getDefaultSelectClause();
|
||||
this.defaultSelectClauseSet = deploy.parseDefaultSelectClause(defaultSelectClause);
|
||||
this.defaultSelectDbArray = deploy.getDefaultSelectDbArray(defaultSelectClauseSet);
|
||||
|
||||
this.idType = deploy.getIdType();
|
||||
this.idGenerator = deploy.getIdGenerator();
|
||||
@@ -564,14 +563,6 @@ public class BeanDescriptor<T> {
|
||||
return entityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default strategy for using a lucene index (if an index is
|
||||
* defined on this bean type).
|
||||
*/
|
||||
public UseIndex getUseIndex() {
|
||||
return useIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the Id properties first.
|
||||
* <p>
|
||||
@@ -803,13 +794,6 @@ public class BeanDescriptor<T> {
|
||||
return defaultSelectClauseSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* For LDAP return array of (DB) attributes to include in query by default.
|
||||
*/
|
||||
public String[] getDefaultSelectDbArray() {
|
||||
return defaultSelectDbArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this object is the root level object in its entity
|
||||
* inheritance.
|
||||
@@ -853,8 +837,7 @@ public class BeanDescriptor<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is L2 caching (Lucene or Bean cache) for this bean
|
||||
* type.
|
||||
* Return true if there is L2 bean caching for this bean type.
|
||||
*/
|
||||
public boolean isUsingL2Cache() {
|
||||
return isBeanCaching();
|
||||
@@ -965,11 +948,14 @@ public class BeanDescriptor<T> {
|
||||
|
||||
public void cachePutMany(BeanPropertyAssocMany<?> many, BeanCollection<?> bc, Object parentId) {
|
||||
BeanDescriptor<?> targetDescriptor = many.getTargetDescriptor();
|
||||
Collection<?> actualDetails = bc.getActualDetails();
|
||||
|
||||
ArrayList<Object> idList = new ArrayList<Object>();
|
||||
|
||||
// get the underlying collection of beans (in the List, Set or Map)
|
||||
Collection<?> actualDetails = bc.getActualDetails();
|
||||
for (Object bean : actualDetails) {
|
||||
Object id = targetDescriptor.getId(bean);
|
||||
idList.add(id);
|
||||
// Collect the id values
|
||||
idList.add(targetDescriptor.getId(bean));
|
||||
}
|
||||
CachedManyIds ids = new CachedManyIds(idList);
|
||||
cachePutCachedManyIds(parentId, many.getName(), ids);
|
||||
@@ -1055,7 +1041,7 @@ public class BeanDescriptor<T> {
|
||||
for (int i = 0; i < propertiesOneImported.length; i++) {
|
||||
BeanPropertyAssocMany<?> many = propertiesOneImported[i].getRelationshipProperty();
|
||||
if (many != null) {
|
||||
propertiesOneImported[i].cacheDelete(true, deleteRequest);
|
||||
propertiesOneImported[i].cacheDelete(true, deleteRequest.getBean());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1148,6 +1134,27 @@ public class BeanDescriptor<T> {
|
||||
return new DeployUpdateParser(this).parse(ormUpdateStatement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset) {
|
||||
return collectQueryPlanStatisticsInternal(reset, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlanStatistic> collectAllQueryPlanStatistics(boolean reset) {
|
||||
return collectQueryPlanStatisticsInternal(reset, false);
|
||||
}
|
||||
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatisticsInternal(boolean reset, boolean collectAll) {
|
||||
List<MetaQueryPlanStatistic> list = new ArrayList<MetaQueryPlanStatistic>(queryPlanCache.size());
|
||||
for (CQueryPlan queryPlan : queryPlanCache.values()) {
|
||||
Snapshot snapshot = queryPlan.getSnapshot(reset);
|
||||
if (collectAll || snapshot.getExecutionCount() > 0) {
|
||||
list.add(snapshot);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the statistics on all the query plans.
|
||||
*/
|
||||
@@ -1177,11 +1184,11 @@ public class BeanDescriptor<T> {
|
||||
return queryPlanCache.values().iterator();
|
||||
}
|
||||
|
||||
public CQueryPlan getQueryPlan(Integer key) {
|
||||
public CQueryPlan getQueryPlan(HashQueryPlan key) {
|
||||
return queryPlanCache.get(key);
|
||||
}
|
||||
|
||||
public void putQueryPlan(Integer key, CQueryPlan plan) {
|
||||
public void putQueryPlan(HashQueryPlan key, CQueryPlan plan) {
|
||||
queryPlanCache.put(key, plan);
|
||||
}
|
||||
|
||||
@@ -1318,10 +1325,8 @@ public class BeanDescriptor<T> {
|
||||
public EntityBean createEntityBean() {
|
||||
try {
|
||||
// Note factoryType is used indirectly via beanReflect
|
||||
EntityBean eb = (EntityBean) beanReflect.createEntityBean();
|
||||
|
||||
return eb;
|
||||
|
||||
return (EntityBean) beanReflect.createEntityBean();
|
||||
|
||||
} catch (Exception ex) {
|
||||
throw new PersistenceException(ex);
|
||||
}
|
||||
@@ -2308,6 +2313,9 @@ public class BeanDescriptor<T> {
|
||||
for (int j = 0; j < propertiesNonTransient.length; j++) {
|
||||
propertiesNonTransient[j].jsonWrite(ctx, bean);
|
||||
}
|
||||
for (int j = 0; j < propertiesTransient.length; j++) {
|
||||
propertiesTransient[j].jsonWrite(ctx, bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2372,9 +2380,13 @@ public class BeanDescriptor<T> {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private T createJsonBean() {
|
||||
return (T)createEntityBean();
|
||||
}
|
||||
|
||||
private ReadBeanState jsonReadObject(ReadJsonContext ctx, String path) {
|
||||
|
||||
T bean = (T) createEntityBean();
|
||||
T bean = createJsonBean();
|
||||
ctx.pushBean(bean, path, this);
|
||||
|
||||
do {
|
||||
@@ -2432,5 +2444,13 @@ public class BeanDescriptor<T> {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void flushPersistenceContextOnIterate(PersistenceContext persistenceContext) {
|
||||
persistenceContext.clear(beanType);
|
||||
for (int i = 0; i < propertiesMany.length; i++) {
|
||||
persistenceContext.clear(propertiesMany[i].getBeanDescriptor().getBeanType());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -106,9 +106,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
private final BeanManagerFactory beanManagerFactory;
|
||||
|
||||
private int enhancedClassCount;
|
||||
private int subclassClassCount;
|
||||
private final HashSet<String> subclassedEntities = new HashSet<String>();
|
||||
|
||||
|
||||
private final boolean updateChangesOnly;
|
||||
|
||||
private final BootupClasses bootupClasses;
|
||||
@@ -447,26 +445,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
logger.debug("BeanPersistControllers[" + cc + "] BeanFinders[" + fc + "] BeanPersistListeners[" + lc + "] BeanQueryAdapters[" + qa + "]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Log Warning if mixing subclass and enhancement.
|
||||
* <p>
|
||||
* If enhancement is used for some classes it is expected to be used for all
|
||||
* and vice versa.
|
||||
* </p>
|
||||
*/
|
||||
private void logStatus() {
|
||||
|
||||
String msg = "Entities enhanced[" + enhancedClassCount + "] subclassed[" + subclassClassCount + "]";
|
||||
logger.info(msg);
|
||||
|
||||
if (enhancedClassCount > 0) {
|
||||
if (subclassClassCount > 0) {
|
||||
String subclassEntityNames = subclassedEntities.toString();
|
||||
|
||||
String m = "Mixing enhanced and subclassed entities. Subclassed classes:" + subclassEntityNames;
|
||||
logger.warn(m);
|
||||
}
|
||||
}
|
||||
logger.info("Entities enhanced[" + enhancedClassCount + "]");
|
||||
}
|
||||
|
||||
private <T> BeanDescriptor<T> createEmbedded(Class<T> beanClass) {
|
||||
@@ -610,6 +590,11 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
secondaryPropsJoins(info);
|
||||
}
|
||||
|
||||
// Set inheritance info
|
||||
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
|
||||
setInheritanceInfo(info);
|
||||
}
|
||||
|
||||
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
|
||||
DeployBeanDescriptor<?> deployBeanDescriptor = info.getDescriptor();
|
||||
Integer key = getUniqueHash(deployBeanDescriptor);
|
||||
@@ -617,6 +602,33 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the inheritance info. ~EMG fix for join problem
|
||||
*
|
||||
* @param info the new inheritance info
|
||||
*/
|
||||
private void setInheritanceInfo(DeployBeanInfo<?> info) {
|
||||
for (DeployBeanPropertyAssocOne<?> oneProp : info.getDescriptor().propertiesAssocOne()) {
|
||||
if (!oneProp.isTransient()) {
|
||||
DeployBeanInfo<?> assoc = deplyInfoMap.get(oneProp.getTargetType());
|
||||
|
||||
if (assoc != null){
|
||||
oneProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (DeployBeanPropertyAssocMany<?> manyProp : info.getDescriptor().propertiesAssocMany()) {
|
||||
if (!manyProp.isTransient()) {
|
||||
DeployBeanInfo<?> assoc = deplyInfoMap.get(manyProp.getTargetType());
|
||||
|
||||
if (assoc != null){
|
||||
manyProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Integer getUniqueHash(DeployBeanDescriptor<?> deployBeanDescriptor) {
|
||||
|
||||
int hashCode = deployBeanDescriptor.getFullName().hashCode();
|
||||
@@ -1430,25 +1442,13 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
private void checkEnhanced(DeployBeanDescriptor<?> desc, Class<?> beanClass) {
|
||||
// the bean already implements EntityBean
|
||||
checkInheritedClasses(true, beanClass);
|
||||
|
||||
desc.setFactoryType(beanClass);
|
||||
if (!beanClass.getName().startsWith("com.avaje.ebean.meta")) {
|
||||
enhancedClassCount++;
|
||||
}
|
||||
enhancedClassCount++;
|
||||
}
|
||||
|
||||
private void checkSubclass(DeployBeanDescriptor<?> desc, Class<?> beanClass) {
|
||||
|
||||
checkInheritedClasses(false, beanClass);
|
||||
desc.checkReadAndWriteMethods();
|
||||
|
||||
EntityType entityType = desc.getEntityType();
|
||||
if (EntityType.XMLELEMENT.equals(entityType)) {
|
||||
desc.setFactoryType(beanClass);
|
||||
|
||||
} else {
|
||||
throw new PersistenceException("Entity type "+beanClass+" is not an enhanced entity bean. Subclassing is not longer supported in Ebean");
|
||||
}
|
||||
throw new PersistenceException("Entity type "+beanClass+" is not an enhanced entity bean. Subclassing is not longer supported in Ebean");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -63,6 +63,11 @@ public final class BeanFkeyProperty implements ElPropertyValue {
|
||||
public boolean isDeployOnly() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsFormulaWithJoin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false.
|
||||
|
||||
@@ -36,6 +36,9 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal add bypassing any modify listening.
|
||||
*/
|
||||
public void add(BeanCollection<?> collection, Object bean) {
|
||||
collection.internalAdd(bean);
|
||||
}
|
||||
@@ -77,12 +80,21 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
|
||||
}
|
||||
|
||||
public Object createEmpty(boolean vanilla) {
|
||||
return vanilla ? new ArrayList<T>() : new BeanList<T>();
|
||||
if (vanilla) {
|
||||
return new ArrayList<T>();
|
||||
}
|
||||
BeanList<T> beanList = new BeanList<T>();
|
||||
if (many != null) {
|
||||
beanList.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
return beanList;
|
||||
}
|
||||
|
||||
public BeanCollection<T> createReference(Object parentBean, String propertyName) {
|
||||
|
||||
return new BeanList<T>(loader, parentBean, propertyName);
|
||||
BeanList<T> beanList = new BeanList<T>(loader, parentBean, propertyName);
|
||||
beanList.setModifyListening(many.getModifyListenMode());
|
||||
return beanList;
|
||||
}
|
||||
|
||||
public void refresh(EbeanServer server, Query<?> query, Transaction t, Object parentBean) {
|
||||
|
||||
@@ -101,22 +101,34 @@ public final class BeanMapHelp<T> implements BeanCollectionHelp<T> {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Object createEmpty(boolean vanilla) {
|
||||
return vanilla ? new LinkedHashMap() : new BeanMap();
|
||||
if (vanilla) {
|
||||
return new LinkedHashMap();
|
||||
}
|
||||
BeanMap beanMap = new BeanMap();
|
||||
if (many != null) {
|
||||
beanMap.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
return beanMap;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
/**
|
||||
* Internal add bypassing any modify listening.
|
||||
*/
|
||||
public void add(BeanCollection<?> collection, Object bean) {
|
||||
|
||||
Object keyValue = beanProperty.getValueIntercept(bean);
|
||||
|
||||
Map<Object, Object> map = (Map<Object, Object>) collection;
|
||||
map.put(keyValue, bean);
|
||||
((BeanMap<?,?>) collection).internalPut(keyValue, bean);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public BeanCollection<T> createReference(Object parentBean, String propertyName) {
|
||||
|
||||
return new BeanMap(loader, parentBean, propertyName);
|
||||
BeanMap beanMap = new BeanMap(loader, parentBean, propertyName);
|
||||
if (many != null) {
|
||||
beanMap.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
return beanMap;
|
||||
}
|
||||
|
||||
public void refresh(EbeanServer server, Query<?> query, Transaction t, Object parentBean) {
|
||||
|
||||
@@ -837,6 +837,12 @@ public class BeanProperty implements ElPropertyValue {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean containsFormulaWithJoin() {
|
||||
return formula && sqlFormulaJoin != null;
|
||||
}
|
||||
|
||||
public boolean containsManySince(String sinceProperty) {
|
||||
return containsMany();
|
||||
}
|
||||
@@ -1043,19 +1049,6 @@ public class BeanProperty implements ElPropertyValue {
|
||||
return value;
|
||||
}
|
||||
|
||||
// private ArrayList<LuceneIndex> luceneIndexes;
|
||||
//
|
||||
// public void registerLuceneIndex(LuceneIndex luceneIndex) {
|
||||
// if (luceneIndexes == null) {
|
||||
// luceneIndexes = new ArrayList<LuceneIndex>();
|
||||
// }
|
||||
// luceneIndexes.add(luceneIndex);
|
||||
// }
|
||||
//
|
||||
// public boolean isDeltaRequired() {
|
||||
// return true;//luceneIndexes != null;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Return true if by default this property is set to fetch eager.
|
||||
* Lob's usually default to fetch lazy.
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@@ -139,11 +140,23 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
} else {
|
||||
delStmt = "delete from "+targetDescriptor.getBaseTable()+" where ";
|
||||
}
|
||||
deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false);
|
||||
deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true);
|
||||
deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false,"");
|
||||
deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true,"");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add the bean to the appropriate collection on the parent bean.
|
||||
*/
|
||||
public void addBeanToCollectionWithCreate(Object parentBean, Object detailBean) {
|
||||
BeanCollection<?> bc = (BeanCollection<?>)super.getValue(parentBean);
|
||||
if (bc == null) {
|
||||
bc = (BeanCollection<?>)help.createEmpty(false);
|
||||
setValue(parentBean, bc);
|
||||
}
|
||||
help.add(bc, detailBean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(Object bean) {
|
||||
return super.getValue(bean);
|
||||
@@ -195,7 +208,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
private List<Object> findIdsByParentId(Object parentId, Transaction t, ArrayList<Object> excludeDetailIds) {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(false);
|
||||
String rawWhere = deriveWhereParentIdSql(false,"");
|
||||
|
||||
EbeanServer server = getBeanDescriptor().getEbeanServer();
|
||||
Query<?> q = server.find(getPropertyType())
|
||||
@@ -211,9 +224,29 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
return server.findIds(q, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a where clause to the query for a given list of parent Id's.
|
||||
*/
|
||||
public void addWhereParentIdIn(SpiQuery<?> query, List<Object> parentIds) {
|
||||
|
||||
String tableAlias = manyToMany ? "int_." : "t0.";
|
||||
if (manyToMany) {
|
||||
query.setIncludeTableJoin(inverseJoin);
|
||||
}
|
||||
String rawWhere = deriveWhereParentIdSql(true, tableAlias);
|
||||
String inClause = descriptor.getIdBinder().getIdInValueExpr(parentIds.size());
|
||||
|
||||
String expr = rawWhere+inClause;
|
||||
|
||||
// Flatten the bind values if needed (embeddedId)
|
||||
List<Object> bindValues = getBindParentIds(parentIds);
|
||||
|
||||
query.where().raw(expr, bindValues.toArray());
|
||||
}
|
||||
|
||||
private List<Object> findIdsByParentIdList(List<Object> parentIdist, Transaction t, ArrayList<Object> excludeDetailIds) {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(true);
|
||||
String rawWhere = deriveWhereParentIdSql(true,"");
|
||||
String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size());
|
||||
|
||||
String expr = rawWhere+inClause;
|
||||
@@ -464,6 +497,20 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
public Object getParentId(Object parentBean) {
|
||||
return descriptor.getId(parentBean);
|
||||
}
|
||||
|
||||
public List<Object> getBindParentIds(List<Object> parentIds) {
|
||||
if (exportedProperties.length == 1){
|
||||
return parentIds;
|
||||
}
|
||||
List<Object> expandedList = new ArrayList<Object>(parentIds.size()*exportedProperties.length);
|
||||
for (int i=0; i < parentIds.size(); i++) {
|
||||
for (int y = 0; y < exportedProperties.length; y++) {
|
||||
Object compId = parentIds.get(i);
|
||||
expandedList.add(exportedProperties[y].getValue(compId));
|
||||
}
|
||||
}
|
||||
return expandedList;
|
||||
}
|
||||
|
||||
private void bindWhereParendId(DefaultSqlUpdate sqlUpd, Object parentId){
|
||||
|
||||
@@ -492,7 +539,18 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
return pos;
|
||||
}
|
||||
|
||||
private String deriveWhereParentIdSql(boolean inClause) {
|
||||
public void addSelectExported(DbSqlContext ctx, String tableAlias) {
|
||||
|
||||
String alias = manyToMany ? "int_" : tableAlias;
|
||||
if (alias == null) {
|
||||
alias = "t0";
|
||||
}
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
ctx.appendColumn(alias, exportedProperties[i].getForeignDbColumn());
|
||||
}
|
||||
}
|
||||
|
||||
private String deriveWhereParentIdSql(boolean inClause, String tableAlias) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -505,7 +563,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
String s = inClause ? "," : " and ";
|
||||
sb.append(s);
|
||||
}
|
||||
sb.append(fkColumn);
|
||||
sb.append(tableAlias).append(fkColumn);
|
||||
if (!inClause){
|
||||
sb.append("=? ");
|
||||
}
|
||||
@@ -752,7 +810,11 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
Object value = getValueIntercept(bean);
|
||||
if (value != null){
|
||||
ctx.pushParentBeanMany(bean);
|
||||
help.jsonWrite(ctx, name, value, include != null);
|
||||
if (help != null){
|
||||
help.jsonWrite(ctx, name, value, include != null);
|
||||
} else {
|
||||
ctx.toJson(name, (Collection<?>)value);
|
||||
}
|
||||
ctx.popParentBeanMany();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,17 +77,29 @@ public final class BeanSetHelp<T> implements BeanCollectionHelp<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal add bypassing any modify listening.
|
||||
*/
|
||||
public void add(BeanCollection<?> collection, Object bean) {
|
||||
collection.internalAdd(bean);
|
||||
}
|
||||
|
||||
public Object createEmpty(boolean vanilla) {
|
||||
return vanilla ? new LinkedHashSet<T>() : new BeanSet<T>();
|
||||
if (vanilla) {
|
||||
return new LinkedHashSet<T>();
|
||||
}
|
||||
BeanSet<T> beanSet = new BeanSet<T>();
|
||||
if (many != null) {
|
||||
beanSet.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
return beanSet;
|
||||
}
|
||||
|
||||
public BeanCollection<T> createReference(Object parentBean, String propertyName) {
|
||||
|
||||
return new BeanSet<T>(loader, parentBean, propertyName);
|
||||
BeanSet<T> beanSet = new BeanSet<T>(loader, parentBean, propertyName);
|
||||
beanSet.setModifyListening(many.getModifyListenMode());
|
||||
return beanSet;
|
||||
}
|
||||
|
||||
public void refresh(EbeanServer server, Query<?> query, Transaction t, Object parentBean) {
|
||||
|
||||
@@ -72,7 +72,7 @@ public interface DbReadContext {
|
||||
/**
|
||||
* Set back the bean that has just been loaded with its id.
|
||||
*/
|
||||
public void setLoadedBean(Object loadedBean, Object id);
|
||||
public void setLoadedBean(Object loadedBean, Object id, Object lazyLoadParentId);
|
||||
|
||||
/**
|
||||
* Set back the 'detail' bean that has just been loaded.
|
||||
|
||||
@@ -8,7 +8,7 @@ public interface DbSqlContext {
|
||||
/**
|
||||
* Add a join to the sql query.
|
||||
*/
|
||||
public void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2);
|
||||
public void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2, String inheritance);
|
||||
|
||||
public void pushSecondaryTableAlias(String alias);
|
||||
|
||||
|
||||
@@ -38,6 +38,10 @@ public class ManyType {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isMap() {
|
||||
return Underlying.MAP.equals(underlying);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the matching Query type.
|
||||
*/
|
||||
|
||||
@@ -40,6 +40,8 @@ public final class TableJoin {
|
||||
*/
|
||||
private final BeanCascadeInfo cascadeInfo;
|
||||
|
||||
private final InheritInfo inheritInfo;
|
||||
|
||||
/**
|
||||
* Properties as an array.
|
||||
*/
|
||||
@@ -59,6 +61,7 @@ public final class TableJoin {
|
||||
this.table = InternString.intern(deploy.getTable());
|
||||
this.type = InternString.intern(deploy.getType());
|
||||
this.cascadeInfo = deploy.getCascadeInfo();
|
||||
this.inheritInfo = deploy.getInheritInfo();
|
||||
|
||||
DeployTableJoinColumn[] deployCols = deploy.columns();
|
||||
this.columns = new TableJoinColumn[deployCols.length];
|
||||
@@ -79,28 +82,6 @@ public final class TableJoin {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a tableJoin based on this object but with different alias.
|
||||
*/
|
||||
public TableJoin createWithAlias(String localAlias, String foreignAlias) {
|
||||
|
||||
return new TableJoin(this, localAlias, foreignAlias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a copy but with different table alias'.
|
||||
*/
|
||||
private TableJoin(TableJoin join, String localAlias, String foreignAlias) {
|
||||
|
||||
// copy the immutable fields
|
||||
this.importedPrimaryKey = join.importedPrimaryKey;
|
||||
this.table = join.table;
|
||||
this.type = join.type;
|
||||
this.cascadeInfo = join.cascadeInfo;
|
||||
this.properties = join.properties;
|
||||
this.columns = join.columns;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(30);
|
||||
sb.append(type).append(" ").append(table).append(" ");
|
||||
@@ -189,8 +170,10 @@ public final class TableJoin {
|
||||
|
||||
public boolean addJoin(boolean forceOuterJoin, String a1, String a2, DbSqlContext ctx) {
|
||||
|
||||
ctx.addJoin(forceOuterJoin ? LEFT_OUTER : type, table, columns(), a1, a2);
|
||||
String inheritance = inheritInfo != null ? inheritInfo.getWhere() : null;
|
||||
|
||||
ctx.addJoin(forceOuterJoin?LEFT_OUTER:type, table, columns(), a1, a2, inheritance);
|
||||
|
||||
return forceOuterJoin || LEFT_OUTER.equals(type);
|
||||
}
|
||||
|
||||
@@ -198,6 +181,7 @@ public final class TableJoin {
|
||||
* Explicitly add a (non-outer) join.
|
||||
*/
|
||||
public void addInnerJoin(String a1, String a2, DbSqlContext ctx) {
|
||||
ctx.addJoin(JOIN, table, columns(), a1, a2);
|
||||
String inheritance = inheritInfo != null ? inheritInfo.getWhere() : null;
|
||||
ctx.addJoin(JOIN, table, columns(), a1, a2, inheritance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,6 @@ import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.naming.InvalidNameException;
|
||||
import javax.naming.ldap.LdapName;
|
||||
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
@@ -21,172 +18,156 @@ import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
*/
|
||||
public interface IdBinder {
|
||||
|
||||
/**
|
||||
* Initialise the binder.
|
||||
*/
|
||||
public void initialise();
|
||||
|
||||
/**
|
||||
* Convert the Id value as a Lucene string term.
|
||||
*/
|
||||
public String writeTerm(Object idValue);
|
||||
/**
|
||||
* Initialise the binder.
|
||||
*/
|
||||
public void initialise();
|
||||
|
||||
/**
|
||||
* Convert the Lucene string term into an Id value.
|
||||
*/
|
||||
public Object readTerm(String idTermValue);
|
||||
/**
|
||||
* Write the Id value to binary DataOuput.
|
||||
*/
|
||||
public void writeData(DataOutput dataOutput, Object idValue) throws IOException;
|
||||
|
||||
/**
|
||||
* Write the Id value to binary DataOuput.
|
||||
*/
|
||||
public void writeData(DataOutput dataOutput, Object idValue) throws IOException;
|
||||
/**
|
||||
* Read the Id value from the binary DataInput.
|
||||
*/
|
||||
public Object readData(DataInput dataInput) throws IOException;
|
||||
|
||||
/**
|
||||
* Read the Id value from the binary DataInput.
|
||||
*/
|
||||
public Object readData(DataInput dataInput) throws IOException;
|
||||
/**
|
||||
* Return the name(s) of the Id property(s). Comma delimited if there is more
|
||||
* than one.
|
||||
* <p>
|
||||
* This can be used to include in a query.
|
||||
* </p>
|
||||
*/
|
||||
public String getIdProperty();
|
||||
|
||||
/**
|
||||
* Adds RDN's to the LdapName using the id value.
|
||||
*/
|
||||
public void createLdapNameById(LdapName name, Object id) throws InvalidNameException;
|
||||
/**
|
||||
* Find a BeanProperty that is mapped to the database column.
|
||||
*/
|
||||
public BeanProperty findBeanProperty(String dbColumnName);
|
||||
|
||||
/**
|
||||
* Adds RDN's to the LdapName using the id value from the bean.
|
||||
*/
|
||||
public void createLdapNameByBean(LdapName name, Object bean) throws InvalidNameException;
|
||||
/**
|
||||
* Return the number of scalar properties for this id.
|
||||
*/
|
||||
public int getPropertyCount();
|
||||
|
||||
/**
|
||||
* Return the name(s) of the Id property(s).
|
||||
* Comma delimited if there is more than one.
|
||||
* <p>
|
||||
* This can be used to include in a query.
|
||||
* </p>
|
||||
*/
|
||||
public String getIdProperty();
|
||||
/**
|
||||
* Return false if the id is a simple scalar and false if it is embedded or
|
||||
* concatenated.
|
||||
*/
|
||||
public boolean isComplexId();
|
||||
|
||||
/**
|
||||
* Find a BeanProperty that is mapped to the database column.
|
||||
*/
|
||||
public BeanProperty findBeanProperty(String dbColumnName);
|
||||
|
||||
/**
|
||||
* Return the number of scalar properties for this id.
|
||||
*/
|
||||
public int getPropertyCount();
|
||||
|
||||
/**
|
||||
* Return false if the id is a simple scalar and false if it is embedded or concatenated.
|
||||
*/
|
||||
public boolean isComplexId();
|
||||
|
||||
/**
|
||||
* Return the default order by that may need to be used if the query includes a many property.
|
||||
*/
|
||||
public String getDefaultOrderBy();
|
||||
|
||||
public String getOrderBy(String pathPrefix, boolean ascending);
|
||||
|
||||
/**
|
||||
* Return the values as an array of scalar bindable values.
|
||||
* <p>
|
||||
* For concatenated keys that use an Embedded bean or multiple id properties
|
||||
* this determines the field values are returns them as an Object array.
|
||||
* </p>
|
||||
* <p>
|
||||
* Added primarily for Query.addWhere().add(Expr.idEq()) support.
|
||||
* </p>
|
||||
*/
|
||||
public Object[] getBindValues(Object idValue);
|
||||
|
||||
/**
|
||||
* Return the id values for a given bean.
|
||||
*/
|
||||
public Object[] getIdValues(Object bean);
|
||||
|
||||
/**
|
||||
* Build a string of the logical expressions.
|
||||
* <p>
|
||||
* Typically used to build a id = ? string.
|
||||
* </p>
|
||||
*/
|
||||
public String getAssocOneIdExpr(String prefix, String operator);
|
||||
|
||||
/**
|
||||
* Return the logical id in expression taking into account embedded id's.
|
||||
*/
|
||||
public String getAssocIdInExpr(String prefix);
|
||||
/**
|
||||
* Return the default order by that may need to be used if the query includes
|
||||
* a many property.
|
||||
*/
|
||||
public String getDefaultOrderBy();
|
||||
|
||||
/**
|
||||
* Binds an id value to a prepared statement.
|
||||
*/
|
||||
public void bindId(DataBind dataBind, Object value) throws SQLException;
|
||||
public String getOrderBy(String pathPrefix, boolean ascending);
|
||||
|
||||
/**
|
||||
* Bind the id value to a SqlUpdate statement.
|
||||
*/
|
||||
public void bindId(DefaultSqlUpdate sqlUpdate, Object value);
|
||||
|
||||
public void addIdInBindValue(SpiExpressionRequest request, Object value);
|
||||
|
||||
/**
|
||||
* Return the sql for binding the id using an IN clause.
|
||||
*/
|
||||
public String getBindIdInSql(String baseTableAlias);
|
||||
|
||||
/**
|
||||
* Return the binding expression (like "?" or "(?,?)")for the Id.
|
||||
*/
|
||||
public String getIdInValueExpr(int size);
|
||||
|
||||
/**
|
||||
* Same as getIdInValueExpr but for delete by id.
|
||||
*/
|
||||
public String getIdInValueExprDelete(int size);
|
||||
|
||||
public void buildSelectExpressionChain(String prefix, List<String> selectChain);
|
||||
|
||||
/**
|
||||
* Read the id value from the result set and set it to the bean also returning it.
|
||||
*/
|
||||
public Object readSet(DbReadContext ctx, Object bean) throws SQLException;
|
||||
/**
|
||||
* Return the values as an array of scalar bindable values.
|
||||
* <p>
|
||||
* For concatenated keys that use an Embedded bean or multiple id properties
|
||||
* this determines the field values are returns them as an Object array.
|
||||
* </p>
|
||||
* <p>
|
||||
* Added primarily for Query.addWhere().add(Expr.idEq()) support.
|
||||
* </p>
|
||||
*/
|
||||
public Object[] getBindValues(Object idValue);
|
||||
|
||||
/**
|
||||
* Ignore the appropriate number of scalar properties for this id.
|
||||
*/
|
||||
public void loadIgnore(DbReadContext ctx);
|
||||
|
||||
/**
|
||||
* Read the id value from the result set and return it.
|
||||
*/
|
||||
public Object read(DbReadContext ctx) throws SQLException;
|
||||
/**
|
||||
* Return the id values for a given bean.
|
||||
*/
|
||||
public Object[] getIdValues(Object bean);
|
||||
|
||||
/**
|
||||
* Append to the select clause.
|
||||
*/
|
||||
public void appendSelect(DbSqlContext ctx, boolean subQuery);
|
||||
|
||||
/**
|
||||
* Return the sql for binding the id to. This includes table alias and columns that make up the id.
|
||||
*/
|
||||
public String getBindIdSql(String baseTableAlias);
|
||||
|
||||
/**
|
||||
* Return the id properties in flat form.
|
||||
*/
|
||||
public BeanProperty[] getProperties();
|
||||
|
||||
/**
|
||||
* Cast or convert the Id value if necessary and optionally set it.
|
||||
* <p>
|
||||
* The Id value is not assumed to be the correct type so it is converted to
|
||||
* the correct type. Typically this is because we could get a Integer, Long
|
||||
* or BigDecimal depending on the JDBC driver and situation.
|
||||
* </p>
|
||||
* <p>
|
||||
* If the bean is not null, then the value is set to the bean.
|
||||
* </p>
|
||||
*/
|
||||
public Object convertSetId(Object idValue, Object bean);
|
||||
/**
|
||||
* Build a string of the logical expressions.
|
||||
* <p>
|
||||
* Typically used to build a id = ? string.
|
||||
* </p>
|
||||
*/
|
||||
public String getAssocOneIdExpr(String prefix, String operator);
|
||||
|
||||
/**
|
||||
* Return the logical id in expression taking into account embedded id's.
|
||||
*/
|
||||
public String getAssocIdInExpr(String prefix);
|
||||
|
||||
/**
|
||||
* Binds an id value to a prepared statement.
|
||||
*/
|
||||
public void bindId(DataBind dataBind, Object value) throws SQLException;
|
||||
|
||||
/**
|
||||
* Bind the id value to a SqlUpdate statement.
|
||||
*/
|
||||
public void bindId(DefaultSqlUpdate sqlUpdate, Object value);
|
||||
|
||||
public void addIdInBindValue(SpiExpressionRequest request, Object value);
|
||||
|
||||
/**
|
||||
* Return the sql for binding the id using an IN clause.
|
||||
*/
|
||||
public String getBindIdInSql(String baseTableAlias);
|
||||
|
||||
/**
|
||||
* Return the binding expression (like "?" or "(?,?)")for the Id.
|
||||
*/
|
||||
public String getIdInValueExpr(int size);
|
||||
|
||||
/**
|
||||
* Same as getIdInValueExpr but for delete by id.
|
||||
*/
|
||||
public String getIdInValueExprDelete(int size);
|
||||
|
||||
public void buildSelectExpressionChain(String prefix, List<String> selectChain);
|
||||
|
||||
/**
|
||||
* Read the id value from the result set and set it to the bean also returning
|
||||
* it.
|
||||
*/
|
||||
public Object readSet(DbReadContext ctx, Object bean) throws SQLException;
|
||||
|
||||
/**
|
||||
* Ignore the appropriate number of scalar properties for this id.
|
||||
*/
|
||||
public void loadIgnore(DbReadContext ctx);
|
||||
|
||||
/**
|
||||
* Read the id value from the result set and return it.
|
||||
*/
|
||||
public Object read(DbReadContext ctx) throws SQLException;
|
||||
|
||||
/**
|
||||
* Append to the select clause.
|
||||
*/
|
||||
public void appendSelect(DbSqlContext ctx, boolean subQuery);
|
||||
|
||||
/**
|
||||
* Return the sql for binding the id to. This includes table alias and columns
|
||||
* that make up the id.
|
||||
*/
|
||||
public String getBindIdSql(String baseTableAlias);
|
||||
|
||||
/**
|
||||
* Return the id properties in flat form.
|
||||
*/
|
||||
public BeanProperty[] getProperties();
|
||||
|
||||
/**
|
||||
* Cast or convert the Id value if necessary and optionally set it.
|
||||
* <p>
|
||||
* The Id value is not assumed to be the correct type so it is converted to
|
||||
* the correct type. Typically this is because we could get a Integer, Long or
|
||||
* BigDecimal depending on the JDBC driver and situation.
|
||||
* </p>
|
||||
* <p>
|
||||
* If the bean is not null, then the value is set to the bean.
|
||||
* </p>
|
||||
*/
|
||||
public Object convertSetId(Object idValue, Object bean);
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user