mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
159
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0edf90056c | ||
|
|
6b27a9d280 | ||
|
|
2b0669c2ae | ||
|
|
9fc10e8e04 | ||
|
|
fb5eb0cdd2 | ||
|
|
3351882915 | ||
|
|
31c6d16e73 | ||
|
|
cd8c12a9a8 | ||
|
|
3207ae1f08 | ||
|
|
5fe532c393 | ||
|
|
93b9cbf9ac | ||
|
|
8a34d6050b | ||
|
|
ab3e6ccbf7 | ||
|
|
cc58fc8195 | ||
|
|
1c34c09f3c | ||
|
|
8544712448 | ||
|
|
5e22079366 | ||
|
|
903b0a3929 | ||
|
|
b18bc82b8a | ||
|
|
50de32af29 | ||
|
|
0b18189e62 | ||
|
|
0b6e73d729 | ||
|
|
3108076953 | ||
|
|
7ed4435ccc | ||
|
|
7aef4ff1bd | ||
|
|
5bcad17ae1 | ||
|
|
b1beb0b787 | ||
|
|
585eb2b57b | ||
|
|
2653ec50aa | ||
|
|
cb4432a588 | ||
|
|
4d93b70bf9 | ||
|
|
0b299edf0d | ||
|
|
cf10246783 | ||
|
|
babba914a0 | ||
|
|
0397140e0b | ||
|
|
82def2710e | ||
|
|
60ad417e1b | ||
|
|
3af1b574fd | ||
|
|
60c8683914 | ||
|
|
72263c0af0 | ||
|
|
f66316138d | ||
|
|
b403942491 | ||
|
|
baa3909c63 | ||
|
|
81f42f8e95 | ||
|
|
0377e3ec5c | ||
|
|
58f6c67d78 | ||
|
|
84f7fc5d81 | ||
|
|
ea02bebc6f | ||
|
|
da874d84a8 | ||
|
|
049cacad1c | ||
|
|
ced8c79458 | ||
|
|
a66edf8077 | ||
|
|
94591954b3 | ||
|
|
e611353163 | ||
|
|
e7811910e9 | ||
|
|
138b587ff7 | ||
|
|
8fc6c4c648 | ||
|
|
ebdc28e1cd | ||
|
|
51f79e769c | ||
|
|
c2a69714ca | ||
|
|
f3213e7b90 | ||
|
|
55de997297 | ||
|
|
65890f9777 | ||
|
|
41c1e7260c | ||
|
|
7d35789db4 | ||
|
|
d266d6d440 | ||
|
|
6d5374e66a | ||
|
|
8410de8831 | ||
|
|
4b0230e862 | ||
|
|
fa96f81f8c | ||
|
|
a5fea5e9be | ||
|
|
bafba0babf | ||
|
|
e4088c5fbe | ||
|
|
0405ceb96c | ||
|
|
ebf96c87bf | ||
|
|
fcaf11f262 | ||
|
|
05df47c0d7 | ||
|
|
a8892292fb | ||
|
|
e876ef8d87 | ||
|
|
315b39813b | ||
|
|
6d5e0c4766 | ||
|
|
a2ce17aec0 | ||
|
|
fcffa92c55 | ||
|
|
7fda8079c2 | ||
|
|
cca9bb4427 | ||
|
|
f45089240a | ||
|
|
8252a51545 | ||
|
|
c440c768cf | ||
|
|
c6ae501354 | ||
|
|
70e2b1c2ae | ||
|
|
211f28eea3 | ||
|
|
174fe46485 | ||
|
|
890cc6f66d | ||
|
|
2f0e5e6e46 | ||
|
|
54ba78218b | ||
|
|
ba16cea405 | ||
|
|
e81a61378c | ||
|
|
b4154031cc | ||
|
|
ecf5b56652 | ||
|
|
426c6c8111 | ||
|
|
9dbb69f4f4 | ||
|
|
83776a1eae | ||
|
|
6bc72ae6bb | ||
|
|
4fe2016891 | ||
|
|
7be4675d3e | ||
|
|
06b71781f5 | ||
|
|
55e4c7bc26 | ||
|
|
b3584a81b4 | ||
|
|
68da8944ed | ||
|
|
903ee35d63 | ||
|
|
658ee3d39f | ||
|
|
c074b7c457 | ||
|
|
528ad7c7fb | ||
|
|
c3f41f4157 | ||
|
|
33d104e6aa | ||
|
|
4bd3a0f293 | ||
|
|
54a4fd75f1 | ||
|
|
105f08cca1 | ||
|
|
01d9dd3fe8 | ||
|
|
f09b38c58c | ||
|
|
cb6d2e7ec0 | ||
|
|
89c0324e42 | ||
|
|
7b4215f4f8 | ||
|
|
e4cb5f0443 | ||
|
|
ec24374d72 | ||
|
|
cfba4d2deb | ||
|
|
0ab019ca2c | ||
|
|
8c9dae0f88 | ||
|
|
88a0c34d37 | ||
|
|
ce403e72a7 | ||
|
|
3c6a52da31 | ||
|
|
2806972032 | ||
|
|
acd6ba0a42 | ||
|
|
b386c9a5a8 | ||
|
|
c5536aaeec | ||
|
|
1b2bfc5df8 | ||
|
|
808004165c | ||
|
|
61eedb4e9a | ||
|
|
662ffdd070 | ||
|
|
13a5511318 | ||
|
|
0f9c98964e | ||
|
|
b87e6d90f6 | ||
|
|
166906a814 | ||
|
|
1733d2382c | ||
|
|
a6f9120905 | ||
|
|
32b599512b | ||
|
|
34fd24d620 | ||
|
|
f928923b5b | ||
|
|
1f928e65a4 | ||
|
|
d920d8eb76 | ||
|
|
23d2c64046 | ||
|
|
19d29cd836 | ||
|
|
f14c3e8cb4 | ||
|
|
b9ca8be95a | ||
|
|
d627bb47e2 | ||
|
|
d395c666fc | ||
|
|
939397acfc | ||
|
|
fecb5fa731 | ||
|
|
bdc9917811 |
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>11.23.1</version>
|
||||
<version>11.32.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ebean</name>
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-11.23.1</tag>
|
||||
<tag>ebean-11.32.1</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -117,13 +117,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-annotation</artifactId>
|
||||
<version>4.3</version>
|
||||
<version>4.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-datasource</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-datasource</artifactId>
|
||||
<version>4.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -135,7 +135,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-migration</artifactId>
|
||||
<version>11.9.1</version>
|
||||
<version>11.13.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -229,7 +229,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-agent</artifactId>
|
||||
<version>11.11.1</version>
|
||||
<version>11.27.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -238,7 +238,7 @@
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>1.4.196</version>
|
||||
<version>1.4.197</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -270,7 +270,7 @@
|
||||
<version>5.1.43</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.sap.cloud.db.jdbc</groupId>
|
||||
<artifactId>ngdbc</artifactId>
|
||||
@@ -309,7 +309,7 @@
|
||||
<plugin>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-maven-plugin</artifactId>
|
||||
<version>11.11.1</version>
|
||||
<version>11.27.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package io.ebean;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@@ -37,4 +39,21 @@ public interface BackgroundExecutor {
|
||||
* </p>
|
||||
*/
|
||||
void executePeriodically(Runnable r, long delay, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Schedules a Runnable for one-shot action that becomes enabled after the given delay.
|
||||
*
|
||||
* @return a ScheduledFuture representing pending completion of the task and
|
||||
* whose get() method will return null upon completion
|
||||
*/
|
||||
ScheduledFuture<?> schedule(Runnable r, long delay, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Schedules a Callable for one-shot action that becomes enabled after the given delay.
|
||||
*
|
||||
* @return a ScheduledFuture that can be used to extract result or cancel
|
||||
*/
|
||||
<V> ScheduledFuture<V> schedule(Callable<V> c, long delay, TimeUnit unit);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package io.ebean;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Provides access to the internal state of an entity bean.
|
||||
*/
|
||||
@@ -111,4 +113,10 @@ public interface BeanState {
|
||||
* Reset the bean putting it into NEW state such that a save() results in an insert.
|
||||
*/
|
||||
void resetForInsert();
|
||||
|
||||
/**
|
||||
* Returns a map with load erros.
|
||||
*/
|
||||
@Nullable
|
||||
Map<String, Exception> getLoadErrors();
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import java.util.function.Predicate;
|
||||
* String sql = "select id, name from customer where name like :name and status_code = :status";
|
||||
*
|
||||
* List<CustomerDto> beans =
|
||||
* Ebean.findDto(CustomrDto.class, sql)
|
||||
* Ebean.findDto(CustomerDto.class, sql)
|
||||
* .setParameter("name", "Acme%")
|
||||
* .setParameter("status", "ACTIVE")
|
||||
* .findList();
|
||||
|
||||
@@ -2,7 +2,9 @@ package io.ebean;
|
||||
|
||||
import io.ebean.annotation.TxIsolation;
|
||||
import io.ebean.cache.ServerCacheManager;
|
||||
import io.ebean.config.BeanNotEnhancedException;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.datasource.DataSourceConfigurationException;
|
||||
import io.ebean.plugin.Property;
|
||||
import io.ebean.text.csv.CsvReader;
|
||||
import io.ebean.text.json.JsonContext;
|
||||
@@ -160,6 +162,15 @@ public final class Ebean {
|
||||
defaultServer = getWithCreate(defaultName.trim());
|
||||
}
|
||||
}
|
||||
} catch (BeanNotEnhancedException e) {
|
||||
throw e;
|
||||
|
||||
} catch (DataSourceConfigurationException e) {
|
||||
String msg = "Configuration error creating DataSource for the default EbeanServer." +
|
||||
" This typically means a missing application-test.yaml or missing ebean-test-config dependency." +
|
||||
" See https://ebean.io/docs/trouble-shooting#datasource";
|
||||
throw new DataSourceConfigurationException(msg, e);
|
||||
|
||||
} catch (Throwable e) {
|
||||
logger.error("Error trying to create the default EbeanServer", e);
|
||||
throw new RuntimeException(e);
|
||||
|
||||
@@ -11,17 +11,13 @@ import io.ebean.text.json.JsonContext;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.NonUniqueResultException;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Provides the API for fetching and saving beans to a particular DataSource.
|
||||
@@ -1446,6 +1442,13 @@ public interface EbeanServer {
|
||||
*/
|
||||
JsonContext json();
|
||||
|
||||
/**
|
||||
* Return a ScriptRunner for running SQL or DDL scripts.
|
||||
* <p/>
|
||||
* Intended to use mostly in testing to run seed SQL scripts or truncate table scripts etc.
|
||||
*/
|
||||
ScriptRunner script();
|
||||
|
||||
/**
|
||||
* Return the Document store.
|
||||
*/
|
||||
|
||||
@@ -133,11 +133,22 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression ieq(String propertyName, String value);
|
||||
|
||||
/**
|
||||
* Case Insensitive Not Equal To - property not equal to the given value (typically
|
||||
* using a lower() function to make it case insensitive).
|
||||
*/
|
||||
Expression ine(String propertyName, String value);
|
||||
|
||||
/**
|
||||
* Case Insensitive Equal To that allows for named parameter use.
|
||||
*/
|
||||
Expression ieqObject(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Case Insensitive Not Equal To that allows for named parameter use.
|
||||
*/
|
||||
Expression ineObject(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Between - property between the two given values.
|
||||
*/
|
||||
|
||||
@@ -128,6 +128,38 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
<D> DtoQuery<D> asDto(Class<D> dtoClass);
|
||||
|
||||
/**
|
||||
* Return the underlying query as an UpdateQuery.
|
||||
* <p>
|
||||
* Typically this is used with query beans to covert a query bean
|
||||
* query into an UpdateQuery like the examples below.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rowsUpdated = new QCustomer()
|
||||
* .name.startsWith("Rob")
|
||||
* .asUpdate()
|
||||
* .set("active", false)
|
||||
* .update();;
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rowsUpdated = new QContact()
|
||||
* .notes.note.startsWith("Make Inactive")
|
||||
* .email.endsWith("@foo.com")
|
||||
* .customer.id.equalTo(42)
|
||||
* .asUpdate()
|
||||
* .set("inactive", true)
|
||||
* .setRaw("email = lower(email)")
|
||||
* .update();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
UpdateQuery<T> asUpdate();
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause which results in the DB locking the record.
|
||||
*/
|
||||
@@ -166,6 +198,18 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
int delete();
|
||||
|
||||
/**
|
||||
* Execute as a delete query deleting the 'root level' beans that match the predicates
|
||||
* in the query.
|
||||
* <p>
|
||||
* Note that if the query includes joins then the generated delete statement may not be
|
||||
* optimal depending on the database platform.
|
||||
* </p>
|
||||
*
|
||||
* @return the number of rows that were deleted.
|
||||
*/
|
||||
int delete(Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute as a update query.
|
||||
*
|
||||
@@ -174,6 +218,40 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
int update();
|
||||
|
||||
/**
|
||||
* Execute as a update query with the given transaction.
|
||||
*
|
||||
* @return the number of rows that were updated.
|
||||
* @see UpdateQuery
|
||||
*/
|
||||
int update(Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query returning true if a row is found.
|
||||
* <p>
|
||||
* The query is executed using max rows of 1 and will only select the id property.
|
||||
* This method is really just a convenient way to optimise a query to perform a
|
||||
* 'does a row exist in the db' check.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Example:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists = query().where().eq("email", "rob@foo.com").exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h2>Example using a query bean:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists = new QContact().email.equalTo("rob@foo.com").exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return True if the query finds a matching row in the database
|
||||
*/
|
||||
boolean exists();
|
||||
|
||||
/**
|
||||
* Execute the query iterating over the results.
|
||||
*
|
||||
@@ -729,6 +807,12 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> ieq(String propertyName, String value);
|
||||
|
||||
/**
|
||||
* Case Insensitive Not Equal To - property not equal to the given value (typically
|
||||
* using a lower() function to make it case insensitive).
|
||||
*/
|
||||
ExpressionList<T> ine(String propertyName, String value);
|
||||
|
||||
/**
|
||||
* Between - property between the two given values.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package io.ebean;
|
||||
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.NonUniqueResultException;
|
||||
@@ -42,6 +45,32 @@ public interface ExtendedServer {
|
||||
*/
|
||||
void setClock(Clock clock);
|
||||
|
||||
/**
|
||||
* Execute the query returning true if a row is found.
|
||||
* <p>
|
||||
* The query is executed using max rows of 1 and will only select the id property.
|
||||
* This method is really just a convenient way to optimise a query to perform a
|
||||
* 'does a row exist in the db' check.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Example:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists = query().where().eq("email", "rob@foo.com").exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h2>Example using a query bean:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists = new QContact().email.equalTo("rob@foo.com").exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return True if the query finds a matching row in the database
|
||||
*/
|
||||
<T> boolean exists(SpiQuery<?> ormQuery, SpiTransaction transaction);
|
||||
|
||||
/**
|
||||
* Return the number of 'top level' or 'root' entities this query should return.
|
||||
*
|
||||
|
||||
@@ -10,7 +10,7 @@ import javax.annotation.Nonnull;
|
||||
* that can be defined separately from the query and combined with other FetchGroups.
|
||||
* </p>
|
||||
*
|
||||
* <h3>Select example</h3>*
|
||||
* <h3>Select example</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class, "name, status");
|
||||
|
||||
@@ -78,6 +78,7 @@ public class MergeOptionsBuilder {
|
||||
this.deletePermanent = deletePermanent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> paths() {
|
||||
return paths;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package io.ebean;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
/**
|
||||
* A MappedSuperclass base class that provides convenience methods for inserting, updating and
|
||||
@@ -74,7 +73,6 @@ import javax.persistence.MappedSuperclass;
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public abstract class Model {
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebean;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -125,7 +126,7 @@ public class Pairs {
|
||||
* Return all the value pairs.
|
||||
*/
|
||||
public List<Entry> getEntries() {
|
||||
return entries;
|
||||
return Collections.unmodifiableList(entries);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -244,6 +244,38 @@ public interface Query<T> {
|
||||
*/
|
||||
<D> DtoQuery<D> asDto(Class<D> dtoClass);
|
||||
|
||||
/**
|
||||
* Convert the query to a UpdateQuery.
|
||||
* <p>
|
||||
* Typically this is used with query beans to covert a query bean
|
||||
* query into an UpdateQuery like the examples below.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rowsUpdated = new QCustomer()
|
||||
* .name.startsWith("Rob")
|
||||
* .asUpdate()
|
||||
* .set("active", false)
|
||||
* .update();;
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rowsUpdated = new QContact()
|
||||
* .notes.note.startsWith("Make Inactive")
|
||||
* .email.endsWith("@foo.com")
|
||||
* .customer.id.equalTo(42)
|
||||
* .asUpdate()
|
||||
* .set("inactive", true)
|
||||
* .setRaw("email = lower(email)")
|
||||
* .update();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
UpdateQuery<T> asUpdate();
|
||||
|
||||
/**
|
||||
* Cancel the query execution if supported by the underlying database and
|
||||
* driver.
|
||||
@@ -340,6 +372,27 @@ public interface Query<T> {
|
||||
*/
|
||||
Query<T> setAutoTune(boolean autoTune);
|
||||
|
||||
/**
|
||||
* Execute the query allowing properties with invalid JSON to be collected and not fail the query.
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch a bean with JSON content
|
||||
* EBasicJsonList bean= Ebean.find(EBasicJsonList.class)
|
||||
* .setId(42)
|
||||
* .setAllowLoadErrors() // collect errors into bean state if we have invalid JSON
|
||||
* .findOne();
|
||||
*
|
||||
*
|
||||
* // get the invalid JSON errors from the bean state
|
||||
* Map<String, Exception> errors = server().getBeanState(bean).getLoadErrors();
|
||||
*
|
||||
* // If this map is not empty tell we have invalid JSON
|
||||
* // and should try and fix the JSON content or inform the user
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
Query<T> setAllowLoadErrors();
|
||||
|
||||
/**
|
||||
* Set the default lazy loading batch size to use.
|
||||
* <p>
|
||||
@@ -636,15 +689,13 @@ public interface Query<T> {
|
||||
* .where().eq("status", Status.NEW)
|
||||
* .order().asc("id");
|
||||
*
|
||||
* QueryIterator<Customer> it = query.findIterate();
|
||||
* try {
|
||||
* // use try with resources to ensure QueryIterator is closed
|
||||
*
|
||||
* try (QueryIterator<Customer> it = query.findIterate()) {
|
||||
* while (it.hasNext()) {
|
||||
* Customer customer = it.next();
|
||||
* // do something with customer ...
|
||||
* }
|
||||
* } finally {
|
||||
* // close the underlying resources
|
||||
* it.close();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
@@ -838,6 +889,32 @@ public interface Query<T> {
|
||||
*/
|
||||
boolean isCountDistinct();
|
||||
|
||||
/**
|
||||
* Execute the query returning true if a row is found.
|
||||
* <p>
|
||||
* The query is executed using max rows of 1 and will only select the id property.
|
||||
* This method is really just a convenient way to optimise a query to perform a
|
||||
* 'does a row exist in the db' check.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Example:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists = query().where().eq("email", "rob@foo.com").exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h2>Example using a query bean:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists = new QContact().email.equalTo("rob@foo.com").exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return True if the query finds a matching row in the database
|
||||
*/
|
||||
boolean exists();
|
||||
|
||||
/**
|
||||
* Execute the query returning either a single bean or null (if no matching
|
||||
* bean is found).
|
||||
@@ -924,11 +1001,31 @@ public interface Query<T> {
|
||||
*/
|
||||
int delete();
|
||||
|
||||
/**
|
||||
* Execute as a delete query returning the number of rows deleted using the given transaction.
|
||||
* <p>
|
||||
* Note that if the query includes joins then the generated delete statement may not be
|
||||
* optimal depending on the database platform.
|
||||
* </p>
|
||||
*
|
||||
* @return the number of beans/rows that were deleted.
|
||||
*/
|
||||
int delete(Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the UpdateQuery returning the number of rows updated.
|
||||
*
|
||||
* @return the number of beans/rows updated.
|
||||
*/
|
||||
int update();
|
||||
|
||||
/**
|
||||
* Execute the UpdateQuery returning the number of rows updated using the given transaction.
|
||||
*
|
||||
* @return the number of beans/rows updated.
|
||||
*/
|
||||
int update(Transaction transaction);
|
||||
|
||||
/**
|
||||
* Return the count of entities this query should return.
|
||||
* <p>
|
||||
@@ -1309,7 +1406,6 @@ public interface Query<T> {
|
||||
* count:1 orderStatus:COMPLETE
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*/
|
||||
Query<T> setCountDistinct(CountDistinctOrder orderBy);
|
||||
|
||||
@@ -1485,15 +1581,6 @@ public interface Query<T> {
|
||||
*/
|
||||
String getGeneratedSql();
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to forUpdate().
|
||||
*
|
||||
* executed the select with "for update" which should lock the record
|
||||
* "on read"
|
||||
*/
|
||||
@Deprecated
|
||||
Query<T> setForUpdate(boolean forUpdate);
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause which results in the DB locking the record.
|
||||
*/
|
||||
@@ -1530,11 +1617,30 @@ public interface Query<T> {
|
||||
*/
|
||||
Query<T> alias(String alias);
|
||||
|
||||
/**
|
||||
* Set the base table to use for this query.
|
||||
* <p>
|
||||
* Typically this is used when a table has partitioning and we wish to specify a specific
|
||||
* partition/table to query against.
|
||||
* </p>
|
||||
*/
|
||||
Query<T> setBaseTable(String baseTable);
|
||||
|
||||
/**
|
||||
* Return the type of beans being queried.
|
||||
*/
|
||||
Class<T> getBeanType();
|
||||
|
||||
/**
|
||||
* Sets the inherit type. Must be a subtype of getBeanType
|
||||
*/
|
||||
Query<T> setInheritType(Class<? extends T> type);
|
||||
|
||||
/**
|
||||
* Returns the inherit type. This is normally the same as getBeanType() returns as long as no other type is set.
|
||||
*/
|
||||
Class<? extends T> getInheritType();
|
||||
|
||||
/**
|
||||
* Return the type of query being executed.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package io.ebean;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Runs DDL and SQL scripts.
|
||||
* <p/>
|
||||
* Typically these are scripts used for testing such as seed SQL scripts or truncate SQL scripts.
|
||||
* <p/>
|
||||
* Scripts are executed in their own transaction and committed on successful completion.
|
||||
*
|
||||
* <h3>Example of simple use</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* EbeanServer server = Ebean.getDefaultServer();
|
||||
* server.script().run("/scripts/test-script.sql");
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*
|
||||
* <h3>Example using place holders in the script</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Map<String,String> placeholders = new HashMap<>();
|
||||
* placeholders.put("tableName", "e_basic");
|
||||
*
|
||||
* EbeanServer server = Ebean.getDefaultServer();
|
||||
* server.script().run("/scripts/test-script.sql");
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public interface ScriptRunner {
|
||||
|
||||
/**
|
||||
* Run a script given the resource path (that should start with "/").
|
||||
*/
|
||||
void run(String path);
|
||||
|
||||
/**
|
||||
* Run a script given the resource path (that should start with "/") and place holders.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* Map<String,String> placeholders = new HashMap<>();
|
||||
* placeholders.put("tableName", "e_basic");
|
||||
*
|
||||
* EbeanServer server = Ebean.getDefaultServer();
|
||||
* server.script().run("/scripts/test-script.sql");
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
void run(String path, Map<String, String> placeholderMap);
|
||||
|
||||
/**
|
||||
* Run a DDL or SQL script given the resource.
|
||||
*/
|
||||
void run(URL resource);
|
||||
|
||||
/**
|
||||
* Run a DDL or SQL script given the resource and place holders.
|
||||
*/
|
||||
void run(URL resource, Map<String, String> placeholderMap);
|
||||
|
||||
}
|
||||
@@ -84,7 +84,7 @@ public interface Transaction extends AutoCloseable {
|
||||
* <p>
|
||||
* This is similar to commit() but leaves the transaction "Active".
|
||||
* </p>
|
||||
* <h3>Functions/h3>
|
||||
* <h3>Functions</h3>
|
||||
* <ul>
|
||||
* <li>Flush the JDBC batch buffer</li>
|
||||
* <li>Call commit on the underlying JDBC connection</li>
|
||||
@@ -100,7 +100,7 @@ public interface Transaction extends AutoCloseable {
|
||||
* This performs commit and completes the transaction closing underlying resources and
|
||||
* marking the transaction as "In active".
|
||||
* </p>
|
||||
* <h3>Functions/h3>
|
||||
* <h3>Functions</h3>
|
||||
* <ul>
|
||||
* <li>Flush the JDBC batch buffer</li>
|
||||
* <li>Call commit on the underlying JDBC connection</li>
|
||||
@@ -117,7 +117,7 @@ public interface Transaction extends AutoCloseable {
|
||||
* <p>
|
||||
* This performs rollback, closes underlying resources and marks the transaction as "In active".
|
||||
* </p>
|
||||
* <h3>Functions/h3>
|
||||
* <h3>Functions</h3>
|
||||
* <ul>
|
||||
* <li>Call rollback on the underlying JDBC connection</li>
|
||||
* <li>Trigger any registered TransactionCallbacks</li>
|
||||
@@ -358,29 +358,6 @@ public interface Transaction extends AutoCloseable {
|
||||
*/
|
||||
void setBatchMode(boolean useBatch);
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to {@link #setBatchMode(boolean)}.
|
||||
* <p>
|
||||
* Set the JDBC batch mode to use for this transaction.
|
||||
* </p>
|
||||
* <p>
|
||||
* If this is NONE then JDBC batch can still be used for each request - save(), insert(), update() or delete()
|
||||
* and this would be useful if the request cascades to detail beans.
|
||||
* </p>
|
||||
*
|
||||
* @param persistBatchMode the batch mode to use for this transaction
|
||||
* @see io.ebean.config.ServerConfig#setPersistBatch(PersistBatch)
|
||||
*/
|
||||
@Deprecated
|
||||
void setBatch(PersistBatch persistBatchMode);
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to {@link #isBatchMode()}.
|
||||
* Return the batch mode at the transaction level.
|
||||
*/
|
||||
@Deprecated
|
||||
PersistBatch getBatch();
|
||||
|
||||
/**
|
||||
* Return the batch mode at the transaction level.
|
||||
*/
|
||||
@@ -405,22 +382,6 @@ public interface Transaction extends AutoCloseable {
|
||||
*/
|
||||
void setBatchOnCascade(boolean batchMode);
|
||||
|
||||
/**
|
||||
* Set the batch mode when cascading.
|
||||
* <p>
|
||||
* Deprecated in favour of {@link #setBatchOnCascade(boolean)}
|
||||
* </p>
|
||||
*/
|
||||
@Deprecated
|
||||
void setBatchOnCascade(PersistBatch batchOnCascadeMode);
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to {@link #isBatchMode()}.
|
||||
* Return the batch mode at the request level (for each save(), insert(), update() or delete()).
|
||||
*/
|
||||
@Deprecated
|
||||
PersistBatch getBatchOnCascade();
|
||||
|
||||
/**
|
||||
* Return the batch mode at the request level.
|
||||
*/
|
||||
|
||||
@@ -432,4 +432,11 @@ public final class TxScope {
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isBatchMode() {
|
||||
return PersistBatch.ALL.equals(batch);
|
||||
}
|
||||
|
||||
public boolean isBatchOnCascade() {
|
||||
return PersistBatch.ALL.equals(batchOnCascade);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,9 +154,19 @@ public interface UpdateQuery<T> {
|
||||
*/
|
||||
UpdateQuery<T> setProfileLocation(ProfileLocation profileLocation);
|
||||
|
||||
/**
|
||||
* Set the label on the update query.
|
||||
*/
|
||||
UpdateQuery<T> setLabel(String label);
|
||||
|
||||
/**
|
||||
* Return the query expression list to add predicates to.
|
||||
*/
|
||||
ExpressionList<T> where();
|
||||
|
||||
/**
|
||||
* Execute the update returning the number of rows updated.
|
||||
*/
|
||||
int update();
|
||||
|
||||
}
|
||||
|
||||
@@ -98,6 +98,8 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
|
||||
private Object[] origValues;
|
||||
|
||||
private Exception[] loadErrors;
|
||||
|
||||
private int lazyLoadProperty = -1;
|
||||
|
||||
private Object ownerId;
|
||||
@@ -829,7 +831,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
|
||||
if (lazyLoadFailure) {
|
||||
// failed when batch lazy loaded by another bean in the batch
|
||||
throw new EntityNotFoundException("Lazy loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted");
|
||||
throw new EntityNotFoundException("(Lazy) loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted");
|
||||
}
|
||||
|
||||
if (lazyLoadProperty == -1) {
|
||||
@@ -1115,4 +1117,36 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
public void setSortOrder(int sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the load error that happened on this property.
|
||||
*/
|
||||
public void setLoadError(int propertyIndex, Exception t) {
|
||||
if (loadErrors == null) {
|
||||
loadErrors = new Exception[owner._ebean_getPropertyNames().length];
|
||||
}
|
||||
loadErrors[propertyIndex] = t;
|
||||
flags[propertyIndex] |= FLAG_LOADED_PROP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the loadErrors.
|
||||
*/
|
||||
public Map<String, Exception> getLoadErrors() {
|
||||
if (loadErrors == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Exception> ret = null;
|
||||
int len = getPropertyLength();
|
||||
for (int i = 0; i < len; i++) {
|
||||
Exception loadError = loadErrors[i];
|
||||
if (loadError != null) {
|
||||
if (ret == null) {
|
||||
ret = new LinkedHashMap<>();
|
||||
}
|
||||
ret.put(getProperty(i), loadError);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@ public class ServerCacheNotification {
|
||||
this.dependentTables = dependentTables;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ts:" + modifyTimestamp + " tables:" + dependentTables;
|
||||
}
|
||||
|
||||
public long getModifyTimestamp() {
|
||||
return modifyTimestamp;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ public class ServerCacheOptions {
|
||||
private int maxIdleSecs;
|
||||
private int maxSecsToLive;
|
||||
private int trimFrequency;
|
||||
private boolean nearCache;
|
||||
|
||||
/**
|
||||
* Construct with no set options.
|
||||
@@ -40,6 +41,14 @@ public class ServerCacheOptions {
|
||||
this.trimFrequency = cacheTuning.trimFrequency();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create with nearCache option.
|
||||
*/
|
||||
public ServerCacheOptions(boolean nearCache, CacheBeanTuning tuning) {
|
||||
this(tuning);
|
||||
this.nearCache = nearCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply any settings from the default settings that have not already been
|
||||
* specifically set.
|
||||
@@ -70,9 +79,33 @@ public class ServerCacheOptions {
|
||||
copy.maxIdleSecs = maxIdleSecs;
|
||||
copy.maxSecsToLive = maxSecsToLive;
|
||||
copy.trimFrequency = trimFrequency;
|
||||
copy.nearCache = this.nearCache;
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this object with nearCache option.
|
||||
*/
|
||||
public ServerCacheOptions copy(boolean nearCache) {
|
||||
ServerCacheOptions copy = copy();
|
||||
copy.nearCache = nearCache;
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if nearCache was explicitly turned on.
|
||||
*/
|
||||
public boolean isNearCache() {
|
||||
return nearCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn on nearCache option.
|
||||
*/
|
||||
public void setNearCache(boolean nearCache) {
|
||||
this.nearCache = nearCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum cache size.
|
||||
*/
|
||||
|
||||
@@ -123,7 +123,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
private void initClear() {
|
||||
synchronized (this) {
|
||||
if (map == null) {
|
||||
if (modifyListening) {
|
||||
if (!disableLazyLoad && modifyListening) {
|
||||
lazyLoadCollection(true);
|
||||
} else {
|
||||
map = new LinkedHashMap<>();
|
||||
@@ -135,7 +135,11 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
private void init() {
|
||||
synchronized (this) {
|
||||
if (map == null) {
|
||||
lazyLoadCollection(false);
|
||||
if (disableLazyLoad) {
|
||||
map = new LinkedHashMap<>();
|
||||
} else {
|
||||
lazyLoadCollection(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
private void initClear() {
|
||||
synchronized (this) {
|
||||
if (set == null) {
|
||||
if (modifyListening) {
|
||||
if (!disableLazyLoad && modifyListening) {
|
||||
lazyLoadCollection(true);
|
||||
} else {
|
||||
set = new LinkedHashSet<>();
|
||||
@@ -128,7 +128,11 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
private void init() {
|
||||
synchronized (this) {
|
||||
if (set == null) {
|
||||
lazyLoadCollection(true);
|
||||
if (disableLazyLoad) {
|
||||
set = new LinkedHashSet<>();
|
||||
} else {
|
||||
lazyLoadCollection(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -377,7 +381,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
throw new IllegalStateException("This collection is in ReadOnly mode");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public BeanCollection<E> getShallowCopy() {
|
||||
BeanSet<E> copy = new BeanSet<>(new LinkedHashSet<>(set));
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package io.ebean.config;
|
||||
|
||||
/**
|
||||
* Throw when an processing an entity bean that is not bytecode enhanced.
|
||||
*
|
||||
* Refer: https://ebean.io/docs/trouble-shooting#not-enhanced
|
||||
*/
|
||||
public class BeanNotEnhancedException extends IllegalStateException {
|
||||
|
||||
private static final long serialVersionUID = 3008101919425876799L;
|
||||
|
||||
public BeanNotEnhancedException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package io.ebean.config;
|
||||
|
||||
/**
|
||||
* Throw when an processing thinks a bean is not registered.
|
||||
*
|
||||
* Refer: https://ebean.io/docs/trouble-shooting#not-registered
|
||||
*/
|
||||
public class BeanNotRegisteredException extends IllegalStateException {
|
||||
|
||||
private static final long serialVersionUID = -1411298126011136552L;
|
||||
|
||||
public BeanNotRegisteredException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import io.ebean.annotation.Platform;
|
||||
import io.ebean.migration.MigrationConfig;
|
||||
import io.ebean.migration.MigrationRunner;
|
||||
import io.ebean.util.StringHelper;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -33,6 +32,8 @@ public class DbMigrationConfig {
|
||||
*/
|
||||
protected String migrationPath = "dbmigration";
|
||||
|
||||
protected String migrationInitPath = "dbinit";
|
||||
|
||||
/**
|
||||
* Subdirectory the model xml files go into.
|
||||
*/
|
||||
@@ -118,6 +119,15 @@ public class DbMigrationConfig {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path for normal migrations or dbinit migrations.
|
||||
*
|
||||
* @param dbinitMigration When true return the path for dbinit migrations.
|
||||
*/
|
||||
public String getMigrationPath(boolean dbinitMigration) {
|
||||
return dbinitMigration ? migrationInitPath : migrationPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resource path for db migrations.
|
||||
*/
|
||||
@@ -419,6 +429,7 @@ public class DbMigrationConfig {
|
||||
public void loadSettings(PropertiesWrapper properties, String serverName) {
|
||||
|
||||
migrationPath = properties.get("migration.migrationPath", migrationPath);
|
||||
migrationInitPath = properties.get("migration.migrationInitPath", migrationInitPath);
|
||||
modelPath = properties.get("migration.modelPath", modelPath);
|
||||
applyPrefix = properties.get("migration.applyPrefix", applyPrefix);
|
||||
applySuffix = properties.get("migration.applySuffix", applySuffix);
|
||||
@@ -472,6 +483,7 @@ public class DbMigrationConfig {
|
||||
runnerConfig.setMetaTable(metaTable);
|
||||
runnerConfig.setApplySuffix(applySuffix);
|
||||
runnerConfig.setMigrationPath(migrationPath);
|
||||
runnerConfig.setMigrationInitPath(migrationInitPath);
|
||||
runnerConfig.setRunPlaceholderMap(runPlaceholderMap);
|
||||
runnerConfig.setRunPlaceholders(runPlaceholders);
|
||||
runnerConfig.setDbUsername(getDbUsername());
|
||||
|
||||
@@ -14,6 +14,7 @@ import io.ebean.config.dbplatform.DbEncrypt;
|
||||
import io.ebean.config.dbplatform.DbType;
|
||||
import io.ebean.config.dbplatform.IdType;
|
||||
import io.ebean.config.properties.PropertiesLoader;
|
||||
import io.ebean.datasource.DataSourceConfig;
|
||||
import io.ebean.event.BeanFindController;
|
||||
import io.ebean.event.BeanPersistController;
|
||||
import io.ebean.event.BeanPersistListener;
|
||||
@@ -30,7 +31,6 @@ import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.migration.MigrationRunner;
|
||||
import io.ebean.util.StringHelper;
|
||||
import org.avaje.datasource.DataSourceConfig;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.time.Clock;
|
||||
@@ -229,6 +229,8 @@ public class ServerConfig {
|
||||
|
||||
private int persistBatchSize = 20;
|
||||
|
||||
private boolean disableLazyLoading;
|
||||
|
||||
/**
|
||||
* The default batch size for lazy loading
|
||||
*/
|
||||
@@ -250,6 +252,8 @@ public class ServerConfig {
|
||||
|
||||
private boolean ddlRun;
|
||||
|
||||
private boolean ddlExtra = true;
|
||||
|
||||
private boolean ddlCreateOnly;
|
||||
|
||||
private String ddlInitSql;
|
||||
@@ -364,9 +368,10 @@ public class ServerConfig {
|
||||
private UuidVersion uuidVersion = UuidVersion.VERSION4;
|
||||
|
||||
/**
|
||||
* The UUID state file (for Version 1 UUIDs).
|
||||
* The UUID state file (for Version 1 UUIDs). By default, the file is created in
|
||||
* ${HOME}/.ebean/${servername}-uuid.state
|
||||
*/
|
||||
private String uuidStateFile = "ebean-uuid.state";
|
||||
private String uuidStateFile;
|
||||
|
||||
/**
|
||||
* The clock used for setting the timestamps (e.g. @UpdatedTimestamp) on objects.
|
||||
@@ -484,6 +489,11 @@ public class ServerConfig {
|
||||
*/
|
||||
private boolean notifyL2CacheInForeground;
|
||||
|
||||
/**
|
||||
* Set to true to support query plan capture.
|
||||
*/
|
||||
private boolean collectQueryPlans;
|
||||
|
||||
/**
|
||||
* The time in millis used to determine when a query is alerted for being slow.
|
||||
*/
|
||||
@@ -592,6 +602,47 @@ public class ServerConfig {
|
||||
return serviceObject.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a service object into configuration such that it can be passed to a plugin.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* JedisPool jedisPool = ..
|
||||
*
|
||||
* serverConfig.putServiceObject(jedisPool);
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public void putServiceObject(Object configObject) {
|
||||
String key = serviceObjectKey(configObject);
|
||||
serviceObject.put(key, configObject);
|
||||
}
|
||||
|
||||
private String serviceObjectKey(Object configObject) {
|
||||
return serviceObjectKey(configObject.getClass());
|
||||
}
|
||||
|
||||
private String serviceObjectKey(Class<?> cls) {
|
||||
String simpleName = cls.getSimpleName();
|
||||
return Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by plugins to obtain service objects.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* JedisPool jedisPool = serverConfig.getServiceObject(JedisPool.class);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param cls The type of the service object to obtain
|
||||
* @return The service object given the class type
|
||||
*/
|
||||
public <P> P getServiceObject(Class<P> cls) {
|
||||
return (P) serviceObject.get(serviceObjectKey(cls));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Jackson JsonFactory to use.
|
||||
* <p>
|
||||
@@ -927,6 +978,22 @@ public class ServerConfig {
|
||||
this.queryBatchSize = queryBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if lazy loading is disabled on queries by default.
|
||||
*/
|
||||
public boolean isDisableLazyLoading() {
|
||||
return disableLazyLoading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to disable lazy loading by default.
|
||||
* <p>
|
||||
* It can be turned on per query via {@link Query#setDisableLazyLoading(boolean)}.
|
||||
*/
|
||||
public void setDisableLazyLoading(boolean disableLazyLoading) {
|
||||
this.disableLazyLoading = disableLazyLoading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default batch size for lazy loading of beans and collections.
|
||||
*/
|
||||
@@ -2051,6 +2118,16 @@ public class ServerConfig {
|
||||
this.ddlRun = ddlRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to false if you not want to run the extra-ddl.xml scripts. (default = true)
|
||||
* <p>
|
||||
* Typically we want this on when we are running tests.
|
||||
*/
|
||||
public void setDdlExtra(boolean ddlExtra) {
|
||||
this.ddlExtra = ddlExtra;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the "drop all ddl" should be skipped.
|
||||
* <p>
|
||||
@@ -2121,6 +2198,13 @@ public class ServerConfig {
|
||||
return ddlRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true, if extra-ddl.xml should be executed.
|
||||
*/
|
||||
public boolean isDdlExtra() {
|
||||
return ddlExtra;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the class path search should be disabled.
|
||||
*/
|
||||
@@ -2745,20 +2829,6 @@ public class ServerConfig {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated - this does nothing now, we always try to read test configuration.
|
||||
* <p>
|
||||
* Load settings from test-ebean.properties and do nothing if the properties is not found.
|
||||
* <p>
|
||||
* This is typically used when test-ebean.properties is put into the test class path and used
|
||||
* to configure Ebean for running tests.
|
||||
* </p>
|
||||
*/
|
||||
@Deprecated
|
||||
public void loadTestProperties() {
|
||||
// do nothing now ... as we always try to read test configuration and that should only
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the properties that we used for configuration and were set via a call to loadFromProperties().
|
||||
*/
|
||||
@@ -2828,6 +2898,7 @@ public class ServerConfig {
|
||||
|
||||
queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds);
|
||||
slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis);
|
||||
collectQueryPlans = p.getBoolean("collectQueryPlans", collectQueryPlans);
|
||||
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
|
||||
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
|
||||
notifyL2CacheInForeground = p.getBoolean("notifyL2CacheInForeground", notifyL2CacheInForeground);
|
||||
@@ -2896,6 +2967,7 @@ public class ServerConfig {
|
||||
localTimeWithNanos = p.getBoolean("localTimeWithNanos", localTimeWithNanos);
|
||||
jodaLocalTimeMode = p.get("jodaLocalTimeMode", jodaLocalTimeMode);
|
||||
|
||||
disableLazyLoading = p.getBoolean("disableLazyLoading", disableLazyLoading);
|
||||
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", lazyLoadBatchSize);
|
||||
queryBatchSize = p.getInt("queryBatchSize", queryBatchSize);
|
||||
|
||||
@@ -2909,6 +2981,7 @@ public class ServerConfig {
|
||||
|
||||
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
|
||||
ddlRun = p.getBoolean("ddl.run", ddlRun);
|
||||
ddlExtra = p.getBoolean("ddl.extra", ddlExtra);
|
||||
ddlCreateOnly = p.getBoolean("ddl.createOnly", ddlCreateOnly);
|
||||
ddlInitSql = p.get("ddl.initSql", ddlInitSql);
|
||||
ddlSeedSql = p.get("ddl.seedSql", ddlSeedSql);
|
||||
@@ -3192,6 +3265,20 @@ public class ServerConfig {
|
||||
this.idGeneratorAutomatic = idGeneratorAutomatic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if query plan capture is enabled.
|
||||
*/
|
||||
public boolean isCollectQueryPlans() {
|
||||
return collectQueryPlans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to enable query plan capture.
|
||||
*/
|
||||
public void setCollectQueryPlans(boolean collectQueryPlans) {
|
||||
this.collectQueryPlans = collectQueryPlans;
|
||||
}
|
||||
|
||||
public enum UuidVersion {
|
||||
VERSION4,
|
||||
VERSION1,
|
||||
|
||||
@@ -136,8 +136,9 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
/**
|
||||
* Return SQL using built in partition helper functions to create some initial partitions.
|
||||
*
|
||||
* Only use this if extra-dll doesn't have some initial partitions defined (which it should).
|
||||
* Only use this if extra-ddl doesn't have some initial partitions defined (which it should).
|
||||
*/
|
||||
@Override
|
||||
public String tablePartitionInit(String tableName, PartitionMode mode, String property, String pkey) {
|
||||
if (property == null) {
|
||||
property = "";
|
||||
|
||||
@@ -63,6 +63,7 @@ class Loader {
|
||||
*/
|
||||
private void loadTest() {
|
||||
loadProperties("application-test.properties", Source.RESOURCE);
|
||||
loadYaml("application-test.yaml", Source.RESOURCE);
|
||||
loadYaml("application-test.yml", Source.RESOURCE);
|
||||
loadProperties("test-ebean.properties", Source.RESOURCE);
|
||||
}
|
||||
@@ -83,6 +84,7 @@ class Loader {
|
||||
* Load the main configuration for the given source.
|
||||
*/
|
||||
private void loadMain(Source source) {
|
||||
loadYaml("application.yaml", source);
|
||||
loadYaml("application.yml", source);
|
||||
loadProperties("application.properties", source);
|
||||
loadProperties("ebean.properties", source);
|
||||
@@ -99,7 +101,7 @@ class Loader {
|
||||
}
|
||||
|
||||
void loadWithExtensionCheck(String fileName) {
|
||||
if (fileName.endsWith("yml")) {
|
||||
if (fileName.endsWith("yaml") || fileName.endsWith("yml")) {
|
||||
loadYaml(fileName, Source.FILE);
|
||||
} else if (fileName.endsWith("properties")) {
|
||||
loadProperties(fileName, Source.FILE);
|
||||
|
||||
@@ -18,14 +18,14 @@ public class PropertiesLoader {
|
||||
* <p>Firstly loads from main resources</p>
|
||||
* <pre>
|
||||
* - application.properties
|
||||
* - application.yml
|
||||
* - application.yaml
|
||||
* - ebean.properties
|
||||
* </pre>
|
||||
* <p>
|
||||
* <p>Then loads from local files</p>
|
||||
* <pre>
|
||||
* - application.properties
|
||||
* - application.yml
|
||||
* - application.yaml
|
||||
* - ebean.properties
|
||||
* </pre>
|
||||
* <p>
|
||||
@@ -41,7 +41,7 @@ public class PropertiesLoader {
|
||||
* <p>Loads from test resources</p>
|
||||
* <pre>
|
||||
* - application-test.properties
|
||||
* - application-test.yml
|
||||
* - application-test.yaml
|
||||
* - test-ebean.properties
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
@@ -154,7 +154,7 @@ public interface DbMigration {
|
||||
List<String> getPendingDrops();
|
||||
|
||||
/**
|
||||
* Generate the next migration xml file and associated apply and rollback sql scripts.
|
||||
* Generate the next migration sql script and associated model xml.
|
||||
* <p>
|
||||
* This does not run the migration or ddl scripts but just generates them.
|
||||
* </p>
|
||||
@@ -186,4 +186,15 @@ public interface DbMigration {
|
||||
* @return the version of the generated migration or null
|
||||
*/
|
||||
String generateMigration() throws IOException;
|
||||
|
||||
/**
|
||||
* Generate an "init" migration which has all changes.
|
||||
* <p>
|
||||
* An "init" migration can only be executed and used on a database that has had no
|
||||
* prior migrations run on it.
|
||||
* </p>
|
||||
* @return the version of the generated migration
|
||||
*/
|
||||
String generateInitMigration() throws IOException;
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,11 @@ import java.util.List;
|
||||
*/
|
||||
public interface MetaInfoManager {
|
||||
|
||||
/**
|
||||
* Collect query plans.
|
||||
*/
|
||||
List<MetaQueryPlan> collectQueryPlans(QueryPlanRequest request);
|
||||
|
||||
/**
|
||||
* Visit the metrics resetting and collecting/reporting as desired.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
public interface MetaQueryPlan {
|
||||
|
||||
Class<?> getBeanType();
|
||||
|
||||
/**
|
||||
* Return a string representation of the query plan hash.
|
||||
*/
|
||||
String getQueryPlanHash();
|
||||
|
||||
String getLabel();
|
||||
|
||||
String getSql();
|
||||
|
||||
String getBind();
|
||||
|
||||
String getPlan();
|
||||
|
||||
long getQueryTimeMicros();
|
||||
|
||||
long getCaptureCount();
|
||||
}
|
||||
@@ -25,6 +25,11 @@ public enum MetricType {
|
||||
* <p>
|
||||
* SqlQuery and SqlUpdate without a label have no metrics collected.
|
||||
*/
|
||||
SQL
|
||||
SQL,
|
||||
|
||||
/**
|
||||
* L2 cache metrics.
|
||||
*/
|
||||
L2
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Request used to capture query plans.
|
||||
*/
|
||||
public class QueryPlanRequest {
|
||||
|
||||
private List<MetaQueryPlan> plans = new ArrayList<>();
|
||||
|
||||
private Connection connection;
|
||||
|
||||
private boolean store;
|
||||
|
||||
private long since;
|
||||
|
||||
private Set<Class<?>> includedBeanTypes;
|
||||
|
||||
private Set<String> includedLabels;
|
||||
|
||||
public List<MetaQueryPlan> getPlans() {
|
||||
return plans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the connection to use to capture the query plans.
|
||||
*/
|
||||
public Connection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connection to use to capture the query plans.
|
||||
*/
|
||||
public void setConnection(Connection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the captured query plan is stored.
|
||||
*/
|
||||
public boolean isStore() {
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to store the captured query plan.
|
||||
*/
|
||||
public void setStore(boolean store) {
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the epoch time after which the query plan was capture (to be included).
|
||||
*/
|
||||
public long getSince() {
|
||||
return since;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the epoch time after which the query plan was captured.
|
||||
* <p>
|
||||
* This is used to only capture plans that have changed since a given time (like the time of last capture).
|
||||
* </p>
|
||||
*
|
||||
* @param since The time after which the query plan was captured to be included
|
||||
*/
|
||||
public void setSince(long since) {
|
||||
this.since = since;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process consume the query plan.
|
||||
*/
|
||||
public void process(MetaQueryPlan plan) {
|
||||
plans.add(plan);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the bean type should be included in the query plan capture.
|
||||
*/
|
||||
public boolean includeType(Class<?> beanType) {
|
||||
return includedBeanTypes == null || includedBeanTypes.isEmpty() || includedBeanTypes.contains(beanType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the label should be included in the query plan capture.
|
||||
*/
|
||||
public boolean includeLabel(String label) {
|
||||
return includedLabels == null || includedLabels.isEmpty() || includedLabels.contains(label);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package io.ebeaninternal.metric;
|
||||
package io.ebean.metric;
|
||||
|
||||
import io.ebean.meta.MetricType;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package io.ebeaninternal.metric;
|
||||
package io.ebean.metric;
|
||||
|
||||
import io.ebeaninternal.server.profile.DMetricFactory;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package io.ebeaninternal.metric;
|
||||
package io.ebean.metric;
|
||||
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package io.ebeaninternal.metric;
|
||||
package io.ebean.metric;
|
||||
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
|
||||
+11
-1
@@ -1,4 +1,4 @@
|
||||
package io.ebeaninternal.metric;
|
||||
package io.ebean.metric;
|
||||
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
|
||||
@@ -17,6 +17,16 @@ public interface TimedMetric {
|
||||
*/
|
||||
void add(long micros, long beans);
|
||||
|
||||
/**
|
||||
* Add a time event given the start nanos.
|
||||
*/
|
||||
void addSinceNanos(long startNanos);
|
||||
|
||||
/**
|
||||
* Add a time event given the start nanos and bean count.
|
||||
*/
|
||||
void addSinceNanos(long startNanos, long beans);
|
||||
|
||||
/**
|
||||
* Return true if there are no metrics collected since the last collection.
|
||||
*/
|
||||
+11
-1
@@ -1,4 +1,4 @@
|
||||
package io.ebeaninternal.metric;
|
||||
package io.ebean.metric;
|
||||
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
|
||||
@@ -7,6 +7,16 @@ import io.ebean.meta.MetricVisitor;
|
||||
*/
|
||||
public interface TimedMetricMap {
|
||||
|
||||
/**
|
||||
* Add a time event given the start nanos.
|
||||
*/
|
||||
void addSinceNanos(String key, long startNanos);
|
||||
|
||||
/**
|
||||
* Add a time event given the start nanos and beans.
|
||||
*/
|
||||
void addSinceNanos(String key, long startNanos, int beans);
|
||||
|
||||
/**
|
||||
* Add an execution for the given key.
|
||||
*/
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package io.ebeaninternal.metric;
|
||||
package io.ebean.metric;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
|
||||
@@ -73,4 +73,14 @@ public interface ExpressionPath {
|
||||
* Return the Id values for the given bean value.
|
||||
*/
|
||||
Object[] getAssocIdValues(EntityBean bean);
|
||||
|
||||
/**
|
||||
* Return the underlying bean property.
|
||||
*/
|
||||
public Property getProperty();
|
||||
|
||||
/**
|
||||
* The ElPrefix plus name.
|
||||
*/
|
||||
String getElName();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.ebean.plugin;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
|
||||
/**
|
||||
* Errorhandler to handle load errors and may be recover correct value.
|
||||
*
|
||||
* @author Roland Praml, FOCONIS AG
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface LoadErrorHandler {
|
||||
void handleLoadError(EntityBean bean, Property prop, String fullName, Exception e);
|
||||
}
|
||||
@@ -6,6 +6,7 @@ package io.ebean.text;
|
||||
public class TextException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1601310159486033148L;
|
||||
private String text;
|
||||
|
||||
/**
|
||||
* Construct with an error message.
|
||||
@@ -27,4 +28,41 @@ public class TextException extends RuntimeException {
|
||||
public TextException(Exception e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a detailed exception.
|
||||
*
|
||||
* @param message
|
||||
* the message. The placeholder {} will be replaced by
|
||||
* <code>text</code>
|
||||
* @param text
|
||||
* the fault text.
|
||||
* @param cause
|
||||
* the case
|
||||
*/
|
||||
public TextException(String message, String text, Exception cause) {
|
||||
super(message.replace("{}", String.valueOf(text)), cause);
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a detailed exception.
|
||||
*
|
||||
* @param message
|
||||
* the message. The placeholder {} will be replaced by
|
||||
* <code>text</code>
|
||||
* @param text
|
||||
* the fault text.
|
||||
*/
|
||||
public TextException(String message, String text) {
|
||||
super(message.replace("{}", String.valueOf(text)));
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text, that caused the error. (e.g. the JSON). May be null.
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ public class CamelCaseHelper {
|
||||
} else if (Character.isDigit(c)) {
|
||||
if (i > lastUpper + 1 && !digitsCompressed) {
|
||||
sb.append("_");
|
||||
lastUpper = i;
|
||||
}
|
||||
sb.append(c);
|
||||
lastUpper = i;
|
||||
|
||||
} else if (Character.isUpperCase(c)) {
|
||||
if (i > lastUpper + 1) {
|
||||
|
||||
@@ -168,13 +168,8 @@ public class LoadBeanRequest extends LoadRequest {
|
||||
// necessary but allow processing to continue until it is accessed by client code
|
||||
Object id = desc.getId(ebi.getOwner());
|
||||
if (!loadedIds.contains(id)) {
|
||||
if (desc.isSoftDelete()) {
|
||||
// assume this is logically deleted (hence not found)
|
||||
desc.setSoftDeleteValue(ebi.getOwner());
|
||||
} else {
|
||||
logger.info("Lazy loading unsuccessful for type:" + desc.getName() + " id:" + id + " - expecting when bean has been deleted");
|
||||
ebi.setLazyLoadFailure(id);
|
||||
}
|
||||
// assume this is logically deleted (hence not found)
|
||||
desc.markAsDeleted(ebi.getOwner());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package io.ebeaninternal.api;
|
||||
import io.ebean.Pairs;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -25,7 +24,7 @@ public class NaturalKeyQueryData<T> {
|
||||
private List<Pairs.Entry> inPairs;
|
||||
|
||||
// IN clause - only one allowed
|
||||
private Collection<?> inValues;
|
||||
private List<Object> inValues;
|
||||
private String inProperty;
|
||||
|
||||
// normal EQ expressions
|
||||
@@ -52,36 +51,36 @@ public class NaturalKeyQueryData<T> {
|
||||
/**
|
||||
* Match for In Pairs expression. We only allow one IN clause.
|
||||
*/
|
||||
public boolean matchInPairs(Pairs pairs) {
|
||||
public List<Pairs.Entry> matchInPairs(String property0, String property1, List<Pairs.Entry> inPairs) {
|
||||
if (hasIn) {
|
||||
// only 1 IN allowed (to project naturalIds)
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
if (matchProperty(pairs.getProperty0()) && matchProperty(pairs.getProperty1())) {
|
||||
if (matchProperty(property0) && matchProperty(property1)) {
|
||||
this.hasIn = true;
|
||||
this.inProperty0 = pairs.getProperty0();
|
||||
this.inProperty1 = pairs.getProperty1();
|
||||
this.inPairs = pairs.getEntries();
|
||||
return true;
|
||||
this.inProperty0 = property0;
|
||||
this.inProperty1 = property1;
|
||||
this.inPairs = new ArrayList<>(inPairs); // will be modified
|
||||
return this.inPairs;
|
||||
}
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match for IN expression. We only allow one IN clause.
|
||||
*/
|
||||
public boolean matchIn(String propName, Collection<?> sourceValues) {
|
||||
public List<Object> matchIn(String propName, List<Object> sourceValues) {
|
||||
if (hasIn) {
|
||||
// only 1 IN allowed (to project naturalIds)
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
if (matchProperty(propName)) {
|
||||
this.hasIn = true;
|
||||
this.inProperty = propName;
|
||||
this.inValues = sourceValues;
|
||||
return true;
|
||||
this.inValues = new ArrayList<>(sourceValues);
|
||||
return this.inValues;
|
||||
}
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.TxScope;
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -38,9 +37,9 @@ public class ScopeTrans {
|
||||
*/
|
||||
private final ArrayList<Class<? extends Throwable>> rollbackFor;
|
||||
|
||||
private PersistBatch restoreBatch;
|
||||
private Boolean restoreBatch;
|
||||
|
||||
private PersistBatch restoreBatchOnCascade;
|
||||
private Boolean restoreBatchOnCascade;
|
||||
|
||||
private int restoreBatchSize;
|
||||
|
||||
@@ -68,20 +67,20 @@ public class ScopeTrans {
|
||||
|
||||
if (transaction != null) {
|
||||
if (!created && txScope.isBatchSet() || txScope.isBatchOnCascadeSet() || txScope.isBatchSizeSet()) {
|
||||
restoreBatch = transaction.getBatch();
|
||||
restoreBatchOnCascade = transaction.getBatchOnCascade();
|
||||
restoreBatch = transaction.isBatchMode();
|
||||
restoreBatchOnCascade = transaction.isBatchOnCascade();
|
||||
restoreBatchSize = transaction.getBatchSize();
|
||||
restoreBatchGeneratedKeys = transaction.getBatchGetGeneratedKeys();
|
||||
restoreBatchFlushOnQuery = transaction.isBatchFlushOnQuery();
|
||||
}
|
||||
if (txScope.isBatchSet()) {
|
||||
transaction.setBatch(txScope.getBatch());
|
||||
transaction.setBatchMode(txScope.isBatchMode());
|
||||
}
|
||||
if (!txScope.isFlushOnQuery()) {
|
||||
transaction.setBatchFlushOnQuery(false);
|
||||
}
|
||||
if (txScope.isBatchOnCascadeSet()) {
|
||||
transaction.setBatchOnCascade(txScope.getBatchOnCascade());
|
||||
transaction.setBatchOnCascade(txScope.isBatchOnCascade());
|
||||
}
|
||||
if (txScope.isBatchSizeSet()) {
|
||||
transaction.setBatchSize(txScope.getBatchSize());
|
||||
@@ -139,7 +138,7 @@ public class ScopeTrans {
|
||||
nestedCommit = true;
|
||||
transaction.setBatchFlushOnQuery(restoreBatchFlushOnQuery);
|
||||
if (restoreBatch != null) {
|
||||
transaction.setBatch(restoreBatch);
|
||||
transaction.setBatchMode(restoreBatch);
|
||||
}
|
||||
if (restoreBatchOnCascade != null) {
|
||||
transaction.setBatchOnCascade(restoreBatchOnCascade);
|
||||
|
||||
@@ -37,6 +37,7 @@ public interface SpiDtoQuery<T> extends DtoQuery<T>, SpiSqlBinding {
|
||||
/**
|
||||
* Return the label for the query.
|
||||
*/
|
||||
@Override
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
|
||||
@@ -749,23 +749,6 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
*/
|
||||
boolean isDisableLazyLoading();
|
||||
|
||||
/**
|
||||
* Internally set by Ebean when this query must use the DISTINCT keyword.
|
||||
* <p>
|
||||
* This does not exclude/remove the use of the id property.
|
||||
*/
|
||||
void setSqlDistinct(boolean sqlDistinct);
|
||||
|
||||
/**
|
||||
* Return true if this query has been specified by a user or internally by Ebean to use DISTINCT.
|
||||
*/
|
||||
boolean isDistinctQuery();
|
||||
|
||||
/**
|
||||
* Return true if this was internally set to sql distinct (ie. many where predicate).
|
||||
*/
|
||||
boolean isSqlDistinct();
|
||||
|
||||
/**
|
||||
* Return true if this query has been specified by a user to use DISTINCT.
|
||||
*/
|
||||
@@ -837,6 +820,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
*/
|
||||
boolean isCancelled();
|
||||
|
||||
/**
|
||||
* Return the base table to use if user defined on the query.
|
||||
*/
|
||||
String getBaseTable();
|
||||
|
||||
/**
|
||||
* Return root table alias set by {@link #alias(String)} command.
|
||||
*/
|
||||
@@ -861,4 +849,9 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
* Returns the count distinct order setting.
|
||||
*/
|
||||
CountDistinctOrder getCountDistinctOrder();
|
||||
|
||||
/**
|
||||
* Handles load errors.
|
||||
*/
|
||||
void handleLoadError(String fullName, Exception e);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
import io.ebeaninternal.server.core.PersistRequest;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeaninternal.server.transaction.ProfileStream;
|
||||
|
||||
@@ -3,12 +3,10 @@ package io.ebeaninternal.api;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.TransactionCallback;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
import io.ebeaninternal.server.core.PersistRequest;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeaninternal.server.transaction.ProfileStream;
|
||||
@@ -255,36 +253,16 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
return transaction.isBatchMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatch(PersistBatch persistBatchMode) {
|
||||
transaction.setBatch(persistBatchMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistBatch getBatch() {
|
||||
return transaction.getBatch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatchOnCascade(boolean batchMode) {
|
||||
transaction.setBatchOnCascade(batchMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatchOnCascade(PersistBatch batchOnCascadeMode) {
|
||||
transaction.setBatchOnCascade(batchOnCascadeMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBatchOnCascade() {
|
||||
return transaction.isBatchOnCascade();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistBatch getBatchOnCascade() {
|
||||
return transaction.getBatchOnCascade();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatchSize(int batchSize) {
|
||||
transaction.setBatchSize(batchSize);
|
||||
|
||||
@@ -9,6 +9,7 @@ import io.ebeaninternal.server.transaction.TransactionManager;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdates;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -29,16 +30,21 @@ public class TransactionEvent implements Serializable {
|
||||
*/
|
||||
private final transient boolean local;
|
||||
|
||||
private final long startMillis;
|
||||
|
||||
private TransactionEventTable eventTables;
|
||||
|
||||
private transient TransactionEventBeans eventBeans;
|
||||
private transient List<PersistRequestBean<?>> listenerNotify;
|
||||
|
||||
private transient DeleteByIdMap deleteByIdMap;
|
||||
|
||||
private transient CacheChangeSet changeSet;
|
||||
|
||||
/**
|
||||
* Create the TransactionEvent, one per Transaction.
|
||||
*/
|
||||
public TransactionEvent() {
|
||||
public TransactionEvent(long startMillis) {
|
||||
this.startMillis = startMillis;
|
||||
this.local = true;
|
||||
}
|
||||
|
||||
@@ -71,8 +77,8 @@ public class TransactionEvent implements Serializable {
|
||||
/**
|
||||
* Return the list of PersistRequestBean's for this transaction.
|
||||
*/
|
||||
public List<PersistRequestBean<?>> getPersistRequestBeans() {
|
||||
return (eventBeans == null) ? null : eventBeans.getRequests();
|
||||
public List<PersistRequestBean<?>> getListenerNotify() {
|
||||
return (listenerNotify == null) ? null : listenerNotify;
|
||||
}
|
||||
|
||||
public TransactionEventTable getEventTables() {
|
||||
@@ -94,17 +100,13 @@ public class TransactionEvent implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a inserted updated or deleted bean to the event.
|
||||
* Add post commit listeners. Watch this for large transactions.
|
||||
*/
|
||||
public void add(PersistRequestBean<?> request) {
|
||||
|
||||
if (request.isNotify()) {
|
||||
// either a BeanListener or Cache is interested
|
||||
if (eventBeans == null) {
|
||||
eventBeans = new TransactionEventBeans();
|
||||
}
|
||||
eventBeans.add(request);
|
||||
public void addListenerNotify(PersistRequestBean<?> request) {
|
||||
if (listenerNotify == null) {
|
||||
listenerNotify = new ArrayList<>();
|
||||
}
|
||||
listenerNotify.add(request);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,11 +114,13 @@ public class TransactionEvent implements Serializable {
|
||||
*/
|
||||
public CacheChangeSet buildCacheChanges(TransactionManager manager) {
|
||||
|
||||
if (eventBeans == null && deleteByIdMap == null && eventTables == null) {
|
||||
if (changeSet == null && deleteByIdMap == null && eventTables == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CacheChangeSet changeSet = new CacheChangeSet(manager.clockNowMillis());
|
||||
if (changeSet == null) {
|
||||
changeSet = new CacheChangeSet(manager.clockNowMillis());
|
||||
}
|
||||
if (eventTables != null && !eventTables.isEmpty()) {
|
||||
// notify cache with table based changes
|
||||
BeanDescriptorManager dm = manager.getBeanDescriptorManager();
|
||||
@@ -124,9 +128,6 @@ public class TransactionEvent implements Serializable {
|
||||
dm.cacheNotify(tableIUD, changeSet);
|
||||
}
|
||||
}
|
||||
if (eventBeans != null) {
|
||||
eventBeans.notifyCache(changeSet);
|
||||
}
|
||||
if (deleteByIdMap != null) {
|
||||
deleteByIdMap.notifyCache(changeSet);
|
||||
}
|
||||
@@ -137,12 +138,25 @@ public class TransactionEvent implements Serializable {
|
||||
* Add any relevant PersistRequestBean's to DocStoreUpdates for later processing.
|
||||
*/
|
||||
public void addDocStoreUpdates(DocStoreUpdates docStoreUpdates) {
|
||||
|
||||
List<PersistRequestBean<?>> persistRequestBeans = getPersistRequestBeans();
|
||||
if (persistRequestBeans != null) {
|
||||
for (PersistRequestBean<?> persistRequestBean : persistRequestBeans) {
|
||||
List<PersistRequestBean<?>> requests = getListenerNotify();
|
||||
if (requests != null) {
|
||||
for (PersistRequestBean<?> persistRequestBean : requests) {
|
||||
persistRequestBean.addDocStoreUpdates(docStoreUpdates);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the CacheChangeSet that we add cache notification messages to.
|
||||
*
|
||||
* We want to add to this change set as we process requests allowing the
|
||||
* PersistRequestBean to be garbage collected for large transactions.
|
||||
*/
|
||||
public CacheChangeSet obtainCacheChangeSet() {
|
||||
if (changeSet == null) {
|
||||
changeSet = new CacheChangeSet(startMillis);
|
||||
}
|
||||
return changeSet;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.cache.CacheChangeSet;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Lists of inserted updated and deleted beans that have a BeanPersistListener.
|
||||
* <p>
|
||||
* These beans will be sent to the appropriate BeanListeners after a successful
|
||||
* commit of the transaction.
|
||||
* </p>
|
||||
*/
|
||||
public class TransactionEventBeans {
|
||||
|
||||
final ArrayList<PersistRequestBean<?>> requests = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Return the list of PersistRequests that BeanListeners are interested in.
|
||||
*/
|
||||
public List<PersistRequestBean<?>> getRequests() {
|
||||
return requests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a bean for BeanListener notification.
|
||||
*/
|
||||
public void add(PersistRequestBean<?> request) {
|
||||
|
||||
requests.add(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the cache changes.
|
||||
*/
|
||||
public void notifyCache(CacheChangeSet changeSet) {
|
||||
for (PersistRequestBean<?> request : requests) {
|
||||
request.notifyCache(changeSet);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package io.ebeaninternal.dbmigration;
|
||||
|
||||
import io.ebean.config.DbMigrationConfig;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.migration.ddl.DdlRunner;
|
||||
import io.ebean.migration.runner.ScriptTransform;
|
||||
import io.ebean.util.JdbcClose;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.dbmigration.model.CurrentModel;
|
||||
@@ -38,10 +40,12 @@ public class DdlGenerator {
|
||||
|
||||
private final boolean generateDdl;
|
||||
private final boolean runDdl;
|
||||
private final boolean extraDdl;
|
||||
private final boolean createOnly;
|
||||
private final boolean jaxbPresent;
|
||||
private final boolean ddlCommitOnCreateIndex;
|
||||
private final String dbSchema;
|
||||
private final ScriptTransform scriptTransform;
|
||||
|
||||
private CurrentModel currentModel;
|
||||
private String dropAllContent;
|
||||
@@ -51,6 +55,7 @@ public class DdlGenerator {
|
||||
this.server = server;
|
||||
this.jaxbPresent = serverConfig.getClassLoadConfig().isJavaxJAXBPresent();
|
||||
this.generateDdl = serverConfig.isDdlGenerate();
|
||||
this.extraDdl = serverConfig.isDdlExtra();
|
||||
this.createOnly = serverConfig.isDdlCreateOnly();
|
||||
this.dbSchema = serverConfig.getDbSchema();
|
||||
if (!serverConfig.getTenantMode().isDdlEnabled() && serverConfig.isDdlRun()) {
|
||||
@@ -61,6 +66,7 @@ public class DdlGenerator {
|
||||
this.runDdl = serverConfig.isDdlRun();
|
||||
this.ddlCommitOnCreateIndex = server.getDatabasePlatform().isDdlCommitOnCreateIndex();
|
||||
}
|
||||
this.scriptTransform = createScriptTransform(serverConfig.getMigrationConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,7 +132,9 @@ public class DdlGenerator {
|
||||
|
||||
private void createSchemaIfRequired(Connection connection) {
|
||||
try {
|
||||
server.getDatabasePlatform().createSchemaIfNotExists(dbSchema, connection);
|
||||
for (String schema : dbSchema.split(",")) {
|
||||
server.getDatabasePlatform().createSchemaIfNotExists(schema, connection);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException("Failed to create DB Schema", e);
|
||||
}
|
||||
@@ -144,7 +152,7 @@ public class DdlGenerator {
|
||||
} else if (ddlCommitOnCreateIndex) {
|
||||
runner.setCommitOnCreateIndex();
|
||||
}
|
||||
int count = runner.runAll(content, connection);
|
||||
int count = runner.runAll(scriptTransform.transform(content), connection);
|
||||
if (expectErrors) {
|
||||
connection.setAutoCommit(false);
|
||||
}
|
||||
@@ -160,11 +168,10 @@ public class DdlGenerator {
|
||||
|
||||
protected void runDropSql(Connection connection) throws IOException {
|
||||
if (!createOnly) {
|
||||
String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl");
|
||||
if (!"true".equalsIgnoreCase(ignoreExtraDdl) && jaxbPresent) {
|
||||
if (extraDdl && jaxbPresent) {
|
||||
String extraApply = ExtraDdlXmlReader.buildExtra(server.getDatabasePlatform().getName(), true);
|
||||
if (extraApply != null) {
|
||||
runScript(connection, false, extraApply, "extra-dll");
|
||||
runScript(connection, false, extraApply, "extra-ddl");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,18 +188,17 @@ public class DdlGenerator {
|
||||
}
|
||||
runScript(connection, false, createAllContent, getCreateFileName());
|
||||
|
||||
String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl");
|
||||
if (!"true".equalsIgnoreCase(ignoreExtraDdl) && jaxbPresent) {
|
||||
if (extraDdl && jaxbPresent) {
|
||||
if (currentModel.isTablePartitioning()) {
|
||||
String extraPartitioning = ExtraDdlXmlReader.buildPartitioning(server.getDatabasePlatform().getName());
|
||||
if (extraPartitioning != null && !extraPartitioning.isEmpty()) {
|
||||
runScript(connection, false, extraPartitioning, "builtin-partitioning-dll");
|
||||
runScript(connection, false, extraPartitioning, "builtin-partitioning-ddl");
|
||||
}
|
||||
}
|
||||
|
||||
String extraApply = ExtraDdlXmlReader.buildExtra(server.getDatabasePlatform().getName(), false);
|
||||
if (extraApply != null) {
|
||||
runScript(connection, false, extraApply, "extra-dll");
|
||||
runScript(connection, false, extraApply, "extra-ddl");
|
||||
}
|
||||
|
||||
if (currentModel.isTablePartitioning()) {
|
||||
@@ -203,7 +209,7 @@ public class DdlGenerator {
|
||||
|
||||
/**
|
||||
* Check if table partitions exist and if not create some. The expectation is that
|
||||
* extra-dll.xml should have some partition initialisation but this helps people get going.
|
||||
* extra-ddl.xml should have some partition initialisation but this helps people get going.
|
||||
*/
|
||||
private void checkInitialTablePartitions(Connection connection) {
|
||||
|
||||
@@ -342,8 +348,15 @@ public class DdlGenerator {
|
||||
buf.append(s).append("\n");
|
||||
}
|
||||
return buf.toString();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the ScriptTransform for placeholder key/value replacement.
|
||||
*/
|
||||
private ScriptTransform createScriptTransform(DbMigrationConfig config) {
|
||||
|
||||
return ScriptTransform.build(config.getRunPlaceholders(), config.getRunPlaceholderMap());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -271,6 +271,15 @@ public class DefaultDbMigration implements DbMigration {
|
||||
*/
|
||||
@Override
|
||||
public String generateMigration() throws IOException {
|
||||
return generateMigrationFor(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateInitMigration() throws IOException {
|
||||
return generateMigrationFor(true);
|
||||
}
|
||||
|
||||
private String generateMigrationFor(boolean dbinitMigration) throws IOException {
|
||||
|
||||
// use this flag to stop other plugins like full DDL generation
|
||||
if (!online) {
|
||||
@@ -286,9 +295,18 @@ public class DefaultDbMigration implements DbMigration {
|
||||
configurePlatforms();
|
||||
}
|
||||
try {
|
||||
Request request = createRequest();
|
||||
if (platforms.isEmpty()) {
|
||||
generateExtraDdl(request.migrationDir, databasePlatform, request.isTablePartitioning());
|
||||
Request request = createRequest(dbinitMigration);
|
||||
if (!dbinitMigration) {
|
||||
// repeatable migrations
|
||||
if (platforms.isEmpty()) {
|
||||
generateExtraDdl(request.migrationDir, databasePlatform, request.isTablePartitioning());
|
||||
} else {
|
||||
for (Pair pair : platforms) {
|
||||
PlatformDdlWriter platformWriter = createDdlWriter(pair.platform);
|
||||
File subPath = platformWriter.subPath(request.migrationDir, pair.prefix);
|
||||
generateExtraDdl(subPath, pair.platform, request.isTablePartitioning());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String pendingVersion = generatePendingDrop();
|
||||
@@ -308,13 +326,14 @@ public class DefaultDbMigration implements DbMigration {
|
||||
/**
|
||||
* Return the versions containing pending drops.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getPendingDrops() {
|
||||
if (!online) {
|
||||
DbOffline.setGenerateMigration();
|
||||
}
|
||||
setDefaults();
|
||||
try {
|
||||
return createRequest().getPendingDrops();
|
||||
return createRequest(false).getPendingDrops();
|
||||
} finally {
|
||||
if (!online) {
|
||||
DbOffline.reset();
|
||||
@@ -335,7 +354,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
/**
|
||||
* Generate "repeatable" migration scripts.
|
||||
* <p>
|
||||
* These take scrips from extra-dll.xml (typically views) and outputs "repeatable"
|
||||
* These take scrips from extra-ddl.xml (typically views) and outputs "repeatable"
|
||||
* migration scripts (starting with "R__") to be run by FlywayDb or Ebean's own
|
||||
* migration runner.
|
||||
* </p>
|
||||
@@ -426,36 +445,52 @@ public class DefaultDbMigration implements DbMigration {
|
||||
return version;
|
||||
}
|
||||
|
||||
private Request createRequest() {
|
||||
return new Request();
|
||||
private Request createRequest(boolean dbinitMigration) {
|
||||
return new Request(dbinitMigration);
|
||||
}
|
||||
|
||||
private class Request {
|
||||
|
||||
final boolean dbinitMigration;
|
||||
final File migrationDir;
|
||||
final File modelDir;
|
||||
final MigrationModel migrationModel;
|
||||
final CurrentModel currentModel;
|
||||
final ModelContainer migrated;
|
||||
final ModelContainer current;
|
||||
|
||||
private Request() {
|
||||
this.migrationDir = getMigrationDirectory();
|
||||
this.modelDir = getModelDirectory(migrationDir);
|
||||
this.migrationModel = new MigrationModel(modelDir, migrationConfig.getModelSuffix());
|
||||
this.migrated = migrationModel.read();
|
||||
private Request(boolean dbinitMigration) {
|
||||
this.dbinitMigration = dbinitMigration;
|
||||
this.currentModel = new CurrentModel(server, constraintNaming);
|
||||
this.current = currentModel.read();
|
||||
this.migrationDir = getMigrationDirectory(dbinitMigration);
|
||||
if (dbinitMigration) {
|
||||
this.modelDir = null;
|
||||
this.migrated = new ModelContainer();
|
||||
} else {
|
||||
this.modelDir = getModelDirectory(migrationDir);
|
||||
MigrationModel migrationModel = new MigrationModel(modelDir, migrationConfig.getModelSuffix());
|
||||
this.migrated = migrationModel.read(dbinitMigration);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isTablePartitioning() {
|
||||
return current.isTablePartitioning();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next migration version (based on existing migration versions).
|
||||
*/
|
||||
String nextVersion() {
|
||||
// always read the next version using the main migration directory (not dbinit)
|
||||
File migDirectory = getMigrationDirectory(false);
|
||||
File modelDir = getModelDirectory(migDirectory);
|
||||
return LastMigration.nextVersion(migDirectory, modelDir, dbinitMigration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the migration for the pending drops for a given version.
|
||||
*/
|
||||
public Migration migrationForPendingDrop(String pendingVersion) {
|
||||
Migration migrationForPendingDrop(String pendingVersion) {
|
||||
|
||||
Migration migration = migrated.migrationForPendingDrop(pendingVersion);
|
||||
|
||||
@@ -474,7 +509,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
/**
|
||||
* Create and return the diff of the current model to the migration model.
|
||||
*/
|
||||
public Migration createDiffMigration() {
|
||||
Migration createDiffMigration() {
|
||||
ModelDiff diff = new ModelDiff(migrated);
|
||||
diff.compareTo(current);
|
||||
return diff.isEmpty() ? null : diff.getMigration();
|
||||
@@ -483,10 +518,10 @@ public class DefaultDbMigration implements DbMigration {
|
||||
|
||||
private String generateMigration(Request request, Migration dbMigration, String dropsFor) throws IOException {
|
||||
|
||||
String fullVersion = getFullVersion(request.migrationModel, dropsFor);
|
||||
String fullVersion = getFullVersion(request.nextVersion(), dropsFor);
|
||||
|
||||
logger.info("generating migration:{}", fullVersion);
|
||||
if (!writeMigrationXml(dbMigration, request.modelDir, fullVersion)) {
|
||||
if (!request.dbinitMigration && !writeMigrationXml(dbMigration, request.modelDir, fullVersion)) {
|
||||
logger.warn("migration already exists, not generating DDL");
|
||||
return null;
|
||||
} else {
|
||||
@@ -521,11 +556,11 @@ public class DefaultDbMigration implements DbMigration {
|
||||
* <p>
|
||||
* The full version can contain a comment suffix after a "__" double underscore.
|
||||
*/
|
||||
private String getFullVersion(MigrationModel migrationModel, String dropsFor) {
|
||||
private String getFullVersion(String nextVersion, String dropsFor) {
|
||||
|
||||
String version = getVersion();
|
||||
if (version == null) {
|
||||
version = migrationModel.getNextVersion(initialVersion);
|
||||
version = (nextVersion != null) ? nextVersion : initialVersion;
|
||||
}
|
||||
|
||||
String fullVersion = migrationConfig.getApplyPrefix() + version;
|
||||
@@ -552,15 +587,13 @@ public class DefaultDbMigration implements DbMigration {
|
||||
/**
|
||||
* Write any extra platform ddl.
|
||||
*/
|
||||
protected void writeExtraPlatformDdl(String fullVersion, CurrentModel currentModel, Migration dbMigration, File writePath) throws IOException {
|
||||
private void writeExtraPlatformDdl(String fullVersion, CurrentModel currentModel, Migration dbMigration, File writePath) throws IOException {
|
||||
|
||||
for (Pair pair : platforms) {
|
||||
DdlWrite platformBuffer = new DdlWrite(new MConfiguration(), currentModel.read());
|
||||
PlatformDdlWriter platformWriter = createDdlWriter(pair.platform);
|
||||
File subPath = platformWriter.subPath(writePath, pair.prefix);
|
||||
platformWriter.processMigration(dbMigration, platformBuffer, subPath, fullVersion);
|
||||
|
||||
generateExtraDdl(subPath, pair.platform, currentModel.isTablePartitioning());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -571,7 +604,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
/**
|
||||
* Write the migration xml.
|
||||
*/
|
||||
protected boolean writeMigrationXml(Migration dbMigration, File resourcePath, String fullVersion) {
|
||||
private boolean writeMigrationXml(Migration dbMigration, File resourcePath, String fullVersion) {
|
||||
|
||||
String modelFile = fullVersion + migrationConfig.getModelSuffix();
|
||||
File file = new File(resourcePath, modelFile);
|
||||
@@ -587,7 +620,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
/**
|
||||
* Set default server and platform if necessary.
|
||||
*/
|
||||
protected void setDefaults() {
|
||||
private void setDefaults() {
|
||||
if (server == null) {
|
||||
setServer(Ebean.getDefaultServer());
|
||||
}
|
||||
@@ -666,14 +699,21 @@ public class DefaultDbMigration implements DbMigration {
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the main migration directory.
|
||||
*/
|
||||
File getMigrationDirectory() {
|
||||
return getMigrationDirectory(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file path to write the xml and sql to.
|
||||
*/
|
||||
protected File getMigrationDirectory() {
|
||||
File getMigrationDirectory(boolean dbinitMigration) {
|
||||
|
||||
// path to src/main/resources in typical maven project
|
||||
File resourceRootDir = new File(pathToResources);
|
||||
String resourcePath = migrationConfig.getMigrationPath();
|
||||
String resourcePath = migrationConfig.getMigrationPath(dbinitMigration);
|
||||
|
||||
// expect to be a path to something like - src/main/resources/dbmigration/model
|
||||
File path = new File(resourceRootDir, resourcePath);
|
||||
@@ -688,7 +728,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
/**
|
||||
* Return the model directory (relative to the migration directory).
|
||||
*/
|
||||
protected File getModelDirectory(File migrationDirectory) {
|
||||
private File getModelDirectory(File migrationDirectory) {
|
||||
String modelPath = migrationConfig.getModelPath();
|
||||
if (modelPath == null || modelPath.isEmpty()) {
|
||||
return migrationDirectory;
|
||||
@@ -741,19 +781,19 @@ public class DefaultDbMigration implements DbMigration {
|
||||
* Holds a platform and prefix. Used to generate multiple platform specific DDL
|
||||
* for a single migration.
|
||||
*/
|
||||
public static class Pair {
|
||||
static class Pair {
|
||||
|
||||
/**
|
||||
* The platform to generate the DDL for.
|
||||
*/
|
||||
public final DatabasePlatform platform;
|
||||
final DatabasePlatform platform;
|
||||
|
||||
/**
|
||||
* A prefix included into the file/resource names indicating the platform.
|
||||
*/
|
||||
public final String prefix;
|
||||
final String prefix;
|
||||
|
||||
public Pair(DatabasePlatform platform, String prefix) {
|
||||
Pair(DatabasePlatform platform, String prefix) {
|
||||
this.platform = platform;
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package io.ebeaninternal.dbmigration;
|
||||
|
||||
import io.ebeaninternal.dbmigration.model.MigrationVersion;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility to determine the last sql migration version and next version.
|
||||
*/
|
||||
class LastMigration {
|
||||
|
||||
private static final String SQL = ".sql";
|
||||
|
||||
private static final String MODEL_XML = ".model.xml";
|
||||
|
||||
/**
|
||||
* Return the next migation version given the migration directory.
|
||||
*/
|
||||
static String nextVersion(File migDir, File modelDir, boolean dbinitMigration) {
|
||||
|
||||
String last = lastVersion(migDir, modelDir);
|
||||
if (last == null) {
|
||||
return null;
|
||||
}
|
||||
return (dbinitMigration) ? last : MigrationVersion.parse(last).nextVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last migation version given the migration directory.
|
||||
*/
|
||||
static String lastVersion(File migDirectory, File modelDir) {
|
||||
|
||||
List<String> fileNames = new ArrayList<>();
|
||||
|
||||
File[] sqlFiles = migDirectory.listFiles(pathname -> includeSqlFile(pathname.getName().toLowerCase()));
|
||||
if (sqlFiles != null) {
|
||||
for (File file : sqlFiles) {
|
||||
fileNames.add(trim(file.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
if (modelDir != null) {
|
||||
File[] xmlFiles = modelDir.listFiles(pathname -> includeModelFile(pathname.getName().toLowerCase()));
|
||||
if (xmlFiles != null) {
|
||||
for (File file : xmlFiles) {
|
||||
fileNames.add(trim(file.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(fileNames);
|
||||
if (!fileNames.isEmpty()) {
|
||||
return fileNames.get(fileNames.size() - 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean includeSqlFile(String lowerFileName) {
|
||||
if (lowerFileName.startsWith("r") || lowerFileName.startsWith("i") || !lowerFileName.endsWith(SQL)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean includeModelFile(String lowerFileName) {
|
||||
return lowerFileName.endsWith(MODEL_XML);
|
||||
}
|
||||
|
||||
private static String trim(String name) {
|
||||
name = name.toLowerCase();
|
||||
char c = name.charAt(0);
|
||||
if (c == 'v') {
|
||||
name = name.substring(1);
|
||||
}
|
||||
int p = name.indexOf("__");
|
||||
if (p > -1) {
|
||||
name = name.substring(0, p);
|
||||
}
|
||||
if (name.endsWith(SQL)) {
|
||||
name = name.substring(0, name.length() - 4);
|
||||
}
|
||||
if (name.endsWith(MODEL_XML)) {
|
||||
name = name.substring(0, name.length() - 10);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -909,7 +909,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
|
||||
if (isTrue(alter.isWithHistory()) && alter.getType() != null && historySupport == historySupport.TRIGGER_BASED) {
|
||||
if (isTrue(alter.isWithHistory()) && alter.getType() != null && historySupport == HistorySupport.TRIGGER_BASED) {
|
||||
// mysql and sql server column type change allowing nulls in the history table column
|
||||
regenerateHistoryTriggers(alter.getTableName(), HistoryTableUpdate.Change.ALTER, alter.getColumnName());
|
||||
AlterColumn alterHistoryColumn = new AlterColumn();
|
||||
|
||||
@@ -15,6 +15,8 @@ public class H2HistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
private static final String TRIGGER_CLASS = H2HistoryTrigger.class.getName();
|
||||
|
||||
public H2HistoryDdl() {
|
||||
this.sysPeriodType = "timestamp";
|
||||
this.currentTimestamp = "now()";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class HanaColumnStoreDdl extends AbstractHanaDdl {
|
||||
|
||||
public HanaColumnStoreDdl(DatabasePlatform platform) {
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import io.ebean.config.PropertiesWrapper;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
|
||||
@@ -12,6 +9,9 @@ import io.ebeaninternal.dbmigration.migration.Column;
|
||||
import io.ebeaninternal.dbmigration.migration.DropColumn;
|
||||
import io.ebeaninternal.dbmigration.model.MTable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class HanaTableDdl extends BaseTableDdl {
|
||||
|
||||
private final HanaHistoryDdl historyDdl;
|
||||
|
||||
@@ -20,7 +20,6 @@ import io.ebeaninternal.dbmigration.migration.Column;
|
||||
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
|
||||
import io.ebeaninternal.dbmigration.migration.IdentityType;
|
||||
import io.ebeaninternal.dbmigration.model.MTable;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -78,7 +77,7 @@ public class PlatformDdl {
|
||||
protected String dropIndexIfExists = "drop index if exists ";
|
||||
|
||||
protected String alterColumn = "alter column";
|
||||
|
||||
|
||||
protected String alterColumnSuffix = "";
|
||||
|
||||
protected String dropUniqueConstraint = "drop constraint";
|
||||
@@ -86,7 +85,7 @@ public class PlatformDdl {
|
||||
protected String addConstraint = "add constraint";
|
||||
|
||||
protected String addColumn = "add column";
|
||||
|
||||
|
||||
protected String addColumnSuffix = "";
|
||||
|
||||
protected String columnSetType = "";
|
||||
@@ -100,11 +99,11 @@ public class PlatformDdl {
|
||||
protected String columnSetNull = "set null";
|
||||
|
||||
protected String updateNullWithDefault = "update ${table} set ${column} = ${default} where ${column} is null";
|
||||
|
||||
|
||||
protected String createTable = "create table";
|
||||
|
||||
|
||||
protected String dropColumn = "drop column";
|
||||
|
||||
|
||||
protected String dropColumnSuffix = "";
|
||||
|
||||
/**
|
||||
@@ -674,12 +673,12 @@ public class PlatformDdl {
|
||||
public void unlockTables(DdlBuffer buffer, Collection<String> tables) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the database-specific "create table" command prefix. For HANA this is
|
||||
* either "create column table" or "create row table", for all other databases
|
||||
* it is "create table".
|
||||
*
|
||||
*
|
||||
* @return The "create table" command prefix
|
||||
*/
|
||||
public String getCreateTableCommandPrefix() {
|
||||
|
||||
@@ -29,6 +29,8 @@ public class CurrentModel {
|
||||
|
||||
private final boolean platformTypes;
|
||||
|
||||
private final boolean jaxbPresent;
|
||||
|
||||
private ModelContainer model;
|
||||
|
||||
private ChangeSet changeSet;
|
||||
@@ -58,6 +60,7 @@ public class CurrentModel {
|
||||
this.constraintNaming = constraintNaming;
|
||||
this.maxLength = maxLength(server, constraintNaming);
|
||||
this.platformTypes = platformTypes;
|
||||
this.jaxbPresent = server.getServerConfig().getClassLoadConfig().isJavaxJAXBPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,7 +133,9 @@ public class CurrentModel {
|
||||
ddl.append(header).append('\n');
|
||||
}
|
||||
|
||||
addExtraDdl(ddl, ExtraDdlXmlReader.readBuiltin(), "-- init script ");
|
||||
if (jaxbPresent) {
|
||||
addExtraDdl(ddl, ExtraDdlXmlReader.readBuiltin(), "-- init script ");
|
||||
}
|
||||
|
||||
ddl.append(write.apply().getBuffer());
|
||||
ddl.append(write.applyForeignKeys().getBuffer());
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.ebeaninternal.dbmigration.model;
|
||||
|
||||
import io.ebean.annotation.PartitionMode;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.DdlHelp;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.SplitColumns;
|
||||
import io.ebeaninternal.dbmigration.migration.AddColumn;
|
||||
|
||||
@@ -31,14 +31,16 @@ public class MigrationModel {
|
||||
/**
|
||||
* Read all the migrations returning the model with all
|
||||
* the migrations applied in version order.
|
||||
*
|
||||
* @param dbinitMigration If true we don't apply model changes, migration is from scratch.
|
||||
*/
|
||||
public ModelContainer read() {
|
||||
public ModelContainer read(boolean dbinitMigration) {
|
||||
|
||||
readMigrations();
|
||||
readMigrations(dbinitMigration);
|
||||
return model;
|
||||
}
|
||||
|
||||
private void readMigrations() {
|
||||
private void readMigrations(boolean dbinitMigration) {
|
||||
|
||||
// find all the migration xml files
|
||||
File[] xmlFiles = modelDirectory.listFiles(pathname -> pathname.getName().toLowerCase().endsWith(modelSuffix));
|
||||
@@ -53,9 +55,11 @@ public class MigrationModel {
|
||||
// sort into version order before applying
|
||||
Collections.sort(resources);
|
||||
|
||||
for (MigrationResource migrationResource : resources) {
|
||||
logger.debug("read {}", migrationResource);
|
||||
model.apply(migrationResource.read(), migrationResource.getVersion());
|
||||
if (!dbinitMigration) {
|
||||
for (MigrationResource migrationResource : resources) {
|
||||
logger.debug("read {}", migrationResource);
|
||||
model.apply(migrationResource.read(), migrationResource.getVersion());
|
||||
}
|
||||
}
|
||||
|
||||
// remember the last version
|
||||
|
||||
+5
-1
@@ -147,7 +147,11 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
@Override
|
||||
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
|
||||
|
||||
visitScalar(p);
|
||||
if (p instanceof BeanPropertyAssocOne) {
|
||||
visitOneImported((BeanPropertyAssocOne)p);
|
||||
} else {
|
||||
visitScalar(p);
|
||||
}
|
||||
if (embedded.isId()) {
|
||||
// compound primary key
|
||||
lastColumn.setPrimaryKey(true);
|
||||
|
||||
@@ -10,14 +10,14 @@ import java.util.Map;
|
||||
class CacheChangeBeanUpdate implements CacheChange {
|
||||
|
||||
private final BeanDescriptor<?> desc;
|
||||
private final Object id;
|
||||
private final String key;
|
||||
private final Map<String, Object> changes;
|
||||
private final boolean updateNaturalKey;
|
||||
private final long version;
|
||||
|
||||
CacheChangeBeanUpdate(BeanDescriptor<?> desc, Object id, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
CacheChangeBeanUpdate(BeanDescriptor<?> desc, String key, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
this.desc = desc;
|
||||
this.id = id;
|
||||
this.key = key;
|
||||
this.changes = changes;
|
||||
this.updateNaturalKey = updateNaturalKey;
|
||||
this.version = version;
|
||||
@@ -25,6 +25,6 @@ class CacheChangeBeanUpdate implements CacheChange {
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
desc.cacheApplyBeanUpdate(id, changes, updateNaturalKey, version);
|
||||
desc.cacheApplyBeanUpdate(key, changes, updateNaturalKey, version);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,17 @@ import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
class CacheChangeNaturalKeyPut implements CacheChange {
|
||||
|
||||
private final BeanDescriptor<?> descriptor;
|
||||
private final Object id;
|
||||
private final Object newKey;
|
||||
private final String key;
|
||||
private final String newKey;
|
||||
|
||||
CacheChangeNaturalKeyPut(BeanDescriptor<?> descriptor, Object id, Object newKey) {
|
||||
CacheChangeNaturalKeyPut(BeanDescriptor<?> descriptor, String key, String newKey) {
|
||||
this.descriptor = descriptor;
|
||||
this.id = id;
|
||||
this.key = key;
|
||||
this.newKey = newKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
descriptor.cacheNaturalKeyPut(id, newKey);
|
||||
descriptor.cacheNaturalKeyPut(key, newKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,16 +152,16 @@ public class CacheChangeSet {
|
||||
/**
|
||||
* Update a bean entry.
|
||||
*/
|
||||
public <T> void addBeanUpdate(BeanDescriptor<T> desc, Object id, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
public <T> void addBeanUpdate(BeanDescriptor<T> desc, String key, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
touchedTables.add(desc.getBaseTable());
|
||||
entries.add(new CacheChangeBeanUpdate(desc, id, changes, updateNaturalKey, version));
|
||||
entries.add(new CacheChangeBeanUpdate(desc, key, changes, updateNaturalKey, version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a natural key.
|
||||
*/
|
||||
public <T> void addNaturalKeyPut(BeanDescriptor<T> desc, Object id, Object val) {
|
||||
entries.add(new CacheChangeNaturalKeyPut(desc, id, val));
|
||||
public <T> void addNaturalKeyPut(BeanDescriptor<T> desc, String key, String val) {
|
||||
entries.add(new CacheChangeNaturalKeyPut(desc, key, val));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -66,7 +66,7 @@ public class CachedBeanDataFromBean {
|
||||
Object v = aPropertiesNonTransient.getValue(bean);
|
||||
aPropertiesNonTransient.setValue(sharableBean, v);
|
||||
}
|
||||
EntityBeanIntercept intercept = sharableBean._ebean_intercept();
|
||||
EntityBeanIntercept intercept = sharableBean._ebean_getIntercept();
|
||||
intercept.setReadOnly(true);
|
||||
intercept.setLoaded();
|
||||
return sharableBean;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.cache;
|
||||
|
||||
import io.ebean.annotation.Cache;
|
||||
import io.ebean.annotation.CacheBeanTuning;
|
||||
import io.ebean.annotation.CacheQueryTuning;
|
||||
import io.ebean.cache.QueryCacheEntryValidate;
|
||||
@@ -123,11 +124,15 @@ class DefaultCacheHolder {
|
||||
}
|
||||
|
||||
private ServerCacheOptions getBeanOptions(Class<?> cls) {
|
||||
|
||||
Cache cache = cls.getAnnotation(Cache.class);
|
||||
boolean nearCache = (cache != null && cache.nearCache());
|
||||
|
||||
CacheBeanTuning tuning = cls.getAnnotation(CacheBeanTuning.class);
|
||||
if (tuning != null) {
|
||||
return new ServerCacheOptions(tuning).applyDefaults(beanDefault);
|
||||
return new ServerCacheOptions(nearCache, tuning).applyDefaults(beanDefault);
|
||||
}
|
||||
return beanDefault.copy();
|
||||
return beanDefault.copy(nearCache);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ public class DefaultServerQueryCache extends DefaultServerCache {
|
||||
this.queryCacheEntryValidate = config.getQueryCacheEntryValidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object unwrapEntry(CacheEntry entry) {
|
||||
return ((QueryCacheEntry) entry.getValue()).getValue();
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class ClusterManager implements ServerLookup {
|
||||
|
||||
public ClusterManager(ContainerConfig config) {
|
||||
ClusterBroadcastFactory factory = createFactory();
|
||||
if (factory != null) {
|
||||
if (factory != null && config.isActive()) {
|
||||
broadcast = factory.create(this, config);
|
||||
} else {
|
||||
broadcast = null;
|
||||
@@ -59,6 +59,7 @@ public class ClusterManager implements ServerLookup {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EbeanServer getServer(String name) {
|
||||
synchronized (monitor) {
|
||||
return serverMap.get(name);
|
||||
|
||||
@@ -11,7 +11,6 @@ import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.lib.util.Str;
|
||||
import io.ebeaninternal.server.persist.Binder;
|
||||
import io.ebeaninternal.server.persist.TrimLogSql;
|
||||
import io.ebeaninternal.server.transaction.TransactionManager;
|
||||
import io.ebeaninternal.server.util.BindParamsParser;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
||||
@@ -7,6 +7,9 @@ import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.sql.Timestamp;
|
||||
import java.sql.Types;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.Calendar;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -341,6 +344,9 @@ public final class BasicTypeConverter implements Serializable {
|
||||
} else if (value instanceof String) {
|
||||
return Timestamp.valueOf((String) value);
|
||||
|
||||
} else if (value instanceof LocalDateTime) {
|
||||
return Timestamp.valueOf((LocalDateTime) value);
|
||||
|
||||
} else if (value instanceof Number) {
|
||||
return new Timestamp(((Number) value).longValue());
|
||||
|
||||
@@ -361,6 +367,9 @@ public final class BasicTypeConverter implements Serializable {
|
||||
} else if (value instanceof String) {
|
||||
return java.sql.Time.valueOf((String) value);
|
||||
|
||||
} else if (value instanceof LocalTime) {
|
||||
return java.sql.Time.valueOf((LocalTime) value);
|
||||
|
||||
} else {
|
||||
String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
|
||||
throw new RuntimeException(m);
|
||||
@@ -390,6 +399,9 @@ public final class BasicTypeConverter implements Serializable {
|
||||
} else if (value instanceof Number) {
|
||||
return new java.sql.Date(((Number) value).longValue());
|
||||
|
||||
} else if (value instanceof LocalDate) {
|
||||
return java.sql.Date.valueOf((LocalDate)value);
|
||||
|
||||
} else {
|
||||
String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
|
||||
throw new RuntimeException(m);
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.ScriptRunner;
|
||||
import io.ebean.migration.ddl.DdlRunner;
|
||||
import io.ebean.migration.runner.ScriptTransform;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.util.UrlHelper;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.LineNumberReader;
|
||||
import java.io.Reader;
|
||||
import java.net.URL;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
|
||||
class DScriptRunner implements ScriptRunner {
|
||||
|
||||
private static final String NEWLINE = "\n";
|
||||
|
||||
private final SpiEbeanServer server;
|
||||
|
||||
DScriptRunner(SpiEbeanServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String path) {
|
||||
run(path, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String path, Map<String, String> placeholderMap) {
|
||||
run(this.getClass().getResource(path), path, placeholderMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(URL resource) {
|
||||
run(resource, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(URL resource, Map<String, String> placeholderMap) {
|
||||
run(resource, null, placeholderMap);
|
||||
}
|
||||
|
||||
private void run(URL resource, String scriptName, Map<String, String> placeholderMap) {
|
||||
if (resource == null) {
|
||||
throw new IllegalArgumentException("resource is null?");
|
||||
}
|
||||
if (scriptName == null) {
|
||||
scriptName = resource.getFile();
|
||||
}
|
||||
|
||||
String content = content(resource);
|
||||
runScript(content, scriptName, placeholderMap);
|
||||
}
|
||||
|
||||
private String content(URL resource) {
|
||||
if (resource == null) {
|
||||
throw new IllegalArgumentException("resource is null?");
|
||||
}
|
||||
|
||||
try (InputStream inputStream = UrlHelper.openNoCache(resource)) {
|
||||
return readContent(new InputStreamReader(inputStream));
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException("Failed to read script content", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute all the DDL statements in the script.
|
||||
*/
|
||||
private void runScript(String content, String scriptName, Map<String, String> placeholderMap) {
|
||||
|
||||
try {
|
||||
if (placeholderMap != null) {
|
||||
content = ScriptTransform.build(null, placeholderMap).transform(content);
|
||||
}
|
||||
|
||||
try (Connection connection = obtainConnection()) {
|
||||
DdlRunner runner = new DdlRunner(false, scriptName);
|
||||
runner.runAll(content, connection);
|
||||
connection.commit();
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException("Failed to run script", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Connection obtainConnection() {
|
||||
try {
|
||||
return server.getPluginApi().getDataSource().getConnection();
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException("Failed to obtain connection to run script", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String readContent(Reader reader) throws IOException {
|
||||
|
||||
StringBuilder buf = new StringBuilder();
|
||||
try (LineNumberReader lineReader = new LineNumberReader(reader)) {
|
||||
String line;
|
||||
while ((line = lineReader.readLine()) != null) {
|
||||
buf.append(line).append(NEWLINE);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import io.ebeaninternal.server.lib.DaemonScheduleThreadPool;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@@ -64,6 +66,46 @@ public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledFuture<?> schedule(Runnable r, long delay, TimeUnit unit) {
|
||||
final Map<String, String> map = MDC.getCopyOfContextMap();
|
||||
|
||||
if (map == null) {
|
||||
return schedulePool.schedule(r, delay, unit);
|
||||
} else {
|
||||
return schedulePool.schedule(() -> {
|
||||
MDC.setContextMap(map);
|
||||
try {
|
||||
r.run();
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
}, delay, unit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> ScheduledFuture<V> schedule(Callable<V> c, long delay, TimeUnit unit) {
|
||||
final Map<String, String> map = MDC.getCopyOfContextMap();
|
||||
|
||||
if (map == null) {
|
||||
return schedulePool.schedule(c, delay, unit);
|
||||
} else {
|
||||
return schedulePool.schedule(new Callable<V>() {
|
||||
@Override
|
||||
public V call() throws Exception {
|
||||
MDC.setContextMap(map);
|
||||
try {
|
||||
return c.call();
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
}
|
||||
|
||||
}, delay, unit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
pool.shutdown();
|
||||
|
||||
@@ -88,4 +88,10 @@ public class DefaultBeanState implements BeanState {
|
||||
public void resetForInsert() {
|
||||
intercept.setNew();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Exception> getLoadErrors() {
|
||||
return intercept.getLoadErrors();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@ import io.ebean.config.TenantMode;
|
||||
import io.ebean.config.UnderscoreNamingConvention;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.h2.H2Platform;
|
||||
import io.ebean.datasource.DataSourceAlertFactory;
|
||||
import io.ebean.datasource.DataSourceConfig;
|
||||
import io.ebean.datasource.DataSourceFactory;
|
||||
import io.ebean.datasource.DataSourcePoolListener;
|
||||
import io.ebean.service.SpiContainer;
|
||||
import io.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
@@ -15,10 +19,6 @@ import io.ebeaninternal.server.cluster.ClusterManager;
|
||||
import io.ebeaninternal.server.core.bootup.BootupClassPathSearch;
|
||||
import io.ebeaninternal.server.core.bootup.BootupClasses;
|
||||
import io.ebeaninternal.server.lib.ShutdownManager;
|
||||
import org.avaje.datasource.DataSourceAlertFactory;
|
||||
import org.avaje.datasource.DataSourceConfig;
|
||||
import org.avaje.datasource.DataSourceFactory;
|
||||
import org.avaje.datasource.DataSourcePoolListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -249,7 +249,7 @@ public class DefaultContainer implements SpiContainer {
|
||||
return null;
|
||||
}
|
||||
|
||||
DataSourceFactory factory = config.service(DataSourceFactory.class);
|
||||
DataSourceFactory factory = DataSourceFactory.get();
|
||||
if (factory == null) {
|
||||
throw new IllegalStateException("No DataSourceFactory service implementation found in class path."
|
||||
+ " Probably missing dependency to avaje-datasource?");
|
||||
|
||||
@@ -6,8 +6,10 @@ import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.meta.MetaOrmQueryMetric;
|
||||
import io.ebean.meta.MetaOrmQueryNode;
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -23,6 +25,11 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlan> collectQueryPlans(QueryPlanRequest request) {
|
||||
return server.collectQueryPlans(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
server.visitMetrics(visitor);
|
||||
|
||||
@@ -22,6 +22,7 @@ import io.ebean.Query;
|
||||
import io.ebean.QueryIterator;
|
||||
import io.ebean.RowConsumer;
|
||||
import io.ebean.RowMapper;
|
||||
import io.ebean.ScriptRunner;
|
||||
import io.ebean.SqlQuery;
|
||||
import io.ebean.SqlRow;
|
||||
import io.ebean.SqlUpdate;
|
||||
@@ -53,13 +54,16 @@ import io.ebean.event.BeanPersistController;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.plugin.Plugin;
|
||||
import io.ebean.plugin.Property;
|
||||
import io.ebean.plugin.SpiServer;
|
||||
import io.ebean.text.csv.CsvReader;
|
||||
import io.ebean.text.json.JsonContext;
|
||||
import io.ebean.util.JdbcClose;
|
||||
import io.ebeaninternal.api.LoadBeanRequest;
|
||||
import io.ebeaninternal.api.LoadManyRequest;
|
||||
import io.ebeaninternal.api.ScopedTransaction;
|
||||
@@ -119,6 +123,8 @@ import javax.persistence.NonUniqueResultException;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.time.Clock;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -185,6 +191,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private final DdlGenerator ddlGenerator;
|
||||
|
||||
private final ScriptRunner scriptRunner;
|
||||
|
||||
private final ExpressionFactory expressionFactory;
|
||||
|
||||
private final SpiBackgroundExecutor backgroundExecutor;
|
||||
@@ -291,6 +299,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
this.serverPlugins = config.getPlugins();
|
||||
this.ddlGenerator = new DdlGenerator(this, serverConfig);
|
||||
this.scriptRunner = new DScriptRunner(this);
|
||||
|
||||
configureServerPlugins();
|
||||
|
||||
@@ -370,6 +379,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return databasePlatform;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptRunner script() {
|
||||
return scriptRunner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DdlHandler createDdlHandler() {
|
||||
return PlatformDdlBuilder.create(databasePlatform).createDdlHandler(serverConfig);
|
||||
@@ -1349,6 +1363,21 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return !ids.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean exists(SpiQuery<?> ormQuery, SpiTransaction transaction) {
|
||||
|
||||
ormQuery.setMaxRows(1);
|
||||
SpiOrmQueryRequest<?> request = createQueryRequest(Type.ID_LIST, ormQuery, transaction);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
List<Object> ids = request.findIds();
|
||||
return !ids.isEmpty();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A, T> List<A> findIds(Query<T> query, Transaction t) {
|
||||
|
||||
@@ -1430,10 +1459,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return queryFuture;
|
||||
}
|
||||
|
||||
public <T> FutureRowCount<T> findFutureRowCount(Query<T> q, Transaction t) {
|
||||
return findFutureCount(q, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> FutureIds<T> findFutureIds(Query<T> query, Transaction t) {
|
||||
|
||||
@@ -2326,10 +2351,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public void slowQueryCheck(long timeMicros, int rowCount, SpiQuery<?> query) {
|
||||
if (timeMicros > slowQueryMicros) {
|
||||
if (slowQueryListener != null) {
|
||||
slowQueryListener.process(new SlowQueryEvent(query.getGeneratedSql(), timeMicros / 1000L, rowCount, query.getParentNode()));
|
||||
}
|
||||
if (timeMicros > slowQueryMicros && slowQueryListener != null) {
|
||||
slowQueryListener.process(new SlowQueryEvent(query.getGeneratedSql(), timeMicros / 1000L, rowCount, query.getParentNode()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2351,7 +2374,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
Object id = idProperty.getVal(entityBean);
|
||||
if (entityBean._ebean_intercept().isNew() && id != null) {
|
||||
if (entityBean._ebean_getIntercept().isNew() && id != null) {
|
||||
// Primary Key is changeable only on new models - so skip check if we are not
|
||||
// new.
|
||||
Query<?> query = new DefaultOrmQuery<>(beanDesc, this, expressionFactory);
|
||||
@@ -2381,7 +2404,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
Query<?> query = new DefaultOrmQuery<>(beanDesc, this, expressionFactory);
|
||||
ExpressionList<?> exprList = query.where();
|
||||
|
||||
if (!entityBean._ebean_intercept().isNew()) {
|
||||
if (!entityBean._ebean_getIntercept().isNew()) {
|
||||
// if model is not new, exclude ourself.
|
||||
exprList.ne(idProperty.getName(), idProperty.getVal(entityBean));
|
||||
}
|
||||
@@ -2416,4 +2439,19 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
visitor.visitEnd();
|
||||
}
|
||||
|
||||
public List<MetaQueryPlan> collectQueryPlans(QueryPlanRequest request) {
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = getDataSource().getConnection();
|
||||
request.setConnection(connection);
|
||||
beanDescriptorManager.collectQueryPlans(request);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
} finally {
|
||||
JdbcClose.close(connection);
|
||||
}
|
||||
return request.getPlans();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +239,7 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlUpdate setNextParameter(Object value) {
|
||||
setParameter(++addPos, value);
|
||||
return this;
|
||||
|
||||
@@ -15,6 +15,7 @@ import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.SlowQueryListener;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbHistorySupport;
|
||||
import io.ebean.datasource.DataSourcePool;
|
||||
import io.ebean.event.changelog.ChangeLogListener;
|
||||
import io.ebean.event.changelog.ChangeLogPrepare;
|
||||
import io.ebean.event.changelog.ChangeLogRegister;
|
||||
@@ -87,7 +88,6 @@ import io.ebeanservice.docstore.api.DocStoreFactory;
|
||||
import io.ebeanservice.docstore.api.DocStoreIntegration;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdateProcessor;
|
||||
import io.ebeanservice.docstore.none.NoneDocStoreFactory;
|
||||
import org.avaje.datasource.DataSourcePool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.config.CurrentTenantProvider;
|
||||
import io.ebean.config.TenantCatalogProvider;
|
||||
import io.ebean.datasource.DataSourcePool;
|
||||
import io.ebeaninternal.server.transaction.DataSourceSupplier;
|
||||
import org.avaje.datasource.DataSourcePool;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
@@ -2,8 +2,8 @@ package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.config.CurrentTenantProvider;
|
||||
import io.ebean.config.TenantSchemaProvider;
|
||||
import io.ebean.datasource.DataSourcePool;
|
||||
import io.ebeaninternal.server.transaction.DataSourceSupplier;
|
||||
import org.avaje.datasource.DataSourcePool;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
@@ -173,6 +173,16 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
*/
|
||||
private int pendingPostUpdateNotify;
|
||||
|
||||
/**
|
||||
* Set to true when post execute has occured (so includes batch flush).
|
||||
*/
|
||||
private boolean postExecute;
|
||||
|
||||
/**
|
||||
* Set to true after many properties have been persisted (so includes element collections).
|
||||
*/
|
||||
private boolean complete;
|
||||
|
||||
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
|
||||
PersistExecute persistExecute, PersistRequest.Type type, int flags) {
|
||||
|
||||
@@ -441,10 +451,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this change should notify cache, listener or doc store.
|
||||
* Return true if this change should notify persist listener or doc store (and keep the request).
|
||||
*/
|
||||
public boolean isNotify() {
|
||||
return notifyCache || isNotifyPersistListener() || isDocStoreNotify();
|
||||
private boolean isNotifyListeners() {
|
||||
return isNotifyPersistListener() || isDocStoreNotify();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -918,7 +928,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
*/
|
||||
@Override
|
||||
public void postExecute() {
|
||||
|
||||
postExecute = true;
|
||||
if (controller != null) {
|
||||
controllerPost();
|
||||
}
|
||||
@@ -939,7 +949,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
postInsert();
|
||||
}
|
||||
|
||||
addEvent();
|
||||
addPostCommitListeners();
|
||||
notifyCacheOnPostExecute();
|
||||
|
||||
if (isLogSummary()) {
|
||||
logSummary();
|
||||
@@ -1017,14 +1028,12 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the bean to the TransactionEvent. This will be used by TransactionManager to sync Cache,
|
||||
* Cluster and text indexes.
|
||||
* Add the request to TransactionEvent if there are post commit listeners.
|
||||
*/
|
||||
private void addEvent() {
|
||||
|
||||
private void addPostCommitListeners() {
|
||||
TransactionEvent event = transaction.getEvent();
|
||||
if (event != null) {
|
||||
event.add(this);
|
||||
if (event != null && isNotifyListeners()) {
|
||||
event.addListenerNotify(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1087,23 +1096,62 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any of its many properties where cascade saved and hence we need to update related
|
||||
* many property caches.
|
||||
* Completed insert or delete request. Do cache notify in non-batched case.
|
||||
*/
|
||||
public void checkUpdatedManysOnly() {
|
||||
public void complete() {
|
||||
notifyCacheOnComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Completed update request handling cases for element collection and where ONLY
|
||||
* many properties were updated.
|
||||
*/
|
||||
public void completeUpdate() {
|
||||
if (!dirty && updatedManys != null) {
|
||||
// set the flag and register for post commit processing if there
|
||||
// is caching or registered listeners
|
||||
if (idValue == null) {
|
||||
this.idValue = beanDescriptor.getId(entityBean);
|
||||
}
|
||||
// not dirty so postExecute() will never be called
|
||||
// set true to trigger cache notify if needed
|
||||
postExecute = true;
|
||||
updatedManysOnly = true;
|
||||
setNotifyCache();
|
||||
addEvent();
|
||||
addPostCommitListeners();
|
||||
}
|
||||
notifyCacheOnComplete();
|
||||
postUpdateNotify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify cache when using batched persist.
|
||||
*/
|
||||
private void notifyCacheOnPostExecute() {
|
||||
postExecute = true;
|
||||
if (notifyCache && complete) {
|
||||
// add cache notification (on batch persist)
|
||||
TransactionEvent event = transaction.getEvent();
|
||||
if (event != null) {
|
||||
notifyCache(event.obtainCacheChangeSet());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify cache when not use batched persist.
|
||||
*/
|
||||
private void notifyCacheOnComplete() {
|
||||
complete = true;
|
||||
if (notifyCache && postExecute) {
|
||||
// add cache notification (on non-batch persist)
|
||||
TransactionEvent event = transaction.getEvent();
|
||||
if (event != null) {
|
||||
notifyCache(event.obtainCacheChangeSet());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For requests that update document store add this event to either the list
|
||||
* of queue events or list of update events.
|
||||
@@ -1357,6 +1405,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
|
||||
boolean updateNaturalKey = false;
|
||||
|
||||
String key = beanDescriptor.cacheKey(idValue);
|
||||
|
||||
Map<String, Object> changes = new LinkedHashMap<>();
|
||||
EntityBean bean = getEntityBean();
|
||||
boolean[] dirtyProperties = getDirtyProperties();
|
||||
@@ -1369,7 +1419,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
changes.put(property.getName(), val);
|
||||
if (property.isNaturalKey()) {
|
||||
updateNaturalKey = true;
|
||||
changeSet.addNaturalKeyPut(beanDescriptor, idValue, val);
|
||||
changeSet.addNaturalKeyPut(beanDescriptor, key, val.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1380,7 +1430,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
changes.putAll(collectionChanges);
|
||||
}
|
||||
|
||||
changeSet.addBeanUpdate(beanDescriptor, idValue, changes, updateNaturalKey, getVersion());
|
||||
changeSet.addBeanUpdate(beanDescriptor, key, changes, updateNaturalKey, getVersion());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1390,7 +1440,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
public void setImportedOrphanForRemoval(BeanPropertyAssocOne<?> prop) {
|
||||
Object orphan = getOrigValue(prop);
|
||||
if (orphan instanceof EntityBean) {
|
||||
orphanBean = (EntityBean)orphan;
|
||||
orphanBean = (EntityBean) orphan;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebeaninternal.server.lib.ShutdownManager;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to io.ebean.event.ServletContextListener.
|
||||
* <p>
|
||||
* Listens for webserver server starting and stopping events.
|
||||
* <p>
|
||||
* Register this listener in the web.xml configuration file. This will listen
|
||||
* for startup and shutdown events.
|
||||
* </p>
|
||||
*/
|
||||
@Deprecated
|
||||
public class ServletContextListener implements javax.servlet.ServletContextListener {
|
||||
|
||||
/**
|
||||
* The servlet container is stopping.
|
||||
*/
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent event) {
|
||||
ShutdownManager.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do nothing on startup.
|
||||
*/
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent event) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.datasource.DataSourcePool;
|
||||
import io.ebeaninternal.server.transaction.DataSourceSupplier;
|
||||
import org.avaje.datasource.DataSourcePool;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.core.bootup;
|
||||
|
||||
import io.ebean.util.StringHelper;
|
||||
import io.ebeaninternal.util.UrlHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -59,7 +60,7 @@ class ManifestReader {
|
||||
try {
|
||||
Enumeration<URL> resources = classLoader.getResources(resourcePath);
|
||||
while (resources.hasMoreElements()) {
|
||||
try (InputStream is = resources.nextElement().openStream()) {
|
||||
try (InputStream is = UrlHelper.openNoCache(resources.nextElement())) {
|
||||
read(new Manifest(is));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.deploy;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
@@ -16,9 +17,16 @@ abstract class AssocOneHelp {
|
||||
|
||||
protected final BeanDescriptor<?> target;
|
||||
|
||||
private final String path;
|
||||
|
||||
AssocOneHelp(BeanPropertyAssocOne<?> property) {
|
||||
this(property, null);
|
||||
}
|
||||
|
||||
AssocOneHelp(BeanPropertyAssocOne<?> property, String embeddedPrefix) {
|
||||
this.property = property;
|
||||
this.target = property.targetDescriptor;
|
||||
this.path = (embeddedPrefix == null) ? property.name : embeddedPrefix + "." + property.name;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,6 +36,24 @@ abstract class AssocOneHelp {
|
||||
property.targetIdBinder.loadIgnore(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and return the property.
|
||||
*/
|
||||
Object read(DataReader reader) throws SQLException {
|
||||
return property.read(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and return the property setting value into the bean.
|
||||
*/
|
||||
Object readSet(DataReader reader, EntityBean bean) throws SQLException {
|
||||
Object val = read(reader);
|
||||
if (bean != null) {
|
||||
property.setValue(bean, val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and return the bean.
|
||||
*/
|
||||
@@ -48,7 +74,7 @@ abstract class AssocOneHelp {
|
||||
boolean disableLazyLoading = ctx.isDisableLazyLoading();
|
||||
Object ref = target.contextRef(pc, ctx.isReadOnly(), disableLazyLoading, id);
|
||||
if (!disableLazyLoading) {
|
||||
ctx.register(property.name, ((EntityBean) ref)._ebean_getIntercept());
|
||||
ctx.register(path, ((EntityBean) ref)._ebean_getIntercept());
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
@@ -9,14 +10,41 @@ import java.sql.SQLException;
|
||||
*/
|
||||
final class AssocOneHelpEmbedded extends AssocOneHelp {
|
||||
|
||||
public AssocOneHelpEmbedded(BeanPropertyAssocOne<?> property) {
|
||||
AssocOneHelpEmbedded(BeanPropertyAssocOne<?> property) {
|
||||
super(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
void loadIgnore(DbReadContext ctx) {
|
||||
for (int i = 0; i < property.embeddedProps.length; i++) {
|
||||
property.embeddedProps[i].loadIgnore(ctx);
|
||||
for (BeanProperty property : property.embeddedProps) {
|
||||
property.loadIgnore(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
Object readSet(DataReader reader, EntityBean bean) throws SQLException {
|
||||
Object dbVal = read(reader);
|
||||
if (bean != null) {
|
||||
property.setValue(bean, dbVal);
|
||||
}
|
||||
return dbVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
Object read(DataReader reader) throws SQLException {
|
||||
|
||||
EntityBean embeddedBean = property.targetDescriptor.createEntityBean();
|
||||
boolean notNull = false;
|
||||
for (BeanProperty property : property.embeddedProps) {
|
||||
Object value = property.readSet(reader, embeddedBean);
|
||||
if (value != null) {
|
||||
notNull = true;
|
||||
}
|
||||
}
|
||||
if (notNull) {
|
||||
return embeddedBean;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +67,8 @@ final class AssocOneHelpEmbedded extends AssocOneHelp {
|
||||
EntityBean embeddedBean = property.targetDescriptor.createEntityBean();
|
||||
|
||||
boolean notNull = false;
|
||||
for (int i = 0; i < property.embeddedProps.length; i++) {
|
||||
Object value = property.embeddedProps[i].readSet(ctx, embeddedBean);
|
||||
for (BeanProperty property : property.embeddedProps) {
|
||||
Object value = property.readSet(ctx, embeddedBean);
|
||||
if (value != null) {
|
||||
notNull = true;
|
||||
}
|
||||
@@ -55,8 +83,8 @@ final class AssocOneHelpEmbedded extends AssocOneHelp {
|
||||
|
||||
@Override
|
||||
void appendSelect(DbSqlContext ctx, boolean subQuery) {
|
||||
for (int i = 0; i < property.embeddedProps.length; i++) {
|
||||
property.embeddedProps[i].appendSelect(ctx, subQuery);
|
||||
for (BeanProperty property : property.embeddedProps) {
|
||||
property.appendSelect(ctx, subQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class AssocOneHelpRefInherit extends AssocOneHelp {
|
||||
// for inheritance hierarchy create the correct type for this row...
|
||||
boolean disableLazyLoading = ctx.isDisableLazyLoading();
|
||||
Object ref = desc.contextRef(pc, ctx.isReadOnly(), disableLazyLoading, id);
|
||||
if (disableLazyLoading) {
|
||||
if (!disableLazyLoading) {
|
||||
ctx.register(property.name, ((EntityBean) ref)._ebean_getIntercept());
|
||||
}
|
||||
return ref;
|
||||
|
||||
@@ -5,8 +5,8 @@ package io.ebeaninternal.server.deploy;
|
||||
*/
|
||||
class AssocOneHelpRefSimple extends AssocOneHelp {
|
||||
|
||||
AssocOneHelpRefSimple(BeanPropertyAssocOne<?> property) {
|
||||
super(property);
|
||||
AssocOneHelpRefSimple(BeanPropertyAssocOne<?> property, String embeddedPrefix) {
|
||||
super(property, embeddedPrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,6 +42,7 @@ abstract class BaseCollectionHelp<T> implements BeanCollectionHelp<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public Collection underlying(Object value) {
|
||||
if (value instanceof BeanCollection) {
|
||||
|
||||
@@ -39,6 +39,7 @@ public interface BeanCollectionHelp<T> extends CQueryCollectionAdd<T> {
|
||||
/**
|
||||
* Create an empty collection of the correct type without a parent bean.
|
||||
*/
|
||||
@Override
|
||||
BeanCollection<T> createEmptyNoParent();
|
||||
|
||||
/**
|
||||
@@ -49,6 +50,7 @@ public interface BeanCollectionHelp<T> extends CQueryCollectionAdd<T> {
|
||||
/**
|
||||
* Add a bean to the List Set or Map.
|
||||
*/
|
||||
@Override
|
||||
void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck);
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,7 @@ public class BeanCollectionUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the details of the collection or map taking care to avoid
|
||||
* Return the details (map entry set) of the collection or map taking care to avoid
|
||||
* unnecessary fetching of the data.
|
||||
*/
|
||||
public static Collection<?> getActualEntries(Object o) {
|
||||
@@ -48,4 +48,32 @@ public class BeanCollectionUtil {
|
||||
}
|
||||
throw new PersistenceException("expecting a Map or Collection but got [" + o.getClass().getName() + "]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the details (map values) of the collection or map taking care to avoid
|
||||
* unnecessary fetching of the data.
|
||||
*/
|
||||
public static Collection<?> getActualDetails(Object o) {
|
||||
if (o == null) {
|
||||
return null;
|
||||
}
|
||||
if (o instanceof BeanCollection<?>) {
|
||||
BeanCollection<?> bc = (BeanCollection<?>) o;
|
||||
if (!bc.isPopulated()) {
|
||||
return null;
|
||||
}
|
||||
// For maps this is a collection of Map.Entry, otherwise it
|
||||
// returns a collection of beans
|
||||
return bc.getActualDetails();
|
||||
}
|
||||
|
||||
if (o instanceof Map<?, ?>) {
|
||||
// yes, we want the entrySet (to set the keys)
|
||||
return ((Map<?, ?>) o).values();
|
||||
|
||||
} else if (o instanceof Collection<?>) {
|
||||
return ((Collection<?>) o);
|
||||
}
|
||||
throw new PersistenceException("expecting a Map or Collection but got [" + o.getClass().getName() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import io.ebean.SqlUpdate;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.ValuePair;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.annotation.PartitionMode;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
@@ -30,6 +29,7 @@ import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.event.readaudit.ReadEvent;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
import io.ebean.plugin.BeanDocType;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.plugin.ExpressionPath;
|
||||
@@ -61,6 +61,7 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyLists;
|
||||
import io.ebeaninternal.server.el.ElComparator;
|
||||
import io.ebeaninternal.server.el.ElComparatorCompound;
|
||||
import io.ebeaninternal.server.el.ElComparatorNoop;
|
||||
import io.ebeaninternal.server.el.ElComparatorProperty;
|
||||
import io.ebeaninternal.server.el.ElPropertyChainBuilder;
|
||||
import io.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
@@ -497,7 +498,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
DeployBeanPropertyLists listHelper = new DeployBeanPropertyLists(owner, this, deploy);
|
||||
|
||||
this.softDeleteProperty = listHelper.getSoftDeleteProperty();
|
||||
this.softDelete = (softDeleteProperty != null);
|
||||
// if formula is set, the property is virtual only (there is no column in db) the formula must evaluate to true,
|
||||
// if there is a join to a deleted bean. Example: '@Formula(select = "${ta}.user_id is null")'
|
||||
// this is required to support markAsDelete on beans that may have no FK constraint.
|
||||
this.softDelete = (softDeleteProperty != null && !softDeleteProperty.isFormula());
|
||||
this.idProperty = listHelper.getId();
|
||||
this.versionProperty = listHelper.getVersionProperty();
|
||||
this.unmappedJson = listHelper.getUnmappedJson();
|
||||
@@ -720,16 +724,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
inheritInfo.setDescriptor(this);
|
||||
}
|
||||
|
||||
if (isEmbedded()) {
|
||||
// initialise all the properties
|
||||
for (BeanProperty prop : propertiesAll()) {
|
||||
prop.initialise(initContext);
|
||||
}
|
||||
} else {
|
||||
// initialise just the Id properties
|
||||
if (idProperty != null) {
|
||||
idProperty.initialise(initContext);
|
||||
}
|
||||
// initialise just the Id property only
|
||||
if (idProperty != null) {
|
||||
idProperty.initialise(initContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -755,14 +752,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
}
|
||||
|
||||
if (!isEmbedded()) {
|
||||
// initialise all the non-id properties
|
||||
for (BeanProperty prop : propertiesAll()) {
|
||||
if (!prop.isId()) {
|
||||
prop.initialise(initContext);
|
||||
}
|
||||
prop.registerColumn(this, null);
|
||||
// initialise all the non-id properties
|
||||
for (BeanProperty prop : propertiesAll()) {
|
||||
if (!prop.isId()) {
|
||||
prop.initialise(initContext);
|
||||
}
|
||||
prop.registerColumn(this, null);
|
||||
}
|
||||
|
||||
if (unidirectional != null) {
|
||||
@@ -1483,30 +1478,33 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Return a bean from the bean cache (or null).
|
||||
*/
|
||||
public T cacheBeanGet(Object id, Boolean readOnly, PersistenceContext context) {
|
||||
return cacheHelp.beanCacheGet(id, readOnly, context);
|
||||
return cacheHelp.beanCacheGet(cacheKey(id), readOnly, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a collection of beans from the cache given the ids.
|
||||
*/
|
||||
public void cacheApplyInvalidate(Collection<Object> ids) {
|
||||
cacheHelp.beanCacheApplyInvalidate(ids);
|
||||
List<String> keys = new ArrayList<>(ids.size());
|
||||
for (Object id : ids) {
|
||||
keys.add(cacheKey(id));
|
||||
}
|
||||
cacheHelp.beanCacheApplyInvalidate(keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hit the bean cache trying to load a list/batch of entities.
|
||||
* Return the set of entities that were successfully loaded from L2 cache.
|
||||
*/
|
||||
public Set<EntityBeanIntercept> cacheBeanLoadAll(List<EntityBeanIntercept> list, PersistenceContext persistenceContext, int lazyLoadProperty, String propertyName) {
|
||||
return cacheHelp.beanCacheLoadAll(list, persistenceContext, lazyLoadProperty, propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if it managed to populate/load the bean from the cache.
|
||||
*/
|
||||
public boolean cacheBeanLoad(EntityBean bean, EntityBeanIntercept ebi, Object id, PersistenceContext context) {
|
||||
return cacheHelp.beanCacheLoad(bean, ebi, id, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if it managed to populate/load the bean from the cache.
|
||||
*/
|
||||
public boolean cacheBeanLoad(EntityBeanIntercept ebi, PersistenceContext context) {
|
||||
EntityBean bean = ebi.getOwner();
|
||||
Object id = getId(bean);
|
||||
return cacheBeanLoad(bean, ebi, id, context);
|
||||
return cacheHelp.beanCacheLoad(bean, ebi, cacheKey(id), context);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1516,8 +1514,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return cacheHelp.naturalKeyLookup(context, keys);
|
||||
}
|
||||
|
||||
public void cacheNaturalKeyPut(Object id, Object newKey) {
|
||||
cacheHelp.cacheNaturalKeyPut(id, newKey);
|
||||
public void cacheNaturalKeyPut(String key, String newKey) {
|
||||
cacheHelp.cacheNaturalKeyPut(key, newKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1565,8 +1563,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
/**
|
||||
* Apply the update to the cache.
|
||||
*/
|
||||
public void cacheApplyBeanUpdate(Object id, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
cacheHelp.cacheBeanUpdate(id, changes, updateNaturalKey, version);
|
||||
public void cacheApplyBeanUpdate(String key, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
cacheHelp.cacheBeanUpdate(key, changes, updateNaturalKey, version);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1648,6 +1646,14 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return new DeployUpdateParser(this).parse(ormUpdateStatement);
|
||||
}
|
||||
|
||||
public void collectQueryPlans(QueryPlanRequest request) {
|
||||
for (CQueryPlan queryPlan : queryPlanCache.values()) {
|
||||
if (request.includeLabel(queryPlan.getLabel())) {
|
||||
queryPlan.collectQueryPlan(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit all the ORM query plan metrics (includes UpdateQuery with updates and deletes).
|
||||
*/
|
||||
@@ -1835,6 +1841,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
/**
|
||||
* Return the IdBinder which is helpful for handling the various types of Id.
|
||||
*/
|
||||
@Override
|
||||
public IdBinder getIdBinder() {
|
||||
return idBinder;
|
||||
}
|
||||
@@ -1943,6 +1950,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
/**
|
||||
* Creates a new entity bean without invoking {@link BeanPostConstructListener#postCreate(Object)}
|
||||
*/
|
||||
@Override
|
||||
public EntityBean createEntityBean() {
|
||||
return createEntityBean(false);
|
||||
}
|
||||
@@ -1975,7 +1983,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
public T createReference(Boolean readOnly, boolean disableLazyLoad, Object id, PersistenceContext pc) {
|
||||
|
||||
if (cacheSharableBeans && !disableLazyLoad && !Boolean.FALSE.equals(readOnly)) {
|
||||
CachedBeanData d = cacheHelp.beanCacheGetData(id);
|
||||
CachedBeanData d = cacheHelp.beanCacheGetData(cacheKey(id));
|
||||
if (d != null) {
|
||||
Object shareableBean = d.getSharableBean();
|
||||
if (shareableBean != null) {
|
||||
@@ -2213,6 +2221,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
/**
|
||||
* Put the bean into the persistence context if it is absent.
|
||||
*/
|
||||
@Override
|
||||
public Object contextPutIfAbsent(PersistenceContext pc, Object id, EntityBean localBean) {
|
||||
return pc.putIfAbsent(rootBeanType, id, localBean);
|
||||
}
|
||||
@@ -2255,6 +2264,20 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return (idProperty == null) ? null : idProperty.getValueIntercept(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cache key for the given bean (based on id value).
|
||||
*/
|
||||
public String cacheKeyForBean(EntityBean bean) {
|
||||
return cacheKey(idProperty.getValue(bean));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cache key for the given id value.
|
||||
*/
|
||||
public String cacheKey(Object id) {
|
||||
return idBinder.cacheKey(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object beanId(Object bean) {
|
||||
return getId((EntityBean) bean);
|
||||
@@ -2437,7 +2460,15 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
private ElComparator<T> createPropertyComparator(SortByClause.Property sortProp) {
|
||||
|
||||
ElPropertyValue elGetValue = getElGetValue(sortProp.getName());
|
||||
if (elGetValue == null) {
|
||||
logger.error("Sort property [" + sortProp + "] not found in " + beanType + ". Cannot sort.");
|
||||
return new ElComparatorNoop<>();
|
||||
}
|
||||
|
||||
if (elGetValue.isAssocMany()) {
|
||||
logger.error("Sort property [" + sortProp + "] in " + beanType + " is a many-property. Cannot sort.");
|
||||
return new ElComparatorNoop<>();
|
||||
}
|
||||
Boolean nullsHigh = sortProp.getNullsHigh();
|
||||
if (nullsHigh == null) {
|
||||
nullsHigh = Boolean.TRUE;
|
||||
@@ -2639,6 +2670,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return autoTunable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isElementType() {
|
||||
return false;
|
||||
}
|
||||
@@ -2647,6 +2679,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Returns the Inheritance mapping information. This will be null if this type
|
||||
* of bean is not involved in any ORM inheritance mapping.
|
||||
*/
|
||||
@Override
|
||||
public InheritInfo getInheritInfo() {
|
||||
return inheritInfo;
|
||||
}
|
||||
@@ -2815,6 +2848,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
/**
|
||||
* Returns true if this bean is based on RawSql.
|
||||
*/
|
||||
@Override
|
||||
public boolean isRawSqlBased() {
|
||||
return EntityType.SQL == entityType;
|
||||
}
|
||||
@@ -2869,6 +2903,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
/**
|
||||
* Return the base table to use given the query temporal mode.
|
||||
*/
|
||||
@Override
|
||||
public String getBaseTable(SpiQuery.TemporalMode mode) {
|
||||
switch (mode) {
|
||||
case DRAFT:
|
||||
@@ -2896,6 +2931,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return readAuditing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSoftDelete() {
|
||||
return softDelete;
|
||||
}
|
||||
@@ -2908,10 +2944,25 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return softDeleteProperty.getSoftDeleteDbSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSoftDeletePredicate(String tableAlias) {
|
||||
return softDeleteProperty.getSoftDeleteDbPredicate(tableAlias);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markAsDeleted(EntityBean bean) {
|
||||
if (softDeleteProperty == null) {
|
||||
Object id = getId(bean);
|
||||
logger.info("(Lazy) loading unsuccessful for type:{} id:{} - expecting when bean has been deleted", getName(), id);
|
||||
bean._ebean_getIntercept().setLazyLoadFailure(id);
|
||||
} else {
|
||||
setSoftDeleteValue(bean);
|
||||
bean._ebean_getIntercept().setLoaded();
|
||||
setAllLoaded(bean);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if this entity type is draftable.
|
||||
*/
|
||||
@@ -2980,6 +3031,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* Set the draft to true for this entity bean instance.
|
||||
* This bean is being loaded via asDraft() query.
|
||||
*/
|
||||
@Override
|
||||
public void setDraft(EntityBean entityBean) {
|
||||
if (draft != null) {
|
||||
draft.setValue(entityBean, true);
|
||||
@@ -3033,6 +3085,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
/**
|
||||
* Return true if this entity bean has history support.
|
||||
*/
|
||||
@Override
|
||||
public boolean isHistorySupport() {
|
||||
return historySupport;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -282,7 +283,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
|
||||
if (many.isElementCollection()) {
|
||||
// held as part of the bean cache so skip
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
CachedManyIds entry = manyPropGet(parentId, many.getName());
|
||||
@@ -317,7 +318,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
try {
|
||||
// add as JSON to bean cache
|
||||
String asJson = many.jsonWriteCollection(details);
|
||||
Map<String,Object> changes = new HashMap<>();
|
||||
Map<String, Object> changes = new HashMap<>();
|
||||
changes.put(many.getName(), asJson);
|
||||
|
||||
CachedBeanData newData = data.update(changes, data.getVersion());
|
||||
@@ -348,13 +349,12 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
|
||||
private CachedManyIds createManyIds(BeanPropertyAssocMany<?> many, Object details) {
|
||||
|
||||
BeanDescriptor<?> targetDescriptor = many.getTargetDescriptor();
|
||||
|
||||
Collection<?> actualDetails = BeanCollectionUtil.getActualEntries(details);
|
||||
Collection<?> actualDetails = BeanCollectionUtil.getActualDetails(details);
|
||||
if (actualDetails == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BeanDescriptor<?> targetDescriptor = many.getTargetDescriptor();
|
||||
List<Object> idList = new ArrayList<>(actualDetails.size());
|
||||
for (Object bean : actualDetails) {
|
||||
idList.add(targetDescriptor.getId((EntityBean) bean));
|
||||
@@ -476,7 +476,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
|
||||
private Class<?> theClassOf(Collection<EntityBean> beans) {
|
||||
if (beans instanceof List) {
|
||||
return ((List<?>)beans).get(0).getClass();
|
||||
return ((List<?>) beans).get(0).getClass();
|
||||
}
|
||||
return beans.iterator().next().getClass();
|
||||
}
|
||||
@@ -495,20 +495,20 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
|
||||
void beanCachePutAllDirect(Collection<EntityBean> beans) {
|
||||
|
||||
Map<Object,Object> natKeys = null;
|
||||
Map<Object, Object> natKeys = null;
|
||||
if (naturalKey != null) {
|
||||
natKeys = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
Map<Object,Object> map = new LinkedHashMap<>();
|
||||
Map<Object, Object> map = new LinkedHashMap<>();
|
||||
for (EntityBean bean : beans) {
|
||||
CachedBeanData beanData = beanExtractData(desc, bean);
|
||||
Object id = desc.getId(bean);
|
||||
map.put(id, beanData);
|
||||
String key = desc.cacheKeyForBean(bean);
|
||||
map.put(key, beanData);
|
||||
if (naturalKey != null) {
|
||||
Object naturalKey = calculateNaturalKey(beanData);
|
||||
if (naturalKey != null) {
|
||||
natKeys.put(naturalKey, id);
|
||||
natKeys.put(naturalKey, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -526,32 +526,33 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Put the bean into the bean cache.
|
||||
*/
|
||||
* Put the bean into the bean cache.
|
||||
*/
|
||||
void beanCachePutDirect(EntityBean bean) {
|
||||
|
||||
CachedBeanData beanData = beanExtractData(desc, bean);
|
||||
|
||||
Object id = desc.getId(bean);
|
||||
String key = desc.cacheKeyForBean(bean);
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" PUT {}({}) data:{}", cacheName, id, beanData);
|
||||
beanLog.debug(" PUT {}({}) data:{}", cacheName, key, beanData);
|
||||
}
|
||||
getBeanCache().put(id, beanData);
|
||||
getBeanCache().put(key, beanData);
|
||||
|
||||
if (naturalKey != null) {
|
||||
Object naturalKey = calculateNaturalKey(beanData);
|
||||
String naturalKey = calculateNaturalKey(beanData);
|
||||
if (naturalKey != null) {
|
||||
if (natLog.isDebugEnabled()) {
|
||||
natLog.debug(" PUT {}({}, {})", cacheName, naturalKey, id);
|
||||
natLog.debug(" PUT {}({}, {})", cacheName, naturalKey, key);
|
||||
}
|
||||
naturalKeyCache.put(naturalKey, id);
|
||||
naturalKeyCache.put(naturalKey, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object calculateNaturalKey(CachedBeanData beanData) {
|
||||
private String calculateNaturalKey(CachedBeanData beanData) {
|
||||
if (naturalKey.length == 1) {
|
||||
return beanData.getData(naturalKey[0]);
|
||||
Object data = beanData.getData(naturalKey[0]);
|
||||
return (data == null) ? null : data.toString();
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String key : naturalKey) {
|
||||
@@ -564,12 +565,12 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
CachedBeanData beanCacheGetData(Object id) {
|
||||
return (CachedBeanData) getBeanCache().get(id);
|
||||
CachedBeanData beanCacheGetData(String key) {
|
||||
return (CachedBeanData) getBeanCache().get(key);
|
||||
}
|
||||
|
||||
T beanCacheGet(Object id, Boolean readOnly, PersistenceContext context) {
|
||||
T bean = beanCacheGetInternal(id, readOnly, context);
|
||||
T beanCacheGet(String key, Boolean readOnly, PersistenceContext context) {
|
||||
T bean = beanCacheGetInternal(key, readOnly, context);
|
||||
if (bean != null) {
|
||||
setupContext(bean, context);
|
||||
}
|
||||
@@ -579,19 +580,19 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
/**
|
||||
* Return a bean from the bean cache.
|
||||
*/
|
||||
private T beanCacheGetInternal(Object id, Boolean readOnly, PersistenceContext context) {
|
||||
private T beanCacheGetInternal(String key, Boolean readOnly, PersistenceContext context) {
|
||||
|
||||
CachedBeanData data = (CachedBeanData) getBeanCache().get(id);
|
||||
CachedBeanData data = (CachedBeanData) getBeanCache().get(key);
|
||||
if (data == null) {
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" GET {}({}) - cache miss", cacheName, id);
|
||||
beanLog.trace(" GET {}({}) - cache miss", cacheName, key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" GET {}({}) - hit", cacheName, id);
|
||||
beanLog.trace(" GET {}({}) - hit", cacheName, key);
|
||||
}
|
||||
return convertToBean(id, readOnly, context, data);
|
||||
return convertToBean(key, readOnly, context, data);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -687,12 +688,12 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
/**
|
||||
* Remove a bean from the cache given its Id.
|
||||
*/
|
||||
void beanCacheApplyInvalidate(Collection<Object> ids) {
|
||||
void beanCacheApplyInvalidate(Collection<String> keys) {
|
||||
if (beanCache != null) {
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" REMOVE {}({})", cacheName, ids);
|
||||
beanLog.debug(" REMOVE {}({})", cacheName, keys);
|
||||
}
|
||||
beanCache.removeAll(new HashSet<>(ids));
|
||||
beanCache.removeAll(new HashSet<>(keys));
|
||||
}
|
||||
for (BeanPropertyAssocOne<?> imported : propertiesOneImported) {
|
||||
imported.cacheClear();
|
||||
@@ -700,28 +701,77 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if it managed to populate/load the bean from the cache.
|
||||
* Load a batch of entities from L2 bean cache checking the lazy loaded property is loaded.
|
||||
*/
|
||||
boolean beanCacheLoad(EntityBean bean, EntityBeanIntercept ebi, Object id, PersistenceContext context) {
|
||||
Set<EntityBeanIntercept> beanCacheLoadAll(List<EntityBeanIntercept> list, PersistenceContext context, int lazyLoadProperty, String propertyName) {
|
||||
|
||||
CachedBeanData cacheData = (CachedBeanData) getBeanCache().get(id);
|
||||
Map<Object, EntityBeanIntercept> ebis = new HashMap<>();
|
||||
for (EntityBeanIntercept ebi : list) {
|
||||
ebis.put(desc.cacheKeyForBean(ebi.getOwner()), ebi);
|
||||
}
|
||||
|
||||
|
||||
Map<Object, Object> hits = getBeanCache().getAll(ebis.keySet());
|
||||
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" LOAD ALL {}({}) - got hits ({})", cacheName, ebis.keySet(), hits.size());
|
||||
}
|
||||
|
||||
Set<EntityBeanIntercept> loaded = new HashSet<>();
|
||||
|
||||
Iterator<Map.Entry<Object, Object>> iterator = hits.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<Object, Object> hit = iterator.next();
|
||||
|
||||
Object key = hit.getKey();
|
||||
EntityBeanIntercept ebi = ebis.remove(key);
|
||||
CachedBeanData cacheData = (CachedBeanData) hit.getValue();
|
||||
|
||||
if (lazyLoadProperty > -1 && !cacheData.isLoaded(propertyName)) {
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" LOAD {}({}) - cache miss on property({})", cacheName, key, propertyName);
|
||||
}
|
||||
iterator.remove();
|
||||
|
||||
} else {
|
||||
CachedBeanDataToBean.load(desc, ebi.getOwner(), cacheData, context);
|
||||
loaded.add(ebi);
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" LOAD {}({}) - hit", cacheName, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ebis.isEmpty() && beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" LOAD {}({}) - cache miss", cacheName, ebis.keySet());
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if it managed to populate/load the single bean from the cache.
|
||||
*/
|
||||
boolean beanCacheLoad(EntityBean bean, EntityBeanIntercept ebi, String key, PersistenceContext context) {
|
||||
|
||||
CachedBeanData cacheData = (CachedBeanData) getBeanCache().get(key);
|
||||
if (cacheData == null) {
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" LOAD {}({}) - cache miss", cacheName, id);
|
||||
beanLog.trace(" LOAD {}({}) - cache miss", cacheName, key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int lazyLoadProperty = ebi.getLazyLoadPropertyIndex();
|
||||
if (lazyLoadProperty > -1 && !cacheData.isLoaded(ebi.getLazyLoadProperty())) {
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" LOAD {}({}) - cache miss on property({})", cacheName, id, ebi.getLazyLoadProperty());
|
||||
beanLog.trace(" LOAD {}({}) - cache miss on property({})", cacheName, key, ebi.getLazyLoadProperty());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CachedBeanDataToBean.load(desc, bean, cacheData, context);
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" LOAD {}({}) - hit", cacheName, id);
|
||||
beanLog.debug(" LOAD {}({}) - hit", cacheName, key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -737,7 +787,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
*/
|
||||
void persistDeleteIds(Collection<Object> ids, CacheChangeSet changeSet) {
|
||||
if (invalidateQueryCache) {
|
||||
changeSet.addInvalidate(desc);
|
||||
changeSet.addInvalidate(desc);
|
||||
} else {
|
||||
queryCacheClear(changeSet);
|
||||
if (beanCache != null) {
|
||||
@@ -794,7 +844,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
// query caching only
|
||||
return;
|
||||
}
|
||||
|
||||
List<BeanPropertyAssocMany<?>> manyCollections = updateRequest.getUpdatedManyCollections();
|
||||
if (manyCollections != null) {
|
||||
for (BeanPropertyAssocMany<?> many : manyCollections) {
|
||||
@@ -835,42 +884,42 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
}
|
||||
|
||||
void cacheNaturalKeyPut(Object id, Object newKey) {
|
||||
void cacheNaturalKeyPut(String key, String newKey) {
|
||||
if (newKey != null) {
|
||||
naturalKeyCache.put(newKey, id);
|
||||
naturalKeyCache.put(newKey, key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply changes to the bean cache entry.
|
||||
*/
|
||||
void cacheBeanUpdate(Object id, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
void cacheBeanUpdate(String key, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
|
||||
ServerCache cache = getBeanCache();
|
||||
CachedBeanData existingData = (CachedBeanData) cache.get(id);
|
||||
CachedBeanData existingData = (CachedBeanData) cache.get(key);
|
||||
if (existingData != null) {
|
||||
long currentVersion = existingData.getVersion();
|
||||
if (version > 0 && version < currentVersion) {
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" REMOVE {}({}) - version conflict old:{} new:{}", cacheName, id, currentVersion, version);
|
||||
beanLog.debug(" REMOVE {}({}) - version conflict old:{} new:{}", cacheName, key, currentVersion, version);
|
||||
}
|
||||
cache.remove(id);
|
||||
cache.remove(key);
|
||||
} else {
|
||||
if (version == 0) {
|
||||
version = currentVersion;
|
||||
}
|
||||
CachedBeanData newData = existingData.update(changes, version);
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" UPDATE {}({}) changes:{}", cacheName, id, changes);
|
||||
beanLog.debug(" UPDATE {}({}) changes:{}", cacheName, key, changes);
|
||||
}
|
||||
cache.put(id, newData);
|
||||
cache.put(key, newData);
|
||||
}
|
||||
|
||||
if (updateNaturalKey) {
|
||||
Object oldKey = calculateNaturalKey(existingData);
|
||||
if (oldKey != null) {
|
||||
if (natLog.isDebugEnabled()) {
|
||||
natLog.debug(".. update {} REMOVE({}) - old key for ({})", cacheName, oldKey, id);
|
||||
natLog.debug(".. update {} REMOVE({}) - old key for ({})", cacheName, oldKey, key);
|
||||
}
|
||||
naturalKeyCache.remove(oldKey);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ class BeanDescriptorElementEmbedded<T> extends BeanDescriptorElement<T> {
|
||||
return (EntityBean)prototype._ebean_newInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindElementValue(SqlUpdate insert, Object value) {
|
||||
targetDescriptor.bindElementValue(insert, value);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user