mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
58
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
508f2129f5 | ||
|
|
cdfcbeec43 | ||
|
|
0e9782e6b3 | ||
|
|
927fa5f7d3 | ||
|
|
53a80c7dd1 | ||
|
|
89789f3347 | ||
|
|
9dc5ca602c | ||
|
|
95737cd3c5 | ||
|
|
0a49fc9b62 | ||
|
|
f8c80c7fd2 | ||
|
|
fc837b6472 | ||
|
|
cfaedcdbc5 | ||
|
|
111bac7514 | ||
|
|
1a2ca63d65 | ||
|
|
33031d1c69 | ||
|
|
49d9d88fbe | ||
|
|
756f867ed2 | ||
|
|
12108ada38 | ||
|
|
fb2fdc9b53 | ||
|
|
d570971fd1 | ||
|
|
a8434f22ff | ||
|
|
b0d3921264 | ||
|
|
4167dfc4af | ||
|
|
e49a0c9ffb | ||
|
|
dd6f223ae1 | ||
|
|
05aff53241 | ||
|
|
9a3362e842 | ||
|
|
04e0069eb3 | ||
|
|
b8c4e1c3d3 | ||
|
|
6b136248fa | ||
|
|
aa004f11af | ||
|
|
acd5578d8a | ||
|
|
07946cd505 | ||
|
|
57870aaa05 | ||
|
|
cca837bcfa | ||
|
|
534eb2f0e2 | ||
|
|
dfa7b765f9 | ||
|
|
4ef581f87c | ||
|
|
eb96c726fd | ||
|
|
abbcd35ee6 | ||
|
|
db5a2b194d | ||
|
|
74e0a16531 | ||
|
|
8601f51a6c | ||
|
|
6534feb5da | ||
|
|
6cf5e3386d | ||
|
|
7651256083 | ||
|
|
057d7480b0 | ||
|
|
85c4385498 | ||
|
|
74a9019535 | ||
|
|
c44931ea25 | ||
|
|
012322503f | ||
|
|
5575273c96 | ||
|
|
f3251691e2 | ||
|
|
9d336cb20b | ||
|
|
9895e48485 | ||
|
|
4799c56e25 | ||
|
|
602a35d0b6 | ||
|
|
4bc1f2ede4 |
@@ -11,7 +11,7 @@ Goto [https://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Web
|
||||
[Maven central - all related projects](http://search.maven.org/#search%7Cga%7C1%7Cebean "maven central all related projects")
|
||||
|
||||
## Current versions
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean) - ebean
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean-agent) - ebean-agent
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean-maven-plugin) - ebean-maven-plugin
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean) - ebean
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent) - ebean-agent
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin) - ebean-maven-plugin
|
||||
|
||||
|
||||
@@ -9,15 +9,20 @@
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>11.15.1</version>
|
||||
<version>11.15.8</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ebean</name>
|
||||
<url>http://ebean-orm.github.io/</url>
|
||||
|
||||
<properties>
|
||||
<jackson-core.version>2.9.5</jackson-core.version>
|
||||
<snakeyaml.version>1.21</snakeyaml.version>
|
||||
</properties>
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-11.15.1</tag>
|
||||
<tag>ebean-11.15.8</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -89,7 +94,7 @@
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>1.19</version>
|
||||
<version>${snakeyaml.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
@@ -118,7 +123,7 @@
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-datasource</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<version>3.2.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -130,7 +135,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-migration</artifactId>
|
||||
<version>11.5.1</version>
|
||||
<version>11.5.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -143,14 +148,14 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>2.9.0</version>
|
||||
<version>${jackson-core.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- provided scope for JsonNode support -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.9.0</version>
|
||||
<version>${jackson-core.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -169,13 +174,6 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
@@ -224,7 +222,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-agent</artifactId>
|
||||
<version>11.10.1</version>
|
||||
<version>11.11.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -254,7 +252,7 @@
|
||||
<dependency>
|
||||
<groupId>com.microsoft.sqlserver</groupId>
|
||||
<artifactId>mssql-jdbc</artifactId>
|
||||
<version>6.1.3.jre8-preview</version>
|
||||
<version>6.4.0.jre8</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -297,7 +295,7 @@
|
||||
<plugin>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-maven-plugin</artifactId>
|
||||
<version>11.10.1</version>
|
||||
<version>11.11.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
|
||||
@@ -437,6 +437,19 @@ public interface EbeanServer {
|
||||
*/
|
||||
<T> DtoQuery<T> findDto(Class<T> dtoType, String sql);
|
||||
|
||||
/**
|
||||
* Create a named Query for DTO beans.
|
||||
* <p>
|
||||
* DTO beans are just normal bean like classes with public constructor(s) and setters.
|
||||
* They do not need to be registered with Ebean before use.
|
||||
* </p>
|
||||
*
|
||||
* @param dtoType The type of the DTO bean the rows will be mapped into.
|
||||
* @param namedQuery The name of the query
|
||||
* @param <T> The type of the DTO bean.
|
||||
*/
|
||||
<T> DtoQuery<T> createNamedDtoQuery(Class<T> dtoType, String namedQuery);
|
||||
|
||||
/**
|
||||
* Create a SqlQuery for executing native sql
|
||||
* query statements.
|
||||
@@ -508,30 +521,44 @@ public interface EbeanServer {
|
||||
* etc.
|
||||
* </p>
|
||||
* <p>
|
||||
* <h3>Using try with resources</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // start a transaction (stored in a ThreadLocal)
|
||||
* ebeanServer.beginTransaction();
|
||||
*
|
||||
* try (Transaction txn = ebeanServer.beginTransaction()) {
|
||||
*
|
||||
* Order order = ebeanServer.find(Order.class,10);
|
||||
* ...
|
||||
* ebeanServer.save(order);
|
||||
*
|
||||
* txn.commit();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* <h3>Using try finally block</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // start a transaction (stored in a ThreadLocal)
|
||||
* Transaction txn = ebeanServer.beginTransaction();
|
||||
* try {
|
||||
* Order order = ebeanServer.find(Order.class,10);
|
||||
*
|
||||
* ebeanServer.save(order);
|
||||
*
|
||||
* ebeanServer.commitTransaction();
|
||||
* txn.commit();
|
||||
*
|
||||
* } finally {
|
||||
* // rollback if we didn't commit
|
||||
* // i.e. an exception occurred before commitTransaction().
|
||||
* ebeanServer.endTransaction();
|
||||
* txn.end();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* <h3>Transaction options:</h3>
|
||||
* <h3>Transaction options</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Transaction txn = ebeanServer.beginTransaction();
|
||||
* try {
|
||||
* try (Transaction txn = ebeanServer.beginTransaction()) {
|
||||
* // explicitly turn on/off JDBC batch use
|
||||
* txn.setBatchMode(true);
|
||||
* txn.setBatchSize(50);
|
||||
@@ -552,10 +579,6 @@ public interface EbeanServer {
|
||||
* ...
|
||||
*
|
||||
* txn.commit();
|
||||
*
|
||||
* } finally {
|
||||
* // rollback if necessary
|
||||
* txn.end();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
@@ -585,19 +608,16 @@ public interface EbeanServer {
|
||||
* <pre>{@code
|
||||
* // Start a new transaction. If there is a current transaction
|
||||
* // suspend it until this transaction ends
|
||||
* Transaction txn = server.beginTransaction(TxScope.requiresNew());
|
||||
* try {
|
||||
* try (Transaction txn = server.beginTransaction(TxScope.requiresNew())) {
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* // commit the transaction
|
||||
* txn.commit();
|
||||
*
|
||||
* } finally {
|
||||
* // end this transaction which:
|
||||
* // A) will rollback transaction if it has not been committed already
|
||||
* // At end this transaction will:
|
||||
* // A) will rollback transaction if it has not been committed
|
||||
* // B) will restore a previously suspended transaction
|
||||
* txn.end();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
@@ -606,20 +626,13 @@ public interface EbeanServer {
|
||||
* <pre>{@code
|
||||
*
|
||||
* // start a new transaction if there is not a current transaction
|
||||
* Transaction txn = server.beginTransaction(TxScope.required());
|
||||
* try {
|
||||
* try (Transaction txn = server.beginTransaction(TxScope.required())) {
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* // commit the transaction if it was created or
|
||||
* // do nothing if there was already a current transaction
|
||||
* txn.commit();
|
||||
*
|
||||
* } finally {
|
||||
* // end this transaction which will rollback the transaction
|
||||
* // if it was created for this try finally scope and has not
|
||||
* // already been committed
|
||||
* txn.end();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
|
||||
@@ -327,6 +327,30 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression allEq(Map<String, Object> propertyMap);
|
||||
|
||||
/**
|
||||
* Add expression for ANY of the given bit flags to be set.
|
||||
*
|
||||
* @param propertyName The property that holds the flags value
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
Expression bitwiseAny(String propertyName, long flags);
|
||||
|
||||
/**
|
||||
* Add expression for ALL of the given bit flags to be set.
|
||||
*
|
||||
* @param propertyName The property that holds the flags value
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
Expression bitwiseAll(String propertyName, long flags);
|
||||
|
||||
/**
|
||||
* Add bitwise AND expression of the given bit flags to compare with the match/mask.
|
||||
*
|
||||
* @param propertyName The property that holds the flags value
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
Expression bitwiseAnd(String propertyName, long flags, long match);
|
||||
|
||||
/**
|
||||
* Add raw expression with a single parameter.
|
||||
* <p>
|
||||
|
||||
@@ -482,7 +482,7 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Extended version for setDistinct in conjunction with "findSingleAttributeList";
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<CountedValue<Order.Status>> orderStatusCount =
|
||||
@@ -878,7 +878,7 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* In - using a subQuery.
|
||||
*
|
||||
* <p>
|
||||
* This is exactly the same as in() and provided due to "in" being a Kotlin keyword
|
||||
* (and hence to avoid the slightly ugly escaping when using in() in Kotlin)
|
||||
*/
|
||||
@@ -888,7 +888,7 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* In - property has a value in the array of values.
|
||||
*
|
||||
* <p>
|
||||
* This is exactly the same as in() and provided due to "in" being a Kotlin keyword
|
||||
* (and hence to avoid the slightly ugly escaping when using in() in Kotlin)
|
||||
*/
|
||||
@@ -898,7 +898,7 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* In - property has a value in the collection of values.
|
||||
*
|
||||
* <p>
|
||||
* This is exactly the same as in() and provided due to "in" being a Kotlin keyword
|
||||
* (and hence to avoid the slightly ugly escaping when using in() in Kotlin)
|
||||
*/
|
||||
@@ -996,6 +996,53 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> arrayIsNotEmpty(String propertyName);
|
||||
|
||||
/**
|
||||
* Add expression for ANY of the given bit flags to be set.
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().bitwiseAny("flags", BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param propertyName The property that holds the flags value
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
ExpressionList<T> bitwiseAny(String propertyName, long flags);
|
||||
|
||||
/**
|
||||
* Add expression for ALL of the given bit flags to be set.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().bitwiseAll("flags", BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param propertyName The property that holds the flags value
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
ExpressionList<T> bitwiseAll(String propertyName, long flags);
|
||||
|
||||
/**
|
||||
* Add bitwise AND expression of the given bit flags to compare with the match/mask.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Flags Bulk + Size = Size
|
||||
* // ... meaning Bulk is not set and Size is set
|
||||
*
|
||||
* long selectedFlags = BwFlags.HAS_BULK + BwFlags.HAS_SIZE;
|
||||
* long mask = BwFlags.HAS_SIZE; // Only Size flag set
|
||||
*
|
||||
* where().bitwiseAnd("flags", selectedFlags, mask)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param propertyName The property that holds the flags value
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
ExpressionList<T> bitwiseAnd(String propertyName, long flags, long match);
|
||||
|
||||
/**
|
||||
* Add raw expression with a single parameter.
|
||||
* <p>
|
||||
|
||||
@@ -30,6 +30,35 @@ package io.ebean;
|
||||
* String msg = "There were " + modifiedCount + " rows updated";
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* <h3>Example: Using setNextParameter()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql = "insert into audit_log (id, description, modified_description) values (?,?,?)";
|
||||
* SqlUpdate insert = Ebean.createSqlUpdate(sql);
|
||||
*
|
||||
* try (Transaction txn = Ebean.beginTransaction()) {
|
||||
* txn.setBatchMode(true);
|
||||
*
|
||||
* insert.setNextParameter(10000);
|
||||
* insert.setNextParameter("hello");
|
||||
* insert.setNextParameter("rob");
|
||||
* insert.execute();
|
||||
*
|
||||
* insert.setNextParameter(10001);
|
||||
* insert.setNextParameter("goodbye");
|
||||
* insert.setNextParameter("rob");
|
||||
* insert.execute();
|
||||
*
|
||||
* insert.setNextParameter(10002);
|
||||
* insert.setNextParameter("chow");
|
||||
* insert.setNextParameter("bob");
|
||||
* insert.execute();
|
||||
*
|
||||
* txn.commit();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see Update
|
||||
* @see SqlQuery
|
||||
@@ -61,7 +90,7 @@ public interface SqlUpdate {
|
||||
|
||||
/**
|
||||
* Execute and return the generated key. This is effectively a short cut for:
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* sqlUpdate.execute();
|
||||
@@ -137,6 +166,13 @@ public interface SqlUpdate {
|
||||
*/
|
||||
SqlUpdate setTimeout(int secs);
|
||||
|
||||
/**
|
||||
* Set the next positioned parameter.
|
||||
*
|
||||
* @param value The value to bind
|
||||
*/
|
||||
SqlUpdate setNextParameter(Object value);
|
||||
|
||||
/**
|
||||
* Set a parameter via its index position.
|
||||
*/
|
||||
|
||||
@@ -23,6 +23,7 @@ public interface Transaction extends AutoCloseable {
|
||||
* This returns the current transaction for the 'default server'. If you are using
|
||||
* multiple EbeanServer's then use {@link EbeanServer#currentTransaction()}.
|
||||
* </p>
|
||||
*
|
||||
* @see Ebean#currentTransaction()
|
||||
* @see EbeanServer#currentTransaction()
|
||||
*/
|
||||
@@ -136,6 +137,18 @@ public interface Transaction extends AutoCloseable {
|
||||
*/
|
||||
void rollback(Throwable e) throws PersistenceException;
|
||||
|
||||
/**
|
||||
* Set when we want nested transactions to use Savepoint's.
|
||||
* <p>
|
||||
* This means that for a nested transaction:
|
||||
* <ul>
|
||||
* <li>begin transaction maps to creating a savepoint</li>
|
||||
* <li>commit transaction maps to releasing a savepoint</li>
|
||||
* <li>rollback transaction maps to rollback a savepoint</li>
|
||||
* </ul>
|
||||
*/
|
||||
void setNestedUseSavepoint();
|
||||
|
||||
/**
|
||||
* Mark the transaction for rollback only.
|
||||
*/
|
||||
@@ -151,7 +164,6 @@ public interface Transaction extends AutoCloseable {
|
||||
*/
|
||||
void end();
|
||||
|
||||
|
||||
/**
|
||||
* Synonym for end() to support AutoClosable.
|
||||
*/
|
||||
@@ -227,7 +239,7 @@ public interface Transaction extends AutoCloseable {
|
||||
* Refer to {@link ServerConfig#setSkipCacheAfterWrite(boolean)} for configuring the default behavior
|
||||
* for using the L2 bean cache in transactions spanning multiple query/persist requests.
|
||||
* </p>
|
||||
* <p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // assume Customer has L2 bean caching enabled ...
|
||||
@@ -309,7 +321,7 @@ public interface Transaction extends AutoCloseable {
|
||||
* <p>
|
||||
* Example: batch processing executing every 3 rows
|
||||
* </p>
|
||||
* <p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* String data = "This is a simple test of the batch processing"
|
||||
|
||||
@@ -72,6 +72,11 @@ public interface BeanCollection<E> extends Serializable {
|
||||
*/
|
||||
boolean isSkipSave();
|
||||
|
||||
/**
|
||||
* Return true if the collection holds modifications.
|
||||
*/
|
||||
boolean holdsModifications();
|
||||
|
||||
/**
|
||||
* Return the bean that owns this collection.
|
||||
*/
|
||||
|
||||
@@ -133,6 +133,20 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
this.nodeUsageCollector = usageCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ownerId (IdClass).
|
||||
*/
|
||||
public Object getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ownerId (IdClass).
|
||||
*/
|
||||
public void setOwnerId(Object ownerId) {
|
||||
this.ownerId = ownerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the owning bean for an embedded bean.
|
||||
*/
|
||||
|
||||
@@ -6,7 +6,6 @@ import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -64,7 +63,7 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
this.ebeanServerName = loader.getName();
|
||||
this.ownerBean = ownerBean;
|
||||
this.propertyName = propertyName;
|
||||
this.readOnly = ownerBean._ebean_getIntercept().isReadOnly();
|
||||
this.readOnly = ownerBean != null && ownerBean._ebean_getIntercept().isReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,13 +95,6 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
if (loader == null) {
|
||||
loader = (BeanCollectionLoader) Ebean.getServer(ebeanServerName);
|
||||
}
|
||||
if (loader == null) {
|
||||
String msg = "Lazy loading but LazyLoadEbeanServer is null?"
|
||||
+ " The LazyLoadEbeanServer needs to be set after deserialization"
|
||||
+ " to support lazy loading.";
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
|
||||
loader.loadMany(this, onlyIds);
|
||||
checkEmptyLazyLoad();
|
||||
}
|
||||
@@ -215,7 +207,8 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
/**
|
||||
* Return true if there are underlying additions or removals.
|
||||
*/
|
||||
boolean holdsModifications() {
|
||||
@Override
|
||||
public boolean holdsModifications() {
|
||||
return modifyHolder != null && modifyHolder.hasModifications();
|
||||
}
|
||||
|
||||
|
||||
@@ -488,6 +488,11 @@ public class ServerConfig {
|
||||
*/
|
||||
private boolean defaultOrderById = false;
|
||||
|
||||
/**
|
||||
* The mappingLocations for searching xml mapping.
|
||||
*/
|
||||
private List<String> mappingLocations = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Construct a Server Configuration for programmatically creating an EbeanServer.
|
||||
*/
|
||||
@@ -2780,10 +2785,8 @@ public class ServerConfig {
|
||||
dbOffline = p.getBoolean("dbOffline", dbOffline);
|
||||
serverCachePlugin = p.createInstance(ServerCachePlugin.class, "serverCachePlugin", serverCachePlugin);
|
||||
|
||||
if (packages != null) {
|
||||
String packagesProp = p.get("search.packages", p.get("packages", null));
|
||||
packages = getSearchJarsPackages(packagesProp);
|
||||
}
|
||||
String packagesProp = p.get("search.packages", p.get("packages", null));
|
||||
packages = getSearchList(packagesProp, packages);
|
||||
|
||||
collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", collectQueryStatsByNode);
|
||||
collectQueryOrigins = p.getBoolean("collectQueryOrigins", collectQueryOrigins);
|
||||
@@ -2862,10 +2865,13 @@ public class ServerConfig {
|
||||
tenantSchemaProvider = p.createInstance(TenantSchemaProvider.class, "tenant.schemaProvider", tenantSchemaProvider);
|
||||
tenantPartitionColumn = p.get("tenant.partitionColumn", tenantPartitionColumn);
|
||||
classes = getClasses(p);
|
||||
|
||||
String mappingsProp = p.get("mappingLocations", null);
|
||||
mappingLocations = getSearchList(mappingsProp, mappingLocations);
|
||||
}
|
||||
|
||||
private NamingConvention createNamingConvention(PropertiesWrapper properties, NamingConvention namingConvention) {
|
||||
NamingConvention nc = properties.createInstance(NamingConvention.class, "namingconvention", null);
|
||||
NamingConvention nc = properties.createInstance(NamingConvention.class, "namingConvention", null);
|
||||
return (nc != null) ? nc : namingConvention;
|
||||
}
|
||||
|
||||
@@ -2898,17 +2904,17 @@ public class ServerConfig {
|
||||
return classes;
|
||||
}
|
||||
|
||||
private List<String> getSearchJarsPackages(String searchPackages) {
|
||||
private List<String> getSearchList(String searchNames, List<String> defaultValue) {
|
||||
|
||||
if (searchPackages != null) {
|
||||
String[] entries = StringHelper.splitNames(searchPackages);
|
||||
if (searchNames != null) {
|
||||
String[] entries = StringHelper.splitNames(searchNames);
|
||||
|
||||
List<String> hitList = new ArrayList<>(entries.length);
|
||||
Collections.addAll(hitList, entries);
|
||||
|
||||
return hitList;
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3076,6 +3082,33 @@ public class ServerConfig {
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a mapping location to search for xml mapping via class path search.
|
||||
*/
|
||||
public void addMappingLocation(String mappingLocation) {
|
||||
if (mappingLocations == null) {
|
||||
mappingLocations = new ArrayList<>();
|
||||
}
|
||||
mappingLocations.add(mappingLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return mapping locations to search for xml mapping via class path search.
|
||||
*/
|
||||
public List<String> getMappingLocations() {
|
||||
return mappingLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set mapping locations to search for xml mapping via class path search.
|
||||
* <p>
|
||||
* This is only used if classes have not been explicitly specified.
|
||||
* </p>
|
||||
*/
|
||||
public void setMappingLocations(List<String> mappingLocations) {
|
||||
this.mappingLocations = mappingLocations;
|
||||
}
|
||||
|
||||
public enum UuidVersion {
|
||||
VERSION4,
|
||||
VERSION1,
|
||||
|
||||
@@ -112,39 +112,7 @@ public class UnderscoreNamingConvention extends AbstractNamingConvention {
|
||||
* Convert and return the string to underscore from camel case.
|
||||
*/
|
||||
protected String toUnderscoreFromCamel(String camelCase) {
|
||||
|
||||
int lastUpper = -1;
|
||||
StringBuilder sb = new StringBuilder(camelCase.length() + 4);
|
||||
for (int i = 0; i < camelCase.length(); i++) {
|
||||
char c = camelCase.charAt(i);
|
||||
|
||||
if ('_' == c) {
|
||||
// Underscores should just be passed through
|
||||
sb.append(c);
|
||||
lastUpper = i;
|
||||
} else if (Character.isDigit(c)) {
|
||||
if (i > lastUpper + 1 && !digitsCompressed) {
|
||||
sb.append("_");
|
||||
}
|
||||
sb.append(c);
|
||||
lastUpper = i;
|
||||
|
||||
} else if (Character.isUpperCase(c)) {
|
||||
if (i > lastUpper + 1) {
|
||||
sb.append("_");
|
||||
}
|
||||
sb.append(Character.toLowerCase(c));
|
||||
lastUpper = i;
|
||||
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
String ret = sb.toString();
|
||||
if (forceUpperCase) {
|
||||
ret = ret.toUpperCase();
|
||||
}
|
||||
return ret;
|
||||
return CamelCaseHelper.toUnderscoreFromCamel(camelCase, digitsCompressed, forceUpperCase);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,52 @@ package io.ebean.util;
|
||||
|
||||
public class CamelCaseHelper {
|
||||
|
||||
/**
|
||||
* To underscore from camel case using digits compressed true and force upper case false.
|
||||
*/
|
||||
public static String toUnderscoreFromCamel(String camelCase) {
|
||||
return toUnderscoreFromCamel(camelCase, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert and return the string to underscore from camel case.
|
||||
*/
|
||||
public static String toUnderscoreFromCamel(String camelCase, boolean digitsCompressed, boolean forceUpperCase) {
|
||||
|
||||
int lastUpper = -1;
|
||||
StringBuilder sb = new StringBuilder(camelCase.length() + 4);
|
||||
for (int i = 0; i < camelCase.length(); i++) {
|
||||
char c = camelCase.charAt(i);
|
||||
|
||||
if ('_' == c) {
|
||||
// Underscores should just be passed through
|
||||
sb.append(c);
|
||||
lastUpper = i;
|
||||
} else if (Character.isDigit(c)) {
|
||||
if (i > lastUpper + 1 && !digitsCompressed) {
|
||||
sb.append("_");
|
||||
}
|
||||
sb.append(c);
|
||||
lastUpper = i;
|
||||
|
||||
} else if (Character.isUpperCase(c)) {
|
||||
if (i > lastUpper + 1) {
|
||||
sb.append("_");
|
||||
}
|
||||
sb.append(Character.toLowerCase(c));
|
||||
lastUpper = i;
|
||||
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
String ret = sb.toString();
|
||||
if (forceUpperCase) {
|
||||
ret = ret.toUpperCase();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* To camel from underscore.
|
||||
*
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.util.StringHelper;
|
||||
@@ -124,11 +123,11 @@ public class LoadManyRequest extends LoadRequest {
|
||||
return loadContext.getBeanProperty();
|
||||
}
|
||||
|
||||
public SpiQuery<?> createQuery(EbeanServer server, int batchSize) {
|
||||
public SpiQuery<?> createQuery(SpiEbeanServer server, int batchSize) {
|
||||
|
||||
BeanPropertyAssocMany<?> many = getMany();
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(many.getTargetType());
|
||||
SpiQuery<?> query = many.newQuery(server);
|
||||
String orderBy = many.getLazyFetchOrderBy();
|
||||
if (orderBy != null) {
|
||||
query.orderBy(orderBy);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.core.DbExpressionHandler;
|
||||
import io.ebeaninternal.server.core.SpiOrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.expression.platform.DbExpressionHandler;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.text.json.JsonContext;
|
||||
import io.ebean.text.json.JsonWriteOptions;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
|
||||
import java.io.Writer;
|
||||
|
||||
@@ -21,4 +23,10 @@ public interface SpiJsonContext extends JsonContext {
|
||||
* Create a Json Writer for writing beans as JSON supplying a writer.
|
||||
*/
|
||||
SpiJsonWriter createJsonWriter(Writer writer);
|
||||
|
||||
/**
|
||||
* Create a Json Reader for reading the JSON content.
|
||||
*/
|
||||
SpiJsonReader createJsonRead(BeanType<?> beanType, String json);
|
||||
|
||||
}
|
||||
|
||||
@@ -23,4 +23,9 @@ public interface SpiSqlUpdate extends SqlUpdate {
|
||||
* Set the generated key value.
|
||||
*/
|
||||
void setGeneratedKey(Object idValue);
|
||||
|
||||
/**
|
||||
* Reset bind position to be ready for another bind execute.
|
||||
*/
|
||||
void reset();
|
||||
}
|
||||
|
||||
@@ -319,4 +319,9 @@ public interface SpiTransaction extends Transaction {
|
||||
* Return the profile location for this transaction.
|
||||
*/
|
||||
ProfileLocation getProfileLocation();
|
||||
|
||||
/**
|
||||
* Return true when nested transactions should create Savepoints.
|
||||
*/
|
||||
boolean isNestedUseSavepoint();
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.sql.SQLException;
|
||||
/**
|
||||
* Proxy for an underlying SpiTransaction (most of the API).
|
||||
*/
|
||||
abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
|
||||
protected SpiTransaction transaction;
|
||||
|
||||
@@ -50,6 +50,16 @@ abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
return transaction.isRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNestedUseSavepoint() {
|
||||
transaction.setNestedUseSavepoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNestedUseSavepoint() {
|
||||
return transaction.isNestedUseSavepoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long profileOffset() {
|
||||
return transaction.profileOffset();
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package io.ebeaninternal.api.json;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
public interface SpiJsonReader {
|
||||
|
||||
PersistenceContext getPersistenceContext();
|
||||
|
||||
SpiJsonReader forJson(JsonParser moreJson, boolean resetContext);
|
||||
|
||||
<T> void persistenceContextPut(Object beanId, T currentBean);
|
||||
|
||||
Object persistenceContextPutIfAbsent(Object id, EntityBean bean, BeanDescriptor<?> beanDesc);
|
||||
|
||||
ObjectMapper getObjectMapper();
|
||||
|
||||
JsonParser getParser();
|
||||
|
||||
JsonToken nextToken() throws IOException;
|
||||
|
||||
void pushPath(String path);
|
||||
|
||||
void popPath();
|
||||
|
||||
void beanVisitor(Object bean, Map<String, Object> unmappedProperties);
|
||||
|
||||
Object readValueUsingObjectMapper(Class<?> propertyType) throws IOException;
|
||||
}
|
||||
+8
-2
@@ -1,9 +1,10 @@
|
||||
package io.ebeaninternal.server.text.json;
|
||||
package io.ebeaninternal.api.json;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.text.json.JsonWriter;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
@@ -11,6 +12,11 @@ import java.util.Collection;
|
||||
*/
|
||||
public interface SpiJsonWriter extends JsonWriter {
|
||||
|
||||
/**
|
||||
* Flush the buffer.
|
||||
*/
|
||||
void flush() throws IOException;
|
||||
|
||||
/**
|
||||
* Return true if the value is a parent bean.
|
||||
*/
|
||||
@@ -60,7 +66,7 @@ public interface SpiJsonWriter extends JsonWriter {
|
||||
* Write value using underlying Jaskson object mapper if available.
|
||||
*/
|
||||
void writeValueUsingObjectMapper(String name, Object value);
|
||||
|
||||
|
||||
/**
|
||||
* Write the bean properties.
|
||||
*/
|
||||
@@ -4,13 +4,18 @@ import io.ebean.config.DbConstraintNaming;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.config.dbplatform.DbPlatformTypeMapping;
|
||||
import io.ebeaninternal.dbmigration.model.MColumn;
|
||||
import io.ebeaninternal.dbmigration.model.MCompoundForeignKey;
|
||||
import io.ebeaninternal.dbmigration.model.MTable;
|
||||
import io.ebeaninternal.dbmigration.model.ModelContainer;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.deploy.TableJoinColumn;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* The context used during DDL generation.
|
||||
@@ -187,4 +192,49 @@ public class ModelBuildContext {
|
||||
addTable(draftTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a builder to add foreign keys.
|
||||
*/
|
||||
public FkeyBuilder fkeyBuilder(MTable destTable) {
|
||||
return new FkeyBuilder(this, destTable);
|
||||
}
|
||||
|
||||
public static class FkeyBuilder {
|
||||
|
||||
private final AtomicInteger count = new AtomicInteger();
|
||||
|
||||
private final ModelBuildContext ctx;
|
||||
|
||||
private final MTable destTable;
|
||||
|
||||
private final String tableName;
|
||||
|
||||
FkeyBuilder(ModelBuildContext ctx, MTable destTable) {
|
||||
this.ctx = ctx;
|
||||
this.destTable = destTable;
|
||||
this.tableName = destTable.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a foreign key based on the table join.
|
||||
*/
|
||||
public FkeyBuilder addForeignKey(BeanDescriptor<?> desc, TableJoin tableJoin, boolean direction) {
|
||||
|
||||
String baseTable = ctx.normaliseTable(desc.getBaseTable());
|
||||
String fkName = ctx.foreignKeyConstraintName(tableName, baseTable, count.incrementAndGet());
|
||||
String fkIndex = ctx.foreignKeyIndexName(tableName, baseTable, count.get());
|
||||
|
||||
MCompoundForeignKey foreignKey = new MCompoundForeignKey(fkName, desc.getBaseTable(), fkIndex);
|
||||
|
||||
for (TableJoinColumn column : tableJoin.columns()) {
|
||||
String localCol = direction ? column.getForeignDbColumn() : column.getLocalDbColumn();
|
||||
String refCol = !direction ? column.getForeignDbColumn() : column.getLocalDbColumn();
|
||||
foreignKey.addColumnPair(localCol, refCol);
|
||||
}
|
||||
|
||||
destTable.addForeignKey(foreignKey);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package io.ebeaninternal.dbmigration.model.build;
|
||||
|
||||
import io.ebeaninternal.dbmigration.model.MTable;
|
||||
import io.ebeaninternal.dbmigration.model.visitor.VisitAllUsing;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.BeanTable;
|
||||
|
||||
/**
|
||||
* Add the element collection table to the model.
|
||||
*/
|
||||
public class ModelBuildElementTable {
|
||||
|
||||
/**
|
||||
* Build and add the MTable model for the ElementCollection property.
|
||||
*/
|
||||
public static void build(ModelBuildContext ctx, BeanPropertyAssocMany<?> manyProp) {
|
||||
|
||||
BeanTable beanTable = manyProp.getBeanTable();
|
||||
|
||||
BeanDescriptor<?> targetDescriptor = manyProp.getTargetDescriptor();
|
||||
|
||||
MTable table = new MTable(beanTable.getBaseTable());
|
||||
|
||||
VisitAllUsing.visitOne(targetDescriptor, new ModelBuildPropertyVisitor(ctx, table, targetDescriptor));
|
||||
|
||||
ctx.fkeyBuilder(table)
|
||||
.addForeignKey(manyProp.getBeanDescriptor(), manyProp.getTableJoin(), true);
|
||||
|
||||
ctx.addTable(table);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+5
-28
@@ -1,7 +1,6 @@
|
||||
package io.ebeaninternal.dbmigration.model.build;
|
||||
|
||||
import io.ebeaninternal.dbmigration.model.MColumn;
|
||||
import io.ebeaninternal.dbmigration.model.MCompoundForeignKey;
|
||||
import io.ebeaninternal.dbmigration.model.MTable;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
@@ -12,7 +11,7 @@ import io.ebeaninternal.server.deploy.TableJoinColumn;
|
||||
/**
|
||||
* Add the intersection table to the model.
|
||||
*/
|
||||
public class ModelBuildIntersectionTable {
|
||||
class ModelBuildIntersectionTable {
|
||||
|
||||
private final ModelBuildContext ctx;
|
||||
|
||||
@@ -22,9 +21,7 @@ public class ModelBuildIntersectionTable {
|
||||
|
||||
private MTable intersectionTable;
|
||||
|
||||
private int countForeignKey;
|
||||
|
||||
public ModelBuildIntersectionTable(ModelBuildContext ctx, BeanPropertyAssocMany<?> manyProp) {
|
||||
ModelBuildIntersectionTable(ModelBuildContext ctx, BeanPropertyAssocMany<?> manyProp) {
|
||||
this.ctx = ctx;
|
||||
this.manyProp = manyProp;
|
||||
this.intersectionTableJoin = manyProp.getIntersectionTableJoin();
|
||||
@@ -51,33 +48,13 @@ public class ModelBuildIntersectionTable {
|
||||
|
||||
private void buildFkConstraints() {
|
||||
|
||||
BeanDescriptor<?> localDesc = manyProp.getBeanDescriptor();
|
||||
buildFkConstraints(localDesc, intersectionTableJoin.columns(), true);
|
||||
|
||||
BeanDescriptor<?> targetDesc = manyProp.getTargetDescriptor();
|
||||
buildFkConstraints(targetDesc, tableJoin.columns(), false);
|
||||
ctx.fkeyBuilder(intersectionTable)
|
||||
.addForeignKey(manyProp.getBeanDescriptor(), intersectionTableJoin, true)
|
||||
.addForeignKey(manyProp.getTargetDescriptor(), tableJoin, false);
|
||||
|
||||
intersectionTable.checkDuplicateForeignKeys();
|
||||
}
|
||||
|
||||
|
||||
private void buildFkConstraints(BeanDescriptor<?> desc, TableJoinColumn[] columns, boolean direction) {
|
||||
|
||||
String tableName = intersectionTableJoin.getTable();
|
||||
String baseTable = ctx.normaliseTable(desc.getBaseTable());
|
||||
String fkName = ctx.foreignKeyConstraintName(tableName, baseTable, ++countForeignKey);
|
||||
String fkIndex = ctx.foreignKeyIndexName(tableName, baseTable, countForeignKey);
|
||||
|
||||
MCompoundForeignKey foreignKey = new MCompoundForeignKey(fkName, desc.getBaseTable(), fkIndex);
|
||||
intersectionTable.addForeignKey(foreignKey);
|
||||
|
||||
for (TableJoinColumn column : columns) {
|
||||
String localCol = direction ? column.getForeignDbColumn() : column.getLocalDbColumn();
|
||||
String refCol = !direction ? column.getForeignDbColumn() : column.getLocalDbColumn();
|
||||
foreignKey.addColumnPair(localCol, refCol);
|
||||
}
|
||||
}
|
||||
|
||||
private MTable createTable() {
|
||||
|
||||
BeanDescriptor<?> localDesc = manyProp.getBeanDescriptor();
|
||||
|
||||
+15
-18
@@ -123,27 +123,24 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
|
||||
@Override
|
||||
public void visitMany(BeanPropertyAssocMany<?> p) {
|
||||
if (p.hasJoinTable()) {
|
||||
if (p.getMappedBy() == null) {
|
||||
// only create on other 'owning' side
|
||||
if (p.hasJoinTable() && p.getMappedBy() == null) {
|
||||
// only create on other 'owning' side
|
||||
|
||||
//TableJoin intersectionTableJoin = p.getIntersectionTableJoin();
|
||||
// check if the intersection table has already been created
|
||||
|
||||
// build the create table and fkey constraints
|
||||
// putting the DDL into ctx for later output as we are
|
||||
// in the middle of rendering the create table DDL
|
||||
MTable intersectionTable = new ModelBuildIntersectionTable(ctx, p).build();
|
||||
if (p.isO2mJoinTable()) {
|
||||
intersectionTable.clearForeignKeyIndexes();
|
||||
Collection<MColumn> cols = intersectionTable.allColumns();
|
||||
if (cols.size() == 2) {
|
||||
// always the second column that we put the unique constraint on
|
||||
MColumn col = new ArrayList<>(cols).get(1);
|
||||
col.setUnique(determineUniqueConstraintName(col.getName()));
|
||||
}
|
||||
// build the create table and fkey constraints
|
||||
// putting the DDL into ctx for later output as we are
|
||||
// in the middle of rendering the create table DDL
|
||||
MTable intersectionTable = new ModelBuildIntersectionTable(ctx, p).build();
|
||||
if (p.isO2mJoinTable()) {
|
||||
intersectionTable.clearForeignKeyIndexes();
|
||||
Collection<MColumn> cols = intersectionTable.allColumns();
|
||||
if (cols.size() == 2) {
|
||||
// always the second column that we put the unique constraint on
|
||||
MColumn col = new ArrayList<>(cols).get(1);
|
||||
col.setUnique(determineUniqueConstraintName(col.getName()));
|
||||
}
|
||||
}
|
||||
} else if (p.isElementCollection()) {
|
||||
ModelBuildElementTable.build(ctx, p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,14 @@ import java.util.List;
|
||||
*/
|
||||
public class VisitAllUsing {
|
||||
|
||||
/**
|
||||
* Visit a single Descriptor using the given visitor.
|
||||
*/
|
||||
public static void visitOne(BeanDescriptor<?> descriptor, BeanPropertyVisitor visitor) {
|
||||
|
||||
new VisitAllUsing().visitProperties(descriptor, visitor);
|
||||
}
|
||||
|
||||
protected final BeanVisitor visitor;
|
||||
|
||||
protected final List<BeanDescriptor<?>> descriptors;
|
||||
@@ -36,6 +44,11 @@ public class VisitAllUsing {
|
||||
this.descriptors = descriptors;
|
||||
}
|
||||
|
||||
private VisitAllUsing() {
|
||||
this.visitor = null;
|
||||
this.descriptors = null;
|
||||
}
|
||||
|
||||
public void visitAllBeans() {
|
||||
for (BeanDescriptor<?> desc : descriptors) {
|
||||
if (desc.isBaseTable()) {
|
||||
@@ -51,29 +64,32 @@ public class VisitAllUsing {
|
||||
|
||||
BeanPropertyVisitor propertyVisitor = visitor.visitBean(desc);
|
||||
if (propertyVisitor != null) {
|
||||
|
||||
BeanProperty idProp = desc.getIdProperty();
|
||||
if (idProp != null) {
|
||||
visit(propertyVisitor, idProp);
|
||||
}
|
||||
|
||||
BeanPropertyAssocOne<?> unidirectional = desc.getUnidirectional();
|
||||
if (unidirectional != null) {
|
||||
visit(propertyVisitor, unidirectional);
|
||||
}
|
||||
|
||||
BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient();
|
||||
for (BeanProperty p : propertiesNonTransient) {
|
||||
if (p.isDDLColumn()) {
|
||||
visit(propertyVisitor, p);
|
||||
}
|
||||
}
|
||||
|
||||
visitInheritanceProperties(desc, propertyVisitor);
|
||||
propertyVisitor.visitEnd();
|
||||
visitProperties(desc, propertyVisitor);
|
||||
}
|
||||
}
|
||||
|
||||
private void visitProperties(BeanDescriptor<?> desc, BeanPropertyVisitor propertyVisitor) {
|
||||
BeanProperty idProp = desc.getIdProperty();
|
||||
if (idProp != null) {
|
||||
visit(propertyVisitor, idProp);
|
||||
}
|
||||
|
||||
BeanPropertyAssocOne<?> unidirectional = desc.getUnidirectional();
|
||||
if (unidirectional != null) {
|
||||
visit(propertyVisitor, unidirectional);
|
||||
}
|
||||
|
||||
BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient();
|
||||
for (BeanProperty p : propertiesNonTransient) {
|
||||
if (p.isDDLColumn()) {
|
||||
visit(propertyVisitor, p);
|
||||
}
|
||||
}
|
||||
|
||||
visitInheritanceProperties(desc, propertyVisitor);
|
||||
propertyVisitor.visitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit the property.
|
||||
*/
|
||||
|
||||
@@ -2,11 +2,11 @@ package io.ebeaninternal.server.autotune.service;
|
||||
|
||||
import io.ebean.bean.NodeUsageCollector;
|
||||
import io.ebean.text.PathProperties;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebean.util.SplitName;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -56,7 +56,7 @@ public class ProfileOriginNodeUsage {
|
||||
}
|
||||
|
||||
for (String propName : aggregateUsed) {
|
||||
BeanProperty beanProp = desc.getBeanPropertyFromPath(propName);
|
||||
BeanProperty beanProp = desc.findPropertyFromPath(propName);
|
||||
if (beanProp == null) {
|
||||
logger.warn("AutoTune: Can't find property[" + propName + "] for " + desc.getName());
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
@@ -33,6 +34,12 @@ public class CachedBeanDataFromBean {
|
||||
}
|
||||
}
|
||||
|
||||
for (BeanPropertyAssocMany<?> prop : desc.propertiesMany()) {
|
||||
if (prop.isElementCollection()) {
|
||||
data.put(prop.getName(), prop.getCacheDataValue(bean));
|
||||
}
|
||||
}
|
||||
|
||||
long version = desc.getVersion(bean);
|
||||
EntityBean sharableBean = createSharableBean(desc, bean, ebi);
|
||||
return new CachedBeanData(sharableBean, desc.getDiscValue(), data, version);
|
||||
|
||||
@@ -15,20 +15,26 @@ public class CachedBeanDataToBean {
|
||||
EntityBeanIntercept ebi = bean._ebean_getIntercept();
|
||||
|
||||
BeanProperty idProperty = desc.getIdProperty();
|
||||
if (desc.getInheritInfo() != null) {
|
||||
desc = desc.getInheritInfo().readType(bean.getClass()).desc();
|
||||
}
|
||||
|
||||
if (idProperty != null) {
|
||||
// load the id property
|
||||
loadProperty(bean, cacheBeanData, ebi, idProperty, context);
|
||||
}
|
||||
|
||||
// load the non-many properties
|
||||
BeanProperty[] props = desc.propertiesNonMany();
|
||||
for (BeanProperty prop : props) {
|
||||
for (BeanProperty prop : desc.propertiesNonMany()) {
|
||||
loadProperty(bean, cacheBeanData, ebi, prop, context);
|
||||
}
|
||||
|
||||
BeanPropertyAssocMany<?>[] many = desc.propertiesMany();
|
||||
for (BeanPropertyAssocMany<?> aMany : many) {
|
||||
aMany.createReferenceIfNull(bean);
|
||||
for (BeanPropertyAssocMany<?> prop : desc.propertiesMany()) {
|
||||
if (prop.isElementCollection()) {
|
||||
loadProperty(bean, cacheBeanData, ebi, prop, context);
|
||||
} else {
|
||||
prop.createReferenceIfNull(bean);
|
||||
}
|
||||
}
|
||||
|
||||
ebi.setLoadedLazy();
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
/**
|
||||
* Not supported JSON or ARRAY expression handler.
|
||||
*/
|
||||
abstract class BaseDbExpression implements DbExpressionHandler {
|
||||
|
||||
private final String concatOperator;
|
||||
|
||||
BaseDbExpression(String concatOperator) {
|
||||
this.concatOperator = concatOperator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConcatOperator() {
|
||||
return concatOperator;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -802,7 +802,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public void commitTransaction() {
|
||||
transactionManager.scope().commit();
|
||||
currentTransaction().commit();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -810,7 +810,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public void rollbackTransaction() {
|
||||
transactionManager.scope().rollback();
|
||||
currentTransaction().rollback();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -843,7 +843,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
@Override
|
||||
public void endTransaction() {
|
||||
transactionManager.scope().end();
|
||||
Transaction transaction = transactionManager.getInScope();
|
||||
if (transaction != null) {
|
||||
transaction.end();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -966,7 +969,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Query<T> createQuery(Class<T> beanType, String eql) {
|
||||
public <T> DefaultOrmQuery<T> createQuery(Class<T> beanType, String eql) {
|
||||
DefaultOrmQuery<T> query = createQuery(beanType);
|
||||
EqlParser.parse(eql, query);
|
||||
return query;
|
||||
@@ -999,6 +1002,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return new DefaultDtoQuery<>(this, descriptor, sql.trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DtoQuery<T> createNamedDtoQuery(Class<T> dtoType, String namedQuery) {
|
||||
DtoBeanDescriptor<T> descriptor = dtoBeanManager.getDescriptor(dtoType);
|
||||
String sql = descriptor.getNamedRawSql(namedQuery);
|
||||
if (sql == null) {
|
||||
throw new PersistenceException("No named query called " + namedQuery + " for bean:" + dtoType.getName());
|
||||
}
|
||||
return new DefaultDtoQuery<>(this, descriptor, sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DtoQuery<T> findDto(Class<T> dtoType, SpiQuery<?> ormQuery) {
|
||||
|
||||
|
||||
@@ -97,6 +97,11 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
this(null, sql, new BindParams());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
addPos = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object executeGetKey() {
|
||||
execute();
|
||||
@@ -182,8 +187,9 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void addParameter(Object value) {
|
||||
public SqlUpdate setNextParameter(Object value) {
|
||||
setParameter(++addPos, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebeaninternal.server.dto.DtoNamedQueries;
|
||||
import io.ebeaninternal.xmlmapping.XmlMappingReader;
|
||||
import io.ebeaninternal.xmlmapping.model.XmDto;
|
||||
import io.ebeaninternal.xmlmapping.model.XmEbean;
|
||||
import io.ebeaninternal.xmlmapping.model.XmRawSql;
|
||||
import org.avaje.classpath.scanner.ClassPathScanner;
|
||||
import org.avaje.classpath.scanner.Resource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Reads the Xml deployment information.
|
||||
*/
|
||||
class InternalConfigXmlRead {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(InternalConfigXmlRead.class);
|
||||
|
||||
private final ServerConfig serverConfig;
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
private final Map<Class<?>, DtoNamedQueries> dtoNamedQueries = new HashMap<>();
|
||||
|
||||
private List<XmEbean> xmlEbeanList;
|
||||
|
||||
InternalConfigXmlRead(ServerConfig serverConfig) {
|
||||
this.serverConfig = serverConfig;
|
||||
this.classLoader = serverConfig.getClassLoadConfig().getClassLoader();
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
this.xmlEbeanList = XmlMappingReader.readByResourceName(classLoader, "ebean.xml");
|
||||
xmlEbeanList.addAll(XmlMappingReader.readByResourceList(xmlMappingResources()));
|
||||
}
|
||||
|
||||
private List<Resource> xmlMappingResources() {
|
||||
List<ClassPathScanner> scanners = ClassPathScanners.find(serverConfig);
|
||||
List<String> mappingLocations = serverConfig.getMappingLocations();
|
||||
List<Resource> resourceList = new ArrayList<>();
|
||||
|
||||
long st = System.currentTimeMillis();
|
||||
if (mappingLocations != null && !mappingLocations.isEmpty()) {
|
||||
for (ClassPathScanner finder : scanners) {
|
||||
for (String mappingLocation : mappingLocations) {
|
||||
resourceList.addAll(finder.scanForResources(mappingLocation, resourceName -> resourceName.endsWith(".xml")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long searchTime = System.currentTimeMillis() - st;
|
||||
log.debug("Classpath search mappings[{}] searchTime[{}]", resourceList.size(), searchTime);
|
||||
return resourceList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the XML deployment information for entity beans.
|
||||
*/
|
||||
List<XmEbean> xmlDeployment() {
|
||||
return xmlEbeanList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the named queries for Dto beans.
|
||||
*/
|
||||
Map<Class<?>, DtoNamedQueries> readDtoMapping() {
|
||||
for (XmEbean mapping : xmlEbeanList) {
|
||||
List<XmDto> dtoList = mapping.getDto();
|
||||
for (XmDto dto : dtoList) {
|
||||
readDtoMapping(dto);
|
||||
}
|
||||
}
|
||||
|
||||
return dtoNamedQueries;
|
||||
}
|
||||
|
||||
private void readDtoMapping(XmDto dto) {
|
||||
|
||||
String dtoClassName = dto.getClazz();
|
||||
Class<?> dtoClass;
|
||||
try {
|
||||
dtoClass = Class.forName(dtoClassName, false, classLoader);
|
||||
} catch (Exception e) {
|
||||
log.error("Could not load dto bean class " + dtoClassName + " for ebean xml entry");
|
||||
return;
|
||||
}
|
||||
|
||||
DtoNamedQueries namedQueries = dtoNamedQueries.computeIfAbsent(dtoClass, aClass -> new DtoNamedQueries());
|
||||
|
||||
for (XmRawSql sql : dto.getRawSql()) {
|
||||
namedQueries.addRawSql(sql.getName(), sql.getQuery().getValue());
|
||||
}
|
||||
|
||||
if (!dto.getNamedQuery().isEmpty()) {
|
||||
log.error("Only raw-sql named queries supported for DTO beans - bean:" + dtoClass);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,6 +42,8 @@ import io.ebeaninternal.server.deploy.parse.DeployInherit;
|
||||
import io.ebeaninternal.server.deploy.parse.DeployUtil;
|
||||
import io.ebeaninternal.server.dto.DtoBeanManager;
|
||||
import io.ebeaninternal.server.expression.DefaultExpressionFactory;
|
||||
import io.ebeaninternal.server.expression.platform.DbExpressionHandler;
|
||||
import io.ebeaninternal.server.expression.platform.DbExpressionHandlerFactory;
|
||||
import io.ebeaninternal.server.persist.Binder;
|
||||
import io.ebeaninternal.server.persist.DefaultPersister;
|
||||
import io.ebeaninternal.server.persist.platform.MultiValueBind;
|
||||
@@ -153,10 +155,12 @@ public class InternalConfiguration {
|
||||
|
||||
this.deployCreateProperties = new DeployCreateProperties(typeManager);
|
||||
this.deployUtil = new DeployUtil(typeManager, serverConfig);
|
||||
this.dtoBeanManager = new DtoBeanManager(typeManager);
|
||||
|
||||
InternalConfigXmlRead xmlRead = new InternalConfigXmlRead(serverConfig);
|
||||
|
||||
this.dtoBeanManager = new DtoBeanManager(typeManager, xmlRead.readDtoMapping());
|
||||
this.beanDescriptorManager = new BeanDescriptorManager(this);
|
||||
Map<String, String> asOfTableMapping = beanDescriptorManager.deploy();
|
||||
Map<String, String> asOfTableMapping = beanDescriptorManager.deploy(xmlRead.xmlDeployment());
|
||||
Map<String, String> draftTableMap = beanDescriptorManager.getDraftTableMap();
|
||||
beanDescriptorManager.scheduleBackgroundTrim();
|
||||
|
||||
@@ -267,18 +271,7 @@ public class InternalConfiguration {
|
||||
* Return the JSON expression handler for the given database platform.
|
||||
*/
|
||||
private DbExpressionHandler getDbExpressionHandler(DatabasePlatform databasePlatform) {
|
||||
Platform platform = databasePlatform.getPlatform();
|
||||
String concatOperator = databasePlatform.getConcatOperator();
|
||||
switch (platform) {
|
||||
case POSTGRES:
|
||||
return new PostgresDbExpression(concatOperator);
|
||||
case ORACLE:
|
||||
return new OracleDbExpression(concatOperator);
|
||||
case SQLSERVER:
|
||||
return new SqlServerDbExpression(concatOperator);
|
||||
default:
|
||||
return new BasicDbExpression(concatOperator);
|
||||
}
|
||||
return DbExpressionHandlerFactory.from(databasePlatform);
|
||||
}
|
||||
|
||||
private MultiValueBind createMultiValueBind(Platform platform) {
|
||||
|
||||
@@ -79,6 +79,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
|
||||
private List<T> cacheBeans;
|
||||
|
||||
private BeanPropertyAssocMany<?> manyProperty;
|
||||
|
||||
/**
|
||||
* Create the InternalQueryRequest.
|
||||
*/
|
||||
@@ -496,11 +498,18 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the many property that is fetched in the query or null if there is
|
||||
* not one.
|
||||
* Determine and return the ToMany property that is included in the query.
|
||||
*/
|
||||
public BeanPropertyAssocMany<?> determineMany() {
|
||||
manyProperty = beanDescriptor.getManyProperty(query);
|
||||
return manyProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the many property that is fetched in the query or null if there is not one.
|
||||
*/
|
||||
public BeanPropertyAssocMany<?> getManyProperty() {
|
||||
return beanDescriptor.getManyProperty(query);
|
||||
return manyProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,6 +35,7 @@ import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -107,6 +108,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
*/
|
||||
private boolean updatedManysOnly;
|
||||
|
||||
/**
|
||||
* Element collection change as part of bean cache.
|
||||
*/
|
||||
private Map<String, Object> collectionChanges;
|
||||
|
||||
/**
|
||||
* Many properties that were cascade saved (and hence might need caches updated later).
|
||||
*/
|
||||
@@ -154,6 +160,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
*/
|
||||
private boolean getterCallback;
|
||||
|
||||
/**
|
||||
* postUpdate notifications. Used to combine bean and element update updates into single postUpdate event.
|
||||
*/
|
||||
private int pendingPostUpdateNotify;
|
||||
|
||||
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
|
||||
PersistExecute persistExecute, PersistRequest.Type type, int flags) {
|
||||
|
||||
@@ -337,11 +348,16 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
|
||||
@Override
|
||||
public void preGetterTrigger(int propertyIndex) {
|
||||
if (propertyIndex < 0 || beanDescriptor.isGeneratedProperty(propertyIndex)) {
|
||||
if (flushBatchOnGetter(propertyIndex)) {
|
||||
transaction.flushBatch();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean flushBatchOnGetter(int propertyIndex) {
|
||||
// propertyIndex of -1 the Id property, no flush for get Id on UPDATE
|
||||
return propertyIndex == -1 ? type == Type.INSERT : beanDescriptor.isGeneratedProperty(propertyIndex);
|
||||
}
|
||||
|
||||
public void setSkipBatchForTopLevel() {
|
||||
skipBatchForTopLevel = true;
|
||||
}
|
||||
@@ -858,6 +874,16 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
}
|
||||
|
||||
private void postUpdateNotify() {
|
||||
if (pendingPostUpdateNotify > 0) {
|
||||
// invoke the delayed postUpdate notification (combined with element collection update)
|
||||
controller.postUpdate(this);
|
||||
} else {
|
||||
// batched update with no element collection, send postUpdate notification once it executes
|
||||
pendingPostUpdateNotify = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggressive L1 and L2 cache cleanup for deletes.
|
||||
*/
|
||||
@@ -905,13 +931,42 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the preUpdate event fires (for case where only element collection has changed).
|
||||
*/
|
||||
public void preElementCollectionUpdate() {
|
||||
if (controller != null && !dirty) {
|
||||
// fire preUpdate notification when only element collection updated
|
||||
controller.preUpdate(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine with the beans postUpdate event notification.
|
||||
*/
|
||||
public boolean postElementCollectionUpdate() {
|
||||
if (controller != null) {
|
||||
pendingPostUpdateNotify += 2;
|
||||
}
|
||||
if (!dirty) {
|
||||
setNotifyCache();
|
||||
}
|
||||
return notifyCache;
|
||||
}
|
||||
|
||||
private void controllerPost() {
|
||||
switch (type) {
|
||||
case INSERT:
|
||||
controller.postInsert(this);
|
||||
break;
|
||||
case UPDATE:
|
||||
controller.postUpdate(this);
|
||||
if (pendingPostUpdateNotify == -1) {
|
||||
// notify now - batched bean update with no element collection
|
||||
controller.postUpdate(this);
|
||||
} else {
|
||||
// delay notify to combine with element collection update
|
||||
pendingPostUpdateNotify++;
|
||||
}
|
||||
break;
|
||||
case SOFT_DELETE:
|
||||
controller.postSoftDelete(this);
|
||||
@@ -1031,13 +1086,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
setNotifyCache();
|
||||
addEvent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if only many properties where updated.
|
||||
*/
|
||||
public boolean isUpdatedManysOnly() {
|
||||
return updatedManysOnly;
|
||||
postUpdateNotify();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1265,4 +1314,50 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
public boolean isInsertedParent() {
|
||||
return Flags.isInsert(flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an element collection change to L2 bean cache update.
|
||||
*/
|
||||
public void addCollectionChange(String name, Object value) {
|
||||
if (collectionChanges == null) {
|
||||
collectionChanges = new LinkedHashMap<>();
|
||||
}
|
||||
collectionChanges.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the bean update for the L2 cache.
|
||||
*/
|
||||
public void addBeanUpdate(CacheChangeSet changeSet) {
|
||||
|
||||
if (!updatedManysOnly || collectionChanges != null) {
|
||||
|
||||
boolean updateNaturalKey = false;
|
||||
|
||||
Map<String, Object> changes = new LinkedHashMap<>();
|
||||
EntityBean bean = getEntityBean();
|
||||
boolean[] dirtyProperties = getDirtyProperties();
|
||||
if (dirtyProperties != null) {
|
||||
for (int i = 0; i < dirtyProperties.length; i++) {
|
||||
if (dirtyProperties[i]) {
|
||||
BeanProperty property = beanDescriptor.propertyByIndex(i);
|
||||
if (property.isCacheDataInclude()) {
|
||||
Object val = property.getCacheDataValue(bean);
|
||||
changes.put(property.getName(), val);
|
||||
if (property.isNaturalKey()) {
|
||||
updateNaturalKey = true;
|
||||
changeSet.addNaturalKeyPut(beanDescriptor, idValue, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (collectionChanges != null) {
|
||||
// add element collection update
|
||||
changes.putAll(collectionChanges);
|
||||
}
|
||||
|
||||
changeSet.addBeanUpdate(beanDescriptor, idValue, changes, updateNaturalKey, getVersion());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,12 +30,13 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
/**
|
||||
* Create.
|
||||
*/
|
||||
public PersistRequestUpdateSql(SpiEbeanServer server, SqlUpdate updateSql,
|
||||
public PersistRequestUpdateSql(SpiEbeanServer server, SqlUpdate sqlUpdate,
|
||||
SpiTransaction t, PersistExecute persistExecute) {
|
||||
|
||||
super(server, t, persistExecute, updateSql.getLabel());
|
||||
super(server, t, persistExecute, sqlUpdate.getLabel());
|
||||
this.type = Type.UPDATESQL;
|
||||
this.updateSql = (SpiSqlUpdate) updateSql;
|
||||
this.updateSql = (SpiSqlUpdate) sqlUpdate;
|
||||
updateSql.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebeaninternal.server.lib.ShutdownManager;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
|
||||
/**
|
||||
* 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>
|
||||
*/
|
||||
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,11 +1,47 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
abstract class BaseCollectionHelp<T> implements BeanCollectionHelp<T> {
|
||||
|
||||
final BeanPropertyAssocMany<T> many;
|
||||
final BeanDescriptor<T> targetDescriptor;
|
||||
final String propertyName;
|
||||
|
||||
BeanCollectionLoader loader;
|
||||
|
||||
BaseCollectionHelp(BeanPropertyAssocMany<T> many) {
|
||||
this.many = many;
|
||||
this.targetDescriptor = many.getTargetDescriptor();
|
||||
this.propertyName = many.getName();
|
||||
}
|
||||
|
||||
BaseCollectionHelp() {
|
||||
this.many = null;
|
||||
this.targetDescriptor = null;
|
||||
this.propertyName = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoader(BeanCollectionLoader loader) {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck) {
|
||||
if (withCheck) {
|
||||
collection.internalAddWithCheck(bean);
|
||||
} else {
|
||||
collection.internalAdd(bean);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection underlying(Object value) {
|
||||
if (value instanceof BeanCollection) {
|
||||
@@ -14,4 +50,18 @@ abstract class BaseCollectionHelp<T> implements BeanCollectionHelp<T> {
|
||||
return (Collection)value;
|
||||
}
|
||||
}
|
||||
|
||||
void jsonWriteCollection(SpiJsonWriter ctx, String name, Collection<?> list) throws IOException {
|
||||
if (!list.isEmpty() || ctx.isIncludeEmpty()) {
|
||||
ctx.beginAssocMany(name);
|
||||
for (Object bean : list) {
|
||||
jsonWriteElement(ctx, bean);
|
||||
}
|
||||
ctx.endAssocMany();
|
||||
}
|
||||
}
|
||||
|
||||
void jsonWriteElement(SpiJsonWriter ctx, Object bean) throws IOException {
|
||||
targetDescriptor.jsonWrite(ctx, (EntityBean) bean);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.PersistenceIOException;
|
||||
import io.ebean.bean.BeanDiffVisitor;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.util.ArrayStack;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -82,10 +82,10 @@ class BeanChangeJson implements BeanDiffVisitor {
|
||||
void flush() {
|
||||
try {
|
||||
newJson.writeEndObject();
|
||||
newJson.gen().flush();
|
||||
newJson.flush();
|
||||
if (oldJson != null) {
|
||||
oldJson.writeEndObject();
|
||||
oldJson.gen().flush();
|
||||
oldJson.flush();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceIOException(e);
|
||||
|
||||
@@ -7,7 +7,8 @@ import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.query.CQueryCollectionAdd;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
@@ -15,7 +16,7 @@ import java.util.Collection;
|
||||
/**
|
||||
* Helper functions for performing tasks on Lists Sets or Maps.
|
||||
*/
|
||||
public interface BeanCollectionHelp<T> {
|
||||
public interface BeanCollectionHelp<T> extends CQueryCollectionAdd<T> {
|
||||
|
||||
/**
|
||||
* Set the EbeanServer that owns the configuration.
|
||||
|
||||
@@ -18,16 +18,17 @@ public class BeanCollectionHelpFactory {
|
||||
/**
|
||||
* Create the helper based on the many property.
|
||||
*/
|
||||
public static <T> BeanCollectionHelp<T> create(BeanPropertyAssocMany<T> manyProperty) {
|
||||
public static <T> BeanCollectionHelp<T> create(BeanPropertyAssocMany<T> many) {
|
||||
|
||||
ManyType manyType = manyProperty.getManyType();
|
||||
boolean elementCollection = many.isElementCollection();
|
||||
ManyType manyType = many.getManyType();
|
||||
switch (manyType) {
|
||||
case LIST:
|
||||
return new BeanListHelp<>(manyProperty);
|
||||
return elementCollection ? new BeanListHelpElement<>(many) : new BeanListHelp<>(many);
|
||||
case SET:
|
||||
return new BeanSetHelp<>(manyProperty);
|
||||
return elementCollection ? new BeanSetHelpElement<>(many) : new BeanSetHelp<>(many);
|
||||
case MAP:
|
||||
return new BeanMapHelp<>(manyProperty);
|
||||
return elementCollection ? new BeanMapHelpElement<>(many) :new BeanMapHelp<>(many);
|
||||
default:
|
||||
throw new RuntimeException("Invalid type " + manyType);
|
||||
}
|
||||
@@ -35,9 +36,7 @@ public class BeanCollectionHelpFactory {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> BeanCollectionHelp<T> create(OrmQueryRequest<T> request) {
|
||||
|
||||
SpiQuery.Type manyType = request.getQuery().getType();
|
||||
public static <T> BeanCollectionHelp<T> create(SpiQuery.Type manyType, OrmQueryRequest<T> request) {
|
||||
|
||||
if (manyType == SpiQuery.Type.LIST) {
|
||||
return LIST_HELP;
|
||||
|
||||
@@ -11,6 +11,16 @@ import java.util.Map;
|
||||
*/
|
||||
public class BeanCollectionUtil {
|
||||
|
||||
/**
|
||||
* Return true if this is a bean collection and not considered dirty.
|
||||
*/
|
||||
public static boolean isModified(Object collection) {
|
||||
if ((collection instanceof BeanCollection<?>)) {
|
||||
return ((BeanCollection<?>) collection).holdsModifications();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the details of the collection or map taking care to avoid
|
||||
* unnecessary fetching of the data.
|
||||
|
||||
@@ -41,6 +41,8 @@ import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiUpdatePlan;
|
||||
import io.ebeaninternal.api.TransactionEventTable.TableIUD;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.cache.CacheChangeSet;
|
||||
import io.ebeaninternal.server.cache.CachedBeanData;
|
||||
import io.ebeaninternal.server.cache.CachedManyIds;
|
||||
@@ -62,11 +64,16 @@ import io.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.persist.DmlUtil;
|
||||
import io.ebeaninternal.server.query.CQueryPlan;
|
||||
import io.ebeaninternal.server.query.SqlTreeProperty;
|
||||
import io.ebeaninternal.server.query.ExtraJoin;
|
||||
import io.ebeaninternal.server.query.STreeProperty;
|
||||
import io.ebeaninternal.server.query.STreePropertyAssoc;
|
||||
import io.ebeaninternal.server.query.STreePropertyAssocMany;
|
||||
import io.ebeaninternal.server.query.STreePropertyAssocOne;
|
||||
import io.ebeaninternal.server.query.STreeType;
|
||||
import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.querydefn.DefaultOrmQuery;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import io.ebeaninternal.server.rawsql.SpiRawSql;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.util.SortByClause;
|
||||
@@ -100,7 +107,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
/**
|
||||
* Describes Beans including their deployment information.
|
||||
*/
|
||||
public class BeanDescriptor<T> implements BeanType<T> {
|
||||
public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanDescriptor.class);
|
||||
|
||||
@@ -114,7 +121,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
|
||||
private final ConcurrentHashMap<String, ElComparator<T>> comparatorCache = new ConcurrentHashMap<>();
|
||||
|
||||
private final ConcurrentHashMap<String, SqlTreeProperty> dynamicProperty = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, STreeProperty> dynamicProperty = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, SpiRawSql> namedRawSql;
|
||||
|
||||
@@ -229,7 +236,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
/**
|
||||
* The type of bean this describes.
|
||||
*/
|
||||
private final Class<T> beanType;
|
||||
final Class<T> beanType;
|
||||
|
||||
protected final Class<?> rootBeanType;
|
||||
|
||||
@@ -239,7 +246,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
private final BeanDescriptorMap owner;
|
||||
|
||||
|
||||
private final String[] properties;
|
||||
final String[] properties;
|
||||
|
||||
/**
|
||||
* Intercept pre post on insert,update, and delete .
|
||||
@@ -400,7 +407,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
|
||||
private final BeanDescriptorDraftHelp<T> draftHelp;
|
||||
private final BeanDescriptorCacheHelp<T> cacheHelp;
|
||||
private final BeanDescriptorJsonHelp<T> jsonHelp;
|
||||
final BeanDescriptorJsonHelp<T> jsonHelp;
|
||||
private DocStoreBeanAdapter<T> docStoreAdapter;
|
||||
private DocumentMapping docMapping;
|
||||
private boolean docStoreEmbeddedInvalidation;
|
||||
@@ -422,8 +429,8 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
this.name = InternString.intern(deploy.getName());
|
||||
this.baseTableAlias = "t0";
|
||||
this.fullName = InternString.intern(deploy.getFullName());
|
||||
this.locationById = ProfileLocation.createAt(fullName+".byId");
|
||||
this.locationAll = ProfileLocation.createAt(fullName+".all");
|
||||
this.locationById = ProfileLocation.createAt(fullName + ".byId");
|
||||
this.locationAll = ProfileLocation.createAt(fullName + ".all");
|
||||
this.profileBeanId = deploy.getProfileId();
|
||||
this.beanType = deploy.getBeanType();
|
||||
this.rootBeanType = PersistenceContextUtil.root(beanType);
|
||||
@@ -601,7 +608,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
/**
|
||||
* Create an entity bean that is used as a prototype/factory to create new instances.
|
||||
*/
|
||||
private EntityBean createPrototypeEntityBean(Class<T> beanType) {
|
||||
protected EntityBean createPrototypeEntityBean(Class<T> beanType) {
|
||||
if (Modifier.isAbstract(beanType.getModifiers())) {
|
||||
return null;
|
||||
}
|
||||
@@ -656,27 +663,29 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public BeanProperty propertyByIndex(int pos) {
|
||||
return propertiesIndex[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the Id properties first.
|
||||
* <p>
|
||||
* These properties need to be initialised prior to the association properties
|
||||
* as they are used to get the imported and exported properties.
|
||||
* </p>
|
||||
*
|
||||
* @param withHistoryTables map populated if @History is supported on this entity bean
|
||||
*/
|
||||
public void initialiseId(Map<String, String> withHistoryTables, Map<String, String> draftTables) {
|
||||
public void initialiseId(BeanDescriptorInitContext initContext) {
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("BeanDescriptor initialise " + fullName);
|
||||
}
|
||||
|
||||
if (draftable) {
|
||||
draftTables.put(baseTable, draftTable);
|
||||
initContext.addDraft(baseTable, draftTable);
|
||||
}
|
||||
if (historySupport) {
|
||||
// add mapping (used to swap out baseTable for asOf queries)
|
||||
withHistoryTables.put(baseTable, baseTableAsOf);
|
||||
initContext.addHistory(baseTable, baseTableAsOf);
|
||||
}
|
||||
|
||||
if (inheritInfo != null) {
|
||||
@@ -686,28 +695,24 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
if (isEmbedded()) {
|
||||
// initialise all the properties
|
||||
for (BeanProperty prop : propertiesAll()) {
|
||||
prop.initialise();
|
||||
prop.initialise(initContext);
|
||||
}
|
||||
} else {
|
||||
// initialise just the Id properties
|
||||
if (idProperty != null) {
|
||||
idProperty.initialise();
|
||||
idProperty.initialise(initContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the exported and imported parts for associated properties.
|
||||
*
|
||||
* @param asOfTableMap the map of base tables to associated 'with history' tables
|
||||
* @param asOfViewSuffix the suffix added to the table name to derive the 'with history' view name
|
||||
* @param draftTableMap the map of base tables to associated 'draft' tables.
|
||||
*/
|
||||
public void initialiseOther(Map<String, String> asOfTableMap, String asOfViewSuffix, Map<String, String> draftTableMap) {
|
||||
public void initialiseOther(BeanDescriptorInitContext initContext) {
|
||||
|
||||
for (BeanPropertyAssocMany<?> aPropertiesManyToMany1 : propertiesManyToMany) {
|
||||
for (BeanPropertyAssocMany<?> many : propertiesManyToMany) {
|
||||
// register associated draft table for M2M intersection
|
||||
aPropertiesManyToMany1.registerDraftIntersectionTable(draftTableMap);
|
||||
many.registerDraftIntersectionTable(initContext);
|
||||
}
|
||||
|
||||
if (historySupport) {
|
||||
@@ -717,8 +722,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
// register associated history table for M2M intersection
|
||||
if (!aPropertiesManyToMany.isExcludedFromHistory()) {
|
||||
TableJoin intersectionTableJoin = aPropertiesManyToMany.getIntersectionTableJoin();
|
||||
String intersectionTableName = intersectionTableJoin.getTable();
|
||||
asOfTableMap.put(intersectionTableName, intersectionTableName + asOfViewSuffix);
|
||||
initContext.addHistoryIntersection(intersectionTableJoin.getTable());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -727,14 +731,14 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
// initialise all the non-id properties
|
||||
for (BeanProperty prop : propertiesAll()) {
|
||||
if (!prop.isId()) {
|
||||
prop.initialise();
|
||||
prop.initialise(initContext);
|
||||
}
|
||||
prop.registerColumn(this, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (unidirectional != null) {
|
||||
unidirectional.initialise();
|
||||
unidirectional.initialise(initContext);
|
||||
}
|
||||
|
||||
idBinder.initialise();
|
||||
@@ -777,7 +781,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
|
||||
for (BeanProperty prop : propertiesNonTransient) {
|
||||
if (prop.isUnique()) {
|
||||
propertiesUnique.add(new BeanProperty[] { prop });
|
||||
propertiesUnique.add(new BeanProperty[]{prop});
|
||||
}
|
||||
}
|
||||
// convert unique columns to properties
|
||||
@@ -799,7 +803,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
if (propName == null) {
|
||||
return;
|
||||
}
|
||||
props[i] = findBeanProperty(propName);
|
||||
props[i] = findProperty(propName);
|
||||
}
|
||||
if (props.length == 1) {
|
||||
for (BeanProperty[] inserted : propertiesUnique) {
|
||||
@@ -861,6 +865,16 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind all the property values to the SqlUpdate.
|
||||
*/
|
||||
public void bindElementValue(SqlUpdate insert, Object value) {
|
||||
EntityBean bean = (EntityBean) value;
|
||||
for (BeanProperty property : propertiesBaseScalar) {
|
||||
insert.setNextParameter(property.getValue(bean));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ReadAuditLogger for logging read audit events.
|
||||
*/
|
||||
@@ -936,7 +950,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
SpiJsonWriter jsonWriter = createJsonWriter(writer);
|
||||
|
||||
jsonWriteForInsert(jsonWriter, request.getEntityBean());
|
||||
jsonWriter.gen().flush();
|
||||
jsonWriter.flush();
|
||||
|
||||
return beanChange(ChangeType.INSERT, request.getBeanId(), writer.toString(), null);
|
||||
|
||||
@@ -950,6 +964,10 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
return ebeanServer.jsonExtended().createJsonWriter(writer);
|
||||
}
|
||||
|
||||
SpiJsonReader createJsonReader(String json) {
|
||||
return ebeanServer.jsonExtended().createJsonRead(this, json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the diff for inserts with flattened non-null property values.
|
||||
*/
|
||||
@@ -1016,7 +1034,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
|
||||
Object[] bindValues = idBinder.getBindValues(id);
|
||||
for (Object bindValue : bindValues) {
|
||||
sqlDelete.addParameter(bindValue);
|
||||
sqlDelete.setNextParameter(bindValue);
|
||||
}
|
||||
|
||||
return sqlDelete;
|
||||
@@ -1421,7 +1439,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void cacheBeanPutAll(Collection<?> beans) {
|
||||
if (!beans.isEmpty()) {
|
||||
cacheHelp.beanPutAll((Collection<EntityBean>)beans);
|
||||
cacheHelp.beanPutAll((Collection<EntityBean>) beans);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1639,6 +1657,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
/**
|
||||
* Execute the postLoad if a BeanPostLoad exists for this bean.
|
||||
*/
|
||||
@Override
|
||||
public void postLoad(Object bean) {
|
||||
if (beanPostLoad != null) {
|
||||
beanPostLoad.postLoad(bean);
|
||||
@@ -1705,7 +1724,15 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
* associated L2 bean caching.
|
||||
*/
|
||||
public boolean isDeleteByStatement() {
|
||||
return deleteRecurseSkippable && !isBeanCaching();
|
||||
return persistListener == null
|
||||
&& persistController == null
|
||||
&& deleteRecurseSkippable && !isBeanCaching();
|
||||
}
|
||||
|
||||
public boolean isDeleteByBulk() {
|
||||
return persistListener == null
|
||||
&& persistController == null
|
||||
&& propertiesManyToMany.length == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1787,6 +1814,11 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
return idBinder instanceof IdBinderSimple;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasId() {
|
||||
return idProperty != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this type has a simple Id and the platform supports mutli-value binding.
|
||||
*/
|
||||
@@ -1847,6 +1879,9 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
* on first access (lazy load) or immediately (eager load)
|
||||
*/
|
||||
public EntityBean createEntityBean(boolean isNew) {
|
||||
if (prototypeEntityBean == null) {
|
||||
throw new UnsupportedOperationException("cannot create entity bean for abstract entity " + getName());
|
||||
}
|
||||
try {
|
||||
EntityBean bean = (EntityBean) prototypeEntityBean._ebean_newInstance();
|
||||
|
||||
@@ -1874,12 +1909,19 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entitybean without invoking {@link BeanPostConstructListener#postCreate(Object)}
|
||||
* Creates a new entity bean without invoking {@link BeanPostConstructListener#postCreate(Object)}
|
||||
*/
|
||||
public EntityBean createEntityBean() {
|
||||
return createEntityBean(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an entity bean for JSON marshalling (which differs for the element collection case).
|
||||
*/
|
||||
public EntityBean createEntityBeanForJson() {
|
||||
return createEntityBean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reference bean based on the id.
|
||||
*/
|
||||
@@ -1931,6 +1973,13 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
public T createReference(Object id, PersistenceContext pc) {
|
||||
|
||||
try {
|
||||
if (inheritInfo != null && !inheritInfo.isConcrete()) {
|
||||
// we actually need to do a query because we don't know the type without the discriminator
|
||||
// value, just select the id property and discriminator column (auto added)
|
||||
DefaultOrmQuery<T> query = new DefaultOrmQuery<>(this, ebeanServer, ebeanServer.getExpressionFactory());
|
||||
return query.select(getIdProperty().getName()).setId(id).findOne();
|
||||
}
|
||||
|
||||
EntityBean eb = createEntityBean();
|
||||
id = convertSetId(id, eb);
|
||||
EntityBeanIntercept ebi = eb._ebean_getIntercept();
|
||||
@@ -1951,7 +2000,8 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
* Return the bean property traversing the object graph and taking into
|
||||
* account inheritance.
|
||||
*/
|
||||
public BeanProperty getBeanPropertyFromPath(String path) {
|
||||
@Override
|
||||
public BeanProperty findPropertyFromPath(String path) {
|
||||
BeanDescriptor<?> other = this;
|
||||
while (true) {
|
||||
|
||||
@@ -1983,7 +2033,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
}
|
||||
String[] splitBegin = SplitName.splitBegin(path);
|
||||
|
||||
BeanProperty beanProperty = result.findBeanProperty(splitBegin[0]);
|
||||
BeanProperty beanProperty = result.findProperty(splitBegin[0]);
|
||||
if (beanProperty instanceof BeanPropertyAssoc<?>) {
|
||||
BeanPropertyAssoc<?> assocProp = (BeanPropertyAssoc<?>) beanProperty;
|
||||
path = splitBegin[1];
|
||||
@@ -2224,7 +2274,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
|
||||
@Override
|
||||
public Property getProperty(String propName) {
|
||||
return findBeanProperty(propName);
|
||||
return findProperty(propName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2458,17 +2508,18 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
/**
|
||||
* Return a 'dynamic property' used to read a formula.
|
||||
*/
|
||||
private SqlTreeProperty findSqlTreeFormula(String formulaExpression) {
|
||||
private STreeProperty findSqlTreeFormula(String formulaExpression) {
|
||||
|
||||
return dynamicProperty.computeIfAbsent(formulaExpression, (formula) -> new FormulaPropertyPath(this, formula).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a property that is part of the SQL tree.
|
||||
*
|
||||
* <p>
|
||||
* The property can be a dynamic formula or a well known bean property.
|
||||
*/
|
||||
public SqlTreeProperty findSqlTreeProperty(String propName) {
|
||||
@Override
|
||||
public STreeProperty findPropertyWithDynamic(String propName) {
|
||||
if (propName.indexOf('(') > -1) {
|
||||
return findSqlTreeFormula(propName);
|
||||
}
|
||||
@@ -2482,7 +2533,8 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
* inheritance tree (not up).
|
||||
* </p>
|
||||
*/
|
||||
public BeanProperty findBeanProperty(String propName) {
|
||||
@Override
|
||||
public BeanProperty findProperty(String propName) {
|
||||
int basePos = propName.indexOf('.');
|
||||
if (basePos > -1) {
|
||||
// embedded property
|
||||
@@ -2540,6 +2592,10 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
return autoTunable;
|
||||
}
|
||||
|
||||
public boolean isElementType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Inheritance mapping information. This will be null if this type
|
||||
* of bean is not involved in any ORM inheritance mapping.
|
||||
@@ -2809,6 +2865,41 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
return draftableElement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmbeddedPath(String propertyPath) {
|
||||
ElPropertyDeploy elProp = getElPropertyDeploy(propertyPath);
|
||||
if (elProp == null) {
|
||||
throw new PersistenceException("Invalid path " + propertyPath + " from " + getFullName());
|
||||
}
|
||||
return elProp.getBeanProperty().isEmbedded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtraJoin extraJoin(String propertyPath) {
|
||||
|
||||
ElPropertyValue elGetValue = getElGetValue(propertyPath);
|
||||
if (elGetValue != null) {
|
||||
BeanProperty beanProperty = elGetValue.getBeanProperty();
|
||||
if (beanProperty instanceof BeanPropertyAssoc<?>) {
|
||||
BeanPropertyAssoc<?> assocProp = (BeanPropertyAssoc<?>) beanProperty;
|
||||
if (!assocProp.isEmbedded()) {
|
||||
return new ExtraJoin(assocProp, elGetValue.containsMany());
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inheritanceLoad(SqlBeanLoad sqlBeanLoad, STreeProperty property, DbReadContext ctx) {
|
||||
BeanProperty p = getBeanProperty(property.getName());
|
||||
if (p != null) {
|
||||
p.load(sqlBeanLoad);
|
||||
} else {
|
||||
property.loadIgnore(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
public void setUnmappedJson(EntityBean bean, Map<String, Object> unmappedProperties) {
|
||||
if (unmappedJson != null) {
|
||||
unmappedJson.setValueIntercept(bean, unmappedProperties);
|
||||
@@ -2933,16 +3024,6 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
return selectLastInsertedId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the TableJoins.
|
||||
* <p>
|
||||
* For properties mapped to secondary tables rather than the base table.
|
||||
* </p>
|
||||
*/
|
||||
public TableJoin[] tableJoins() {
|
||||
return derivedTableJoins;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Property> allProperties() {
|
||||
return propertiesAll();
|
||||
@@ -3131,6 +3212,26 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public STreeProperty[] propsBaseScalar() {
|
||||
return propertiesBaseScalar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public STreePropertyAssoc[] propsEmbedded() {
|
||||
return propertiesEmbedded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public STreePropertyAssocOne[] propsOne() {
|
||||
return propertiesOne;
|
||||
}
|
||||
|
||||
@Override
|
||||
public STreePropertyAssocMany[] propsMany() {
|
||||
return propertiesMany;
|
||||
}
|
||||
|
||||
/**
|
||||
* All the BeanPropertyAssocOne that are not embedded. These are effectively
|
||||
* joined beans. For ManyToOne and OneToOne associations.
|
||||
@@ -3273,6 +3374,18 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
jsonHelp.jsonWriteDirtyProperties(writeJson, bean, dirtyProps);
|
||||
}
|
||||
|
||||
public void jsonWriteMapEntry(SpiJsonWriter ctx, Map.Entry<?, ?> entry) throws IOException {
|
||||
throw new IllegalStateException("Unexpected - expect Element override");
|
||||
}
|
||||
|
||||
public void jsonWriteElement(SpiJsonWriter ctx, Object element) {
|
||||
throw new IllegalStateException("Unexpected - expect Element override");
|
||||
}
|
||||
|
||||
public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException {
|
||||
throw new IllegalStateException("Unexpected - expect Element override");
|
||||
}
|
||||
|
||||
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
|
||||
jsonHelp.jsonWrite(writeJson, bean, null);
|
||||
}
|
||||
@@ -3285,11 +3398,11 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
jsonHelp.jsonWriteProperties(writeJson, bean);
|
||||
}
|
||||
|
||||
public T jsonRead(ReadJson jsonRead, String path) throws IOException {
|
||||
public T jsonRead(SpiJsonReader jsonRead, String path) throws IOException {
|
||||
return jsonHelp.jsonRead(jsonRead, path);
|
||||
}
|
||||
|
||||
protected T jsonReadObject(ReadJson jsonRead, String path) throws IOException {
|
||||
protected T jsonReadObject(SpiJsonReader jsonRead, String path) throws IOException {
|
||||
return jsonHelp.jsonReadObject(jsonRead, path);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import io.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
@@ -272,6 +273,11 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
*/
|
||||
boolean manyPropLoad(BeanPropertyAssocMany<?> many, BeanCollection<?> bc, Object parentId, Boolean readOnly) {
|
||||
|
||||
if (many.isElementCollection()) {
|
||||
// held as part of the bean cache so skip
|
||||
return false;
|
||||
}
|
||||
|
||||
CachedManyIds entry = manyPropGet(parentId, many.getName());
|
||||
if (entry == null) {
|
||||
// not in cache so return unsuccessful
|
||||
@@ -298,9 +304,29 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
*/
|
||||
void manyPropPut(BeanPropertyAssocMany<?> many, Object details, Object parentId) {
|
||||
|
||||
CachedManyIds entry = createManyIds(many, details);
|
||||
if (entry != null) {
|
||||
cachePutManyIds(parentId, many.getName(), entry);
|
||||
if (many.isElementCollection()) {
|
||||
CachedBeanData data = (CachedBeanData) beanCache.get(parentId);
|
||||
if (data != null) {
|
||||
try {
|
||||
// add as JSON to bean cache
|
||||
String asJson = many.jsonWriteCollection(details);
|
||||
Map<String,Object> changes = new HashMap<>();
|
||||
changes.put(many.getName(), asJson);
|
||||
|
||||
CachedBeanData newData = data.update(changes, data.getVersion());
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" UPDATE {}({}) changes:{}", cacheName, parentId, changes);
|
||||
}
|
||||
beanCache.put(parentId, newData);
|
||||
} catch (IOException e) {
|
||||
logger.error("Error updating L2 cache", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CachedManyIds entry = createManyIds(many, details);
|
||||
if (entry != null) {
|
||||
cachePutManyIds(parentId, many.getName(), entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -730,38 +756,17 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
List<BeanPropertyAssocMany<?>> manyCollections = updateRequest.getUpdatedManyCollections();
|
||||
if (manyCollections != null) {
|
||||
for (BeanPropertyAssocMany<?> many : manyCollections) {
|
||||
Object details = many.getValue(updateRequest.getEntityBean());
|
||||
CachedManyIds entry = createManyIds(many, details);
|
||||
if (entry != null) {
|
||||
changeSet.addManyPut(desc, many.getName(), id, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if the bean itself was updated
|
||||
if (!updateRequest.isUpdatedManysOnly()) {
|
||||
|
||||
boolean updateNaturalKey = false;
|
||||
|
||||
Map<String, Object> changes = new LinkedHashMap<>();
|
||||
EntityBean bean = updateRequest.getEntityBean();
|
||||
boolean[] dirtyProperties = updateRequest.getDirtyProperties();
|
||||
for (int i = 0; i < dirtyProperties.length; i++) {
|
||||
if (dirtyProperties[i]) {
|
||||
BeanProperty property = desc.propertiesIndex[i];
|
||||
if (property.isCacheDataInclude()) {
|
||||
Object val = property.getCacheDataValue(bean);
|
||||
changes.put(property.getName(), val);
|
||||
if (property.isNaturalKey()) {
|
||||
updateNaturalKey = true;
|
||||
changeSet.addNaturalKeyPut(desc, id, val);
|
||||
}
|
||||
if (!many.isElementCollection()) {
|
||||
Object details = many.getValue(updateRequest.getEntityBean());
|
||||
CachedManyIds entry = createManyIds(many, details);
|
||||
if (entry != null) {
|
||||
changeSet.addManyPut(desc, many.getName(), id, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
changeSet.addBeanUpdate(desc, id, changes, updateNaturalKey, updateRequest.getVersion());
|
||||
}
|
||||
|
||||
updateRequest.addBeanUpdate(changeSet);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Bean descriptor used with ElementCollection (where we don't have a mapped type/class).
|
||||
* <p>
|
||||
* This is somewhat a BeanDescriptor created 'on the fly' for a specific element collection property
|
||||
* with a unidirectional property and mapping etc specific to the property (and not the type if embedded).
|
||||
*/
|
||||
abstract class BeanDescriptorElement<T> extends BeanDescriptor<T> {
|
||||
|
||||
final ElementHelp elementHelp;
|
||||
|
||||
BeanDescriptorElement(BeanDescriptorMap owner, DeployBeanDescriptor<T> deploy, ElementHelp elementHelp) {
|
||||
super(owner, deploy);
|
||||
this.elementHelp = elementHelp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return the first base scalar type (and we expect only 1).
|
||||
*/
|
||||
ScalarType<Object> firstBaseScalarType() {
|
||||
BeanProperty[] props = propertiesBaseScalar();
|
||||
if (props.length != 1) {
|
||||
throw new IllegalStateException("Expecting 1 property for element scalar but got " + Arrays.toString(props));
|
||||
}
|
||||
return props[0].getScalarType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isElementType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Our entity beans used are somewhat fake ones (ElementEntityBean) such that we hold the unidirectional property
|
||||
* value (foreign key) and the actual element collection value (scalar or embedded plus map key).
|
||||
*/
|
||||
@Override
|
||||
protected EntityBean createPrototypeEntityBean(Class<T> beanType) {
|
||||
return new ElementEntityBean(properties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.PersistenceIOException;
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Bean descriptor used with element collection of list/set of embeddable.
|
||||
*/
|
||||
class BeanDescriptorElementEmbedded<T> extends BeanDescriptorElement<T> {
|
||||
|
||||
private final BeanPropertyAssocOne embeddedProperty;
|
||||
|
||||
private final EntityBean prototype;
|
||||
|
||||
private BeanDescriptor targetDescriptor;
|
||||
|
||||
BeanDescriptorElementEmbedded(BeanDescriptorMap owner, DeployBeanDescriptor<T> deploy, ElementHelp elementHelp) {
|
||||
super(owner, deploy, elementHelp);
|
||||
try {
|
||||
this.prototype = (EntityBean) beanType.newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Unable to create entity bean prototype for "+beanType);
|
||||
}
|
||||
BeanPropertyAssocOne<?>[] embedded = propertiesEmbedded();
|
||||
if (embedded.length == 1) {
|
||||
embeddedProperty = embedded[0];
|
||||
} else {
|
||||
embeddedProperty = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialiseOther(BeanDescriptorInitContext initContext) {
|
||||
super.initialiseOther(initContext);
|
||||
this.targetDescriptor = embeddedProperty.getTargetDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityBean createEntityBeanForJson() {
|
||||
return (EntityBean)prototype._ebean_newInstance();
|
||||
}
|
||||
|
||||
public void bindElementValue(SqlUpdate insert, Object value) {
|
||||
targetDescriptor.bindElementValue(insert, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWriteElement(SpiJsonWriter ctx, Object element) {
|
||||
writeJsonElement(ctx, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T jsonRead(SpiJsonReader jsonRead, String path) throws IOException {
|
||||
return readJsonElement(jsonRead, path);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
T readJsonElement(SpiJsonReader jsonRead, String path) throws IOException {
|
||||
return (T)targetDescriptor.jsonRead(jsonRead, path);
|
||||
}
|
||||
|
||||
void writeJsonElement(SpiJsonWriter ctx, Object element) {
|
||||
try {
|
||||
if (element == null) {
|
||||
ctx.writeNull();
|
||||
} else {
|
||||
targetDescriptor.jsonWrite(ctx, (EntityBean)element);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceIOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Descriptor for element collection using Map with the value holding an embedded bean.
|
||||
* <p>
|
||||
* The expected limitation is that the key is a scalar type.
|
||||
*/
|
||||
class BeanDescriptorElementEmbeddedMap<T> extends BeanDescriptorElementEmbedded<T> {
|
||||
|
||||
private final ScalarType scalarTypeKey;
|
||||
|
||||
private final boolean stringKey;
|
||||
|
||||
BeanDescriptorElementEmbeddedMap(BeanDescriptorMap owner, DeployBeanDescriptor<T> deploy, ElementHelp elementHelp) {
|
||||
super(owner, deploy, elementHelp);
|
||||
this.scalarTypeKey = firstBaseScalarType();
|
||||
this.stringKey = String.class.equals(scalarTypeKey.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void jsonWriteMapEntry(SpiJsonWriter ctx, Map.Entry<?, ?> entry) throws IOException {
|
||||
ctx.writeStartObject();
|
||||
if (stringKey) {
|
||||
Object key = entry.getKey();
|
||||
String keyName = (key == null) ? "null" : key.toString();
|
||||
ctx.writeFieldName(keyName);
|
||||
writeJsonElement(ctx, entry.getValue());
|
||||
} else {
|
||||
ctx.writeFieldName("key");
|
||||
scalarTypeKey.jsonWrite(ctx.gen(), entry.getKey());
|
||||
ctx.writeFieldName("value");
|
||||
writeJsonElement(ctx, entry.getValue());
|
||||
}
|
||||
ctx.writeEndObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
JsonParser parser = readJson.getParser();
|
||||
ElementCollector add = elementHelp.createCollector();
|
||||
do {
|
||||
JsonToken token = parser.nextToken();
|
||||
if (token != JsonToken.START_OBJECT) {
|
||||
break;
|
||||
}
|
||||
if (stringKey) {
|
||||
String key = parser.nextFieldName();
|
||||
parser.nextToken();
|
||||
Object val = readJsonElement(readJson, null);
|
||||
add.addKeyValue(key, val);
|
||||
|
||||
} else {
|
||||
parser.nextFieldName();
|
||||
Object key = scalarTypeKey.jsonRead(parser);
|
||||
|
||||
parser.nextFieldName();
|
||||
Object val = readJsonElement(readJson, null);
|
||||
add.addKeyValue(key, val);
|
||||
}
|
||||
|
||||
token = parser.nextToken();
|
||||
if (token != JsonToken.END_OBJECT) {
|
||||
break;
|
||||
}
|
||||
|
||||
} while (true);
|
||||
|
||||
return add.collection();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.ebean.PersistenceIOException;
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Bean descriptor used with element collection mapped to a list or set of scalar values.
|
||||
*/
|
||||
class BeanDescriptorElementScalar<T> extends BeanDescriptorElement<T> {
|
||||
|
||||
private final ScalarType<Object> scalarType;
|
||||
|
||||
BeanDescriptorElementScalar(BeanDescriptorMap owner, DeployBeanDescriptor<T> deploy, ElementHelp elementHelp) {
|
||||
super(owner, deploy, elementHelp);
|
||||
this.scalarType = firstBaseScalarType();
|
||||
}
|
||||
|
||||
public void bindElementValue(SqlUpdate insert, Object value) {
|
||||
insert.setNextParameter(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWriteElement(SpiJsonWriter ctx, Object element) {
|
||||
try {
|
||||
scalarType.jsonWrite(ctx.gen(), element);
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
JsonParser parser = readJson.getParser();
|
||||
ElementCollector add = elementHelp.createCollector();
|
||||
do {
|
||||
JsonToken token = parser.nextToken();
|
||||
if (JsonToken.VALUE_NULL == token || JsonToken.END_ARRAY == token) {
|
||||
break;
|
||||
}
|
||||
add.addElement(scalarType.jsonRead(parser));
|
||||
} while (true);
|
||||
|
||||
return add.collection();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Bean descriptor used with element collection mapped to Map where key and value are scalar types.
|
||||
*/
|
||||
class BeanDescriptorElementScalarMap<T> extends BeanDescriptorElement<T> {
|
||||
|
||||
private final ScalarType scalarTypeKey;
|
||||
private final ScalarType scalarTypeVal;
|
||||
private final boolean stringKey;
|
||||
|
||||
BeanDescriptorElementScalarMap(BeanDescriptorMap owner, DeployBeanDescriptor<T> deploy, ElementHelp elementHelp) {
|
||||
super(owner, deploy, elementHelp);
|
||||
BeanProperty[] props = propertiesNonTransient();
|
||||
if (props.length != 2) {
|
||||
throw new IllegalStateException("Expecting 2 properties for key and value but got " + Arrays.toString(props));
|
||||
}
|
||||
this.scalarTypeKey = props[0].getScalarType();
|
||||
this.scalarTypeVal = props[1].getScalarType();
|
||||
this.stringKey = String.class.equals(scalarTypeKey.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void jsonWriteMapEntry(SpiJsonWriter ctx, Map.Entry<?, ?> entry) throws IOException {
|
||||
ctx.writeStartObject();
|
||||
if (stringKey) {
|
||||
Object key = entry.getKey();
|
||||
String keyName = (key == null) ? "null" : key.toString();
|
||||
ctx.writeFieldName(keyName);
|
||||
scalarTypeVal.jsonWrite(ctx.gen(), entry.getValue());
|
||||
} else {
|
||||
ctx.writeFieldName("key");
|
||||
scalarTypeKey.jsonWrite(ctx.gen(), entry.getKey());
|
||||
ctx.writeFieldName("value");
|
||||
scalarTypeVal.jsonWrite(ctx.gen(), entry.getValue());
|
||||
}
|
||||
ctx.writeEndObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
JsonParser parser = readJson.getParser();
|
||||
ElementCollector add = elementHelp.createCollector();
|
||||
do {
|
||||
JsonToken token = parser.nextToken();
|
||||
if (token != JsonToken.START_OBJECT) {
|
||||
break;
|
||||
}
|
||||
if (stringKey) {
|
||||
String key = parser.nextFieldName();
|
||||
parser.nextToken();
|
||||
Object val = scalarTypeVal.jsonRead(parser);
|
||||
add.addKeyValue(key, val);
|
||||
|
||||
} else {
|
||||
parser.nextFieldName();
|
||||
Object key = scalarTypeKey.jsonRead(parser);
|
||||
|
||||
parser.nextFieldName();
|
||||
Object val = scalarTypeVal.jsonRead(parser);
|
||||
add.addKeyValue(key, val);
|
||||
}
|
||||
|
||||
token = parser.nextToken();
|
||||
if (token != JsonToken.END_OBJECT) {
|
||||
break;
|
||||
}
|
||||
|
||||
} while (true);
|
||||
|
||||
return add.collection();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
class BeanDescriptorInitContext {
|
||||
|
||||
private final Map<String, String> withHistoryTables;
|
||||
private final Map<String, String> draftTables;
|
||||
private final String asOfViewSuffix;
|
||||
|
||||
BeanDescriptorInitContext(Map<String, String> withHistoryTables, Map<String, String> draftTables, String asOfViewSuffix) {
|
||||
this.withHistoryTables = withHistoryTables;
|
||||
this.draftTables = draftTables;
|
||||
this.asOfViewSuffix = asOfViewSuffix;
|
||||
}
|
||||
|
||||
void addDraft(String baseTable, String draftTable) {
|
||||
draftTables.put(baseTable, draftTable);
|
||||
}
|
||||
|
||||
void addHistory(String baseTable, String baseTableAsOf) {
|
||||
withHistoryTables.put(baseTable, baseTableAsOf);
|
||||
}
|
||||
|
||||
void addHistoryIntersection(String intersectionTableName) {
|
||||
withHistoryTables.put(intersectionTableName, intersectionTableName + asOfViewSuffix);
|
||||
}
|
||||
|
||||
void addDraftIntersection(String intersectionPublishTable, String intersectionDraftTable) {
|
||||
draftTables.put(intersectionPublishTable, intersectionDraftTable);
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.text.json.EJson;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -70,7 +70,7 @@ public class BeanDescriptorJsonHelp<T> {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T jsonRead(ReadJson jsonRead, String path) throws IOException {
|
||||
public T jsonRead(SpiJsonReader jsonRead, String path) throws IOException {
|
||||
|
||||
JsonParser parser = jsonRead.getParser();
|
||||
//noinspection StatementWithEmptyBody
|
||||
@@ -116,14 +116,14 @@ public class BeanDescriptorJsonHelp<T> {
|
||||
return (T) inheritInfo.readType(discValue).desc().jsonReadObject(jsonRead, path);
|
||||
}
|
||||
|
||||
protected T jsonReadObject(ReadJson readJson, String path) throws IOException {
|
||||
protected T jsonReadObject(SpiJsonReader readJson, String path) throws IOException {
|
||||
|
||||
EntityBean bean = desc.createEntityBean();
|
||||
EntityBean bean = desc.createEntityBeanForJson();
|
||||
return jsonReadProperties(readJson, bean, path);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected T jsonReadProperties(ReadJson readJson, EntityBean bean, String path) throws IOException {
|
||||
protected T jsonReadProperties(SpiJsonReader readJson, EntityBean bean, String path) throws IOException {
|
||||
|
||||
if (path != null) {
|
||||
readJson.pushPath(path);
|
||||
|
||||
@@ -56,7 +56,6 @@ import io.ebeaninternal.server.query.CQueryPlan;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.ScalarTypeInteger;
|
||||
import io.ebeaninternal.server.type.TypeManager;
|
||||
import io.ebeaninternal.xmlmapping.XmlMappingReader;
|
||||
import io.ebeaninternal.xmlmapping.model.XmAliasMapping;
|
||||
import io.ebeaninternal.xmlmapping.model.XmColumnMapping;
|
||||
import io.ebeaninternal.xmlmapping.model.XmEbean;
|
||||
@@ -72,16 +71,12 @@ import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.Transient;
|
||||
import javax.sql.DataSource;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -184,8 +179,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
private final BeanLifecycleAdapterFactory beanLifecycleAdapterFactory;
|
||||
|
||||
private final boolean eagerFetchLobs;
|
||||
|
||||
private final String asOfViewSuffix;
|
||||
|
||||
/**
|
||||
@@ -215,12 +208,11 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
this.databasePlatform = serverConfig.getDatabasePlatform();
|
||||
this.multiValueBind = config.getMultiValueBind();
|
||||
this.idBinderFactory = new IdBinderFactory(databasePlatform.isIdInExpandedForm(), multiValueBind);
|
||||
this.eagerFetchLobs = serverConfig.isEagerFetchLobs();
|
||||
this.queryPlanTTLSeconds = serverConfig.getQueryPlanTTLSeconds();
|
||||
|
||||
this.asOfViewSuffix = getAsOfViewSuffix(databasePlatform, serverConfig);
|
||||
String versionsBetweenSuffix = getVersionsBetweenSuffix(databasePlatform, serverConfig);
|
||||
this.readAnnotations = new ReadAnnotations(config.getGeneratedPropertyFactory(), asOfViewSuffix, versionsBetweenSuffix, serverConfig.isDisableL2Cache());
|
||||
this.readAnnotations = new ReadAnnotations(config.getGeneratedPropertyFactory(), asOfViewSuffix, versionsBetweenSuffix, serverConfig);
|
||||
this.bootupClasses = config.getBootupClasses();
|
||||
this.createProperties = config.getDeployCreateProperties();
|
||||
this.namingConvention = serverConfig.getNamingConvention();
|
||||
@@ -365,12 +357,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
/**
|
||||
* Deploy returning the asOfTableMap (which is required by the SQL builders).
|
||||
*/
|
||||
public Map<String, String> deploy() {
|
||||
public Map<String, String> deploy(List<XmEbean> mappings) {
|
||||
|
||||
try {
|
||||
createListeners();
|
||||
readEntityDeploymentInitial();
|
||||
readXmlMapping();
|
||||
readXmlMapping(mappings);
|
||||
readEmbeddedDeployment();
|
||||
readEntityBeanTable();
|
||||
readEntityDeploymentAssociations();
|
||||
@@ -401,30 +393,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
}
|
||||
|
||||
private void readXmlMapping() {
|
||||
private void readXmlMapping(List<XmEbean> mappings) {
|
||||
ClassLoader classLoader = serverConfig.getClassLoadConfig().getClassLoader();
|
||||
|
||||
try {
|
||||
ClassLoader classLoader = serverConfig.getClassLoadConfig().getClassLoader();
|
||||
|
||||
Enumeration<URL> resources = classLoader.getResources("ebean.xml");
|
||||
|
||||
List<XmEbean> mappings = new ArrayList<>();
|
||||
while (resources.hasMoreElements()) {
|
||||
URL url = resources.nextElement();
|
||||
try (InputStream is = url.openStream()) {
|
||||
mappings.add(XmlMappingReader.read(is));
|
||||
}
|
||||
for (XmEbean mapping : mappings) {
|
||||
List<XmEntity> entityDeploy = mapping.getEntity();
|
||||
for (XmEntity deploy : entityDeploy) {
|
||||
readEntityMapping(classLoader, deploy);
|
||||
}
|
||||
|
||||
for (XmEbean mapping : mappings) {
|
||||
List<XmEntity> entityDeploy = mapping.getEntity();
|
||||
for (XmEntity deploy : entityDeploy) {
|
||||
readEntityMapping(classLoader, deploy);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error reading ebean.xml", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,12 +551,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
// we can initialise them which sorts out circular
|
||||
// dependencies for OneToMany and ManyToOne etc
|
||||
|
||||
BeanDescriptorInitContext initContext = new BeanDescriptorInitContext(asOfTableMap, draftTableMap, asOfViewSuffix);
|
||||
|
||||
// PASS 1:
|
||||
// initialise the ID properties of all the beans
|
||||
// first (as they are needed to initialise the
|
||||
// associated properties in the second pass).
|
||||
for (BeanDescriptor<?> d : descMap.values()) {
|
||||
d.initialiseId(asOfTableMap, draftTableMap);
|
||||
d.initialiseId(initContext);
|
||||
}
|
||||
|
||||
// PASS 2:
|
||||
@@ -595,7 +573,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
// also look for intersection tables with
|
||||
// associated history support and register them
|
||||
// into the asOfTableMap
|
||||
d.initialiseOther(asOfTableMap, asOfViewSuffix, draftTableMap);
|
||||
d.initialiseOther(initContext);
|
||||
}
|
||||
|
||||
// PASS 4:
|
||||
@@ -656,6 +634,13 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
return beanTableMap.get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a BeanTable for an ElementCollection.
|
||||
*/
|
||||
public BeanTable createCollectionBeanTable(String fullTableName, Class<?> targetType) {
|
||||
return new BeanTable(this, fullTableName, targetType);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> BeanManager<T> getBeanManager(Class<T> entityType) {
|
||||
|
||||
@@ -1025,7 +1010,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* </p>
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void makeUnidirectional(DeployBeanInfo<?> info, DeployBeanPropertyAssocMany<?> oneToMany) {
|
||||
private void makeUnidirectional(DeployBeanPropertyAssocMany<?> oneToMany) {
|
||||
|
||||
DeployBeanDescriptor<?> targetDesc = getTargetDescriptor(oneToMany);
|
||||
|
||||
@@ -1045,34 +1030,36 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
// mark this property as unidirectional
|
||||
oneToMany.setUnidirectional();
|
||||
|
||||
// create the 'shadow' unidirectional property
|
||||
// which is put on the target descriptor
|
||||
DeployBeanPropertyAssocOne<?> unidirectional = new DeployBeanPropertyAssocOne(targetDesc, owningType);
|
||||
unidirectional.setUndirectionalShadow();
|
||||
unidirectional.setNullable(false);
|
||||
unidirectional.setDbRead(true);
|
||||
unidirectional.setDbInsertable(true);
|
||||
unidirectional.setDbUpdateable(false);
|
||||
|
||||
targetDesc.setUnidirectional(unidirectional);
|
||||
|
||||
// specify table and table alias...
|
||||
BeanTable beanTable = getBeanTable(owningType);
|
||||
unidirectional.setBeanTable(beanTable);
|
||||
unidirectional.setName(beanTable.getBaseTable());
|
||||
|
||||
info.setBeanJoinType(unidirectional, true);
|
||||
|
||||
// define the TableJoin
|
||||
DeployTableJoin oneToManyJoin = oneToMany.getTableJoin();
|
||||
if (!oneToManyJoin.hasJoinColumns()) {
|
||||
throw new RuntimeException("No join columns");
|
||||
}
|
||||
createUnidirectional(targetDesc, owningType, beanTable, oneToManyJoin);
|
||||
}
|
||||
|
||||
// inverse of the oneToManyJoin
|
||||
DeployTableJoin unidirectionalJoin = unidirectional.getTableJoin();
|
||||
unidirectionalJoin.setColumns(oneToManyJoin.columns(), true);
|
||||
/**
|
||||
* Create and add a Unidirectional property (for ElementCollection) which maps to the foreign key.
|
||||
*/
|
||||
public <A> void createUnidirectional(DeployBeanDescriptor<?> targetDesc, Class<A> targetType, BeanTable beanTable, DeployTableJoin oneToManyJoin) {
|
||||
|
||||
// create the 'shadow' unidirectional property
|
||||
// which is put on the target descriptor
|
||||
DeployBeanPropertyAssocOne<A> unidirectional = new DeployBeanPropertyAssocOne<>(targetDesc, targetType);
|
||||
unidirectional.setUndirectionalShadow();
|
||||
unidirectional.setNullable(false);
|
||||
unidirectional.setDbRead(true);
|
||||
unidirectional.setDbInsertable(true);
|
||||
unidirectional.setDbUpdateable(false);
|
||||
unidirectional.setBeanTable(beanTable);
|
||||
unidirectional.setName(beanTable.getBaseTable());
|
||||
unidirectional.setJoinType(true);
|
||||
unidirectional.setJoinColumns(oneToManyJoin.columns(), true);
|
||||
|
||||
targetDesc.setUnidirectional(unidirectional);
|
||||
}
|
||||
|
||||
private void checkMappedByOneToOne(DeployBeanPropertyAssocOne<?> prop) {
|
||||
@@ -1137,6 +1124,10 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
*/
|
||||
private void checkMappedByOneToMany(DeployBeanInfo<?> info, DeployBeanPropertyAssocMany<?> prop) {
|
||||
|
||||
if (prop.isElementCollection()) {
|
||||
// skip mapping check
|
||||
return;
|
||||
}
|
||||
DeployBeanDescriptor<?> targetDesc = getTargetDescriptor(prop);
|
||||
|
||||
if (targetDesc.isDraftableElement()) {
|
||||
@@ -1159,7 +1150,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
if (!findMappedBy(prop)) {
|
||||
if (!prop.isO2mJoinTable()) {
|
||||
makeUnidirectional(info, prop);
|
||||
makeUnidirectional(prop);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1311,7 +1302,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
DeployBeanInfo<T> info = new DeployBeanInfo<>(deployUtil, desc);
|
||||
|
||||
readAnnotations.readInitial(info, eagerFetchLobs);
|
||||
readAnnotations.readInitial(info);
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -1350,7 +1341,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
// already assigned (So custom or UUID)
|
||||
return;
|
||||
}
|
||||
if (desc.propertiesId().isEmpty()) {
|
||||
if (desc.idProperty() == null) {
|
||||
// bean doesn't have an Id property
|
||||
if (desc.isBaseTableType() && desc.getBeanFinder() == null) {
|
||||
// expecting an id property
|
||||
@@ -1491,6 +1482,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
private boolean isPersistentField(DeployBeanProperty prop) {
|
||||
|
||||
Field field = prop.getField();
|
||||
if (field == null) {
|
||||
return false;
|
||||
}
|
||||
int modifiers = field.getModifiers();
|
||||
return !(Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) && !field.isAnnotationPresent(Transient.class);
|
||||
}
|
||||
@@ -1633,6 +1627,43 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
target.setPrimaryKeyJoin(inverseJoin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a DeployBeanDescriptor for an ElementCollection target.
|
||||
*/
|
||||
public <A> DeployBeanDescriptor<A> createDeployDescriptor(Class<A> targetType) {
|
||||
return new DeployBeanDescriptor<>(this, targetType, serverConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a BeanDescriptor for an ElementCollection target.
|
||||
*/
|
||||
public <A> BeanDescriptor<A> createElementDescriptor(DeployBeanDescriptor<A> elementDescriptor, ManyType manyType, boolean scalar) {
|
||||
|
||||
ElementHelp elementHelp = elementHelper(manyType);
|
||||
if (manyType.isMap()) {
|
||||
if (scalar) {
|
||||
return new BeanDescriptorElementScalarMap<>(this, elementDescriptor, elementHelp);
|
||||
} else {
|
||||
return new BeanDescriptorElementEmbeddedMap<>(this, elementDescriptor, elementHelp);
|
||||
}
|
||||
}
|
||||
if (scalar) {
|
||||
return new BeanDescriptorElementScalar<>(this, elementDescriptor, elementHelp);
|
||||
} else {
|
||||
return new BeanDescriptorElementEmbedded<>(this, elementDescriptor, elementHelp);
|
||||
}
|
||||
}
|
||||
|
||||
private ElementHelp elementHelper(ManyType manyType) {
|
||||
switch (manyType) {
|
||||
case LIST: return new ElementHelpList();
|
||||
case SET: return new ElementHelpSet();
|
||||
case MAP: return new ElementHelpMap();
|
||||
default:
|
||||
throw new IllegalStateException("manyType unexpected "+manyType);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
for (BeanDescriptor<?> desc : immutableDescriptorList) {
|
||||
desc.visitMetrics(visitor);
|
||||
|
||||
@@ -102,7 +102,6 @@ class BeanLifecycleAdapterFactory {
|
||||
postInserts.add(method);
|
||||
hasPersistMethods = true;
|
||||
}
|
||||
|
||||
if (method.isAnnotationPresent(PreUpdate.class)) {
|
||||
preUpdates.add(method);
|
||||
hasPersistMethods = true;
|
||||
@@ -111,7 +110,6 @@ class BeanLifecycleAdapterFactory {
|
||||
postUpdates.add(method);
|
||||
hasPersistMethods = true;
|
||||
}
|
||||
|
||||
if (method.isAnnotationPresent(PreRemove.class)) {
|
||||
preDeletes.add(method);
|
||||
hasPersistMethods = true;
|
||||
@@ -128,7 +126,6 @@ class BeanLifecycleAdapterFactory {
|
||||
postSoftDeletes.add(method);
|
||||
hasPersistMethods = true;
|
||||
}
|
||||
|
||||
if (method.isAnnotationPresent(PostLoad.class)) {
|
||||
postLoads.add(method);
|
||||
}
|
||||
|
||||
@@ -5,10 +5,9 @@ import io.ebean.Query;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.common.BeanList;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@@ -17,48 +16,20 @@ import java.util.List;
|
||||
/**
|
||||
* Helper object for dealing with Lists.
|
||||
*/
|
||||
public final class BeanListHelp<T> extends BaseCollectionHelp<T> {
|
||||
public class BeanListHelp<T> extends BaseCollectionHelp<T> {
|
||||
|
||||
private final BeanPropertyAssocMany<T> many;
|
||||
private final BeanDescriptor<T> targetDescriptor;
|
||||
private final String propertyName;
|
||||
|
||||
private BeanCollectionLoader loader;
|
||||
|
||||
public BeanListHelp(BeanPropertyAssocMany<T> many) {
|
||||
this.many = many;
|
||||
this.targetDescriptor = many.getTargetDescriptor();
|
||||
this.propertyName = many.getName();
|
||||
BeanListHelp(BeanPropertyAssocMany<T> many) {
|
||||
super(many);
|
||||
}
|
||||
|
||||
public BeanListHelp() {
|
||||
this.many = null;
|
||||
this.targetDescriptor = null;
|
||||
this.propertyName = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoader(BeanCollectionLoader loader) {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal add bypassing any modify listening.
|
||||
*/
|
||||
@Override
|
||||
public void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck) {
|
||||
if (withCheck) {
|
||||
collection.internalAddWithCheck(bean);
|
||||
} else {
|
||||
collection.internalAdd(bean);
|
||||
}
|
||||
BeanListHelp() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) {
|
||||
|
||||
if (bc instanceof BeanList<?>) {
|
||||
|
||||
BeanList<?> bl = (BeanList<?>) bc;
|
||||
if (bl.getActualList() == null) {
|
||||
bl.setActualList(new ArrayList<>());
|
||||
@@ -144,13 +115,7 @@ public final class BeanListHelp<T> extends BaseCollectionHelp<T> {
|
||||
list = (List<?>) collection;
|
||||
}
|
||||
|
||||
if (!list.isEmpty() || ctx.isIncludeEmpty()) {
|
||||
ctx.beginAssocMany(name);
|
||||
for (Object aList : list) {
|
||||
targetDescriptor.jsonWrite(ctx, (EntityBean) aList);
|
||||
}
|
||||
ctx.endAssocMany();
|
||||
}
|
||||
jsonWriteCollection(ctx, name, list);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
|
||||
/**
|
||||
* Helper for element collection List.
|
||||
*/
|
||||
public class BeanListHelpElement<T> extends BeanListHelp<T> {
|
||||
|
||||
BeanListHelpElement(BeanPropertyAssocMany<T> many) {
|
||||
super(many);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck) {
|
||||
Object elementValue = bean._ebean_getField(0);
|
||||
if (withCheck) {
|
||||
collection.internalAddWithCheck(elementValue);
|
||||
} else {
|
||||
collection.internalAdd(elementValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void jsonWriteElement(SpiJsonWriter ctx, Object element) {
|
||||
many.jsonWriteElementValue(ctx, element);
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,9 @@ import io.ebean.Query;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.common.BeanMap;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -18,18 +17,17 @@ import java.util.Map.Entry;
|
||||
/**
|
||||
* Helper specifically for dealing with Maps.
|
||||
*/
|
||||
public final class BeanMapHelp<T> extends BaseCollectionHelp<T> {
|
||||
public class BeanMapHelp<T> extends BaseCollectionHelp<T> {
|
||||
|
||||
private final BeanPropertyAssocMany<T> many;
|
||||
private final BeanDescriptor<T> targetDescriptor;
|
||||
private final String propertyName;
|
||||
private final BeanProperty beanProperty;
|
||||
private BeanCollectionLoader loader;
|
||||
|
||||
/**
|
||||
* When created for a given query that will return a map.
|
||||
*/
|
||||
public BeanMapHelp(BeanDescriptor<T> targetDescriptor, String mapKey) {
|
||||
BeanMapHelp(BeanDescriptor<T> targetDescriptor, String mapKey) {
|
||||
this.targetDescriptor = targetDescriptor;
|
||||
this.beanProperty = targetDescriptor.getBeanProperty(mapKey);
|
||||
this.many = null;
|
||||
@@ -39,18 +37,13 @@ public final class BeanMapHelp<T> extends BaseCollectionHelp<T> {
|
||||
/**
|
||||
* When help is attached to a specific many property.
|
||||
*/
|
||||
public BeanMapHelp(BeanPropertyAssocMany<T> many) {
|
||||
BeanMapHelp(BeanPropertyAssocMany<T> many) {
|
||||
this.many = many;
|
||||
this.targetDescriptor = many.getTargetDescriptor();
|
||||
this.propertyName = many.getName();
|
||||
this.beanProperty = targetDescriptor.getBeanProperty(many.getMapKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoader(BeanCollectionLoader loader) {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) {
|
||||
@@ -182,8 +175,7 @@ public final class BeanMapHelp<T> extends BaseCollectionHelp<T> {
|
||||
if (!map.isEmpty() || ctx.isIncludeEmpty()) {
|
||||
ctx.beginAssocMany(name);
|
||||
for (Entry<?, ?> entry : map.entrySet()) {
|
||||
//FIXME: json write map key ...
|
||||
targetDescriptor.jsonWrite(ctx, (EntityBean) entry.getValue());
|
||||
many.jsonWriteMapEntry(ctx, entry);
|
||||
}
|
||||
ctx.endAssocMany();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.common.BeanMap;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
|
||||
public class BeanMapHelpElement<T> extends BeanMapHelp<T> {
|
||||
|
||||
BeanMapHelpElement(BeanPropertyAssocMany<T> many) {
|
||||
super(many);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck) {
|
||||
Object key = bean._ebean_getField(0);
|
||||
Object val = bean._ebean_getField(1);
|
||||
|
||||
BeanMap<?, ?> map = ((BeanMap<?, ?>) collection);
|
||||
if (withCheck) {
|
||||
map.internalPutWithCheck(key, val);
|
||||
} else {
|
||||
map.internalPut(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void jsonWriteElement(SpiJsonWriter ctx, Object element) {
|
||||
throw new IllegalStateException("not called");
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import io.ebean.util.SplitName;
|
||||
import io.ebean.util.StringHelper;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.core.InternString;
|
||||
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
|
||||
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedWhenCreated;
|
||||
@@ -22,11 +24,9 @@ import io.ebeaninternal.server.el.ElPropertyChainBuilder;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.properties.BeanPropertyGetter;
|
||||
import io.ebeaninternal.server.properties.BeanPropertySetter;
|
||||
import io.ebeaninternal.server.query.STreeProperty;
|
||||
import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.query.SqlTreeProperty;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.ScalarTypeBoolean;
|
||||
@@ -56,15 +56,17 @@ import java.util.Set;
|
||||
* Description of a property of a bean. Includes its deployment information such
|
||||
* as database column mapping information.
|
||||
*/
|
||||
public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty {
|
||||
public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanProperty.class);
|
||||
|
||||
/**
|
||||
* Flag to mark this at part of the unique id.
|
||||
* Flag to mark this is the id property.
|
||||
*/
|
||||
final boolean id;
|
||||
|
||||
final boolean importedPrimaryKey;
|
||||
|
||||
/**
|
||||
* Flag to make this as a dummy property for unidirecitonal relationships.
|
||||
*/
|
||||
@@ -274,6 +276,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
this.name = InternString.intern(deploy.getName());
|
||||
this.propertyIndex = deploy.getPropertyIndex();
|
||||
this.unidirectionalShadow = deploy.isUndirectionalShadow();
|
||||
this.importedPrimaryKey = deploy.isImportedPrimaryKey();
|
||||
this.discriminator = deploy.isDiscriminator();
|
||||
this.localEncrypted = deploy.isLocalEncrypted();
|
||||
this.dbEncrypted = deploy.isDbEncrypted();
|
||||
@@ -395,6 +398,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
this.softDeleteDbSet = source.softDeleteDbSet;
|
||||
this.softDeleteDbPredicate = source.softDeleteDbPredicate;
|
||||
this.fetchEager = source.fetchEager;
|
||||
this.importedPrimaryKey = source.importedPrimaryKey;
|
||||
this.unidirectionalShadow = source.unidirectionalShadow;
|
||||
this.discriminator = source.discriminator;
|
||||
this.localEncrypted = source.isLocalEncrypted();
|
||||
@@ -451,11 +455,10 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
* initialise variables that can't be done in construction due to recursive
|
||||
* issues.
|
||||
*/
|
||||
public void initialise() {
|
||||
public void initialise(BeanDescriptorInitContext initContext) {
|
||||
// do nothing for normal BeanProperty
|
||||
if (!isTransient && scalarType == null) {
|
||||
String msg = "No ScalarType assigned to " + descriptor.getFullName() + "." + getName();
|
||||
throw new RuntimeException(msg);
|
||||
throw new RuntimeException("No ScalarType assigned to " + descriptor.getFullName() + "." + getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,8 +476,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
this.deployOrder = deployOrder;
|
||||
}
|
||||
|
||||
public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain,
|
||||
boolean propertyDeploy) {
|
||||
public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -502,6 +504,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
/**
|
||||
* Return true if this property is based on a formula.
|
||||
*/
|
||||
@Override
|
||||
public boolean isFormula() {
|
||||
return formula;
|
||||
}
|
||||
@@ -527,6 +530,11 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
return descriptor.getEncryptKey(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEncryptKeyAsString() {
|
||||
return getEncryptKey().getStringValue();
|
||||
}
|
||||
|
||||
public String getDecryptProperty(String propertyName) {
|
||||
return dbEncryptFunction.getDecryptSql(propertyName);
|
||||
}
|
||||
@@ -549,6 +557,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
* Add any extra joins required to support this property. Generally a no
|
||||
* operation except for a OneToOne exported.
|
||||
*/
|
||||
@Override
|
||||
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
if (formula && sqlFormulaJoin != null) {
|
||||
ctx.appendFormulaJoin(sqlFormulaJoin, joinType);
|
||||
@@ -573,6 +582,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
return aggregation != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
|
||||
|
||||
if (aggregation != null) {
|
||||
@@ -583,8 +593,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
} else if (!isTransient && !ignoreDraftOnlyProperty(ctx.isDraftQuery())) {
|
||||
|
||||
if (secondaryTableJoin != null) {
|
||||
String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix);
|
||||
ctx.pushTableAlias(relativePrefix);
|
||||
ctx.pushTableAlias(ctx.getRelativePrefix(secondaryTableJoinPrefix));
|
||||
}
|
||||
|
||||
if (dbEncrypted) {
|
||||
@@ -611,14 +620,17 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
return owningType.isAssignableFrom(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadIgnore(DbReadContext ctx) {
|
||||
scalarType.loadIgnore(ctx.getDataReader());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(SqlBeanLoad sqlBeanLoad) {
|
||||
sqlBeanLoad.load(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildRawSqlSelectChain(String prefix, List<String> selectChain) {
|
||||
if (prefix == null) {
|
||||
selectChain.add(name);
|
||||
@@ -805,13 +817,6 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
setValue(bean, cacheData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the property value from a compound value type.
|
||||
*/
|
||||
public Object getValueObject(Object bean) {
|
||||
throw new RuntimeException("Expected to be called only on BeanPropertyCompoundScalar");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getVal(Object bean) {
|
||||
return getValue((EntityBean) bean);
|
||||
@@ -935,6 +940,13 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true this bean maps to the primary key.
|
||||
*/
|
||||
public boolean isImportedPrimaryKey() {
|
||||
return importedPrimaryKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssocMany() {
|
||||
// Returns false - override in BeanPropertyAssocMany.
|
||||
@@ -1358,6 +1370,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
/**
|
||||
* Return true if this is included in the unique id.
|
||||
*/
|
||||
@Override
|
||||
public boolean isId() {
|
||||
return id;
|
||||
}
|
||||
@@ -1366,6 +1379,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
* Return true if this is an Embedded property. In this case it shares the
|
||||
* table and primary key of its owner object.
|
||||
*/
|
||||
@Override
|
||||
public boolean isEmbedded() {
|
||||
return embedded;
|
||||
}
|
||||
@@ -1439,7 +1453,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
}
|
||||
}
|
||||
|
||||
public void jsonRead(ReadJson ctx, EntityBean bean) throws IOException {
|
||||
public void jsonRead(SpiJsonReader ctx, EntityBean bean) throws IOException {
|
||||
|
||||
JsonToken event = ctx.nextToken();
|
||||
if (JsonToken.VALUE_NULL == event) {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.text.PathProperties;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import io.ebeaninternal.server.core.InternString;
|
||||
import io.ebeaninternal.server.deploy.id.IdBinder;
|
||||
@@ -15,7 +16,10 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.el.ElPropertyChainBuilder;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.persist.MultiValueWrapper;
|
||||
import io.ebeaninternal.server.query.STreePropertyAssoc;
|
||||
import io.ebeaninternal.server.query.STreeType;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.querydefn.DefaultOrmQuery;
|
||||
import io.ebeanservice.docstore.api.mapping.DocMappingBuilder;
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyMapping;
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
@@ -30,7 +34,7 @@ import java.util.List;
|
||||
/**
|
||||
* Abstract base for properties mapped to an associated bean, list, set or map.
|
||||
*/
|
||||
public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STreePropertyAssoc {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanPropertyAssoc.class);
|
||||
|
||||
@@ -101,15 +105,18 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
* Initialise post construction.
|
||||
*/
|
||||
@Override
|
||||
public void initialise() {
|
||||
public void initialise(BeanDescriptorInitContext initContext) {
|
||||
// this *MUST* execute after the BeanDescriptor is
|
||||
// put into the map to stop infinite recursion
|
||||
initialiseTargetDescriptor(initContext);
|
||||
}
|
||||
|
||||
void initialiseTargetDescriptor(BeanDescriptorInitContext initContext) {
|
||||
targetDescriptor = descriptor.getBeanDescriptor(targetType);
|
||||
if (!isTransient) {
|
||||
targetIdBinder = targetDescriptor.getIdBinder();
|
||||
targetInheritInfo = targetDescriptor.getInheritInfo();
|
||||
saveRecurseSkippable = targetDescriptor.isSaveRecurseSkippable();
|
||||
|
||||
if (!targetIdBinder.isComplexId()) {
|
||||
targetIdProperty = targetIdBinder.getIdProperty();
|
||||
}
|
||||
@@ -142,6 +149,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
/**
|
||||
* Add table join with table alias based on prefix.
|
||||
*/
|
||||
@Override
|
||||
public SqlJoinType addJoin(SqlJoinType joinType, String prefix, DbSqlContext ctx) {
|
||||
return tableJoin.addJoin(joinType, prefix, ctx);
|
||||
}
|
||||
@@ -149,6 +157,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
/**
|
||||
* Add table join with explicit table alias.
|
||||
*/
|
||||
@Override
|
||||
public SqlJoinType addJoin(SqlJoinType joinType, String a1, String a2, DbSqlContext ctx) {
|
||||
return tableJoin.addJoin(joinType, a1, a2, ctx);
|
||||
}
|
||||
@@ -186,6 +195,29 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
return targetDescriptor;
|
||||
}
|
||||
|
||||
SpiEbeanServer server() {
|
||||
return descriptor.getEbeanServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new query for the target type.
|
||||
*
|
||||
* We use target descriptor rather than target property type to support ElementCollection.
|
||||
*/
|
||||
public SpiQuery<T> newQuery(SpiEbeanServer server) {
|
||||
return new DefaultOrmQuery<>(targetDescriptor, server, server.getExpressionFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdBinder getIdBinder() {
|
||||
return descriptor.getIdBinder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public STreeType target() {
|
||||
return targetDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the target side has soft delete.
|
||||
*/
|
||||
@@ -325,6 +357,13 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
return tableJoin.columns().length <= 0 || tableJoin.columns()[0].isInsertable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying BeanTable for this property.
|
||||
*/
|
||||
public BeanTable getBeanTable() {
|
||||
return beanTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the join to use for the bean.
|
||||
*/
|
||||
@@ -440,19 +479,15 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
}
|
||||
}
|
||||
|
||||
EbeanServer server() {
|
||||
return getBeanDescriptor().getEbeanServer();
|
||||
}
|
||||
|
||||
void bindParentIds(DefaultSqlUpdate delete, List<Object> parentIds) {
|
||||
|
||||
if (isExportedSimple()) {
|
||||
delete.addParameter(new MultiValueWrapper(parentIds));
|
||||
delete.setNextParameter(new MultiValueWrapper(parentIds));
|
||||
} else {
|
||||
// embedded ids etc
|
||||
List<Object> bindValues = flattenParentIds(parentIds);
|
||||
for (Object bindValue : bindValues) {
|
||||
delete.addParameter(bindValue);
|
||||
delete.setNextParameter(bindValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -460,12 +495,12 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
void bindParentId(DefaultSqlUpdate sqlUpd, Object parentId) {
|
||||
|
||||
if (isExportedSimple()) {
|
||||
sqlUpd.addParameter(parentId);
|
||||
sqlUpd.setNextParameter(parentId);
|
||||
return;
|
||||
}
|
||||
EntityBean parent = (EntityBean) parentId;
|
||||
for (ExportedProperty exportedProperty : exportedProperties) {
|
||||
sqlUpd.addParameter(exportedProperty.getValue(parent));
|
||||
sqlUpd.setNextParameter(exportedProperty.getValue(parent));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,4 +527,26 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
private boolean isExportedSimple() {
|
||||
return exportedProperties.length == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return the exported property matching to this property.
|
||||
*/
|
||||
ExportedProperty findMatch(boolean embedded, BeanProperty prop, String matchColumn, TableJoin tableJoin) {
|
||||
|
||||
String searchTable = tableJoin.getTable();
|
||||
|
||||
for (TableJoinColumn column : tableJoin.columns()) {
|
||||
String matchTo = column.getLocalDbColumn();
|
||||
|
||||
if (matchColumn.equalsIgnoreCase(matchTo)) {
|
||||
String foreignCol = column.getForeignDbColumn();
|
||||
return new ExportedProperty(embedded, foreignCol, prop);
|
||||
}
|
||||
}
|
||||
|
||||
String msg = "Error with the Join on [" + getFullBeanName()
|
||||
+ "]. Could not find the matching foreign key for [" + matchColumn + "] in table[" + searchTable + "]?"
|
||||
+ " Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped?";
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.Query;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
@@ -9,22 +9,24 @@ import io.ebean.bean.BeanCollection.ModifyListenMode;
|
||||
import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.text.PathProperties;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.deploy.id.ImportedId;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.el.ElPropertyChainBuilder;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.query.STreePropertyAssocMany;
|
||||
import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -35,7 +37,7 @@ import java.util.Map;
|
||||
/**
|
||||
* Property mapped to a List Set or Map.
|
||||
*/
|
||||
public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements STreePropertyAssocMany {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanPropertyAssocMany.class);
|
||||
|
||||
@@ -51,7 +53,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
/**
|
||||
* For ManyToMany this is the Inverse join used to build reference queries.
|
||||
*/
|
||||
private final TableJoin inverseJoin;
|
||||
final TableJoin inverseJoin;
|
||||
|
||||
/**
|
||||
* Flag to indicate that this is a unidirectional relationship.
|
||||
@@ -70,6 +72,13 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
*/
|
||||
private final boolean manyToMany;
|
||||
|
||||
private final boolean elementCollection;
|
||||
|
||||
/**
|
||||
* Descriptor for the 'target' when the property maps to an element collection.
|
||||
*/
|
||||
BeanDescriptor<T> elementDescriptor;
|
||||
|
||||
/**
|
||||
* Order by used when fetch joining the associated many.
|
||||
*/
|
||||
@@ -91,8 +100,6 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
private BeanProperty mapKeyProperty;
|
||||
|
||||
private String exportedPropertyBindProto = "?";
|
||||
|
||||
/**
|
||||
* Property on the 'child' bean that links back to the 'master'.
|
||||
*/
|
||||
@@ -106,9 +113,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
private ImportedId importedId;
|
||||
|
||||
private String deleteByParentIdSql;
|
||||
|
||||
private String deleteByParentIdInSql;
|
||||
private BeanPropertyAssocManySqlHelp<T> sqlHelp;
|
||||
|
||||
/**
|
||||
* Create this property.
|
||||
@@ -119,6 +124,8 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
this.o2mJoinTable = deploy.isO2mJoinTable();
|
||||
this.hasOrderColumn = deploy.hasOrderColumn();
|
||||
this.manyToMany = deploy.isManyToMany();
|
||||
this.elementCollection = deploy.isElementCollection();
|
||||
this.elementDescriptor = deploy.getElementDescriptor();
|
||||
this.manyType = deploy.getManyType();
|
||||
this.mapKey = deploy.getMapKey();
|
||||
this.fetchOrderBy = deploy.getFetchOrderBy();
|
||||
@@ -136,13 +143,28 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise() {
|
||||
super.initialise();
|
||||
public void initialise(BeanDescriptorInitContext initContext) {
|
||||
super.initialise(initContext);
|
||||
initialiseAssocMany();
|
||||
if (elementCollection) {
|
||||
// initialise all non-id properties (we don't have an Id property)
|
||||
elementDescriptor.initialiseOther(initContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void initialiseTargetDescriptor(BeanDescriptorInitContext initContext) {
|
||||
if (elementCollection) {
|
||||
targetDescriptor = elementDescriptor;
|
||||
} else {
|
||||
targetDescriptor = descriptor.getBeanDescriptor(targetType);
|
||||
}
|
||||
}
|
||||
|
||||
private void initialiseAssocMany() {
|
||||
if (!isTransient) {
|
||||
this.help = BeanCollectionHelpFactory.create(this);
|
||||
|
||||
if (hasJoinTable()) {
|
||||
if (hasJoinTable() || elementCollection) {
|
||||
importedId = createImportedId(this, targetDescriptor, tableJoin);
|
||||
|
||||
} else {
|
||||
@@ -159,37 +181,21 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
exportedProperties = createExported();
|
||||
this.sqlHelp = new BeanPropertyAssocManySqlHelp<>(this, exportedProperties);
|
||||
|
||||
if (exportedProperties.length > 0) {
|
||||
embeddedExportedProperties = exportedProperties[0].isEmbedded();
|
||||
exportedPropertyBindProto = deriveExportedPropertyBindProto();
|
||||
|
||||
if (fetchOrderBy != null) {
|
||||
// derive lazyFetchOrderBy
|
||||
StringBuilder sb = new StringBuilder(50);
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
// these fk columns are either on the intersection (int_) or base table (t0)
|
||||
String fkTableAlias = hasJoinTable() ? "int_" : "t0";
|
||||
sb.append(fkTableAlias).append(".").append(exportedProperties[i].getForeignDbColumn());
|
||||
}
|
||||
sb.append(", ").append(fetchOrderBy);
|
||||
lazyFetchOrderBy = sb.toString().trim();
|
||||
lazyFetchOrderBy = sqlHelp.lazyFetchOrderBy(fetchOrderBy);
|
||||
}
|
||||
}
|
||||
|
||||
String delStmt;
|
||||
if (hasJoinTable()) {
|
||||
delStmt = "delete from " + inverseJoin.getTable() + " where ";
|
||||
} else {
|
||||
delStmt = "delete from " + targetDescriptor.getBaseTable() + " where ";
|
||||
}
|
||||
deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false, "");
|
||||
deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true, "");
|
||||
}
|
||||
}
|
||||
|
||||
String targetTable() {
|
||||
return beanTable.getBaseTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise after the target bean descriptors have been all set.
|
||||
*/
|
||||
@@ -295,44 +301,23 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
public SqlUpdate deleteByParentId(Object parentId, List<Object> parentIdist) {
|
||||
if (parentId != null) {
|
||||
return deleteByParentId(parentId);
|
||||
return sqlHelp.deleteByParentId(parentId);
|
||||
} else {
|
||||
return deleteByParentIdList(parentIdist);
|
||||
return sqlHelp.deleteByParentIdList(parentIdist);
|
||||
}
|
||||
}
|
||||
|
||||
private SqlUpdate deleteByParentId(Object parentId) {
|
||||
DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByParentIdSql);
|
||||
bindParentId(sqlDelete, parentId);
|
||||
return sqlDelete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the Id's of detail beans given a parent Id or list of parent Id's.
|
||||
*/
|
||||
public List<Object> findIdsByParentId(Object parentId, List<Object> parentIdList, Transaction t, List<Object> excludeDetailIds) {
|
||||
if (parentId != null) {
|
||||
return findIdsByParentId(parentId, t, excludeDetailIds);
|
||||
return sqlHelp.findIdsByParentId(parentId, t, excludeDetailIds);
|
||||
} else {
|
||||
return findIdsByParentIdList(parentIdList, t, excludeDetailIds);
|
||||
return sqlHelp.findIdsByParentIdList(parentIdList, t, excludeDetailIds);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Object> findIdsByParentId(Object parentId, Transaction t, List<Object> excludeDetailIds) {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(false, "");
|
||||
|
||||
EbeanServer server = server();
|
||||
Query<?> q = server.find(getPropertyType());
|
||||
bindParentIdEq(rawWhere, parentId, q);
|
||||
|
||||
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
|
||||
q.where().not(q.getExpressionFactory().idIn(excludeDetailIds));
|
||||
}
|
||||
|
||||
return server.findIds(q, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude many properties from bean cache data.
|
||||
*/
|
||||
@@ -359,88 +344,10 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
// assumes the ManyToOne property is included
|
||||
query.where().in(childMasterIdProperty, parentIds);
|
||||
} else {
|
||||
addWhereParentIdIn(query, parentIds);
|
||||
sqlHelp.addWhereParentIdIn(query, parentIds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a where clause to the query for a given list of parent Id's.
|
||||
*/
|
||||
private void addWhereParentIdIn(SpiQuery<?> query, List<Object> parentIds) {
|
||||
|
||||
String tableAlias = hasJoinTable() ? "int_." : "t0.";
|
||||
if (hasJoinTable()) {
|
||||
query.setM2MIncludeJoin(inverseJoin);
|
||||
}
|
||||
String rawWhere = deriveWhereParentIdSql(true, tableAlias);
|
||||
String expr = descriptor.getParentIdInExpr(parentIds.size(), rawWhere);
|
||||
|
||||
bindParentIdsIn(expr, parentIds, query);
|
||||
}
|
||||
|
||||
private List<Object> findIdsByParentIdList(List<Object> parentIds, Transaction t, List<Object> excludeDetailIds) {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(true, "");
|
||||
String inClause = buildInClauseBinding(parentIds.size(), exportedPropertyBindProto);
|
||||
|
||||
String expr = rawWhere + inClause;
|
||||
|
||||
EbeanServer server = descriptor.getEbeanServer();
|
||||
Query<?> q = server.find(propertyType);
|
||||
bindParentIdsIn(expr, parentIds, q);
|
||||
|
||||
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
|
||||
q.where().not(q.getExpressionFactory().idIn(excludeDetailIds));
|
||||
}
|
||||
|
||||
return server.findIds(q, t);
|
||||
}
|
||||
|
||||
private SqlUpdate deleteByParentIdList(List<Object> parentIds) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(100);
|
||||
sb.append(deleteByParentIdInSql);
|
||||
sb.append(buildInClauseBinding(parentIds.size(), exportedPropertyBindProto));
|
||||
|
||||
DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString());
|
||||
bindParentIds(delete, parentIds);
|
||||
return delete;
|
||||
}
|
||||
|
||||
private String deriveExportedPropertyBindProto() {
|
||||
if (exportedProperties.length == 1) {
|
||||
return "?";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("(");
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("?");
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String buildInClauseBinding(int size, String bindProto) {
|
||||
|
||||
if (descriptor.isSimpleId()) {
|
||||
return descriptor.getIdBinder().getIdInValueExpr(false, size);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(10 + (size * (bindProto.length() + 1)));
|
||||
sb.append(" in");
|
||||
sb.append(" (");
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(bindProto);
|
||||
}
|
||||
sb.append(") ");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the lazy load server to help create reference collections (that lazy
|
||||
* load on demand).
|
||||
@@ -476,7 +383,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException {
|
||||
public Object readSet(DbReadContext ctx, EntityBean bean) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -616,6 +523,10 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
return manyToMany;
|
||||
}
|
||||
|
||||
public boolean isElementCollection() {
|
||||
return elementCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* ManyToMany only, join from local table to intersection table.
|
||||
*/
|
||||
@@ -705,30 +616,6 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
}
|
||||
|
||||
private String deriveWhereParentIdSql(boolean inClause, String tableAlias) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (inClause) {
|
||||
sb.append("(");
|
||||
}
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
String fkColumn = exportedProperties[i].getForeignDbColumn();
|
||||
if (i > 0) {
|
||||
String s = inClause ? "," : " and ";
|
||||
sb.append(s);
|
||||
}
|
||||
sb.append(tableAlias).append(fkColumn);
|
||||
if (!inClause) {
|
||||
sb.append("=? ");
|
||||
}
|
||||
}
|
||||
if (inClause) {
|
||||
sb.append(")");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the array of ExportedProperty used to build reference objects.
|
||||
*/
|
||||
@@ -741,12 +628,9 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
if (idProp != null && idProp.isEmbedded()) {
|
||||
|
||||
BeanPropertyAssocOne<?> one = (BeanPropertyAssocOne<?>) idProp;
|
||||
BeanDescriptor<?> targetDesc = one.getTargetDescriptor();
|
||||
BeanProperty[] emIds = targetDesc.propertiesBaseScalar();
|
||||
try {
|
||||
for (BeanProperty emId : emIds) {
|
||||
ExportedProperty expProp = findMatch(true, emId);
|
||||
list.add(expProp);
|
||||
for (BeanProperty emId : one.getTargetDescriptor().propertiesBaseScalar()) {
|
||||
list.add(findMatch(true, emId));
|
||||
}
|
||||
} catch (PersistenceException e) {
|
||||
// not found as individual scalar properties
|
||||
@@ -755,8 +639,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
} else {
|
||||
if (idProp != null) {
|
||||
ExportedProperty expProp = findMatch(false, idProp);
|
||||
list.add(expProp);
|
||||
list.add(findMatch(false, idProp));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -768,32 +651,12 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
*/
|
||||
private ExportedProperty findMatch(boolean embedded, BeanProperty prop) {
|
||||
|
||||
String matchColumn = prop.getDbColumn();
|
||||
|
||||
String searchTable;
|
||||
TableJoinColumn[] columns;
|
||||
if (hasJoinTable()) {
|
||||
// look for column going to intersection
|
||||
columns = intersectionJoin.columns();
|
||||
searchTable = intersectionJoin.getTable();
|
||||
|
||||
return findMatch(embedded, prop, prop.getDbColumn(), intersectionJoin);
|
||||
} else {
|
||||
columns = tableJoin.columns();
|
||||
searchTable = tableJoin.getTable();
|
||||
return findMatch(embedded, prop, prop.getDbColumn(), tableJoin);
|
||||
}
|
||||
for (TableJoinColumn column : columns) {
|
||||
String matchTo = column.getLocalDbColumn();
|
||||
|
||||
if (matchColumn.equalsIgnoreCase(matchTo)) {
|
||||
String foreignCol = column.getForeignDbColumn();
|
||||
return new ExportedProperty(embedded, foreignCol, prop);
|
||||
}
|
||||
}
|
||||
|
||||
String msg = "Error with the Join on [" + getFullBeanName()
|
||||
+ "]. Could not find the matching foreign key for [" + matchColumn + "] in table[" + searchTable + "]?"
|
||||
+ " Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped?";
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -813,8 +676,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
Class<?> beanType = descriptor.getBeanType();
|
||||
BeanDescriptor<?> targetDesc = getTargetDescriptor();
|
||||
|
||||
BeanPropertyAssocOne<?>[] ones = targetDesc.propertiesOne();
|
||||
for (BeanPropertyAssocOne<?> prop : ones) {
|
||||
for (BeanPropertyAssocOne<?> prop : targetDesc.propertiesOne()) {
|
||||
if (mappedBy != null) {
|
||||
// match using mappedBy as property name
|
||||
if (mappedBy.equalsIgnoreCase(prop.getName())) {
|
||||
@@ -880,9 +742,9 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
/**
|
||||
* Register the mapping of intersection table to associated draft table.
|
||||
*/
|
||||
public void registerDraftIntersectionTable(Map<String, String> draftTableMap) {
|
||||
public void registerDraftIntersectionTable(BeanDescriptorInitContext initContext) {
|
||||
if (hasDraftIntersection()) {
|
||||
draftTableMap.put(intersectionPublishTable, intersectionDraftTable);
|
||||
initContext.addDraftIntersection(intersectionPublishTable, intersectionDraftTable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -928,7 +790,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
* Skip JSON write value for ToMany property.
|
||||
*/
|
||||
@Override
|
||||
public void jsonWriteValue(SpiJsonWriter writeJson, Object value) throws IOException {
|
||||
public void jsonWriteValue(SpiJsonWriter writeJson, Object value) {
|
||||
// do nothing, exclude ToMany properties
|
||||
}
|
||||
|
||||
@@ -946,7 +808,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
if (value != null) {
|
||||
ctx.pushParentBeanMany(bean);
|
||||
if (help != null) {
|
||||
help.jsonWrite(ctx, name, value, include != null);
|
||||
jsonWriteCollection(ctx, name, value, include != null);
|
||||
} else {
|
||||
if (isTransient && targetDescriptor == null) {
|
||||
ctx.writeValueUsingObjectMapper(name, value);
|
||||
@@ -962,7 +824,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonRead(ReadJson readJson, EntityBean parentBean) throws IOException {
|
||||
public void jsonRead(SpiJsonReader readJson, EntityBean parentBean) throws IOException {
|
||||
jsonHelp.jsonRead(readJson, parentBean);
|
||||
}
|
||||
|
||||
@@ -1030,4 +892,118 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
public boolean isIncludeCascadeDelete() {
|
||||
return cascadeInfo.isDelete() || o2mJoinTable || ModifyListenMode.REMOVALS == modifyListenMode;
|
||||
}
|
||||
|
||||
public String insertElementCollection() {
|
||||
return sqlHelp.insertElementCollection();
|
||||
}
|
||||
|
||||
public boolean isTargetDocStoreMapped() {
|
||||
return targetDescriptor.isDocStoreMapped();
|
||||
}
|
||||
|
||||
public BeanCollectionHelp<T> getHelp() {
|
||||
return help;
|
||||
}
|
||||
|
||||
public void jsonWriteMapEntry(SpiJsonWriter ctx, Map.Entry<?, ?> entry) throws IOException {
|
||||
elementDescriptor.jsonWriteMapEntry(ctx, entry);
|
||||
}
|
||||
|
||||
public void jsonWriteElementValue(SpiJsonWriter ctx, Object element) {
|
||||
elementDescriptor.jsonWriteElement(ctx, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* A Many (element collection) property in bean cache to held as JSON.
|
||||
*/
|
||||
@Override
|
||||
public void setCacheDataValue(EntityBean bean, Object cacheData, PersistenceContext context) {
|
||||
try {
|
||||
String asJson = (String) cacheData;
|
||||
Object collection = jsonReadCollection(asJson);
|
||||
setValue(bean, collection);
|
||||
} catch (Exception e) {
|
||||
logger.error("Error setting value from L2 cache", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCacheDataValue(EntityBean bean) {
|
||||
try {
|
||||
Object collection = getValue(bean);
|
||||
if (collection == null) {
|
||||
return null;
|
||||
}
|
||||
return jsonWriteCollection(collection);
|
||||
} catch (Exception e) {
|
||||
logger.error("Error building value element collection json for L2 cache", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the collection to JSON.
|
||||
*/
|
||||
public String jsonWriteCollection(Object value) throws IOException {
|
||||
StringWriter writer = new StringWriter(300);
|
||||
SpiJsonWriter ctx = descriptor.createJsonWriter(writer);
|
||||
help.jsonWrite(ctx, null, value, false);
|
||||
ctx.flush();
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the collection as JSON.
|
||||
*/
|
||||
public Object jsonReadCollection(String json) throws IOException {
|
||||
SpiJsonReader ctx = descriptor.createJsonReader(json);
|
||||
JsonParser parser = ctx.getParser();
|
||||
JsonToken event = parser.nextToken();
|
||||
if (JsonToken.VALUE_NULL == event) {
|
||||
return null;
|
||||
}
|
||||
return jsonReadCollection(ctx, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the collection to JSON.
|
||||
*/
|
||||
public void jsonWriteCollection(SpiJsonWriter ctx, String name, Object value, boolean explicitInclude) throws IOException {
|
||||
help.jsonWrite(ctx, name, value, explicitInclude);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the collection (JSON Array) containing entity beans.
|
||||
*/
|
||||
public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
if (elementDescriptor != null && manyType.isMap()) {
|
||||
return elementDescriptor.jsonReadCollection(readJson, parentBean);
|
||||
}
|
||||
|
||||
BeanCollection<?> collection = createEmpty(parentBean);
|
||||
BeanCollectionAdd add = getBeanCollectionAdd(collection, null);
|
||||
do {
|
||||
EntityBean detailBean = (EntityBean) targetDescriptor.jsonRead(readJson, name);
|
||||
if (detailBean == null) {
|
||||
// read the entire array
|
||||
break;
|
||||
}
|
||||
add.addEntityBean(detailBean);
|
||||
|
||||
if (parentBean != null && childMasterProperty != null) {
|
||||
// bind detail bean back to master via mappedBy property
|
||||
childMasterProperty.setValue(detailBean, parentBean);
|
||||
}
|
||||
} while (true);
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind all the property values to the SqlUpdate.
|
||||
*/
|
||||
public void bindElementValue(SqlUpdate insert, Object value) {
|
||||
targetDescriptor.bindElementValue(insert, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,17 +3,15 @@ package io.ebeaninternal.server.deploy;
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Help BeanPropertyAssocMany with JSON processing.
|
||||
*/
|
||||
public class BeanPropertyAssocManyJsonHelp {
|
||||
class BeanPropertyAssocManyJsonHelp {
|
||||
|
||||
/**
|
||||
* The associated many property.
|
||||
@@ -28,7 +26,7 @@ public class BeanPropertyAssocManyJsonHelp {
|
||||
/**
|
||||
* Construct for the owning many property.
|
||||
*/
|
||||
public BeanPropertyAssocManyJsonHelp(BeanPropertyAssocMany<?> many) {
|
||||
BeanPropertyAssocManyJsonHelp(BeanPropertyAssocMany<?> many) {
|
||||
this.many = many;
|
||||
boolean objectMapperPresent = many.getBeanDescriptor().getServerConfig().getClassLoadConfig().isJacksonObjectMapperPresent();
|
||||
this.jsonTransient = !objectMapperPresent ? null : new BeanPropertyAssocManyJsonTransient();
|
||||
@@ -37,7 +35,7 @@ public class BeanPropertyAssocManyJsonHelp {
|
||||
/**
|
||||
* Read the JSON for this property.
|
||||
*/
|
||||
public void jsonRead(ReadJson readJson, EntityBean parentBean) throws IOException {
|
||||
public void jsonRead(SpiJsonReader readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
if (!this.many.jsonDeserialize) {
|
||||
return;
|
||||
@@ -57,29 +55,13 @@ public class BeanPropertyAssocManyJsonHelp {
|
||||
return;
|
||||
}
|
||||
|
||||
BeanCollection<?> collection = many.createEmpty(parentBean);
|
||||
BeanCollectionAdd add = many.getBeanCollectionAdd(collection, null);
|
||||
do {
|
||||
EntityBean detailBean = (EntityBean) many.targetDescriptor.jsonRead(readJson, many.name);
|
||||
if (detailBean == null) {
|
||||
// read the entire array
|
||||
break;
|
||||
}
|
||||
add.addEntityBean(detailBean);
|
||||
|
||||
if (parentBean != null && many.childMasterProperty != null) {
|
||||
// bind detail bean back to master via mappedBy property
|
||||
many.childMasterProperty.setValue(detailBean, parentBean);
|
||||
}
|
||||
} while (true);
|
||||
|
||||
many.setValue(parentBean, collection);
|
||||
many.setValue(parentBean, many.jsonReadCollection(readJson, parentBean));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a Transient property using Jackson ObjectMapper.
|
||||
*/
|
||||
private void jsonReadTransientUsingObjectMapper(ReadJson readJson, EntityBean parentBean) throws IOException {
|
||||
private void jsonReadTransientUsingObjectMapper(SpiJsonReader readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
if (jsonTransient == null) {
|
||||
throw new IllegalStateException("Jackson ObjectMapper is required to read this Transient property " + many.getFullBeanName());
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@ import com.fasterxml.jackson.databind.type.CollectionType;
|
||||
import com.fasterxml.jackson.databind.type.MapType;
|
||||
import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -14,12 +14,12 @@ import java.util.LinkedHashMap;
|
||||
/**
|
||||
* Helper used to read transient many properties using Jackson ObjectMapper.
|
||||
*/
|
||||
public class BeanPropertyAssocManyJsonTransient {
|
||||
class BeanPropertyAssocManyJsonTransient {
|
||||
|
||||
/**
|
||||
* Use Jackson ObjectMapper to read the transient 'many' property.
|
||||
*/
|
||||
public void jsonReadUsingObjectMapper(BeanPropertyAssocMany<?> many, ReadJson readJson, EntityBean parentBean) throws IOException {
|
||||
void jsonReadUsingObjectMapper(BeanPropertyAssocMany<?> many, SpiJsonReader readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
ObjectMapper mapper = readJson.getObjectMapper();
|
||||
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.dbmigration.model.visitor.BaseTablePropertyVisitor;
|
||||
import io.ebeaninternal.dbmigration.model.visitor.VisitAllUsing;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class BeanPropertyAssocManySqlHelp<T> {
|
||||
|
||||
private final BeanPropertyAssocMany<T> many;
|
||||
private final ExportedProperty[] exportedProperties;
|
||||
private final boolean hasJoinTable;
|
||||
private final BeanDescriptor<?> descriptor;
|
||||
private final String exportedPropertyBindProto;
|
||||
private final String deleteByParentIdSql;
|
||||
private final String deleteByParentIdInSql;
|
||||
private final String elementCollectionInsertSql;
|
||||
|
||||
BeanPropertyAssocManySqlHelp(BeanPropertyAssocMany<T> many, ExportedProperty[] exportedProperties) {
|
||||
this.many = many;
|
||||
this.exportedProperties = exportedProperties;
|
||||
this.hasJoinTable = many.hasJoinTable();
|
||||
this.descriptor = many.getBeanDescriptor();
|
||||
this.exportedPropertyBindProto = deriveExportedPropertyBindProto();
|
||||
|
||||
String delStmt;
|
||||
if (hasJoinTable) {
|
||||
delStmt = "delete from " + many.inverseJoin.getTable() + " where ";
|
||||
} else {
|
||||
delStmt = "delete from " + many.targetTable() + " where ";
|
||||
}
|
||||
deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false, "");
|
||||
deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true, "");
|
||||
if (many.isElementCollection()) {
|
||||
elementCollectionInsertSql = elementCollectionInsert();
|
||||
} else {
|
||||
elementCollectionInsertSql = null;
|
||||
}
|
||||
}
|
||||
|
||||
private String elementCollectionInsert() {
|
||||
|
||||
StringBuilder sb = new StringBuilder(200);
|
||||
sb.append("insert into ").append(many.targetTable()).append(" (");
|
||||
append(sb, "", ",", "");
|
||||
|
||||
Cols cols = new Cols(sb);
|
||||
VisitAllUsing.visitOne(many.targetDescriptor, cols);
|
||||
sb.append(") values (");
|
||||
appendBind(sb, exportedProperties.length, true);
|
||||
appendBind(sb, cols.colCount, false);
|
||||
sb.append(")");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String insertElementCollection() {
|
||||
return elementCollectionInsertSql;
|
||||
}
|
||||
|
||||
private static class Cols extends BaseTablePropertyVisitor {
|
||||
|
||||
int colCount;
|
||||
|
||||
private final StringBuilder sb;
|
||||
|
||||
private Cols(StringBuilder sb) {
|
||||
this.sb = sb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
|
||||
sb.append(",").append(p.getDbColumn());
|
||||
colCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOneImported(BeanPropertyAssocOne<?> p) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitScalar(BeanProperty p) {
|
||||
sb.append(",").append(p.getDbColumn());
|
||||
colCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
String lazyFetchOrderBy(String fetchOrderBy) {
|
||||
|
||||
// derive lazyFetchOrderBy
|
||||
StringBuilder sb = new StringBuilder(50);
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
// these fk columns are either on the intersection (int_) or base table (t0)
|
||||
String fkTableAlias = hasJoinTable ? "int_" : "t0";
|
||||
sb.append(fkTableAlias).append(".").append(exportedProperties[i].getForeignDbColumn());
|
||||
}
|
||||
sb.append(", ").append(fetchOrderBy);
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a where clause to the query for a given list of parent Id's.
|
||||
*/
|
||||
void addWhereParentIdIn(SpiQuery<?> query, List<Object> parentIds) {
|
||||
|
||||
String tableAlias = hasJoinTable ? "int_." : "t0.";
|
||||
if (hasJoinTable) {
|
||||
query.setM2MIncludeJoin(many.inverseJoin);
|
||||
}
|
||||
String rawWhere = deriveWhereParentIdSql(true, tableAlias);
|
||||
String expr = descriptor.getParentIdInExpr(parentIds.size(), rawWhere);
|
||||
|
||||
many.bindParentIdsIn(expr, parentIds, query);
|
||||
}
|
||||
|
||||
List<Object> findIdsByParentId(Object parentId, Transaction t, List<Object> excludeDetailIds) {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(false, "");
|
||||
|
||||
SpiEbeanServer server = descriptor.getEbeanServer();
|
||||
SpiQuery<?> q = many.newQuery(server);
|
||||
many.bindParentIdEq(rawWhere, parentId, q);
|
||||
|
||||
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
|
||||
q.where().not(q.getExpressionFactory().idIn(excludeDetailIds));
|
||||
}
|
||||
|
||||
return server.findIds(q, t);
|
||||
}
|
||||
|
||||
List<Object> findIdsByParentIdList(List<Object> parentIds, Transaction t, List<Object> excludeDetailIds) {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(true, "");
|
||||
String inClause = buildInClauseBinding(parentIds.size(), exportedPropertyBindProto);
|
||||
|
||||
String expr = rawWhere + inClause;
|
||||
|
||||
SpiEbeanServer server = descriptor.getEbeanServer();
|
||||
SpiQuery<?> q = many.newQuery(server);
|
||||
//Query<?> q = server.find(propertyType);
|
||||
many.bindParentIdsIn(expr, parentIds, q);
|
||||
|
||||
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
|
||||
q.where().not(q.getExpressionFactory().idIn(excludeDetailIds));
|
||||
}
|
||||
|
||||
return server.findIds(q, t);
|
||||
}
|
||||
|
||||
SqlUpdate deleteByParentId(Object parentId) {
|
||||
DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByParentIdSql);
|
||||
many.bindParentId(sqlDelete, parentId);
|
||||
return sqlDelete;
|
||||
}
|
||||
|
||||
SqlUpdate deleteByParentIdList(List<Object> parentIds) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(100);
|
||||
sb.append(deleteByParentIdInSql);
|
||||
sb.append(buildInClauseBinding(parentIds.size(), exportedPropertyBindProto));
|
||||
|
||||
DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString());
|
||||
many.bindParentIds(delete, parentIds);
|
||||
return delete;
|
||||
}
|
||||
|
||||
private void appendBind(StringBuilder sb, int count, boolean skipComma) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!skipComma || i > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("?");
|
||||
}
|
||||
}
|
||||
|
||||
private void append(StringBuilder sb, String tableAlias, String prefix, String suffix) {
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
String fkColumn = exportedProperties[i].getForeignDbColumn();
|
||||
if (i > 0) {
|
||||
sb.append(prefix);
|
||||
}
|
||||
sb.append(tableAlias).append(fkColumn);
|
||||
sb.append(suffix);
|
||||
}
|
||||
}
|
||||
private String deriveWhereParentIdSql(boolean inClause, String tableAlias) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (inClause) {
|
||||
sb.append("(");
|
||||
}
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
String fkColumn = exportedProperties[i].getForeignDbColumn();
|
||||
if (i > 0) {
|
||||
String s = inClause ? "," : " and ";
|
||||
sb.append(s);
|
||||
}
|
||||
sb.append(tableAlias).append(fkColumn);
|
||||
if (!inClause) {
|
||||
sb.append("=? ");
|
||||
}
|
||||
}
|
||||
if (inClause) {
|
||||
sb.append(")");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String buildInClauseBinding(int size, String bindProto) {
|
||||
|
||||
if (descriptor.isSimpleId()) {
|
||||
return descriptor.getIdBinder().getIdInValueExpr(false, size);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(10 + (size * (bindProto.length() + 1)));
|
||||
sb.append(" in");
|
||||
sb.append(" (");
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(bindProto);
|
||||
}
|
||||
sb.append(") ");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String deriveExportedPropertyBindProto() {
|
||||
if (exportedProperties.length == 1) {
|
||||
return "?";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("(");
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("?");
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.cache.CacheChangeSet;
|
||||
import io.ebeaninternal.server.cache.CachedBeanData;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
@@ -16,10 +18,10 @@ import io.ebeaninternal.server.deploy.id.ImportedId;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.el.ElPropertyChainBuilder;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.query.STreePropertyAssocOne;
|
||||
import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
@@ -32,7 +34,7 @@ import java.util.Map;
|
||||
/**
|
||||
* Property mapped to a joined bean.
|
||||
*/
|
||||
public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STreePropertyAssocOne {
|
||||
|
||||
private final boolean oneToOne;
|
||||
|
||||
@@ -40,8 +42,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
private final boolean orphanRemoval;
|
||||
|
||||
private final boolean importedPrimaryKey;
|
||||
|
||||
private final boolean primaryKeyExport;
|
||||
|
||||
private final PropertyForeignKey foreignKey;
|
||||
@@ -76,7 +76,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
foreignKey = deploy.getForeignKey();
|
||||
primaryKeyExport = deploy.isPrimaryKeyExport();
|
||||
importedPrimaryKey = deploy.isImportedPrimaryKey();
|
||||
oneToOne = deploy.isOneToOne();
|
||||
oneToOneExported = deploy.isOneToOneExported();
|
||||
orphanRemoval = deploy.isOrphanRemoval();
|
||||
@@ -97,8 +96,12 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise() {
|
||||
super.initialise();
|
||||
public void initialise(BeanDescriptorInitContext initContext) {
|
||||
super.initialise(initContext);
|
||||
initialiseAssocOne();
|
||||
}
|
||||
|
||||
private void initialiseAssocOne() {
|
||||
localHelp = createHelp(embedded, oneToOneExported);
|
||||
|
||||
if (!isTransient) {
|
||||
@@ -307,6 +310,10 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
String discProperty = prefix + "." + discriminatorColumn;
|
||||
selectChain.add(discProperty);
|
||||
}
|
||||
if (targetIdBinder == null) {
|
||||
throw new IllegalStateException("No Id binding property for " + getFullBeanName()
|
||||
+ ". Probably a missing @OneToOne mapping annotation on this relationship?");
|
||||
}
|
||||
targetIdBinder.buildRawSqlSelectChain(prefix, selectChain);
|
||||
|
||||
} else {
|
||||
@@ -346,13 +353,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
return orphanRemoval;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true this bean maps to the primary key.
|
||||
*/
|
||||
public boolean isImportedPrimaryKey() {
|
||||
return importedPrimaryKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void diff(String prefix, Map<String, ValuePair> map, EntityBean newBean, EntityBean oldBean) {
|
||||
|
||||
@@ -427,6 +427,11 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> getIdScalarType() {
|
||||
return targetDescriptor.getIdProperty().getScalarType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Id values from the given bean.
|
||||
*/
|
||||
@@ -549,24 +554,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
*/
|
||||
private ExportedProperty findMatch(boolean embeddedProp, BeanProperty prop) {
|
||||
|
||||
String matchColumn = prop.getDbColumn();
|
||||
|
||||
String searchTable = tableJoin.getTable();
|
||||
TableJoinColumn[] columns = tableJoin.columns();
|
||||
|
||||
for (TableJoinColumn column : columns) {
|
||||
String matchTo = column.getLocalDbColumn();
|
||||
|
||||
if (matchColumn.equalsIgnoreCase(matchTo)) {
|
||||
String foreignCol = column.getForeignDbColumn();
|
||||
return new ExportedProperty(embeddedProp, foreignCol, prop);
|
||||
}
|
||||
}
|
||||
|
||||
String msg = "Error with the Join on [" + getFullBeanName()
|
||||
+ "]. Could not find the matching foreign key for [" + matchColumn + "] in table[" + searchTable + "]?"
|
||||
+ " Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped?";
|
||||
throw new PersistenceException(msg);
|
||||
return findMatch(embeddedProp, prop, prop.getDbColumn(), tableJoin);
|
||||
}
|
||||
|
||||
|
||||
@@ -626,6 +614,14 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueIntercept(EntityBean bean, Object value) {
|
||||
super.setValueIntercept(bean, value);
|
||||
if (embedded && value instanceof EntityBean) {
|
||||
setEmbeddedOwner(bean, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For embedded bean set the owner and all properties to be loaded (recursively).
|
||||
*/
|
||||
@@ -638,18 +634,10 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
}
|
||||
|
||||
private void setEmbeddedOwner(EntityBean bean, Object value) {
|
||||
void setEmbeddedOwner(EntityBean bean, Object value) {
|
||||
((EntityBean) value)._ebean_getIntercept().setEmbeddedOwner(bean, propertyIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueIntercept(EntityBean bean, Object value) {
|
||||
super.setValueIntercept(bean, value);
|
||||
if (embedded && value instanceof EntityBean) {
|
||||
setEmbeddedOwner(bean, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadIgnore(DbReadContext ctx) {
|
||||
localHelp.loadIgnore(ctx);
|
||||
@@ -708,10 +696,10 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
if (embedded) {
|
||||
writeJson.writeFieldName(name);
|
||||
BeanDescriptor<?> refDesc = descriptor.getBeanDescriptor(value.getClass());
|
||||
refDesc.jsonWriteForInsert(writeJson, (EntityBean)value);
|
||||
refDesc.jsonWriteForInsert(writeJson, (EntityBean) value);
|
||||
|
||||
} else {
|
||||
jsonWriteTargetId(writeJson, (EntityBean)value);
|
||||
jsonWriteTargetId(writeJson, (EntityBean) value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -756,7 +744,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonRead(ReadJson readJson, EntityBean bean) throws IOException {
|
||||
public void jsonRead(SpiJsonReader readJson, EntityBean bean) throws IOException {
|
||||
if (jsonDeserialize && targetDescriptor != null) {
|
||||
T assocBean = targetDescriptor.jsonRead(readJson, name);
|
||||
setValue(bean, assocBean);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
|
||||
/**
|
||||
* Bean property for an IdClass embeddedId.
|
||||
*/
|
||||
public class BeanPropertyIdClass extends BeanPropertyAssocOne {
|
||||
|
||||
public BeanPropertyIdClass(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanPropertyAssocOne deploy) {
|
||||
super(owner, descriptor, deploy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(EntityBean bean, Object value) {
|
||||
bean._ebean_getIntercept().setOwnerId(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueIntercept(EntityBean bean, Object value) {
|
||||
bean._ebean_getIntercept().setOwnerId(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(EntityBean bean) {
|
||||
return bean._ebean_getIntercept().getOwnerId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueIntercept(EntityBean bean) {
|
||||
return bean._ebean_getIntercept().getOwnerId();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,25 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertySimpleCollection;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class BeanPropertySimpleCollection<T> extends BeanPropertyAssocMany<T> {
|
||||
|
||||
public BeanPropertySimpleCollection(BeanDescriptor<?> descriptor, DeployBeanPropertySimpleCollection<T> deploy) {
|
||||
super(descriptor, deploy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindElementValue(SqlUpdate insert, Object value) {
|
||||
insert.setNextParameter(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException {
|
||||
return elementDescriptor.jsonReadCollection(readJson, parentBean);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,9 @@ import io.ebean.Query;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.common.BeanSet;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashSet;
|
||||
@@ -17,34 +16,20 @@ import java.util.Set;
|
||||
/**
|
||||
* Helper specifically for dealing with Sets.
|
||||
*/
|
||||
public final class BeanSetHelp<T> extends BaseCollectionHelp<T> {
|
||||
|
||||
private final BeanPropertyAssocMany<T> many;
|
||||
private final BeanDescriptor<T> targetDescriptor;
|
||||
private final String propertyName;
|
||||
private BeanCollectionLoader loader;
|
||||
public class BeanSetHelp<T> extends BaseCollectionHelp<T> {
|
||||
|
||||
/**
|
||||
* When attached to a specific many property.
|
||||
*/
|
||||
public BeanSetHelp(BeanPropertyAssocMany<T> many) {
|
||||
this.many = many;
|
||||
this.targetDescriptor = many.getTargetDescriptor();
|
||||
this.propertyName = many.getName();
|
||||
BeanSetHelp(BeanPropertyAssocMany<T> many) {
|
||||
super(many);
|
||||
}
|
||||
|
||||
/**
|
||||
* For a query that returns a set.
|
||||
*/
|
||||
public BeanSetHelp() {
|
||||
this.many = null;
|
||||
this.targetDescriptor = null;
|
||||
this.propertyName = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoader(BeanCollectionLoader loader) {
|
||||
this.loader = loader;
|
||||
BeanSetHelp() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,15 +46,6 @@ public final class BeanSetHelp<T> extends BaseCollectionHelp<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck) {
|
||||
if (withCheck) {
|
||||
collection.internalAddWithCheck(bean);
|
||||
} else {
|
||||
collection.internalAdd(bean);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanCollection<T> createEmptyNoParent() {
|
||||
return new BeanSet<>();
|
||||
@@ -142,13 +118,6 @@ public final class BeanSetHelp<T> extends BaseCollectionHelp<T> {
|
||||
} else {
|
||||
set = (Set<?>) collection;
|
||||
}
|
||||
|
||||
if (!set.isEmpty() || ctx.isIncludeEmpty()) {
|
||||
ctx.beginAssocMany(name);
|
||||
for (Object bean : set) {
|
||||
targetDescriptor.jsonWrite(ctx, (EntityBean) bean);
|
||||
}
|
||||
ctx.endAssocMany();
|
||||
}
|
||||
jsonWriteCollection(ctx, name, set);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
|
||||
/**
|
||||
* Helper for element collection List.
|
||||
*/
|
||||
public class BeanSetHelpElement<T> extends BeanSetHelp<T> {
|
||||
|
||||
BeanSetHelpElement(BeanPropertyAssocMany<T> many) {
|
||||
super(many);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck) {
|
||||
Object elementValue = bean._ebean_getField(0);
|
||||
if (withCheck) {
|
||||
collection.internalAddWithCheck(elementValue);
|
||||
} else {
|
||||
collection.internalAdd(elementValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void jsonWriteElement(SpiJsonWriter ctx, Object element) {
|
||||
many.jsonWriteElementValue(ctx, element);
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public class BeanTable {
|
||||
*/
|
||||
private final String baseTable;
|
||||
|
||||
private final BeanProperty[] idProperties;
|
||||
private final BeanProperty idProperty;
|
||||
|
||||
/**
|
||||
* Create the BeanTable.
|
||||
@@ -39,7 +39,17 @@ public class BeanTable {
|
||||
this.owner = owner;
|
||||
this.beanType = mutable.getBeanType();
|
||||
this.baseTable = InternString.intern(mutable.getBaseTable());
|
||||
this.idProperties = mutable.createIdProperties(owner);
|
||||
this.idProperty = mutable.createIdProperty(owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for element collection.
|
||||
*/
|
||||
public BeanTable(BeanDescriptorMap owner, String tableName, Class<?> beanType) {
|
||||
this.owner = owner;
|
||||
this.beanType = beanType;
|
||||
this.baseTable = tableName;
|
||||
this.idProperty = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,8 +79,8 @@ public class BeanTable {
|
||||
/**
|
||||
* Return the Id properties.
|
||||
*/
|
||||
public BeanProperty[] getIdProperties() {
|
||||
return idProperties;
|
||||
public BeanProperty getIdProperty() {
|
||||
return idProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,52 +92,51 @@ public class BeanTable {
|
||||
|
||||
public void createJoinColumn(String foreignKeyPrefix, DeployTableJoin join, boolean reverse, String sqlFormulaSelect) {
|
||||
|
||||
boolean complexKey = false;
|
||||
BeanProperty[] props = idProperties;
|
||||
|
||||
if (idProperties.length == 1) {
|
||||
if (idProperties[0] instanceof BeanPropertyAssocOne<?>) {
|
||||
BeanPropertyAssocOne<?> assocOne = (BeanPropertyAssocOne<?>) idProperties[0];
|
||||
props = assocOne.getProperties();
|
||||
complexKey = true;
|
||||
}
|
||||
if (idProperty == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (BeanProperty prop : props) {
|
||||
if (idProperty instanceof BeanPropertyAssocOne<?>) {
|
||||
BeanPropertyAssocOne<?> assocOne = (BeanPropertyAssocOne<?>) idProperty;
|
||||
BeanProperty[] props = assocOne.getProperties();
|
||||
for (BeanProperty prop : props) {
|
||||
addToJoin(foreignKeyPrefix, join, reverse, sqlFormulaSelect, true, prop);
|
||||
}
|
||||
} else {
|
||||
addToJoin(foreignKeyPrefix, join, reverse, sqlFormulaSelect, false, idProperty);
|
||||
}
|
||||
}
|
||||
|
||||
String lc = prop.getDbColumn();
|
||||
String fk = lc;
|
||||
if (foreignKeyPrefix != null) {
|
||||
fk = owner.getNamingConvention().getForeignKey(foreignKeyPrefix, fk);
|
||||
}
|
||||
private void addToJoin(String foreignKeyPrefix, DeployTableJoin join, boolean reverse, String sqlFormulaSelect, boolean complexKey, BeanProperty prop) {
|
||||
|
||||
if (complexKey) {
|
||||
// just to copy the column name rather than prefix with the foreignKeyPrefix.
|
||||
// I think that with complex keys this is the more common approach.
|
||||
logger.debug("On table[{}] foreign key column [{}]", baseTable, lc);
|
||||
fk = lc;
|
||||
}
|
||||
if (sqlFormulaSelect != null) {
|
||||
fk = sqlFormulaSelect;
|
||||
}
|
||||
|
||||
DeployTableJoinColumn joinCol = new DeployTableJoinColumn(lc, fk);
|
||||
joinCol.setForeignSqlFormula(sqlFormulaSelect);
|
||||
if (reverse) {
|
||||
joinCol = joinCol.reverse();
|
||||
}
|
||||
join.addJoinColumn(joinCol);
|
||||
String lc = prop.getDbColumn();
|
||||
String fk = lc;
|
||||
if (foreignKeyPrefix != null) {
|
||||
fk = owner.getNamingConvention().getForeignKey(foreignKeyPrefix, fk);
|
||||
}
|
||||
|
||||
if (complexKey) {
|
||||
// just to copy the column name rather than prefix with the foreignKeyPrefix.
|
||||
// I think that with complex keys this is the more common approach.
|
||||
logger.debug("On table[{}] foreign key column [{}]", baseTable, lc);
|
||||
fk = lc;
|
||||
}
|
||||
if (sqlFormulaSelect != null) {
|
||||
fk = sqlFormulaSelect;
|
||||
}
|
||||
|
||||
DeployTableJoinColumn joinCol = new DeployTableJoinColumn(lc, fk);
|
||||
joinCol.setForeignSqlFormula(sqlFormulaSelect);
|
||||
if (reverse) {
|
||||
joinCol = joinCol.reverse();
|
||||
}
|
||||
join.addJoinColumn(joinCol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the primary key DB column.
|
||||
*/
|
||||
public String getIdColumn() {
|
||||
if (idProperties.length != 1) {
|
||||
throw new IllegalStateException("Expecting only one Id column to join to on "+beanType);
|
||||
}
|
||||
return idProperties[0].dbColumn;
|
||||
return idProperty.dbColumn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.query.STreePropertyAssocMany;
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -68,7 +69,7 @@ public interface DbReadContext {
|
||||
* Return the property that is associated with the many. There can only be
|
||||
* one. This can be null.
|
||||
*/
|
||||
BeanPropertyAssocMany<?> getManyProperty();
|
||||
STreePropertyAssocMany getManyProperty();
|
||||
|
||||
/**
|
||||
* Set back the bean that has just been loaded with its id.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebeaninternal.server.query.STreeProperty;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.query.SqlTreeProperty;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.util.List;
|
||||
@@ -9,14 +9,14 @@ import java.util.List;
|
||||
/**
|
||||
* Abstract base for dynamic properties.
|
||||
*/
|
||||
abstract class DynamicPropertyBase implements SqlTreeProperty {
|
||||
abstract class DynamicPropertyBase implements STreeProperty {
|
||||
|
||||
final String name;
|
||||
final String fullName;
|
||||
final String elPrefix;
|
||||
final ScalarType<?> scalarType;
|
||||
|
||||
public DynamicPropertyBase(String name, String fullName, String elPrefix, ScalarType<?> scalarType) {
|
||||
DynamicPropertyBase(String name, String fullName, String elPrefix, ScalarType<?> scalarType) {
|
||||
this.name = name;
|
||||
this.fullName = fullName;
|
||||
this.elPrefix = elPrefix;
|
||||
@@ -43,6 +43,11 @@ abstract class DynamicPropertyBase implements SqlTreeProperty {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFormula() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElPrefix() {
|
||||
return elPrefix;
|
||||
@@ -67,4 +72,9 @@ abstract class DynamicPropertyBase implements SqlTreeProperty {
|
||||
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
// do not add to from usually
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEncryptKeyAsString() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
/**
|
||||
* Collects (List/Set/Map) of elements.
|
||||
*/
|
||||
public interface ElementCollector {
|
||||
|
||||
/**
|
||||
* Add an element.
|
||||
*/
|
||||
void addElement(Object element);
|
||||
|
||||
/**
|
||||
* Add an element.
|
||||
*/
|
||||
void addKeyValue(Object key, Object element);
|
||||
|
||||
/**
|
||||
* Return the populated collection/map.
|
||||
*/
|
||||
Object collection();
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
|
||||
class ElementEntityBean implements EntityBean {
|
||||
|
||||
private final String[] properties;
|
||||
|
||||
private Object[] data;
|
||||
|
||||
private EntityBeanIntercept intercept;
|
||||
|
||||
ElementEntityBean(String[] properties) {
|
||||
this.properties = properties;
|
||||
this.intercept = new EntityBeanIntercept(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] _ebean_getPropertyNames() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String _ebean_getPropertyName(int pos) {
|
||||
return properties[pos];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String _ebean_getMarker() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object _ebean_newInstance() {
|
||||
return new ElementEntityBean(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void _ebean_setEmbeddedLoaded() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean _ebean_isEmbeddedNewOrDirty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityBeanIntercept _ebean_getIntercept() {
|
||||
return intercept;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityBeanIntercept _ebean_intercept() {
|
||||
return intercept;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void _ebean_setField(int fieldIndex, Object value) {
|
||||
if (data == null) {
|
||||
data = new Object[properties.length];
|
||||
}
|
||||
data[fieldIndex] = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void _ebean_setFieldIntercept(int fieldIndex, Object value) {
|
||||
_ebean_setField(fieldIndex, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object _ebean_getField(int fieldIndex) {
|
||||
return data == null ? null : data[fieldIndex];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object _ebean_getFieldIntercept(int fieldIndex) {
|
||||
return _ebean_getField(fieldIndex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
/**
|
||||
* Helper for handling ElementCollection.
|
||||
*/
|
||||
interface ElementHelp {
|
||||
|
||||
/**
|
||||
* Return a new collector.
|
||||
*/
|
||||
ElementCollector createCollector();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.common.BeanList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class ElementHelpList implements ElementHelp {
|
||||
|
||||
@Override
|
||||
public ElementCollector createCollector() {
|
||||
return new Collector();
|
||||
}
|
||||
|
||||
private static class Collector implements ElementCollector {
|
||||
|
||||
private List<Object> list = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void addElement(Object element) {
|
||||
list.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addKeyValue(Object key, Object element) {
|
||||
throw new IllegalStateException("never called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object collection() {
|
||||
BeanList<Object> beanList = new BeanList<>(list);
|
||||
beanList.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
return beanList;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.common.BeanMap;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
class ElementHelpMap implements ElementHelp {
|
||||
|
||||
@Override
|
||||
public ElementCollector createCollector() {
|
||||
return new Collector();
|
||||
}
|
||||
|
||||
private static class Collector implements ElementCollector {
|
||||
|
||||
private Map<Object, Object> map = new LinkedHashMap<>();
|
||||
|
||||
@Override
|
||||
public void addElement(Object element) {
|
||||
throw new IllegalStateException("never called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addKeyValue(Object key, Object element) {
|
||||
map.put(key, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object collection() {
|
||||
BeanMap<Object, Object> beanMap = new BeanMap<>(map);
|
||||
beanMap.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
return beanMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.common.BeanSet;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
class ElementHelpSet implements ElementHelp {
|
||||
|
||||
@Override
|
||||
public ElementCollector createCollector() {
|
||||
return new Collector();
|
||||
}
|
||||
|
||||
private static class Collector implements ElementCollector {
|
||||
|
||||
private Set<Object> set = new LinkedHashSet<>();
|
||||
|
||||
@Override
|
||||
public void addElement(Object element) {
|
||||
set.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addKeyValue(Object key, Object element) {
|
||||
throw new IllegalStateException("never called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object collection() {
|
||||
BeanSet<Object> beanSet = new BeanSet<>(set);
|
||||
beanSet.setModifyListening(BeanCollection.ModifyListenMode.ALL);
|
||||
return beanSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import io.ebeaninternal.server.query.SqlTreeProperty;
|
||||
import io.ebeaninternal.server.query.STreeProperty;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.sql.Types;
|
||||
@@ -89,7 +89,7 @@ class FormulaPropertyPath {
|
||||
return alias;
|
||||
}
|
||||
|
||||
SqlTreeProperty build() {
|
||||
STreeProperty build() {
|
||||
|
||||
DeployPropertyParser parser = descriptor.parser().setCatchFirst(true);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.ebeaninternal.server.deploy.id.IdBinder;
|
||||
import io.ebeaninternal.server.deploy.parse.DeployInheritInfo;
|
||||
import io.ebeaninternal.server.query.SqlTreeProperties;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
@@ -184,7 +185,7 @@ public class InheritInfo {
|
||||
|
||||
for (InheritInfo childInfo : children) {
|
||||
// recursively search this child bean descriptor
|
||||
prop = childInfo.desc().findBeanProperty(propertyName);
|
||||
prop = childInfo.desc().findProperty(propertyName);
|
||||
if (prop != null) {
|
||||
return prop;
|
||||
}
|
||||
@@ -291,7 +292,7 @@ public class InheritInfo {
|
||||
* Return true if this is considered a concrete type in the inheritance hierarchy.
|
||||
*/
|
||||
public boolean isConcrete() {
|
||||
return discriminatorValue != null;
|
||||
return !Modifier.isAbstract(type.getModifiers());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.query.STreeProperty;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
|
||||
import java.io.DataInput;
|
||||
@@ -52,7 +53,7 @@ public interface IdBinder {
|
||||
/**
|
||||
* Return the Id BeanProperty.
|
||||
*/
|
||||
BeanProperty getBeanProperty();
|
||||
STreeProperty getBeanProperty();
|
||||
|
||||
/**
|
||||
* Find a BeanProperty that is mapped to the database column.
|
||||
|
||||
@@ -277,7 +277,7 @@ public final class IdBinderEmbedded implements IdBinder {
|
||||
public void bindId(DefaultSqlUpdate sqlUpdate, Object value) {
|
||||
for (BeanProperty prop : props) {
|
||||
Object embFieldValue = prop.getValue((EntityBean) value);
|
||||
sqlUpdate.addParameter(embFieldValue);
|
||||
sqlUpdate.setNextParameter(embFieldValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ public final class IdBinderSimple implements IdBinder {
|
||||
|
||||
@Override
|
||||
public void addIdInBindValues(DefaultSqlUpdate sqlUpdate, Collection<?> ids) {
|
||||
sqlUpdate.addParameter(new MultiValueWrapper(ids));
|
||||
sqlUpdate.setNextParameter(new MultiValueWrapper(ids));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -171,7 +171,7 @@ public final class IdBinderSimple implements IdBinder {
|
||||
|
||||
@Override
|
||||
public void bindId(DefaultSqlUpdate sqlUpdate, Object value) {
|
||||
sqlUpdate.addParameter(value);
|
||||
sqlUpdate.setNextParameter(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package io.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.properties.BeanPropertySetter;
|
||||
|
||||
/**
|
||||
* Setter used for "element beans" with ElementCollection.
|
||||
*/
|
||||
class BeanPropertyElementSetter implements BeanPropertySetter {
|
||||
|
||||
private final int pos;
|
||||
|
||||
BeanPropertyElementSetter(int pos) {
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(EntityBean bean, Object value) {
|
||||
bean._ebean_setField(pos, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIntercept(EntityBean bean, Object value) {
|
||||
set(bean, value);
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import io.ebean.event.BeanPostLoad;
|
||||
import io.ebean.event.BeanQueryAdapter;
|
||||
import io.ebean.event.changelog.ChangeLogFilter;
|
||||
import io.ebean.text.PathProperties;
|
||||
import io.ebean.util.CamelCaseHelper;
|
||||
import io.ebeaninternal.api.ConcurrencyMode;
|
||||
import io.ebeaninternal.server.core.CacheOptions;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
|
||||
@@ -91,6 +90,10 @@ public class DeployBeanDescriptor<T> {
|
||||
*/
|
||||
private IdType idType;
|
||||
|
||||
private Class<?> idClass;
|
||||
|
||||
private DeployBeanPropertyAssocOne<?> idClassProperty;
|
||||
|
||||
/**
|
||||
* Set to true if the identity is default for the platform.
|
||||
*/
|
||||
@@ -205,7 +208,7 @@ public class DeployBeanDescriptor<T> {
|
||||
private DocStoreMode docStoreUpdate;
|
||||
private DocStoreMode docStoreDelete;
|
||||
|
||||
private List<DeployBeanProperty> idProperties;
|
||||
private DeployBeanProperty idProperty;
|
||||
private TableJoin primaryKeyJoin;
|
||||
|
||||
private short profileId;
|
||||
@@ -219,6 +222,20 @@ public class DeployBeanDescriptor<T> {
|
||||
this.beanType = beanType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IdClass to use.
|
||||
*/
|
||||
public void setIdClass(Class idClass) {
|
||||
this.idClass = idClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is a IdClass set.
|
||||
*/
|
||||
public boolean isIdClass() {
|
||||
return idClass != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* PK is also a FK.
|
||||
*/
|
||||
@@ -335,7 +352,7 @@ public class DeployBeanDescriptor<T> {
|
||||
|
||||
DeployBeanTable beanTable = new DeployBeanTable(getBeanType());
|
||||
beanTable.setBaseTable(baseTable);
|
||||
beanTable.setIdProperties(propertiesId());
|
||||
beanTable.setIdProperty(idProperty());
|
||||
|
||||
return beanTable;
|
||||
}
|
||||
@@ -443,6 +460,10 @@ public class DeployBeanDescriptor<T> {
|
||||
return cacheOptions;
|
||||
}
|
||||
|
||||
public DeployBeanPropertyAssocOne<?> getIdClassProperty() {
|
||||
return idClassProperty;
|
||||
}
|
||||
|
||||
public DeployBeanPropertyAssocOne<?> getUnidirectional() {
|
||||
return unidirectional;
|
||||
}
|
||||
@@ -684,6 +705,15 @@ public class DeployBeanDescriptor<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public void postAnnotations() {
|
||||
if (idClass != null) {
|
||||
idClassProperty = new DeployBeanPropertyAssocOne<>(this, idClass);
|
||||
idClassProperty.setName("_idClass");
|
||||
idClassProperty.setEmbedded();
|
||||
idClassProperty.setNullable(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a bean property.
|
||||
*/
|
||||
@@ -691,32 +721,8 @@ public class DeployBeanDescriptor<T> {
|
||||
return propMap.put(prop.getName(), prop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the matching property for a given property name or dbColumn.
|
||||
* <p>
|
||||
* This is primarily to find imported primary key columns (ManyToOne that also match the PK).
|
||||
* </p>
|
||||
*/
|
||||
DeployBeanProperty findMatch(String propertyName, String dbColumn) {
|
||||
|
||||
DeployBeanProperty prop = propMap.get(propertyName);
|
||||
if (prop != null) {
|
||||
return prop;
|
||||
}
|
||||
if (dbColumn != null) {
|
||||
String asCamel = CamelCaseHelper.toCamelFromUnderscore(dbColumn);
|
||||
prop = propMap.get(asCamel);
|
||||
if (prop != null) {
|
||||
return prop;
|
||||
}
|
||||
// scan looking for dbColumn match
|
||||
for (DeployBeanProperty property : propMap.values()) {
|
||||
if (dbColumn.equals(property.getDbColumn())) {
|
||||
return property;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
public Collection<DeployBeanProperty> properties() {
|
||||
return propMap.values();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -935,16 +941,14 @@ public class DeployBeanDescriptor<T> {
|
||||
*/
|
||||
public boolean isPrimaryKeyCompoundOrNonNumeric() {
|
||||
|
||||
List<DeployBeanProperty> ids = propertiesId();
|
||||
if (ids.size() != 1) {
|
||||
// compound key
|
||||
return true;
|
||||
DeployBeanProperty id = idProperty();
|
||||
if (id == null) {
|
||||
return false;
|
||||
}
|
||||
DeployBeanProperty p = ids.get(0);
|
||||
if (p instanceof DeployBeanPropertyAssocOne<?>) {
|
||||
return ((DeployBeanPropertyAssocOne<?>) p).isCompound();
|
||||
if (id instanceof DeployBeanPropertyAssocOne<?>) {
|
||||
return ((DeployBeanPropertyAssocOne<?>) id).isCompound();
|
||||
} else {
|
||||
return !p.isDbNumberType();
|
||||
return !id.isDbNumberType();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -953,37 +957,32 @@ public class DeployBeanDescriptor<T> {
|
||||
* compound). This is for the purpose of defining a sequence name.
|
||||
*/
|
||||
public String getSinglePrimaryKeyColumn() {
|
||||
List<DeployBeanProperty> ids = propertiesId();
|
||||
if (ids.size() == 1) {
|
||||
DeployBeanProperty p = ids.get(0);
|
||||
if (p instanceof DeployBeanPropertyAssoc<?>) {
|
||||
DeployBeanProperty id = idProperty();
|
||||
if (id != null) {
|
||||
if (id instanceof DeployBeanPropertyAssoc<?>) {
|
||||
// its a compound primary key
|
||||
return null;
|
||||
} else {
|
||||
return p.getDbColumn();
|
||||
return id.getDbColumn();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the BeanProperty that make up the unique id.
|
||||
* <p>
|
||||
* The order of these properties can be relied on to be consistent if the bean
|
||||
* itself doesn't change or the xml deployment order does not change.
|
||||
* </p>
|
||||
* Return the BeanProperty that is the Id.
|
||||
*/
|
||||
public List<DeployBeanProperty> propertiesId() {
|
||||
|
||||
if (idProperties == null) {
|
||||
idProperties = new ArrayList<>(2);
|
||||
for (DeployBeanProperty prop : propMap.values()) {
|
||||
if (prop.isId()) {
|
||||
idProperties.add(prop);
|
||||
}
|
||||
public DeployBeanProperty idProperty() {
|
||||
if (idProperty != null) {
|
||||
return idProperty;
|
||||
}
|
||||
for (DeployBeanProperty prop : propMap.values()) {
|
||||
if (prop.isId()) {
|
||||
idProperty = prop;
|
||||
return idProperty;
|
||||
}
|
||||
}
|
||||
return idProperties;
|
||||
return null;
|
||||
}
|
||||
|
||||
public DeployBeanPropertyAssocOne<?> findJoinToTable(String tableName) {
|
||||
|
||||
@@ -51,6 +51,7 @@ public class DeployBeanProperty {
|
||||
private static final int AUDITCOLUMN_ORDER = -1000000;
|
||||
private static final int VERSIONCOLUMN_ORDER = -1000000;
|
||||
private static final Set<Class<?>> PRIMITIVE_NUMBER_TYPES = new HashSet<>();
|
||||
|
||||
static {
|
||||
PRIMITIVE_NUMBER_TYPES.add(float.class);
|
||||
PRIMITIVE_NUMBER_TYPES.add(double.class);
|
||||
@@ -64,6 +65,8 @@ public class DeployBeanProperty {
|
||||
*/
|
||||
private boolean id;
|
||||
|
||||
boolean importedPrimaryKey;
|
||||
|
||||
/**
|
||||
* Flag to mark the property as embedded. This could be on
|
||||
* BeanPropertyAssocOne rather than here. Put it here for checking Id type
|
||||
@@ -207,6 +210,8 @@ public class DeployBeanProperty {
|
||||
|
||||
private boolean undirectionalShadow;
|
||||
|
||||
private boolean elementProperty;
|
||||
|
||||
private int sortOrder;
|
||||
|
||||
private boolean excludedFromHistory;
|
||||
@@ -441,7 +446,11 @@ public class DeployBeanProperty {
|
||||
}
|
||||
|
||||
public BeanPropertySetter getSetter() {
|
||||
return setter;
|
||||
if (elementProperty) {
|
||||
return new BeanPropertyElementSetter(sortOrder);
|
||||
} else {
|
||||
return setter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -611,6 +620,16 @@ public class DeployBeanProperty {
|
||||
this.dbUpdateable = false;
|
||||
}
|
||||
|
||||
public void setImportedPrimaryKey() {
|
||||
this.importedPrimaryKey = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if this is part of the primary key.
|
||||
*/
|
||||
public void setImportedPrimaryKeyColumn(DeployBeanProperty primaryKey) {
|
||||
this.importedPrimaryKey = true;
|
||||
}
|
||||
|
||||
public boolean isAggregation() {
|
||||
return aggregation != null;
|
||||
@@ -889,6 +908,13 @@ public class DeployBeanProperty {
|
||||
return genericType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is part of the primary key.
|
||||
*/
|
||||
public boolean isImportedPrimaryKey() {
|
||||
return importedPrimaryKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is included in the unique id.
|
||||
*/
|
||||
@@ -1046,6 +1072,10 @@ public class DeployBeanProperty {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public boolean isIdClass() {
|
||||
return desc.isIdClass();
|
||||
}
|
||||
|
||||
public void addDbMigrationInfo(DbMigrationInfo info) {
|
||||
if (dbMigrationInfos == null) {
|
||||
dbMigrationInfos = new ArrayList<>();
|
||||
@@ -1057,4 +1087,10 @@ public class DeployBeanProperty {
|
||||
return dbMigrationInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set when this property is part of a 'element bean' used with ElementCollection.
|
||||
*/
|
||||
public void setElementProperty() {
|
||||
this.elementProperty = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package io.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import io.ebean.bean.BeanCollection.ModifyListenMode;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.ManyType;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.type.TypeReflectHelper;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Property mapped to a List Set or Map.
|
||||
@@ -23,6 +27,8 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
|
||||
|
||||
private boolean o2mJoinTable;
|
||||
|
||||
private boolean elementCollection;
|
||||
|
||||
/**
|
||||
* Flag to indicate this is a unidirectional relationship.
|
||||
*/
|
||||
@@ -46,6 +52,11 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
|
||||
|
||||
private DeployOrderColumn orderColumn;
|
||||
|
||||
/**
|
||||
* Effectively the dynamically created target descriptor (that doesn't have a mapped type/class per say).
|
||||
*/
|
||||
private BeanDescriptor<?> elementDescriptor;
|
||||
|
||||
/**
|
||||
* Create this property.
|
||||
*/
|
||||
@@ -171,6 +182,14 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
|
||||
return fetchOrderBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of the map key (valid only when this property is a Map).
|
||||
*/
|
||||
public Class<?> getMapKeyType() {
|
||||
Type genericType = getField().getGenericType();
|
||||
return TypeReflectHelper.getMapKeyType(genericType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default mapKey when returning a Map.
|
||||
*/
|
||||
@@ -231,5 +250,24 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
|
||||
this.o2mJoinTable = true;
|
||||
setModifyListenMode(ModifyListenMode.ALL);
|
||||
}
|
||||
|
||||
public void setElementCollection() {
|
||||
elementCollection = true;
|
||||
cascadeInfo.setSaveDelete(true, true);
|
||||
setModifyListenMode(ModifyListenMode.ALL);
|
||||
}
|
||||
|
||||
public boolean isElementCollection() {
|
||||
return elementCollection;
|
||||
}
|
||||
|
||||
public void setElementDescriptor(BeanDescriptor<?> elementDescriptor) {
|
||||
this.elementDescriptor = elementDescriptor;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <A> BeanDescriptor<A> getElementDescriptor() {
|
||||
return (BeanDescriptor<A>)elementDescriptor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import io.ebeaninternal.server.deploy.PropertyForeignKey;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
|
||||
@@ -17,8 +18,6 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
|
||||
|
||||
private boolean primaryKeyExport;
|
||||
|
||||
private boolean importedPrimaryKey;
|
||||
|
||||
private DeployBeanEmbedded deployEmbedded;
|
||||
|
||||
private String columnPrefix;
|
||||
@@ -92,17 +91,11 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
|
||||
this.oneToOneExported = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is part of the primary key.
|
||||
*/
|
||||
public boolean isImportedPrimaryKey() {
|
||||
return importedPrimaryKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if this is part of the primary key.
|
||||
*/
|
||||
void setImportedPrimaryKey(DeployBeanProperty primaryKey) {
|
||||
@Override
|
||||
public void setImportedPrimaryKeyColumn(DeployBeanProperty primaryKey) {
|
||||
this.importedPrimaryKey = true;
|
||||
String dbColumn = primaryKey.getDbColumn();
|
||||
if (dbColumn != null) {
|
||||
@@ -170,4 +163,12 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
|
||||
public boolean isOrphanRemoval() {
|
||||
return orphanRemoval;
|
||||
}
|
||||
|
||||
public void setJoinType(boolean outerJoin) {
|
||||
tableJoin.setType(outerJoin ? SqlJoinType.OUTER : SqlJoinType.INNER);
|
||||
}
|
||||
|
||||
public void setJoinColumns(DeployTableJoinColumn[] columns, boolean reverse) {
|
||||
tableJoin.setColumns(columns, reverse);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.ebeaninternal.server.deploy.BeanDescriptorMap;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyIdClass;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyOrderColumn;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertySimpleCollection;
|
||||
import io.ebeaninternal.server.deploy.InheritInfo;
|
||||
@@ -39,7 +40,7 @@ public class DeployBeanPropertyLists {
|
||||
|
||||
private final LinkedHashMap<String, BeanProperty> propertyMap;
|
||||
|
||||
private final List<BeanProperty> ids = new ArrayList<>();
|
||||
private BeanProperty id;
|
||||
|
||||
private final List<BeanProperty> local = new ArrayList<>();
|
||||
|
||||
@@ -70,7 +71,13 @@ public class DeployBeanPropertyLists {
|
||||
public DeployBeanPropertyLists(BeanDescriptorMap owner, BeanDescriptor<?> desc, DeployBeanDescriptor<?> deploy) {
|
||||
this.desc = desc;
|
||||
|
||||
setImportedPrimaryKeys(deploy);
|
||||
DeployBeanPropertyAssocOne<?> deployId = deploy.getIdClassProperty();
|
||||
if (deployId != null) {
|
||||
this.id = new BeanPropertyIdClass(owner, desc, deployId);
|
||||
setImportedPrimaryKeysFor(deploy, deployId);
|
||||
} else {
|
||||
setImportedPrimaryKeys(deploy);
|
||||
}
|
||||
|
||||
DeployBeanProperty deployOrderColumn = deploy.getOrderColumn();
|
||||
this.orderColumn = deployOrderColumn != null ? new BeanPropertyOrderColumn(desc, deployOrderColumn) : null;
|
||||
@@ -142,23 +149,43 @@ public class DeployBeanPropertyLists {
|
||||
* </p>
|
||||
*/
|
||||
private void setImportedPrimaryKeys(DeployBeanDescriptor<?> deploy) {
|
||||
DeployBeanProperty id = deploy.idProperty();
|
||||
if (id instanceof DeployBeanPropertyAssocOne<?>) {
|
||||
setImportedPrimaryKeysFor(deploy, (DeployBeanPropertyAssocOne<?>) id);
|
||||
}
|
||||
}
|
||||
|
||||
List<DeployBeanProperty> ids = deploy.propertiesId();
|
||||
if (ids.size() == 1) {
|
||||
DeployBeanProperty id = ids.get(0);
|
||||
if (id instanceof DeployBeanPropertyAssocOne<?>) {
|
||||
// only interested if the primary key is a compound key
|
||||
DeployBeanDescriptor<?> targetDeploy = ((DeployBeanPropertyAssocOne<?>) id).getTargetDeploy();
|
||||
for (DeployBeanPropertyAssocOne<?> assoc : deploy.propertiesAssocOne()) {
|
||||
DeployBeanProperty pkMatch = targetDeploy.findMatch(assoc.getName(), assoc.getDbColumn());
|
||||
if (pkMatch != null) {
|
||||
assoc.setImportedPrimaryKey(pkMatch);
|
||||
}
|
||||
}
|
||||
private void setImportedPrimaryKeysFor(DeployBeanDescriptor<?> deploy, DeployBeanPropertyAssocOne<?> id) {
|
||||
|
||||
for (DeployBeanProperty prop : id.getTargetDeploy().properties()) {
|
||||
DeployBeanProperty match = findImported(deploy, prop);
|
||||
if (match != null) {
|
||||
match.setImportedPrimaryKeyColumn(prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DeployBeanProperty findImported(DeployBeanDescriptor<?> deploy, DeployBeanProperty embeddedScalar) {
|
||||
|
||||
// the logical name and db column we are looking for a match on
|
||||
String name = embeddedScalar.getName();
|
||||
String dbColumn = embeddedScalar.getDbColumn();
|
||||
|
||||
DeployBeanProperty match = deploy.getBeanProperty(name);
|
||||
if (match != null) {
|
||||
return match;
|
||||
}
|
||||
// could look to match more by dbColumn
|
||||
|
||||
for (DeployBeanPropertyAssocOne<?> assocOne : deploy.propertiesAssocOne()) {
|
||||
if (name.equals(assocOne.getName()) || (dbColumn != null && dbColumn.equals(assocOne.getDbColumn()))) {
|
||||
return assocOne;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the unidirectional.
|
||||
*/
|
||||
@@ -188,12 +215,13 @@ public class DeployBeanPropertyLists {
|
||||
return;
|
||||
}
|
||||
if (prop.isId()) {
|
||||
ids.add(prop);
|
||||
if (id != null) {
|
||||
throw new IllegalStateException("More that one @Id property on " + desc.getFullName() + " ?");
|
||||
}
|
||||
id = prop;
|
||||
return;
|
||||
} else {
|
||||
nonTransients.add(prop);
|
||||
}
|
||||
|
||||
nonTransients.add(prop);
|
||||
if (prop.isMutableScalarType()) {
|
||||
mutable.add(prop);
|
||||
}
|
||||
@@ -255,15 +283,7 @@ public class DeployBeanPropertyLists {
|
||||
}
|
||||
|
||||
public BeanProperty getId() {
|
||||
if (ids.size() > 1) {
|
||||
String msg = "Issue with bean " + desc + ". Ebean does not support multiple @Id properties. You need to convert to using an @EmbeddedId."
|
||||
+ " Please email the ebean google group if you need further clarification.";
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
if (ids.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return ids.get(0);
|
||||
return id;
|
||||
}
|
||||
|
||||
public BeanProperty[] getNonTransients() {
|
||||
|
||||
@@ -4,9 +4,6 @@ import io.ebeaninternal.server.deploy.BeanDescriptorMap;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Used for associated beans in place of a BeanDescriptor. This is done to avoid
|
||||
* recursion issues due to the potentially bi-directional and circular
|
||||
@@ -25,12 +22,12 @@ public class DeployBeanTable {
|
||||
*/
|
||||
private String baseTable;
|
||||
|
||||
private List<DeployBeanProperty> idProperties;
|
||||
private DeployBeanProperty idProperty;
|
||||
|
||||
/**
|
||||
* Create the BeanTable.
|
||||
*/
|
||||
public DeployBeanTable(Class<?> beanType) {
|
||||
DeployBeanTable(Class<?> beanType) {
|
||||
this.beanType = beanType;
|
||||
}
|
||||
|
||||
@@ -53,12 +50,8 @@ public class DeployBeanTable {
|
||||
/**
|
||||
* Return the id properties.
|
||||
*/
|
||||
public BeanProperty[] createIdProperties(BeanDescriptorMap owner) {
|
||||
BeanProperty[] props = new BeanProperty[idProperties.size()];
|
||||
for (int i = 0; i < idProperties.size(); i++) {
|
||||
props[i] = createProperty(owner, idProperties.get(i));
|
||||
}
|
||||
return props;
|
||||
public BeanProperty createIdProperty(BeanDescriptorMap owner) {
|
||||
return idProperty == null ? null : createProperty(owner, idProperty);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@@ -70,14 +63,13 @@ public class DeployBeanTable {
|
||||
} else {
|
||||
return new BeanProperty(prop);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Id properties.
|
||||
*/
|
||||
public void setIdProperties(List<DeployBeanProperty> idProperties) {
|
||||
this.idProperties = idProperties;
|
||||
public void setIdProperty(DeployBeanProperty idProperty) {
|
||||
this.idProperty = idProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -92,9 +92,9 @@ public class DeployTableJoinColumn {
|
||||
|
||||
private void setReferencedColumn(BeanTable beanTable) {
|
||||
if (localDbColumn == null) {
|
||||
BeanProperty[] idProperties = beanTable.getIdProperties();
|
||||
if (idProperties.length == 1) {
|
||||
localDbColumn = idProperties[0].getDbColumn();
|
||||
BeanProperty idProperty = beanTable.getIdProperty();
|
||||
if (idProperty != null) {
|
||||
localDbColumn = idProperty.getDbColumn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,22 +7,30 @@ import io.ebean.annotation.Where;
|
||||
import io.ebean.bean.BeanCollection.ModifyListenMode;
|
||||
import io.ebean.config.NamingConvention;
|
||||
import io.ebean.config.TableName;
|
||||
import io.ebean.util.CamelCaseHelper;
|
||||
import io.ebean.util.StringHelper;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanTable;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployOrderColumn;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.CollectionTable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.MapKey;
|
||||
import javax.persistence.MapKeyColumn;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OrderBy;
|
||||
import javax.persistence.OrderColumn;
|
||||
@@ -38,8 +46,8 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
/**
|
||||
* Create with the DeployInfo.
|
||||
*/
|
||||
AnnotationAssocManys(DeployBeanInfo<?> info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) {
|
||||
super(info, javaxValidationAnnotations);
|
||||
AnnotationAssocManys(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig, BeanDescriptorManager factory) {
|
||||
super(info, readConfig);
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@@ -82,6 +90,10 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
if (manyToMany != null) {
|
||||
readToMany(manyToMany, prop);
|
||||
}
|
||||
ElementCollection elementCollection = get(prop, ElementCollection.class);
|
||||
if (elementCollection != null) {
|
||||
readElementCollection(prop, elementCollection);
|
||||
}
|
||||
|
||||
if (get(prop, HistoryExclude.class) != null) {
|
||||
prop.setExcludedFromHistory();
|
||||
@@ -160,6 +172,111 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
}
|
||||
}
|
||||
|
||||
private void readElementCollection(DeployBeanPropertyAssocMany<?> prop, ElementCollection elementCollection) {
|
||||
|
||||
prop.setElementCollection();
|
||||
if (!elementCollection.targetClass().equals(void.class)) {
|
||||
prop.setTargetType(elementCollection.targetClass());
|
||||
}
|
||||
Column column = get(prop, Column.class);
|
||||
if (column != null) {
|
||||
prop.setDbColumn(column.name());
|
||||
prop.setDbLength(column.length());
|
||||
prop.setDbScale(column.scale());
|
||||
}
|
||||
|
||||
CollectionTable collectionTable = get(prop, CollectionTable.class);
|
||||
|
||||
String fullTableName = getFullTableName(collectionTable);
|
||||
if (fullTableName == null) {
|
||||
fullTableName = descriptor.getBaseTable()+"_"+ CamelCaseHelper.toUnderscoreFromCamel(prop.getName());
|
||||
}
|
||||
|
||||
BeanTable localTable = factory.getBeanTable(descriptor.getBeanType());
|
||||
if (collectionTable != null) {
|
||||
prop.getTableJoin().addJoinColumn(true, collectionTable.joinColumns(), localTable);
|
||||
}
|
||||
if (!prop.getTableJoin().hasJoinColumns()) {
|
||||
BeanProperty localId = localTable.getIdProperty();
|
||||
if (localId != null) {
|
||||
// add foreign key based on convention
|
||||
String fkColName = namingConvention.getForeignKey(descriptor.getBaseTable(), localId.getName());
|
||||
prop.getTableJoin().addJoinColumn(new DeployTableJoinColumn(localId.getDbColumn(), fkColName));
|
||||
}
|
||||
}
|
||||
|
||||
BeanTable beanTable = factory.createCollectionBeanTable(fullTableName, prop.getTargetType());
|
||||
prop.setBeanTable(beanTable);
|
||||
|
||||
Class<?> elementType = prop.getTargetType();
|
||||
|
||||
DeployBeanDescriptor<?> elementDescriptor = factory.createDeployDescriptor(elementType);
|
||||
elementDescriptor.setBaseTable(new TableName(fullTableName), readConfig.getAsOfViewSuffix(), readConfig.getVersionsBetweenSuffix());
|
||||
|
||||
int sortOrder = 0;
|
||||
if (!prop.getManyType().isMap()) {
|
||||
elementDescriptor.setProperties(new String[]{"value"});
|
||||
} else {
|
||||
elementDescriptor.setProperties(new String[]{"key", "value"});
|
||||
String dbKeyColumn = "mkey";
|
||||
MapKeyColumn mapKeyColumn = get(prop, MapKeyColumn.class);
|
||||
if (mapKeyColumn != null) {
|
||||
dbKeyColumn = mapKeyColumn.name();
|
||||
}
|
||||
|
||||
ScalarType<?> keyScalarType = util.getTypeManager().getScalarType(prop.getMapKeyType());
|
||||
|
||||
DeployBeanProperty keyProp = new DeployBeanProperty(elementDescriptor, elementType, keyScalarType, null);
|
||||
setElementProperty(keyProp, "key", dbKeyColumn, sortOrder++);
|
||||
elementDescriptor.addBeanProperty(keyProp);
|
||||
if (mapKeyColumn != null) {
|
||||
keyProp.setDbLength(mapKeyColumn.length());
|
||||
keyProp.setDbScale(mapKeyColumn.scale());
|
||||
keyProp.setUnique(mapKeyColumn.unique());
|
||||
}
|
||||
}
|
||||
|
||||
ScalarType<?> valueScalarType = util.getTypeManager().getScalarType(elementType);
|
||||
|
||||
boolean scalar = true;
|
||||
if (valueScalarType == null) {
|
||||
// embedded value type
|
||||
scalar = false;
|
||||
DeployBeanPropertyAssocOne valueProp = new DeployBeanPropertyAssocOne<>(elementDescriptor, elementType);
|
||||
valueProp.setName("value");
|
||||
valueProp.setEmbedded();
|
||||
valueProp.setElementProperty();
|
||||
valueProp.setSortOrder(sortOrder++);
|
||||
elementDescriptor.addBeanProperty(valueProp);
|
||||
|
||||
} else {
|
||||
// scalar value type
|
||||
DeployBeanProperty valueProp = new DeployBeanProperty(elementDescriptor, elementType, valueScalarType, null);
|
||||
setElementProperty(valueProp, "value", prop.getDbColumn(), sortOrder++);
|
||||
if (column != null) {
|
||||
valueProp.setDbLength(column.length());
|
||||
valueProp.setDbScale(column.scale());
|
||||
}
|
||||
elementDescriptor.addBeanProperty(valueProp);
|
||||
}
|
||||
|
||||
elementDescriptor.setName(prop.getFullBeanName());
|
||||
|
||||
factory.createUnidirectional(elementDescriptor, prop.getOwningType(), beanTable, prop.getTableJoin());
|
||||
prop.setElementDescriptor(factory.createElementDescriptor(elementDescriptor, prop.getManyType(), scalar));
|
||||
}
|
||||
|
||||
private void setElementProperty(DeployBeanProperty elementProp, String name, String dbColumn, int sortOrder) {
|
||||
elementProp.setName(name);
|
||||
elementProp.setDbColumn(dbColumn);
|
||||
elementProp.setNullable(false);
|
||||
elementProp.setDbInsertable(true);
|
||||
elementProp.setDbUpdateable(true);
|
||||
elementProp.setDbRead(true);
|
||||
elementProp.setSortOrder(sortOrder);
|
||||
elementProp.setElementProperty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the joins for a ManyToMany relationship.
|
||||
* <p>
|
||||
@@ -204,6 +321,24 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full table name
|
||||
*/
|
||||
private String getFullTableName(CollectionTable collectionTable) {
|
||||
if (collectionTable == null || collectionTable.name().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (!StringHelper.isNull(collectionTable.catalog())) {
|
||||
sb.append(collectionTable.catalog()).append(".");
|
||||
}
|
||||
if (!StringHelper.isNull(collectionTable.schema())) {
|
||||
sb.append(collectionTable.schema()).append(".");
|
||||
}
|
||||
sb.append(collectionTable.name());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define intersection table and foreign key columns for ManyToMany.
|
||||
* <p>
|
||||
@@ -247,8 +382,8 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
}
|
||||
if (!intJoin.hasJoinColumns()) {
|
||||
// define foreign key columns
|
||||
BeanProperty[] localIds = localTable.getIdProperties();
|
||||
for (BeanProperty localId : localIds) {
|
||||
BeanProperty localId = localTable.getIdProperty();
|
||||
if (localId != null) {
|
||||
// add the source to intersection join columns
|
||||
String fkCol = localTableName + "_" + localId.getDbColumn();
|
||||
intJoin.addJoinColumn(new DeployTableJoinColumn(localId.getDbColumn(), namingConvention.getColumnFromProperty(null, fkCol)));
|
||||
@@ -257,8 +392,8 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
|
||||
if (!destJoin.hasJoinColumns()) {
|
||||
// define inverse foreign key columns
|
||||
BeanProperty[] otherIds = otherTable.getIdProperties();
|
||||
for (BeanProperty otherId : otherIds) {
|
||||
BeanProperty otherId = otherTable.getIdProperty();
|
||||
if (otherId != null) {
|
||||
// set the intersection to dest table join columns
|
||||
final String fkCol = otherTableName + "_" + otherId.getDbColumn();
|
||||
destJoin.addJoinColumn(new DeployTableJoinColumn(namingConvention.getColumnFromProperty(null, fkCol), otherId.getDbColumn()));
|
||||
|
||||
@@ -41,8 +41,8 @@ public class AnnotationAssocOnes extends AnnotationParser {
|
||||
/**
|
||||
* Create with the deploy Info.
|
||||
*/
|
||||
AnnotationAssocOnes(DeployBeanInfo<?> info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) {
|
||||
super(info, javaxValidationAnnotations);
|
||||
AnnotationAssocOnes(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig, BeanDescriptorManager factory) {
|
||||
super(info, readConfig);
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@@ -92,9 +92,7 @@ public class AnnotationAssocOnes extends AnnotationParser {
|
||||
// May as well check for Id. Makes sense to me.
|
||||
Id id = get(prop, Id.class);
|
||||
if (id != null) {
|
||||
prop.setEmbedded();
|
||||
prop.setId();
|
||||
prop.setNullable(false);
|
||||
readIdAssocOne(prop);
|
||||
}
|
||||
|
||||
DbForeignKey dbForeignKey = get(prop, DbForeignKey.class);
|
||||
@@ -154,7 +152,7 @@ public class AnnotationAssocOnes extends AnnotationParser {
|
||||
}
|
||||
}
|
||||
|
||||
info.setBeanJoinType(prop, prop.isNullable());
|
||||
prop.setJoinType(prop.isNullable());
|
||||
|
||||
if (!prop.getTableJoin().hasJoinColumns() && beanTable != null) {
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import javax.persistence.AttributeOverride;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
@@ -41,25 +42,15 @@ public class AnnotationClass extends AnnotationParser {
|
||||
|
||||
private final boolean disableL2Cache;
|
||||
|
||||
/**
|
||||
* Create for normal early parse of class level annotations.
|
||||
*/
|
||||
public AnnotationClass(DeployBeanInfo<?> info, boolean validationAnnotations, String asOfViewSuffix, String versionsBetweenSuffix, boolean disableL2Cache) {
|
||||
super(info, validationAnnotations);
|
||||
this.asOfViewSuffix = asOfViewSuffix;
|
||||
this.versionsBetweenSuffix = versionsBetweenSuffix;
|
||||
this.disableL2Cache = disableL2Cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create to parse AttributeOverride annotations which is run last
|
||||
* after all the properties/fields have been parsed fully.
|
||||
*/
|
||||
public AnnotationClass(DeployBeanInfo<?> info) {
|
||||
super(info, false);
|
||||
this.asOfViewSuffix = null;
|
||||
this.versionsBetweenSuffix = null;
|
||||
this.disableL2Cache = false;
|
||||
public AnnotationClass(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig) {
|
||||
super(info, readConfig);
|
||||
this.asOfViewSuffix = readConfig.getAsOfViewSuffix();
|
||||
this.versionsBetweenSuffix = readConfig.getVersionsBetweenSuffix();
|
||||
this.disableL2Cache = readConfig.isDisableL2Cache();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,6 +118,11 @@ public class AnnotationClass extends AnnotationParser {
|
||||
}
|
||||
}
|
||||
|
||||
IdClass idClass = AnnotationUtil.findAnnotationRecursive(cls, IdClass.class);
|
||||
if (idClass != null) {
|
||||
descriptor.setIdClass(idClass.value());
|
||||
}
|
||||
|
||||
Embeddable embeddable = AnnotationUtil.findAnnotationRecursive(cls, Embeddable.class);
|
||||
if (embeddable != null) {
|
||||
descriptor.setEntityType(EntityType.EMBEDDED);
|
||||
|
||||
@@ -91,14 +91,11 @@ public class AnnotationFields extends AnnotationParser {
|
||||
*/
|
||||
private FetchType defaultLobFetchType = FetchType.LAZY;
|
||||
|
||||
public AnnotationFields(GeneratedPropertyFactory generatedPropFactory, DeployBeanInfo<?> info,
|
||||
boolean javaxValidationAnnotations, boolean jacksonAnnotationsPresent, boolean eagerFetchLobs) {
|
||||
|
||||
super(info, javaxValidationAnnotations);
|
||||
this.jacksonAnnotationsPresent = jacksonAnnotationsPresent;
|
||||
this.generatedPropFactory = generatedPropFactory;
|
||||
|
||||
if (eagerFetchLobs) {
|
||||
AnnotationFields(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig) {
|
||||
super(info, readConfig);
|
||||
this.jacksonAnnotationsPresent = readConfig.isJacksonAnnotations();
|
||||
this.generatedPropFactory = readConfig.getGeneratedPropFactory();
|
||||
if (readConfig.isEagerFetchLobs()) {
|
||||
defaultLobFetchType = FetchType.EAGER;
|
||||
}
|
||||
}
|
||||
@@ -127,8 +124,7 @@ public class AnnotationFields extends AnnotationParser {
|
||||
|
||||
Id id = get(prop, Id.class);
|
||||
if (id != null) {
|
||||
prop.setId();
|
||||
prop.setNullable(false);
|
||||
readIdAssocOne(prop);
|
||||
}
|
||||
|
||||
EmbeddedId embeddedId = get(prop, EmbeddedId.class);
|
||||
@@ -211,7 +207,7 @@ public class AnnotationFields extends AnnotationParser {
|
||||
|
||||
Id id = get(prop, Id.class);
|
||||
if (id != null) {
|
||||
readId(prop);
|
||||
readIdScalar(prop);
|
||||
}
|
||||
|
||||
// determine the JDBC type using Lob/Temporal
|
||||
@@ -517,18 +513,6 @@ public class AnnotationFields extends AnnotationParser {
|
||||
return util.createDataEncryptSupport(table, column);
|
||||
}
|
||||
|
||||
private void readId(DeployBeanProperty prop) {
|
||||
|
||||
prop.setId();
|
||||
prop.setNullable(false);
|
||||
|
||||
if (prop.getPropertyType().equals(UUID.class)) {
|
||||
if (descriptor.getIdGeneratorName() == null) {
|
||||
descriptor.setUuidGenerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readGenValue(GeneratedValue gen, DeployBeanProperty prop) {
|
||||
|
||||
String genName = gen.generator();
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.deploy.parse;
|
||||
import io.ebeaninternal.server.deploy.BeanCascadeInfo;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
|
||||
import javax.persistence.AttributeOverride;
|
||||
@@ -11,6 +12,7 @@ import javax.persistence.Column;
|
||||
import javax.validation.groups.Default;
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Base class for reading deployment annotations.
|
||||
@@ -25,9 +27,12 @@ public abstract class AnnotationParser extends AnnotationBase {
|
||||
|
||||
protected final boolean validationAnnotations;
|
||||
|
||||
public AnnotationParser(DeployBeanInfo<?> info, boolean validationAnnotations) {
|
||||
protected final ReadAnnotationConfig readConfig;
|
||||
|
||||
public AnnotationParser(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig) {
|
||||
super(info.getUtil());
|
||||
this.validationAnnotations = validationAnnotations;
|
||||
this.readConfig = readConfig;
|
||||
this.validationAnnotations = readConfig.isJavaxValidationAnnotations();
|
||||
this.info = info;
|
||||
this.beanType = info.getDescriptor().getBeanType();
|
||||
this.descriptor = info.getDescriptor();
|
||||
@@ -39,6 +44,36 @@ public abstract class AnnotationParser extends AnnotationBase {
|
||||
@Override
|
||||
public abstract void parse();
|
||||
|
||||
/**
|
||||
* Read the Id annotation on an embeddedId.
|
||||
*/
|
||||
protected void readIdAssocOne(DeployBeanPropertyAssoc<?> prop) {
|
||||
prop.setNullable(false);
|
||||
if (prop.isIdClass()) {
|
||||
prop.setImportedPrimaryKey();
|
||||
} else {
|
||||
prop.setId();
|
||||
prop.setEmbedded();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the Id annotation on scalar property.
|
||||
*/
|
||||
protected void readIdScalar(DeployBeanProperty prop) {
|
||||
prop.setNullable(false);
|
||||
if (prop.isIdClass()) {
|
||||
prop.setImportedPrimaryKey();
|
||||
} else {
|
||||
prop.setId();
|
||||
if (prop.getPropertyType().equals(UUID.class)) {
|
||||
if (descriptor.getIdGeneratorName() == null) {
|
||||
descriptor.setUuidGenerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to set cascade types to the CascadeInfo on BeanProperty.
|
||||
*/
|
||||
|
||||
@@ -7,10 +7,10 @@ import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
/**
|
||||
* Read the class level deployment annotations.
|
||||
*/
|
||||
public class AnnotationSql extends AnnotationParser {
|
||||
class AnnotationSql extends AnnotationParser {
|
||||
|
||||
public AnnotationSql(DeployBeanInfo<?> info, boolean javaxValidationAnnotations) {
|
||||
super(info, javaxValidationAnnotations);
|
||||
AnnotationSql(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig) {
|
||||
super(info, readConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,7 +3,6 @@ package io.ebeaninternal.server.deploy.parse;
|
||||
import io.ebean.RawSql;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.rawsql.SpiRawSql;
|
||||
@@ -70,15 +69,6 @@ public class DeployBeanInfo<T> {
|
||||
return tableJoin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a the join alias for a assoc one property.
|
||||
*/
|
||||
public void setBeanJoinType(DeployBeanPropertyAssocOne<?> beanProp, boolean outerJoin) {
|
||||
|
||||
DeployTableJoin tableJoin = beanProp.getTableJoin();
|
||||
tableJoin.setType(outerJoin ? SqlJoinType.OUTER : SqlJoinType.INNER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add named RawSql from ebean.xml.
|
||||
*/
|
||||
|
||||
@@ -213,7 +213,6 @@ public class DeployCreateProperties {
|
||||
} catch (NullPointerException e) {
|
||||
logger.debug("expected non-scalar type {}", e.getMessage());
|
||||
}
|
||||
// TODO: Handle Collection of CompoundType and Embedded Type
|
||||
return new DeployBeanPropertyAssocMany(desc, targetType, manyType);
|
||||
}
|
||||
|
||||
|
||||
@@ -123,11 +123,14 @@ public class DeployInherit {
|
||||
info.setColumnDefn(da.columnDefinition());
|
||||
}
|
||||
|
||||
DiscriminatorValue dv = AnnotationUtil.findAnnotation(cls, DiscriminatorValue.class); // do not search recursive
|
||||
if (dv != null) {
|
||||
info.setDiscriminatorValue(dv.value());
|
||||
if (!info.isAbstract()) {
|
||||
DiscriminatorValue dv = AnnotationUtil.findAnnotation(cls, DiscriminatorValue.class); // do not search recursive
|
||||
if (dv != null) {
|
||||
info.setDiscriminatorValue(dv.value());
|
||||
} else {
|
||||
info.setDiscriminatorValue(cls.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user