Compare commits

..
Author SHA1 Message Date
rob bygrave 20d538c428 [maven-release-plugin] prepare release ebean-12.1.3 2019-11-07 16:43:15 +13:00
rob bygrave 01054bf470 #1855 - Add MetaInfoManager collectMetricsAsJson() 2019-11-07 16:35:25 +13:00
rob bygrave affd720390 #1854 - Use profile location label to automatically give good metric name for @Transactional (with profile location enhancement) 2019-11-07 11:18:26 +13:00
rob bygrave 7d77df58c5 #1780 - Add tests for this RawSql case 2019-11-06 23:30:21 +13:00
rob bygrave 51fad2dec6 Refactor CQueryBuilder extract method, no effective change. 2019-11-06 23:29:33 +13:00
rob bygrave 997378ce72 Refactor internal XServiceProvider service loading 2019-11-06 22:01:37 +13:00
rob bygrave 1a8a43603e #1849 - JoinColumn not found when using @Encrypted(dbEncryption = true) and findCount 2019-11-06 20:33:56 +13:00
rob bygrave d2c88bf7c6 #1853 - Classpath problem with avaje-classpath-scanner 2019-11-06 19:53:35 +13:00
rob bygrave 0929147b08 No effective change - tidy test TestQueryFindPagedList 2019-10-18 17:23:31 +13:00
rob bygrave ecab219635 #1848 - ENH: Add EmptyPagedList ... as helper implementation of PagedList<T> 2019-10-18 17:22:19 +13:00
rob bygrave 7a974320ed #1846 - Test for - Inheritance cascade of fields with bean inheritance
Note this includes the workaround RootBean.preRemove(), so doesn't fail
2019-10-18 16:20:38 +13:00
rob bygrave 1b7dc83a9a ENH: Add DB.getBeanId() ... to match database.getBeanId() 2019-10-18 16:08:42 +13:00
rob bygrave 9827d9056f No effective change - add delete batch test with M2M generated sql 2019-10-18 15:51:18 +13:00
rob bygrave c8d5d9b552 No effective change - update test 2019-10-18 15:38:50 +13:00
rob bygrave dac55ab8e9 #1847 - Delete bean with ManyToMany to always delete from intersection (not required cascade REMOVE) 2019-10-18 15:35:21 +13:00
rob bygrave de75bcdaf9 [maven-release-plugin] prepare for next development iteration 2019-10-17 15:25:28 +13:00
rob bygrave 5785124a2b [maven-release-plugin] prepare release ebean-12.1.2 2019-10-17 15:25:18 +13:00
rob bygrave 4d1c0bfaa8 Change parent to java8-oss 2.1 2019-10-17 15:23:35 +13:00
rob bygrave 9a29c988cd #1845 - Refactor for Persisting ElementCollection to limit max JDBC batch size 2019-10-17 15:18:43 +13:00
rob bygrave 80309f844e #1844 - Persisting an update of ElementCollection with JDBC batch, ensure delete, insert batch order 2019-10-17 12:32:13 +13:00
rob bygrave 56ddc49ab4 Element collection persist - initial 2019-10-17 10:17:03 +13:00
rob bygrave 1e7de83019 Element collection persist - initial 2019-10-17 10:15:58 +13:00
rob bygrave 1db1f0312a No effective change - bump provided jaxb-runtime dependency to 2.3.2 2019-10-14 16:03:56 +13:00
rob bygrave d27bf14887 touch readme 2019-10-14 09:21:41 +13:00
rob bygrave d4e7d4eebc [maven-release-plugin] prepare for next development iteration 2019-10-13 12:22:55 +13:00
rob bygrave 30c44d0928 [maven-release-plugin] prepare release ebean-12.1.1 2019-10-13 12:22:44 +13:00
rob bygrave 443c5dba58 Bump ebean-migration version 2019-10-13 12:20:59 +13:00
rob bygrave f24b4dba71 #1837 (#1740 and #1744) - Inheritance on both sides of @ManyToOne
This change also relates back to #1740 and #1744 and changes the way that issue was fixed (by SqlTreeNodeBean on partially populated non-id inheritance bean).
2019-10-13 11:46:11 +13:00
Rob BygraveandGitHub a474880735 #1837 - Inheritance on both sides of @ManyToOne confuses columns (#1843)
* #1837 - Inheritance on both sides of @ManyToOne confuses columns

* #1837 - Inheritance on both sides of @ManyToOne confuses columns

Tidy up whitespace in SQL
2019-10-12 00:25:53 +13:00
rob bygrave 4cfab3a2de #1842 - Refactor persisting to delay the setting of generated Id value (sequences and deferred imported ids) 2019-10-11 20:50:47 +13:00
rob bygrave 4807070147 #1841 - SqlServer findCount with @Aggregation gives SQLException:No column name was specified for column 2 of 'c' 2019-10-11 20:26:35 +13:00
rob bygrave e00aa479ae #1841 - SqlServer findCount with @Aggregation gives SQLException:No column name was specified for column 2 of 'c' 2019-10-11 20:18:18 +13:00
rob bygrave b665d8375c #1831 - Update query with maxRows does not include the limit in the generated SQL 2019-10-11 17:01:30 +13:00
Rob BygraveandGitHub 4e74c68603 Merge pull request #1840 from FOCONIS/exists-use-query-copy
exists() limits the max rows on the query, this should be done on a copy
2019-10-10 23:28:48 +13:00
Manuel Danisch 2694aa3fe1 Fixed test 2019-10-10 11:28:24 +02:00
rob bygrave f7a28aa904 Javadoc fixes 2019-10-10 22:07:14 +13:00
rob bygrave 4bc2559e8e Refactor tidy PersistRequestBean 2019-10-10 21:51:31 +13:00
rob bygrave c2faf0e0a7 No effective change - tidy tests 2019-10-10 21:42:34 +13:00
Manuel Danisch 895b331e8f exists() limits the max rows on the query, this should be done on a copy 2019-10-10 10:42:04 +02:00
rob bygrave 8890079b80 #1839 - Fix JSON marshalling of @ElementCollection maps and scalar 2019-10-10 21:32:44 +13:00
rob bygrave 20dd33ebca Fix DbMigration test for MySql without I__create_procs.sql 2019-10-10 19:07:36 +13:00
rob bygrave e2a1489743 Fix DbMigration test for MySql without I__create_procs.sql 2019-10-10 15:26:50 +13:00
rob bygrave d6bab46ac3 #1838 - Fix for Joda LocalTime timezone handling for MySql 2019-10-10 15:16:12 +13:00
rob bygrave 573673ef2b Fix for tests, make MySql like escape consistent with others 2019-10-10 15:14:38 +13:00
rob bygrave eb69499ca3 #1835 Fix with Refactor batch escalation for deferred imported Id 2019-10-10 15:13:21 +13:00
rob bygrave d9323b8ab6 Fix DbMigration test for MySql without I__create_procs.sql 2019-10-10 15:11:25 +13:00
rob bygrave a01ba128e5 Fix test for TestDbArray_basic.nullItems() for DB's that don't support ARRAY type 2019-10-10 15:10:44 +13:00
rob bygrave fdc67b3ce5 #1823 - ENH: Add query.findStream() and query.findLargeStream() 2019-10-10 13:44:29 +13:00
rob bygrave 5d433ff603 Make it easier for people building using JDK11
Note that our javadoc doclet isn't JDK11 compatible yet
2019-10-10 12:04:09 +13:00
rob bygrave a3f9c4c889 #1836 - NPE with findCount() on bean with @Aggregation, Regression in 11.45.1 2019-10-10 08:55:01 +13:00
Rob BygraveandGitHub 769fc485ec Merge pull request #1835 from ebean-orm/feature/persistCascadeEscalate
Refactor JDBC batch escalation and detection of deferred persist relationship (circular imported ids)
2019-10-09 17:08:09 +13:00
rob bygrave 61ef4fa420 #1835 Refactor JDBC batch escalation 2019-10-09 00:37:06 +13:00
rob bygrave 8cf37e2f0a #1835 Refactor JDBC batch escalation 2019-10-09 00:18:15 +13:00
rob bygrave 80a764a448 #1835 Refactor JDBC batch escalation 2019-10-09 00:17:34 +13:00
rob bygrave 6621c5dfbb Refactor detection of deferred persist relationship (circular imported ids) 2019-10-08 21:49:22 +13:00
rob bygrave 5e7dda1ec8 #1834 - DDL for ElementCollection that is referenced multiple times includes foreign key constraint 2019-10-08 15:53:29 +13:00
rob bygrave 7bc6368f69 #1833 - When cascade persisting twice to ElementCollection ... we get do extra delete and inserts 2019-10-08 13:25:33 +13:00
rob bygrave fb5a69adea #1833 - When cascade persisting twice to ElementCollection ... we get do extra delete and inserts 2019-10-08 13:25:18 +13:00
rob bygrave 0e7607bb27 Log warning when initSql or seedSql is not found as a resource 2019-10-08 09:15:20 +13:00
rob bygrave 1098b572fe #1832 - Cascade insert @ElementCollection when marshalled from JSON 2019-10-07 15:48:31 +13:00
rob bygrave 7a6662a2dc #1830 - Bump ebean-migration dependency to 11.22.1 2019-10-07 13:49:20 +13:00
rob bygrave 1c6c812d4d Update tests change AssertJ StrictAssertions to Assertions 2019-10-06 16:34:44 +13:00
rob bygrave e58a85f0af #1356 - allQuotedIdentifiers must be respected by DDL 2019-10-06 16:32:06 +13:00
rob bygrave e3551152bb #1829 - Bump Jackson core dependency from 2.9.9 to 2.10.0 2019-10-05 23:41:16 +13:00
rob bygrave 5138d1723f Merge branch 'feature/1656' 2019-10-05 23:20:26 +13:00
rob bygrave 9b5ab542d7 #1827 - @UniqueConstraint columnNames should be respecting ebean.allQuotedIdentifiers 2019-10-05 22:37:06 +13:00
rob bygrave f0bc4c786c #1826 - Remove PrivateOwned, migrate to orphanRemoval=true attribute on OneToMany 2019-09-24 22:36:17 +12:00
rob bygrave b98739bb49 #1656 #1824 Stateless updates - Remove update deleteMissingChildren option, instead always use orphanRemoval
Adjust test that doesn't have orphanRemoval (so missing children are not removed here now)
2019-09-20 17:18:07 +12:00
rob bygrave fcadc63338 #1656 #1824 Stateless updates - Remove update deleteMissingChildren option, instead always use orphanRemoval 2019-09-20 17:12:32 +12:00
rob bygrave 3574479627 #1821 - Remove serverConfig updateChangesOnly mode ... as always true 2019-09-15 21:29:25 +12:00
rob bygrave cecf2fc650 [maven-release-plugin] prepare for next development iteration 2019-09-12 23:09:28 +12:00
rob bygrave a1dab24414 [maven-release-plugin] prepare release ebean-11.45.1 2019-09-12 23:09:17 +12:00
rob bygrave c12ea28ea9 #1818 - Cannot auto-generate UUID 2019-09-12 22:02:22 +12:00
rob bygrave a2b418df8b #1820 - ENH: For DB Migration add setLockTimeout(10); ... to add Postgres set lock_timeout = to generated migrations 2019-09-12 20:59:52 +12:00
rob bygrave 2fb533a606 #1819 - ENH: For DB Migration support dbMigration.setAddForeignKeySkipCheck(true); 2019-09-12 20:34:37 +12:00
rob bygrave 5d89897bbd #1817 - Rename @DbHstore to @DbMap 2019-09-12 17:02:53 +12:00
rob bygrave 9640e02b6a #1817 - Rename @DbHstore to @DbMap 2019-09-12 16:58:15 +12:00
rob bygrave 8a0ef33f79 git push origin masterMerge branch 'Ryszard-Trojnacki-master' 2019-09-12 14:55:50 +12:00
rob bygrave d2ece76ca8 #1816 Fix for OneToMany relation to non-cached target bean. 2019-09-12 14:55:37 +12:00
rob bygrave 3495d03706 Merge branch 'master' of https://github.com/Ryszard-Trojnacki/avaje-ebeanorm into Ryszard-Trojnacki-master 2019-09-12 13:58:50 +12:00
Rob BygraveandGitHub bc90fa50d7 #1802 - Completely remove MySQL init stored procedures for drop column with foreign key (#1815) 2019-09-12 09:34:14 +12:00
Ryszard Trojnacki 777fc0ff30 Merge branch 'ebean-orm-master' 2019-09-11 14:24:04 +02:00
Ryszard Trojnacki 9413f72939 Merge from origin. 2019-09-11 14:22:47 +02:00
Ryszard Trojnacki 1416647d04 Test case for changing OneToMany collection in a cached bean, by editing target bean that is not cached (annotated with @Cache). 2019-09-11 14:05:31 +02:00
rob bygrave 63bddb4d54 #1810 - Whan paging over query with @Aggregation ( grouping by) wrong total count is calculated 2019-09-11 23:38:12 +12:00
rob bygrave 6fddd418a1 #1811 Fix for Entity with EmbeddedId not found in cache 2019-09-11 22:28:22 +12:00
RienandRob Bygrave faba55d772 Fix EmbeddedId not found cache (#1811) 2019-09-11 22:06:05 +12:00
rob bygrave ac6db11e3d #1802 - Change MySQL DDL to not use helper stored procedures by default - was auto-generated script containing create procedures fails 2019-09-11 22:04:30 +12:00
rob bygrave ca7d8ecfc1 #1814 - Support older MySQL 5.5 2019-09-11 21:38:16 +12:00
rob bygrave 46eeee68c9 #1814 - Support older MySQL 5.5 2019-09-11 21:37:52 +12:00
rob bygrave c1487daea8 #1812 No effective change - Format code only 2019-09-11 15:33:21 +12:00
Ryszard TrojnackiandRob Bygrave b329ae42db Allow for null items in @DbArray lists. (#1812)
* Added code to allow for null items in @DbArray lists.

* Test case for changing OneToMany collection by editing target bean.
2019-09-11 15:30:32 +12:00
Ryszard Trojnacki 13429daddc Test case for changing OneToMany collection by editing target bean. 2019-09-10 20:43:31 +02:00
Ryszard Trojnacki 98a80295fe Added code to allow for null items in @DbArray lists. 2019-09-10 20:07:33 +02:00
Ryszard TrojnackiandGitHub f8587368ee Merge pull request #2 from ebean-orm/master
Update to master
2019-09-10 19:39:27 +02:00
rob bygrave 8ae2ae7f8b [maven-release-plugin] prepare for next development iteration 2019-08-31 00:16:48 +12:00
331 changed files with 5496 additions and 1827 deletions
-1
View File
@@ -44,4 +44,3 @@ Goto [https://ebean.io/docs/](https://ebean.io/docs/)
## Other versions
* [![Maven Central : ebean-agent](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent) - ebean-agent
* [![Maven Central : ebean-maven-plugin](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin) - ebean-maven-plugin
+15 -20
View File
@@ -3,27 +3,27 @@
<parent>
<groupId>org.avaje</groupId>
<artifactId>java8-parent</artifactId>
<version>1.3</version>
<artifactId>java8-oss</artifactId>
<version>2.1</version>
</parent>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.44.1</version>
<version>12.1.3</version>
<packaging>jar</packaging>
<name>ebean</name>
<url>http://ebean-orm.github.io/</url>
<properties>
<jackson-core.version>2.9.9</jackson-core.version>
<jackson-databind.version>2.9.9.2</jackson-databind.version>
<jackson-core.version>2.10.0</jackson-core.version>
<jackson-databind.version>2.10.0</jackson-databind.version>
<snakeyaml.version>1.21</snakeyaml.version>
</properties>
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-11.44.1</tag>
<tag>ebean-12.1.3</tag>
</scm>
<profiles>
@@ -118,7 +118,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>4.12</version>
<version>6.1</version>
</dependency>
<dependency>
@@ -133,16 +133,10 @@
<version>4.6.1</version>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-classpath-scanner</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration</artifactId>
<version>11.20.1</version>
<version>12.1.1</version>
</dependency>
<dependency>
@@ -199,7 +193,7 @@
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.4.0-b180830.0438</version>
<version>2.3.2</version>
<scope>provided</scope>
</dependency>
@@ -301,10 +295,9 @@
<dependency>
<groupId>org.avaje.composite</groupId>
<artifactId>avaje-composite-testing</artifactId>
<artifactId>junit</artifactId>
<version>1.1</version>
<scope>test</scope>
<type>pom</type>
</dependency>
<dependency>
@@ -330,7 +323,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>11.44.1</version>
<version>12.1.1</version>
<executions>
<execution>
<id>test</id>
@@ -387,9 +380,9 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<version>3.1.1</version>
<configuration>
<doctitle>Ebean 11</doctitle>
<doctitle>Ebean 12</doctitle>
<overview>src/main/java/io/ebean/overview.html</overview>
<source>1.8</source>
<doclet>org.avaje.doclet.PygmentsDoclet</doclet>
@@ -399,6 +392,8 @@
<artifactId>pygments-doclet</artifactId>
<version>1.0.0</version>
</docletArtifact>
<!-- for people using JDK11, our javadoc doclet isn't 11 compatible yet -->
<!-- <javadocExecutable>${env.JAVA8_HOME}/bin/javadoc</javadocExecutable>-->
<additionalparam>
-Xdoclint:none
-include-basedir ${project.basedir}
+7 -13
View File
@@ -2,7 +2,6 @@ package io.ebean;
import io.ebean.annotation.TxIsolation;
import io.ebean.cache.ServerCacheManager;
import io.ebean.config.DatabaseConfig;
import io.ebean.plugin.Property;
import io.ebean.text.csv.CsvReader;
import io.ebean.text.json.JsonContext;
@@ -396,15 +395,6 @@ public class DB {
* called then no optimistic locking is performed (internally ConcurrencyMode.NONE is used).
* </p>
* <p>
* <b>{@link DatabaseConfig#setUpdatesDeleteMissingChildren(boolean)}: </b> When cascade saving to a
* OneToMany or ManyToMany the updatesDeleteMissingChildren setting controls if any other children
* that are in the database but are not in the collection are deleted.
* </p>
* <p>
* <b>{@link DatabaseConfig#setUpdateChangesOnly(boolean)}: </b> The updateChangesOnly setting
* controls if only the changed properties are included in the update or if all the loaded
* properties are included instead.
* </p>
* <pre>{@code
*
* // A 'stateless update' example
@@ -414,9 +404,6 @@ public class DB {
* database.update(customer);
*
* }</pre>
*
* @see DatabaseConfig#setUpdatesDeleteMissingChildren(boolean)
* @see DatabaseConfig#setUpdateChangesOnly(boolean)
*/
public static void update(Object bean) throws OptimisticLockException {
getDefault().update(bean);
@@ -1215,6 +1202,13 @@ public class DB {
return getDefault().getBeanState(bean);
}
/**
* Return the value of the Id property for a given bean.
*/
public static Object getBeanId(Object bean) {
return getDefault().getBeanId(bean);
}
/**
* Return the manager of the level 2 cache ("L2" cache).
*/
-24
View File
@@ -1196,17 +1196,6 @@ public interface Database {
* <b>Optimistic Locking: </b> Note that if the version property is not set when update() is
* called then no optimistic locking is performed (internally ConcurrencyMode.NONE is used).
* </p>
* <p>
* <b>{@link ServerConfig#setUpdatesDeleteMissingChildren(boolean)}: </b> When cascade saving to a
* OneToMany or ManyToMany the updatesDeleteMissingChildren setting controls if any other children
* that are in the database but are not in the collection are deleted.
* </p>
* <p>
* <b>{@link ServerConfig#setUpdateChangesOnly(boolean)}: </b> The updateChangesOnly setting
* controls if only the changed properties are included in the update or if all the loaded
* properties are included instead.
* </p>
* <p>
* <pre>{@code
*
* // A 'stateless update' example
@@ -1216,9 +1205,6 @@ public interface Database {
* database.update(customer);
*
* }</pre>
*
* @see ServerConfig#setUpdatesDeleteMissingChildren(boolean)
* @see ServerConfig#setUpdateChangesOnly(boolean)
*/
void update(Object bean) throws OptimisticLockException;
@@ -1227,16 +1213,6 @@ public interface Database {
*/
void update(Object bean, Transaction transaction) throws OptimisticLockException;
/**
* Update a bean additionally specifying a transaction and the deleteMissingChildren setting.
*
* @param bean the bean to update
* @param transaction the transaction to use (can be null).
* @param deleteMissingChildren specify false if you do not want 'missing children' of a OneToMany
* or ManyToMany to be automatically deleted.
*/
void update(Object bean, Transaction transaction, boolean deleteMissingChildren) throws OptimisticLockException;
/**
* Update a collection of beans. If there is no current transaction one is created and used to
* update all the beans in the collection.
-3
View File
@@ -548,9 +548,6 @@ public final class Ebean {
* DB.update(customer);
*
* }</pre>
*
* @see ServerConfig#setUpdatesDeleteMissingChildren(boolean)
* @see ServerConfig#setUpdateChangesOnly(boolean)
*/
public static void update(Object bean) throws OptimisticLockException {
serverMgr.getDefaultServer().update(bean);
@@ -0,0 +1,74 @@
package io.ebean;
import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Future;
/**
* An empty PagedList.
* <p>
* For use in application code when we need to return a PagedList but don't want to
* execute a query.
* </p>
*
* <pre>{@code
*
* PagedList<Customer> empty = PagedList.emptyList();
*
* }</pre>
*/
public class EmptyPagedList<T> implements PagedList<T> {
@Override
public void loadCount() {
// do nothing
}
@Nonnull
@Override
public Future<Integer> getFutureCount() {
return null;
}
@Nonnull
@Override
public List<T> getList() {
return Collections.emptyList();
}
@Override
public int getTotalCount() {
return 0;
}
@Override
public int getTotalPageCount() {
return 0;
}
@Override
public int getPageSize() {
return 0;
}
@Override
public int getPageIndex() {
return 0;
}
@Override
public boolean hasNext() {
return false;
}
@Override
public boolean hasPrev() {
return false;
}
@Override
public String getDisplayXtoYofZ(String to, String of) {
return "";
}
}
@@ -13,6 +13,7 @@ import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* The extended API for Database.
@@ -107,6 +108,24 @@ public interface ExtendedServer {
@Nonnull
<T> QueryIterator<T> findIterate(Query<T> query, Transaction transaction);
/**
* Return the query result as a Stream using a single persistence context.
* <p>
* Note that the stream needs to be closed so use with try with resources.
* </p>
*/
@Nonnull
<T> Stream<T> findStream(Query<T> query, Transaction transaction);
/**
* Return the query result as a Stream (with multiple persistence contexts).
* <p>
* Note that the stream needs to be closed so use with try with resources.
* </p>
*/
@Nonnull
<T> Stream<T> findLargeStream(Query<T> query, Transaction transaction);
/**
* Execute the query visiting the each bean one at a time.
* <p>
+7
View File
@@ -60,6 +60,13 @@ import java.util.concurrent.Future;
*/
public interface PagedList<T> {
/**
* Return an empty PagedList.
*/
static <B> PagedList<B> emptyList() {
return new EmptyPagedList<>();
}
/**
* Initiate the loading of the total row count in the background.
* <pre>{@code
+44
View File
@@ -11,6 +11,7 @@ import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* Object relational query for finding a List, Set, Map or single entity bean.
@@ -702,6 +703,49 @@ public interface Query<T> {
@Nonnull
QueryIterator<T> findIterate();
/**
* Execute the query returning the result as a Stream.
* <p>
* Note that this will hold all resulting beans in memory using a single
* persistence context. Use findLargeStream() for queries that expect to
* return a large number of results.
* </p>
* <pre>{@code
*
* // use try with resources to ensure Stream is closed
*
* try (Stream<Customer> stream = query.findStream()) {
* stream
* .map(...)
* .collect(...);
* }
*
* }</pre>
*/
@Nonnull
Stream<T> findStream();
/**
* Execute the query returning the result as a Stream.
* <p>
* Note that this uses multiple persistence contexts such that we can use
* it with a large number of results.
* </p>
* <pre>{@code
*
* // use try with resources to ensure Stream is closed
*
* try (Stream<Customer> stream = query.findLargeStream()) {
* stream
* .map(...)
* .collect(...);
* }
*
* }</pre>
*/
@Nonnull
Stream<T> findLargeStream();
/**
* Execute the query processing the beans one at a time.
* <p>
+7 -13
View File
@@ -20,29 +20,23 @@ class XServiceProvider {
private static SpiFetchGroupService fetchGroupService = initSpiFetchGroupService();
private static SpiFetchGroupService initSpiFetchGroupService() {
Iterator<SpiFetchGroupService> loader = ServiceLoader.load(SpiFetchGroupService.class).iterator();
if (loader.hasNext()) {
return loader.next();
}
throw new IllegalStateException("No service implementation found for SpiFetchGroupService?");
return loadFirstService(SpiFetchGroupService.class);
}
private static SpiRawSqlService initRawSql() {
Iterator<SpiRawSqlService> loader = ServiceLoader.load(SpiRawSqlService.class).iterator();
if (loader.hasNext()) {
return loader.next();
}
throw new IllegalStateException("No service implementation found for SpiRawSqlService?");
return loadFirstService(SpiRawSqlService.class);
}
private static SpiProfileLocationFactory initProfileLocation() {
return loadFirstService(SpiProfileLocationFactory.class);
}
Iterator<SpiProfileLocationFactory> loader = ServiceLoader.load(SpiProfileLocationFactory.class).iterator();
private static <T> T loadFirstService(Class<T> cls) {
Iterator<T> loader = ServiceLoader.load(cls).iterator();
if (loader.hasNext()) {
return loader.next();
}
throw new IllegalStateException("No service implementation found for SpiProfileLocationFactory?");
throw new IllegalStateException("No service implementation found for " + cls);
}
/**
@@ -83,10 +83,13 @@ public abstract class AbstractNamingConvention implements NamingConvention {
}
@Override
public String getSequenceName(String tableName, String pkColumn) {
String s = TABLE_REPLACE.matcher(sequenceFormat).replaceAll(Matcher.quoteReplacement(tableName));
public String getSequenceName(String rawTableName, String pkColumn) {
final String tableNameUnquoted = databasePlatform.unQuote(rawTableName);
String s = TABLE_REPLACE.matcher(sequenceFormat).replaceAll(Matcher.quoteReplacement(tableNameUnquoted));
if (pkColumn == null) {
pkColumn = "";
} else {
pkColumn = databasePlatform.unQuote(pkColumn);
}
return COLUMN_REPLACE.matcher(s).replaceAll(Matcher.quoteReplacement(pkColumn));
}
@@ -36,8 +36,7 @@ public class MatchingNamingConvention extends AbstractNamingConvention {
@Override
public TableName getTableNameByConvention(Class<?> beanClass) {
return new TableName(getCatalog(), getSchema(), beanClass.getSimpleName());
return new TableName(quoteIdentifiers(getCatalog()), quoteIdentifiers(getSchema()), quoteIdentifiers(beanClass.getSimpleName()));
}
@Override
@@ -350,21 +350,11 @@ public class ServerConfig {
*/
private DbConstraintNaming constraintNaming = new DbConstraintNaming();
/**
* Behaviour of update to include on the change properties.
*/
private boolean updateChangesOnly = true;
/**
* Behaviour of updates in JDBC batch to by default include all properties.
*/
private boolean updateAllPropertiesInBatch;
/**
* Default behaviour for updates when cascade save on a O2M or M2M to delete any missing children.
*/
private boolean updatesDeleteMissingChildren = true;
/**
* Database platform configuration.
*/
@@ -2409,20 +2399,6 @@ public class ServerConfig {
this.skipCacheAfterWrite = skipCacheAfterWrite;
}
/**
* Return true to only update changed properties.
*/
public boolean isUpdateChangesOnly() {
return updateChangesOnly;
}
/**
* Set to true to only update changed properties.
*/
public void setUpdateChangesOnly(boolean updateChangesOnly) {
this.updateChangesOnly = updateChangesOnly;
}
/**
* Returns true if updates in JDBC batch default to include all properties by default.
*/
@@ -2442,22 +2418,6 @@ public class ServerConfig {
this.updateAllPropertiesInBatch = updateAllPropertiesInBatch;
}
/**
* Return true if updates by default delete missing children when cascading save to a OneToMany or
* ManyToMany. When not set this defaults to true.
*/
public boolean isUpdatesDeleteMissingChildren() {
return updatesDeleteMissingChildren;
}
/**
* Set if updates by default delete missing children when cascading save to a OneToMany or
* ManyToMany. When not set this defaults to true.
*/
public void setUpdatesDeleteMissingChildren(boolean updatesDeleteMissingChildren) {
this.updatesDeleteMissingChildren = updatesDeleteMissingChildren;
}
/**
* Return true if query statistics should be collected by ObjectGraphNode.
*/
@@ -2966,10 +2926,6 @@ public class ServerConfig {
skipCacheAfterWrite = p.getBoolean("skipCacheAfterWrite", skipCacheAfterWrite);
updateAllPropertiesInBatch = p.getBoolean("updateAllPropertiesInBatch", updateAllPropertiesInBatch);
updateChangesOnly = p.getBoolean("updateChangesOnly", updateChangesOnly);
boolean defaultDeleteMissingChildren = p.getBoolean("defaultDeleteMissingChildren", updatesDeleteMissingChildren);
updatesDeleteMissingChildren = p.getBoolean("updatesDeleteMissingChildren", defaultDeleteMissingChildren);
if (p.get("batch.mode") != null || p.get("persistBatching") != null) {
throw new IllegalArgumentException("Property 'batch.mode' or 'persistBatching' is being set but no longer used. Please change to use 'persistBatchMode'");
@@ -75,6 +75,11 @@ public class DatabasePlatform {
protected boolean caseSensitiveCollation = true;
/**
* Set true if the Database support LIMIT clause on sql update.
*/
protected boolean inlineSqlUpdateLimit;
/**
* For limit/offset, row_number etc limiting of SQL queries.
*/
@@ -143,8 +148,6 @@ public class DatabasePlatform {
protected String columnAliasPrefix = "c";
protected String tableAliasPlaceHolder = "${ta}";
/**
* Use a BackTick ` at the beginning and end of table or column names that you
* want to use quoted identifiers for. The backticks get converted to the
@@ -171,13 +174,14 @@ public class DatabasePlatform {
/**
* Characters escaped for startsWith, endsWith and contains.
*/
protected char[] likeSpecialCharacters = { '%', '_', '|' };
protected char[] likeSpecialCharacters = {'%', '_', '|'};
protected DbEncrypt dbEncrypt;
protected boolean idInExpandedForm;
protected boolean selectCountWithAlias;
protected boolean selectCountWithColumnAlias;
/**
* If set then use the FORWARD ONLY hint when creating ResultSets for
@@ -187,7 +191,7 @@ public class DatabasePlatform {
/**
* If set then use the CONCUR_UPDATABLE hint when creating ResultSets.
*
* <p>
* This is {@code false} for HANA
*/
protected boolean supportsResultSetConcurrencyModeUpdatable = true;
@@ -339,6 +343,13 @@ public class DatabasePlatform {
return supportsSavepointId;
}
/**
* Return true if the platform supports LIMIT with sql update.
*/
public boolean isInlineSqlUpdateLimit() {
return inlineSqlUpdateLimit;
}
/**
* Return the maximum table name length.
* <p>
@@ -370,11 +381,11 @@ public class DatabasePlatform {
/**
* Return a DB Sequence based IdGenerator.
*
* @param be the BackgroundExecutor that can be used to load the sequence if
* desired
* @param ds the DataSource
* @param stepSize the sequence allocation size as defined by mapping (defaults to 50)
* @param seqName the name of the sequence
* @param be the BackgroundExecutor that can be used to load the sequence if
* desired
* @param ds the DataSource
* @param stepSize the sequence allocation size as defined by mapping (defaults to 50)
* @param seqName the name of the sequence
*/
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return null;
@@ -466,20 +477,6 @@ public class DatabasePlatform {
this.columnAliasPrefix = columnAliasPrefix;
}
/**
* Return the table alias placeholder.
*/
public String getTableAliasPlaceHolder() {
return tableAliasPlaceHolder;
}
/**
* Set the table alias placeholder.
*/
public void setTableAliasPlaceHolder(String tableAliasPlaceHolder) {
this.tableAliasPlaceHolder = tableAliasPlaceHolder;
}
/**
* Return the close quote for quoted identifiers.
*/
@@ -659,7 +656,7 @@ public class DatabasePlatform {
if (dbName != null && !dbName.isEmpty()) {
if (dbName.startsWith(openQuote)) {
// trim off the open and close quotes
return dbName.substring(1, dbName.length()-1);
return dbName.substring(1, dbName.length() - 1);
}
}
return dbName;
@@ -672,6 +669,14 @@ public class DatabasePlatform {
return selectCountWithAlias;
}
/**
* Return true if select count with subquery needs column alias (SQL Server).
*/
public boolean isSelectCountWithColumnAlias() {
return selectCountWithColumnAlias;
}
public String completeSql(String sql, Query<?> query) {
if (query.isForUpdate()) {
sql = withForUpdate(sql, query.getForUpdateMode());
@@ -786,7 +791,7 @@ public class DatabasePlatform {
for (int i = 0; i < value.length(); i++) {
char ch = value.charAt(i);
boolean escaped = false;
for (char escapeChar: likeSpecialCharacters) {
for (char escapeChar : likeSpecialCharacters) {
if (ch == escapeChar) {
if (sb == null) {
sb = new StringBuilder(value.substring(0, i));
@@ -0,0 +1,4 @@
/**
* Clickhouse DB specific support.
*/
package io.ebean.config.dbplatform.clickhouse;
@@ -0,0 +1,4 @@
/**
* Cockroach DB specific support.
*/
package io.ebean.config.dbplatform.cockroach;
@@ -24,6 +24,7 @@ public class H2Platform extends DatabasePlatform {
this.historySupport = new H2HistorySupport();
this.nativeUuidType = true;
this.supportsDeleteTableAlias = true;
this.inlineSqlUpdateLimit = true;
this.dbDefaultValue.setNow("now()");
this.columnAliasPrefix = null;
this.exceptionTranslator =
@@ -0,0 +1,14 @@
package io.ebean.config.dbplatform.mysql;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
public class MySql55Platform extends MySqlPlatform {
public MySql55Platform() {
super();
this.platform = Platform.MYSQL55;
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime"));
}
}
@@ -28,6 +28,7 @@ public class MySqlPlatform extends DatabasePlatform {
this.useExtraTransactionOnIterateSecondaryQueries = true;
this.selectCountWithAlias = true;
this.supportsSavepointId = false;
this.inlineSqlUpdateLimit = true;
this.dbEncrypt = new MySqlDbEncrypt();
this.historySupport = new MySqlHistorySupport();
this.columnAliasPrefix = null;
@@ -51,8 +52,8 @@ public class MySqlPlatform extends DatabasePlatform {
this.openQuote = "`";
this.closeQuote = "`";
// use pipe for escaping as it depends if mysql runs in no_backslash_escapes or not.
this.likeClauseRaw = "like ? escape ''";
this.likeClauseEscaped = "like ? escape '|'";
this.likeClauseRaw = "like ? escape''";
this.likeClauseEscaped = "like ? escape'|'";
this.forwardOnlyHintOnFindIterate = true;
this.booleanDbType = Types.BIT;
@@ -0,0 +1,4 @@
/**
* NuoDB specific support.
*/
package io.ebean.config.dbplatform.nuodb;
@@ -28,6 +28,7 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
this.persistBatchOnCascade = PersistBatch.NONE;
this.idInExpandedForm = true;
this.selectCountWithAlias = true;
this.selectCountWithColumnAlias = true;
this.sqlLimiter = new SqlServerSqlLimiter();
this.basicSqlLimiter = new SqlServerBasicSqlLimiter();
this.historySupport = new SqlServerHistorySupport();
@@ -136,6 +136,23 @@ public interface DbMigration {
*/
void setGeneratePendingDrop(String generatePendingDrop);
/**
* Set to true if ALTER TABLE ADD FOREIGN KEY should be generated with an option to skip validation.
* <p>
* Currently this is only useful for Postgres DDL adding the <code>NOT VALID</code> option.
* </p>
*/
void setAddForeignKeySkipCheck(boolean addForeignKeySkipCheck);
/**
* Set the lock timeout to be included with the DDL generation.
* <p>
* Currently this is only useful for Postgres migrations adding a <code>set lock_timeout</code>
* statement to the generated database migration.
* </p>
*/
void setLockTimeout(int seconds);
/**
* Add an additional platform to write the migration DDL.
* <p>
@@ -198,6 +215,7 @@ public interface DbMigration {
* An "init" migration can only be executed and used on a database that has had no
* prior migrations run on it.
* </p>
*
* @return the version of the generated migration
*/
String generateInitMigration() throws IOException;
@@ -41,6 +41,11 @@ public interface BeanPersistRequest<T> {
*/
Set<String> getUpdatedProperties();
/**
* Flags set for dirty properties (used by ElasticSearch integration).
*/
boolean[] getDirtyProperties();
/**
* Return true for an update request if at least one of dirty properties is contained
* in the given set of property names.
@@ -16,6 +16,18 @@ public interface MetaInfoManager {
*/
ServerMetrics collectMetrics();
/**
* Collect the metrics in raw JSON form.
* <pre>{@code
*
* String metricsJson = database.getMetaInfoManager()
* .collectMetricsAsJson()
* .json();
*
* }</pre>
*/
ServerMetricsAsJson collectMetricsAsJson();
/**
* Collect query plans.
*/
@@ -2,6 +2,9 @@ package io.ebean.meta;
import io.ebean.ProfileLocation;
/**
* Meta data for captured query plan.
*/
public interface MetaQueryPlan {
/**
@@ -0,0 +1,40 @@
package io.ebean.meta;
import java.util.Comparator;
/**
* Collect the metrics in raw JSON form.
*/
public interface ServerMetricsAsJson {
/**
* Set to false to exclude profile location.
*/
ServerMetricsAsJson withLocation(boolean withLocation);
/**
* Set to false to exclude SQL for query metrics.
*/
ServerMetricsAsJson withSql(boolean withSql);
/**
* Set to false to exclude SQL hash.
*/
ServerMetricsAsJson withHash(boolean withHash);
/**
* Set the sort property - see SortMetric
* @see SortMetric
*/
ServerMetricsAsJson withSort(Comparator<MetaTimedMetric> sortBy);
/**
* Set the new line character to use.
*/
ServerMetricsAsJson withNewLine(boolean withNewLine);
/**
* Return the metrics in raw JSON.
*/
String json();
}
@@ -11,4 +11,9 @@ public interface TimedMetricStats extends MetaTimedMetric {
* Additionally set the location.
*/
void setLocation(String location);
/**
* Override the name based on profile location.
*/
void setName(String name);
}
@@ -5,6 +5,7 @@ import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.bean.PersistenceContext;
import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
/**
* Controls the loading of reference objects for a query instance.
@@ -47,6 +48,11 @@ public interface LoadContext {
*/
void register(String path, EntityBeanIntercept ebi);
/**
* Register a Bean with inheritance.
*/
void register(String path, EntityBeanIntercept ebi, BeanPropertyAssocOne<?> property);
/**
* Register a collection for lazy loading.
*/
@@ -169,7 +169,7 @@ public class LoadManyRequest extends LoadRequest {
Object parentId = desc.getId(ownerBean);
logger.debug("BeanCollection after lazy load was empty. type:" + ownerBean.getClass().getName() + " id:" + parentId + " owner:" + ownerBean);
}
} else if (isLoadCache()) {
} else if (isLoadCache() && many.isUseCache()) {
Object parentId = desc.getId(bc.getOwnerBean());
desc.cacheManyPropPut(many, bc, parentId);
}
@@ -458,7 +458,7 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
/**
* Set the query to be a delete query.
*/
void setDelete();
void setupForDeleteOrUpdate(boolean deleteRequest);
/**
* Set the query to be delete by ids due to cascading delete.
@@ -33,4 +33,9 @@ public interface SpiSqlUpdate extends SqlUpdate {
* Reset bind position to be ready for another bind execute.
*/
void reset();
/**
* Return a copy of the SqlUpdate with empty bind parameters.
*/
SpiSqlUpdate copy();
}
@@ -75,9 +75,10 @@ public interface SpiTransaction extends Transaction {
boolean isRegisteredDeleteBean(Integer hash);
/**
* Unregister the persisted bean.
* Unregister the persisted beans. Expected after persisting top level beans
* and hence including all beans that were cascade persisted.
*/
void unregisterBean(Object bean);
void unregisterBeans();
/**
* Return true if this is a bean that has already been persisted in the
@@ -189,8 +189,8 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public void unregisterBean(Object bean) {
transaction.unregisterBean(bean);
public void unregisterBeans() {
transaction.unregisterBeans();
}
@Override
@@ -62,6 +62,16 @@ public interface SpiJsonWriter extends JsonWriter {
*/
void endAssocMany();
/**
* Start a Many.
*/
void beginAssocManyMap(String name, boolean elementCollection);
/**
* End a Many.
*/
void endAssocManyMap(boolean elementCollection);
/**
* Write value using underlying Jaskson object mapper if available.
*/
@@ -257,7 +257,9 @@ public class DdlGenerator {
protected void runResourceScript(Connection connection, String sqlScript) throws IOException {
if (sqlScript != null) {
try (InputStream is = getClassLoader().getResourceAsStream(sqlScript)) {
if (is != null) {
if (is == null) {
log.warn("sql script {} was not found as a resource", sqlScript);
} else {
String content = readContent(new InputStreamReader(is));
runScript(connection, false, content, sqlScript);
}
@@ -14,6 +14,7 @@ import io.ebean.config.dbplatform.db2.DB2Platform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
import io.ebean.config.dbplatform.mysql.MySql55Platform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
import io.ebean.config.dbplatform.oracle.OraclePlatform;
@@ -25,6 +26,7 @@ import io.ebean.config.dbplatform.sqlserver.SqlServer17Platform;
import io.ebean.dbmigration.DbMigration;
import io.ebean.migration.MigrationVersion;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.Migration;
import io.ebeaninternal.dbmigration.migrationreader.MigrationXmlWriter;
@@ -105,6 +107,8 @@ public class DefaultDbMigration implements DbMigration {
protected String version;
protected String name;
protected String generatePendingDrop;
private boolean addForeignKeySkipCheck;
private int lockTimeoutSeconds;
protected boolean includeBuiltInPartitioning = true;
@@ -179,6 +183,16 @@ public class DefaultDbMigration implements DbMigration {
this.name = name;
}
@Override
public void setAddForeignKeySkipCheck(boolean addForeignKeySkipCheck) {
this.addForeignKeySkipCheck = addForeignKeySkipCheck;
}
@Override
public void setLockTimeout(int seconds) {
this.lockTimeoutSeconds = seconds;
}
@Override
public void setGeneratePendingDrop(String generatePendingDrop) {
this.generatePendingDrop = generatePendingDrop;
@@ -564,7 +578,8 @@ public class DefaultDbMigration implements DbMigration {
} else if (databasePlatform != null) {
// writer needs the current model to provide table/column details for
// history ddl generation (triggers, history tables etc)
DdlWrite write = new DdlWrite(new MConfiguration(), request.current);
DdlOptions options = new DdlOptions(addForeignKeySkipCheck);
DdlWrite write = new DdlWrite(new MConfiguration(), request.current, options);
PlatformDdlWriter writer = createDdlWriter(databasePlatform);
writer.processMigration(dbMigration, write, request.migrationDir, fullVersion);
}
@@ -622,8 +637,9 @@ public class DefaultDbMigration implements DbMigration {
*/
private void writeExtraPlatformDdl(String fullVersion, CurrentModel currentModel, Migration dbMigration, File writePath) throws IOException {
DdlOptions options = new DdlOptions(addForeignKeySkipCheck);
for (Pair pair : platforms) {
DdlWrite platformBuffer = new DdlWrite(new MConfiguration(), currentModel.read());
DdlWrite platformBuffer = new DdlWrite(new MConfiguration(), currentModel.read(), options);
PlatformDdlWriter platformWriter = createDdlWriter(pair.platform);
File subPath = platformWriter.subPath(writePath, pair.prefix);
platformWriter.processMigration(dbMigration, platformBuffer, subPath, fullVersion);
@@ -631,7 +647,7 @@ public class DefaultDbMigration implements DbMigration {
}
private PlatformDdlWriter createDdlWriter(DatabasePlatform platform) {
return new PlatformDdlWriter(platform, serverConfig, migrationConfig);
return new PlatformDdlWriter(platform, serverConfig, migrationConfig, lockTimeoutSeconds);
}
/**
@@ -787,6 +803,8 @@ public class DefaultDbMigration implements DbMigration {
return new HsqldbPlatform();
case POSTGRES:
return new PostgresPlatform();
case MYSQL55:
return new MySql55Platform();
case MYSQL:
return new MySqlPlatform();
case ORACLE:
@@ -0,0 +1,31 @@
package io.ebeaninternal.dbmigration.ddlgeneration;
/**
* Options used when generated DDL.
*/
public class DdlOptions {
private boolean foreignKeySkipCheck;
public DdlOptions(boolean foreignKeySkipCheck) {
this.foreignKeySkipCheck = foreignKeySkipCheck;
}
public DdlOptions() {
}
/**
* Return true if ADD FOREIGN KEY should use a skip check option.
*/
public boolean isForeignKeySkipCheck() {
return foreignKeySkipCheck;
}
/**
* Set to true if ADD FOREIGN KEY should use a skip check option.
*/
public void setForeignKeySkipCheck(boolean foreignKeySkipCheck) {
this.foreignKeySkipCheck = foreignKeySkipCheck;
}
}
@@ -26,17 +26,19 @@ public class DdlWrite {
private final DdlBuffer dropAll;
private final DdlOptions options;
/**
* Create without any configuration or current model (no history support).
*/
public DdlWrite() {
this(new MConfiguration(), new ModelContainer());
this(new MConfiguration(), new ModelContainer(), new DdlOptions());
}
/**
* Create with a configuration.
*/
public DdlWrite(MConfiguration configuration, ModelContainer currentModel) {
public DdlWrite(MConfiguration configuration, ModelContainer currentModel, DdlOptions options) {
this.currentModel = currentModel;
this.applyDropDependencies = new BaseDdlBuffer(configuration);
this.apply = new BaseDdlBuffer(configuration);
@@ -45,6 +47,14 @@ public class DdlWrite {
this.applyHistoryTrigger = new BaseDdlBuffer(configuration);
this.dropAllForeignKeys = new BaseDdlBuffer(configuration);
this.dropAll = new BaseDdlBuffer(configuration);
this.options = options;
}
/**
* Return the DDL options.
*/
public DdlOptions getOptions() {
return options;
}
/**
@@ -7,6 +7,7 @@ import io.ebean.config.dbplatform.DbHistorySupport;
import io.ebean.config.dbplatform.IdType;
import io.ebean.util.StringHelper;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.ddlgeneration.TableDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.IndexSet;
@@ -31,7 +32,6 @@ import io.ebeaninternal.dbmigration.model.MTable;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
@@ -462,7 +462,7 @@ public class BaseTableDdl implements TableDdl {
fkeyBuffer.appendStatement(platformDdl.createIndex(request.indexName(), tableName, request.cols()));
}
alterTableAddForeignKey(fkeyBuffer, request);
alterTableAddForeignKey(write.getOptions(), fkeyBuffer, request);
fkeyBuffer.end();
write.dropAllForeignKeys().appendStatement(platformDdl.alterTableDropForeignKey(tableName, request.fkName()));
@@ -473,9 +473,9 @@ public class BaseTableDdl implements TableDdl {
write.dropAllForeignKeys().end();
}
protected void alterTableAddForeignKey(DdlBuffer buffer, WriteForeignKey request) throws IOException {
protected void alterTableAddForeignKey(DdlOptions options, DdlBuffer buffer, WriteForeignKey request) throws IOException {
buffer.appendStatement(platformDdl.alterTableAddForeignKey(request));
buffer.appendStatement(platformDdl.alterTableAddForeignKey(options, request));
}
protected void appendColumns(String[] columns, DdlBuffer buffer) throws IOException {
@@ -635,7 +635,7 @@ public class BaseTableDdl implements TableDdl {
if (DdlHelp.isDropForeignKey(alterForeignKey.getColumnNames())) {
writer.apply().appendStatement(platformDdl.alterTableDropForeignKey(alterForeignKey.getTableName(), alterForeignKey.getName()));
} else {
writer.apply().appendStatement(platformDdl.alterTableAddForeignKey(new WriteForeignKey(alterForeignKey)));
writer.apply().appendStatement(platformDdl.alterTableAddForeignKey(writer.getOptions(), new WriteForeignKey(alterForeignKey)));
}
}
@@ -899,7 +899,7 @@ public class BaseTableDdl implements TableDdl {
protected void alterColumnAddForeignKey(DdlWrite writer, AlterColumn alterColumn) throws IOException {
alterTableAddForeignKey(writer.apply(), new WriteForeignKey(alterColumn));
alterTableAddForeignKey(writer.getOptions(), writer.apply(), new WriteForeignKey(alterColumn));
}
protected void alterColumnDropForeignKey(DdlWrite writer, AlterColumn alter) throws IOException {
@@ -4,6 +4,7 @@ import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
import java.io.IOException;
@@ -40,7 +41,7 @@ public class ClickHouseDdl extends PlatformDdl {
}
@Override
public String alterTableAddForeignKey(WriteForeignKey request) {
public String alterTableAddForeignKey(DdlOptions options, WriteForeignKey request) {
return null;
}
@@ -42,16 +42,6 @@ public class MySqlDdl extends PlatformDdl {
return "alter table " + tableName + " drop foreign key " + maxConstraintName(fkName);
}
/**
* It is rather complex to delete a column on MySql as there must not exist any foreign keys.
* That's why we call a user stored procedure here
*/
@Override
public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException {
buffer.append("CALL usp_ebean_drop_column('").append(tableName).append("', '").append(columnName).append("')").endOfStatement();
}
@Override
public String createCheckConstraint(String ckName, String checkConstraint) {
if (USE_CHECK_CONSTRAINT) {
@@ -60,6 +50,7 @@ public class MySqlDdl extends PlatformDdl {
return null;
}
}
@Override
public String alterTableAddCheckConstraint(String tableName, String checkConstraintName, String checkConstraint) {
if (USE_CHECK_CONSTRAINT) {
@@ -96,17 +87,13 @@ public class MySqlDdl extends PlatformDdl {
@Override
public String alterColumnNotnull(String tableName, String columnName, boolean notnull) {
// can't alter itself - done in alterColumnBaseAttributes()
return null;
}
@Override
public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) {
String suffix = DdlHelp.isDropDefault(defaultValue) ? columnDropDefault : columnSetDefault + " " + convertDefaultValue(defaultValue);
// use alter
return "alter table " + tableName + " alter " + columnName + " " + suffix;
}
@@ -124,7 +111,6 @@ public class MySqlDdl extends PlatformDdl {
boolean notnull = (alter.isNotnull() != null) ? alter.isNotnull() : Boolean.TRUE.equals(alter.isCurrentNotnull());
String notnullClause = notnull ? " not null" : "";
// use modify
return "alter table " + tableName + " modify " + columnName + " " + type + notnullClause;
}
@@ -161,7 +147,7 @@ public class MySqlDdl extends PlatformDdl {
}
@Override
public void addColumnComment(DdlBuffer apply, String table, String column, String comment) throws IOException {
public void addColumnComment(DdlBuffer apply, String table, String column, String comment) {
// alter comment currently not supported as it requires to repeat whole column definition
}
@@ -182,7 +168,6 @@ public class MySqlDdl extends PlatformDdl {
i++;
}
buffer.endOfStatement();
}
}
@@ -11,6 +11,7 @@ import io.ebean.util.StringHelper;
import io.ebeaninternal.dbmigration.ddlgeneration.BaseDdlHandler;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.PlatformTypeConverter;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.VowelRemover;
@@ -106,6 +107,8 @@ public class PlatformDdl {
protected String dropColumnSuffix = "";
protected String addForeignKeySkipCheck = "";
/**
* Set false for MsSqlServer to allow multiple nulls for OneToOne mapping.
*/
@@ -208,6 +211,13 @@ public class PlatformDdl {
return inlineForeignKeys;
}
/**
* By default this does nothing returning null / no lock timeout.
*/
public String setLockTimeout(int lockTimeoutSeconds) {
return null;
}
/**
* Write all the table columns converting to platform types as necessary.
*/
@@ -405,7 +415,7 @@ public class PlatformDdl {
/**
* Add foreign key.
*/
public String alterTableAddForeignKey(WriteForeignKey request) {
public String alterTableAddForeignKey(DdlOptions options, WriteForeignKey request) {
StringBuilder buffer = new StringBuilder(90);
buffer
@@ -418,6 +428,9 @@ public class PlatformDdl {
.append(lowerTableName(request.refTable()));
appendColumns(request.refCols(), buffer);
appendForeignKeySuffix(request, buffer);
if (options.isForeignKeySkipCheck()) {
buffer.append(addForeignKeySkipCheck);
}
return buffer.toString();
}
@@ -17,6 +17,11 @@ public class PostgresDdl extends PlatformDdl {
this.columnSetType = "type ";
this.alterTableIfExists = "if exists ";
this.columnSetNull = "drop not null";
this.addForeignKeySkipCheck = " not valid";
}
public String setLockTimeout(int lockTimeoutSeconds) {
return "set lock_timeout = " + (lockTimeoutSeconds * 1000);
}
@Override
@@ -2,6 +2,7 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
/**
* DB2 platform specific DDL.
@@ -25,7 +26,7 @@ public class SQLiteDdl extends PlatformDdl {
}
@Override
public String alterTableAddForeignKey(WriteForeignKey request) {
public String alterTableAddForeignKey(DdlOptions options, WriteForeignKey request) {
// not supported
return null;
}
@@ -3,6 +3,7 @@ package io.ebeaninternal.dbmigration.model;
import io.ebean.config.DbConstraintNaming;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.DefaultConstraintMaxLength;
import io.ebeaninternal.dbmigration.migration.ChangeSet;
@@ -37,6 +38,8 @@ public class CurrentModel {
private DdlWrite write;
private DdlOptions ddlOptions = new DdlOptions();
/**
* Construct with a given EbeanServer instance for DDL create all generation, not migration.
*/
@@ -63,6 +66,10 @@ public class CurrentModel {
this.jaxbPresent = server.getServerConfig().getClassLoadConfig().isJavaxJAXBPresent();
}
public DdlOptions getDdlOptions() {
return ddlOptions;
}
/**
* Return true if the model contains tables that are partitioned.
*/
@@ -183,7 +190,7 @@ public class CurrentModel {
if (write == null) {
ChangeSet createChangeSet = getChangeSet();
write = new DdlWrite(new MConfiguration(), model);
write = new DdlWrite(new MConfiguration(), model, ddlOptions);
DdlHandler handler = handler();
handler.generateProlog(write);
@@ -261,6 +261,13 @@ public class MColumn {
return !draftOnly && !historyExclude;
}
public void clearForeignKey() {
this.references = null;
this.foreignKeyName = null;
this.fkeyOnDelete = null;
this.fkeyOnUpdate = null;
}
public Column createColumn() {
Column c = new Column();
@@ -273,8 +273,7 @@ public class MTable {
}
for (MCompoundUniqueConstraint constraint : uniqueConstraints) {
UniqueConstraint uq = constraint.getUniqueConstraint();
createTable.getUniqueConstraint().add(uq);
createTable.getUniqueConstraint().add(constraint.getUniqueConstraint());
}
return createTable;
@@ -789,4 +788,20 @@ public class MTable {
public void setPartitionMeta(PartitionMeta partitionMeta) {
this.partitionMeta = partitionMeta;
}
/**
* Clear foreign key as this element collection table logically references
* back to multiple tables.
*/
public MIndex setReusedElementCollection() {
MIndex index = null;
for (MColumn column : columns.values()) {
final String references = column.getReferences();
if (references != null) {
index = new MIndex(column.getForeignKeyIndex(), name, column.getName());
column.clearForeignKey();
}
}
return index;
}
}
@@ -312,11 +312,29 @@ public class ModelContainer {
return tables.put(table.getName(), table);
}
/**
* Add an element table taking into account if it is reused/references back
* to multiple bean types (and so can't have foreign key).
*/
public void addTableElementCollection(MTable table) {
final MTable reusedElementCollection = tables.get(table.getName());
if (reusedElementCollection != null) {
final MIndex index = reusedElementCollection.setReusedElementCollection();
if (index != null) {
indexes.put(index.getIndexName(), index);
}
} else {
if (table.isPartitioned()) {
partitionedTables.add(table);
}
tables.put(table.getName(), table);
}
}
/**
* Add a single column index.
*/
public void addIndex(String indexName, String tableName, String columnName) {
indexes.put(indexName, new MIndex(indexName, tableName, columnName));
}
@@ -324,7 +342,6 @@ public class ModelContainer {
* Add a multi column index.
*/
public void addIndex(String indexName, String tableName, String[] columnNames) {
indexes.put(indexName, new MIndex(indexName, tableName, columnNames));
}
@@ -33,10 +33,13 @@ public class PlatformDdlWriter {
private final PlatformDdl platformDdl;
public PlatformDdlWriter(DatabasePlatform platform, ServerConfig serverConfig, DbMigrationConfig config) {
private final int lockTimeoutSeconds;
public PlatformDdlWriter(DatabasePlatform platform, ServerConfig serverConfig, DbMigrationConfig config, int lockTimeoutSeconds) {
this.platformDdl = PlatformDdlBuilder.create(platform);
this.serverConfig = serverConfig;
this.config = config;
this.lockTimeoutSeconds = lockTimeoutSeconds;
}
/**
@@ -46,6 +49,13 @@ public class PlatformDdlWriter {
DdlHandler handler = handler();
handler.generateProlog(write);
if (lockTimeoutSeconds > 0) {
String lockSql = platformDdl.setLockTimeout(lockTimeoutSeconds);
if (lockSql != null) {
write.apply().append(lockSql).endOfStatement().newLine();
}
}
List<ChangeSet> changeSets = dbMigration.getChangeSet();
for (ChangeSet changeSet : changeSets) {
if (isApply(changeSet)) {
@@ -95,6 +95,10 @@ public class ModelBuildContext {
return model.addTable(table);
}
public void addTableElementCollection(MTable table) {
model.addTableElementCollection(table);
}
public void addIndex(String indexName, String tableName, String columnName) {
model.addIndex(indexName, tableName, columnName);
}
@@ -17,15 +17,11 @@ public class ModelBuildElementTable {
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.addTable(table);
ctx.addTableElementCollection(table);
}
}
@@ -177,7 +177,7 @@ public class CacheChangeSet {
final ManyKey key;
final List<Object> removes = new ArrayList<>();
final Set<Object> removes = new HashSet<>();
final Map<Object, CachedManyIds> puts = new LinkedHashMap<>();
@@ -8,6 +8,7 @@ import io.ebean.config.dbplatform.db2.DB2Platform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
import io.ebean.config.dbplatform.mysql.MySql55Platform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
import io.ebean.config.dbplatform.oracle.OraclePlatform;
@@ -82,6 +83,9 @@ public class DatabasePlatformFactory {
if (dbName.equals("mysql")) {
return new MySqlPlatform();
}
if (dbName.equals("mysql55")) {
return new MySql55Platform();
}
if (dbName.equals("postgres") || dbName.equals("postgres9")) {
return new PostgresPlatform();
}
@@ -147,15 +151,16 @@ public class DatabasePlatformFactory {
*/
private DatabasePlatform byDatabaseMeta(DatabaseMetaData metaData, Connection connection) throws SQLException {
String dbProductName = metaData.getDatabaseProductName();
dbProductName = dbProductName.toLowerCase();
String dbProductName = metaData.getDatabaseProductName().toLowerCase();
final int majorVersion = metaData.getDatabaseMajorVersion();
final int minorVersion = metaData.getDatabaseMinorVersion();
if (dbProductName.contains("oracle")) {
return new OraclePlatform();
} else if (dbProductName.contains("microsoft")) {
throw new IllegalArgumentException("For SqlServer please explicitly choose either sqlserver16 or sqlserver17 as the platform via ServerConfig.setDatabasePlatformName. Refer to issue #1340 for more details");
} else if (dbProductName.contains("mysql")) {
return new MySqlPlatform();
return mysqlVersion(majorVersion, minorVersion);
} else if (dbProductName.contains("h2")) {
return new H2Platform();
} else if (dbProductName.contains("hsql database engine")) {
@@ -180,6 +185,13 @@ public class DatabasePlatformFactory {
return new DatabasePlatform();
}
private DatabasePlatform mysqlVersion(int majorVersion, int minorVersion) {
if (majorVersion <= 5 && minorVersion <= 5) {
return new MySql55Platform();
}
return new MySqlPlatform();
}
/**
* Use a select version() query as it could be Postgres or CockroachDB.
*/
@@ -82,7 +82,7 @@ class DefaultBeanLoader {
parentDesc.contextPut(pc, parentId, parentBean);
}
boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching();
boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching() && many.isUseCache();
if (useManyIdCache) {
Boolean readOnly = null;
if (ebi.isReadOnly()) {
@@ -12,6 +12,7 @@ import io.ebean.meta.MetaTimedMetric;
import io.ebean.meta.MetricVisitor;
import io.ebean.meta.QueryPlanRequest;
import io.ebean.meta.ServerMetrics;
import io.ebean.meta.ServerMetricsAsJson;
import java.util.ArrayList;
import java.util.List;
@@ -42,6 +43,11 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
return visitBasic();
}
@Override
public ServerMetricsAsJson collectMetricsAsJson() {
return new DumpMetricsJson(server);
}
@Override
public BasicMetricVisitor visitBasic() {
BasicMetricVisitor basic = new BasicMetricVisitor();
@@ -98,11 +98,11 @@ import io.ebeaninternal.server.query.CQueryEngine;
import io.ebeaninternal.server.query.CallableQueryCount;
import io.ebeaninternal.server.query.CallableQueryIds;
import io.ebeaninternal.server.query.CallableQueryList;
import io.ebeaninternal.server.query.DtoQueryEngine;
import io.ebeaninternal.server.query.LimitOffsetPagedList;
import io.ebeaninternal.server.query.QueryFutureIds;
import io.ebeaninternal.server.query.QueryFutureList;
import io.ebeaninternal.server.query.QueryFutureRowCount;
import io.ebeaninternal.server.query.DtoQueryEngine;
import io.ebeaninternal.server.querydefn.DefaultDtoQuery;
import io.ebeaninternal.server.querydefn.DefaultOrmQuery;
import io.ebeaninternal.server.querydefn.DefaultOrmUpdate;
@@ -136,11 +136,16 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static java.util.Spliterators.spliteratorUnknownSize;
import static java.util.stream.StreamSupport.stream;
/**
* The default server side implementation of EbeanServer.
@@ -651,7 +656,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
executeSql(connection, databasePlatform.truncateStatement(table));
}
connection.commit();
} catch(SQLException e) {
} catch (SQLException e) {
throw new PersistenceException("Error executing truncate", e);
}
}
@@ -1346,8 +1351,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public <T> boolean exists(SpiQuery<?> ormQuery, SpiTransaction transaction) {
ormQuery.setMaxRows(1);
SpiOrmQueryRequest<?> request = createQueryRequest(Type.ID_LIST, ormQuery, transaction);
Query<?> ormQueryCopy = ormQuery.copy();
ormQueryCopy.setMaxRows(1);
SpiOrmQueryRequest<?> request = createQueryRequest(Type.ID_LIST, ormQueryCopy, transaction);
try {
request.initTransIfRequired();
List<Object> ids = request.findIds();
@@ -1507,6 +1513,48 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Override
public <T> Stream<T> findLargeStream(Query<T> query, Transaction transaction) {
return findStreamWithSingleContext(false, query, transaction);
}
@Override
public <T> Stream<T> findStream(Query<T> query, Transaction transaction) {
return findStreamWithSingleContext(true, query, transaction);
}
private <T> Stream<T> findStreamWithSingleContext(boolean singleContext, Query<T> query, Transaction transaction) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, transaction);
if (singleContext) {
request.setIterateSingleContext();
}
try {
request.initTransIfRequired();
return toStream(request.findIterate());
} catch (RuntimeException ex) {
request.endTransIfRequired();
throw ex;
}
}
private <T> Stream<T> toStream(QueryIterator<T> queryIterator) {
return stream(spliteratorUnknownSize(queryIterator, Spliterator.ORDERED), false)
.onClose(new QueryIteratorClose(queryIterator));
}
private static class QueryIteratorClose implements Runnable {
private final QueryIterator<?> iterator;
private QueryIteratorClose(QueryIterator<?> iterator) {
this.iterator = iterator;
}
@Override
public void run() {
iterator.close();
}
}
@Override
public <T> void findEach(Query<T> query, Consumer<T> consumer, Transaction t) {
@@ -1736,30 +1784,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
((EntityBean) bean)._ebean_getIntercept().setDirty(true);
}
/**
* Update the bean using the default 'updatesDeleteMissingChildren' setting.
*/
@Override
public void update(Object bean) {
update(bean, null);
}
/**
* Update the bean using the default 'updatesDeleteMissingChildren' setting.
*/
@Override
public void update(Object bean, Transaction t) {
persister.update(checkEntityBean(bean), t);
}
/**
* Update the bean specifying the deleteMissingChildren option.
*/
@Override
public void update(Object bean, Transaction t, boolean deleteMissingChildren) {
persister.update(checkEntityBean(bean), t, deleteMissingChildren);
}
@Override
public void updateAll(Collection<?> beans) throws OptimisticLockException {
updateAll(beans, null);
@@ -117,6 +117,11 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
this(null, sql, new BindParams());
}
@Override
public SpiSqlUpdate copy() {
return new DefaultSqlUpdate(server, origSql, new BindParams());
}
@Override
public void reset() {
addPos = 0;
@@ -0,0 +1,232 @@
package io.ebeaninternal.server.core;
import io.ebean.Database;
import io.ebean.ProfileLocation;
import io.ebean.meta.MetaCountMetric;
import io.ebean.meta.MetaOrmQueryMetric;
import io.ebean.meta.MetaQueryMetric;
import io.ebean.meta.MetaTimedMetric;
import io.ebean.meta.ServerMetrics;
import io.ebean.meta.ServerMetricsAsJson;
import io.ebean.meta.SortMetric;
import java.io.StringWriter;
import java.util.Comparator;
import java.util.List;
class DumpMetricsJson implements ServerMetricsAsJson {
private final Database database;
private final StringWriter writer = new StringWriter();
private boolean dumpHash = true;
private boolean dumpSql = true;
private boolean dumpLoc = true;
private Comparator<MetaTimedMetric> sortBy = SortMetric.NAME;
private int listCounter;
private int objKeyCounter;
private String newLine = "\n";
DumpMetricsJson(Database database) {
this.database = database;
}
@Override
public ServerMetricsAsJson withLocation(boolean withLocation) {
this.dumpLoc = withLocation;
return this;
}
@Override
public ServerMetricsAsJson withSql(boolean withSql) {
this.dumpSql = withSql;
return this;
}
@Override
public ServerMetricsAsJson withHash(boolean withHash) {
this.dumpHash = withHash;
return this;
}
@Override
public ServerMetricsAsJson withSort(Comparator<MetaTimedMetric> sortBy) {
this.sortBy = sortBy;
return this;
}
@Override
public ServerMetricsAsJson withNewLine(boolean withNewLine) {
this.newLine = withNewLine ? "\n" : "";
return this;
}
@Override
public String json() {
collect(database.getMetaInfoManager().collectMetrics());
return writer.toString();
}
private void collect(ServerMetrics serverMetrics) {
start();
for (MetaTimedMetric metric : serverMetrics.getTimedMetrics()) {
log(metric);
}
List<MetaCountMetric> countMetrics = serverMetrics.getCountMetrics();
if (!countMetrics.isEmpty()) {
if (sortBy != null) {
countMetrics.sort(SortMetric.COUNT_NAME);
}
for (MetaCountMetric metric : countMetrics) {
logCount(metric);
}
}
List<MetaOrmQueryMetric> ormQueryMetrics = serverMetrics.getOrmQueryMetrics();
if (!ormQueryMetrics.isEmpty()) {
if (sortBy != null) {
ormQueryMetrics.sort(sortBy);
}
for (MetaOrmQueryMetric metric : ormQueryMetrics) {
logQuery(metric);
}
}
List<MetaQueryMetric> dtoQueryMetrics = serverMetrics.getDtoQueryMetrics();
if (!dtoQueryMetrics.isEmpty()) {
if (sortBy != null) {
dtoQueryMetrics.sort(sortBy);
}
for (MetaQueryMetric metric : dtoQueryMetrics) {
logDtoQuery(metric);
}
}
end();
}
private void start() {
objStart();
key("db");
val(database.getName());
key("metrics");
listStart();
}
private void end() {
listEnd();
objEnd();
}
private void objStart() {
objKeyCounter = 0;
writer.append("{");
}
private void objEnd() {
writer.append("}");
}
private void listStart() {
listCounter = 0;
writer.append("[");
writer.append(newLine);
}
private void listEnd() {
writer.append("]");
}
private void key(String key) {
if (objKeyCounter++ > 0) {
writer.append(", ");
}
writer.append("\"").append(key).append("\":");
}
private void val(long count) {
writer.append(Long.toString(count));
}
private void val(String val) {
writer.append("\"").append(val).append("\"");
}
private void log(MetaTimedMetric metric) {
metricStart();
key("name");
val(metric.getName());
appendCounters(metric);
objEnd();
}
private void metricStart() {
if (listCounter++ > 0) {
writer.append(",").append(newLine);
}
objStart();
}
private void logCount(MetaCountMetric metric) {
metricStart();
key("name");
val(metric.getName());
key("count");
val(metric.getCount());
objEnd();
}
private void logQuery(MetaOrmQueryMetric metric) {
metricStart();
key("name");
val(metric.getName());
appendCounters(metric);
if (dumpHash) {
key("hash");
val(metric.getSqlHash());
}
appendProfileAndSql(metric);
objEnd();
}
private void logDtoQuery(MetaQueryMetric metric) {
metricStart();
key("name");
val(metric.getName());
appendCounters(metric);
appendProfileAndSql(metric);
objEnd();
}
private void appendProfileAndSql(MetaQueryMetric metric) {
ProfileLocation profileLocation = metric.getProfileLocation();
if (dumpLoc && profileLocation != null) {
key("loc");
val(profileLocation.shortDescription());
}
if (dumpSql) {
key("sql");
val(metric.getSql());
}
}
private void appendCounters(MetaTimedMetric timedMetric) {
key("count");
val(timedMetric.getCount());
key("total");
val(timedMetric.getTotal());
key("mean");
val(timedMetric.getMean());
key("max");
val(timedMetric.getMax());
}
}
@@ -514,7 +514,7 @@ public class InternalConfiguration {
String tz = serverConfig.getDataTimeZone();
if (tz == null) {
if (getPlatform() == Platform.MYSQL) {
if (isMySql(getPlatform())) {
return new LocalDataTimeZone();
}
return new NoDataTimeZone();
@@ -526,6 +526,10 @@ public class InternalConfiguration {
}
}
private boolean isMySql(Platform platform) {
return platform.base() == Platform.MYSQL;
}
public DataTimeZone getDataTimeZone() {
return dataTimeZone;
}
@@ -87,6 +87,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
private Set<String> dependentTables;
private boolean iterateSingleContext;
/**
* Create the InternalQueryRequest.
*/
@@ -104,6 +106,15 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
return queryEngine.translate(this, bindLog, sql, e);
}
@Override
public void setIterateSingleContext() {
this.iterateSingleContext = true;
}
public boolean isIterateSingleContext() {
return iterateSingleContext;
}
@Override
public boolean isDeleteByStatement() {
if (!transaction.isPersistCascade() || beanDescriptor.isDeleteByStatement()) {
@@ -322,11 +333,13 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
* For iterate queries reset the persistenceContext and loadContext.
*/
public void flushPersistenceContextOnIterate() {
persistenceContext = new DefaultPersistenceContext();
loadContext.resetPersistenceContext(persistenceContext);
if (jsonRead != null) {
jsonRead.setPersistenceContext(persistenceContext);
jsonRead.setLoadContext(loadContext);
if (!iterateSingleContext) {
persistenceContext = new DefaultPersistenceContext();
loadContext.resetPersistenceContext(persistenceContext);
if (jsonRead != null) {
jsonRead.setPersistenceContext(persistenceContext);
jsonRead.setLoadContext(loadContext);
}
}
}
@@ -782,4 +795,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
dependentTables.addAll(tables);
}
}
/**
* Return true if no MaxRows or use LIMIT in SQL update.
*/
public boolean isInlineSqlUpdateLimit() {
return query.getMaxRows() < 1 || ebeanServer.getDatabasePlatform().isInlineSqlUpdateLimit();
}
}
@@ -20,7 +20,6 @@ public class PersistDeferredRelationship {
private final EntityBean bean;
public PersistDeferredRelationship(SpiEbeanServer ebeanServer, BeanDescriptor<?> beanDescriptor, EntityBean assocBean, ImportedId importedId, EntityBean bean) {
this.ebeanServer = ebeanServer;
this.beanDescriptor = beanDescriptor;
this.assocBean = assocBean;
@@ -41,13 +40,11 @@ public class PersistDeferredRelationship {
// bind the set clause for the importedId
int pos = importedId.bind(1, sqlUpdate, assocBean);
// bind the where clause for the bean
Object[] idValues = beanDescriptor.getIdBinder().getIdValues(bean);
for (int j = 0; j < idValues.length; j++) {
sqlUpdate.setParameter(pos + j, idValues[j]);
}
ebeanServer.execute(sqlUpdate, transaction);
}
}
@@ -66,6 +66,11 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
}
}
@Override
public boolean isFlushQueue() {
return false;
}
/**
* Execute a the request or queue/batch it for later execution.
*/
@@ -27,7 +27,7 @@ import io.ebeaninternal.server.persist.BatchedSqlException;
import io.ebeaninternal.server.persist.DeleteMode;
import io.ebeaninternal.server.persist.Flags;
import io.ebeaninternal.server.persist.PersistExecute;
import io.ebeaninternal.server.persist.SaveManyBeans;
import io.ebeaninternal.server.persist.SaveMany;
import io.ebeaninternal.server.transaction.BeanPersistIdMap;
import io.ebeanservice.docstore.api.DocStoreUpdate;
import io.ebeanservice.docstore.api.DocStoreUpdateContext;
@@ -83,6 +83,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
private int flags;
private boolean saveRecurse;
private DocStoreMode docStoreMode;
private final ConcurrencyMode concurrencyMode;
@@ -104,8 +106,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
private boolean notifyCache;
private boolean deleteMissingChildren;
/**
* Flag used to detect when only many properties where updated via a cascade. Used to ensure
* appropriate caches are updated in that case.
@@ -169,10 +169,7 @@ 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;
private boolean pendingPostUpdateNotify;
/**
* Set to true when post execute has occurred (so includes batch flush).
@@ -187,7 +184,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
/**
* Many to many intersection table changes that are held for later batch processing.
*/
private List<SaveManyBeans> saveManyIntersections;
private List<SaveMany> saveMany;
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
PersistExecute persistExecute, PersistRequest.Type type, int flags) {
@@ -283,10 +280,12 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* Check for batch escalation on cascade.
*/
public void checkBatchEscalationOnCascade() {
if (transaction.checkBatchEscalationOnCascade(this)) {
// we escalated to use batch mode so flush when done
// but if createdTransaction then commit will flush it
batchOnCascadeSet = !createdTransaction;
if (type == Type.UPDATE || beanDescriptor.isBatchEscalateOnCascade(type)) {
if (transaction.checkBatchEscalationOnCascade(this)) {
// we escalated to use batch mode so flush when done
// but if createdTransaction then commit will flush it
batchOnCascadeSet = !createdTransaction;
}
}
persistCascade = transaction.isPersistCascade();
}
@@ -425,6 +424,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
/**
* Return the dirty properties on this request.
*/
@Override
public boolean[] getDirtyProperties() {
return dirtyProperties;
}
@@ -483,7 +483,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
/**
* Collect L2 cache changes to be applied after the transaction has successfully committed.
*/
public void notifyCache(CacheChangeSet changeSet) {
private void notifyCache(CacheChangeSet changeSet) {
if (notifyCache) {
switch (type) {
case INSERT:
@@ -531,8 +531,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
public void addToQueue(DocStoreUpdates docStoreUpdates) {
switch (type) {
case INSERT:
docStoreUpdates.queueIndex(beanDescriptor.getDocStoreQueueId(), idValue);
break;
case UPDATE:
case DELETE_SOFT:
docStoreUpdates.queueIndex(beanDescriptor.getDocStoreQueueId(), idValue);
@@ -587,7 +585,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
public void unRegisterBean() {
transaction.unregisterBean(bean);
if (!saveRecurse) {
// only clear all persisted beans when persisting at the top level
transaction.unregisterBeans();
}
}
/**
@@ -629,20 +630,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
return beanDescriptor;
}
/**
* Return true if a stateless update should also delete any missing details beans.
*/
public boolean isDeleteMissingChildren() {
return deleteMissingChildren;
}
/**
* Set if deleteMissingChildren occurs on cascade save to OneToMany or ManyToMany.
*/
public void setDeleteMissingChildren(boolean deleteMissingChildren) {
this.deleteMissingChildren = deleteMissingChildren;
}
/**
* Prepare the update after potential modifications in a BeanPersistController.
*/
@@ -751,14 +738,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
return parentBean;
}
/**
* Return the controller if there is one associated with this type of bean. This returns null if
* there is no controller associated.
*/
public BeanPersistController getBeanController() {
return controller;
}
/**
* Return the intercept if there is one.
*/
@@ -907,12 +886,8 @@ 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)
if (pendingPostUpdateNotify) {
controller.postUpdate(this);
} else {
// batched update with no element collection, send postUpdate notification once it executes
pendingPostUpdateNotify = -1;
}
}
@@ -935,6 +910,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
*/
@Override
public void postExecute() {
saveQueuedMany();
postExecute = true;
if (controller != null) {
controllerPost();
@@ -962,12 +938,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
if (isLogSummary()) {
logSummary();
}
saveQueuedManyIntersection();
}
private void saveQueuedManyIntersection() {
if (saveManyIntersections != null) {
saveManyIntersections.forEach(SaveManyBeans::saveIntersectionBatch);
private void saveQueuedMany() {
if (saveMany != null) {
saveMany.forEach(SaveMany::saveBatch);
}
}
@@ -978,19 +953,14 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
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;
pendingPostUpdateNotify = true;
}
if (!dirty) {
setNotifyCache();
}
}
public boolean isNotifyCache() {
return notifyCache;
}
@@ -1000,13 +970,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
controller.postInsert(this);
break;
case UPDATE:
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++;
}
controller.postUpdate(this);
break;
case DELETE_SOFT:
controller.postSoftDelete(this);
@@ -1051,16 +1015,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
}
/**
* Return true if the update DML/SQL must be dynamically generated.
* <p>
* This is the case for updates/deletes of partially populated beans.
* </p>
*/
public boolean isDynamicUpdateSql() {
return beanDescriptor.isUpdateChangesOnly() || !intercept.isFullyLoadedBean();
}
/**
* Return true if the property should be included in the update.
*/
@@ -1133,7 +1087,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
updatedManysOnly = true;
setNotifyCache();
addPostCommitListeners();
saveQueuedManyIntersection();
saveQueuedMany();
}
notifyCacheOnComplete();
postUpdateNotify();
@@ -1290,6 +1244,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
private void executeInsert() {
setGeneratedId();
setTenantId();
if (controller == null || controller.preInsert(this)) {
beanManager.getBeanPersister().insert(this);
@@ -1471,19 +1426,34 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
/**
* Return true if the intersection table updates should be queued and batched.
* Return true if the intersection table updates or element collection updates should be queued.
*/
public boolean isQueueManyIntersection() {
public boolean isQueueSaveMany() {
return !postExecute;
}
/**
* The intersection table updates to the batch executed later on postExecute.
* The intersection table updates or element collection to the batch executed later on postExecute.
*/
public void addManyIntersection(SaveManyBeans saveManyIntersection) {
if (this.saveManyIntersections == null) {
this.saveManyIntersections = new ArrayList<>();
public void addSaveMany(SaveMany saveManyRequest) {
if (this.saveMany == null) {
this.saveMany = new ArrayList<>();
}
this.saveManyIntersections.add(saveManyIntersection);
this.saveMany.add(saveManyRequest);
}
public boolean isForcedUpdate() {
return Flags.isUpdateForce(flags);
}
/**
* Set when this request is from cascading persist.
*/
public void setSaveRecurse() {
saveRecurse = true;
}
private void setGeneratedId() {
beanDescriptor.setGeneratedId(entityBean, transaction);
}
}
@@ -32,6 +32,7 @@ public final class PersistRequestUpdateSql extends PersistRequest {
private final boolean forceNoBatch;
private boolean batchThisRequest;
private boolean flushQueue;
public PersistRequestUpdateSql(SpiEbeanServer server, SpiSqlUpdate sqlUpdate,
SpiTransaction t, PersistExecute persistExecute, boolean forceNoBatch) {
@@ -50,7 +51,7 @@ public final class PersistRequestUpdateSql extends PersistRequest {
@Override
public void profile(long offset, int flushCount) {
profileBase(EVT_UPDATESQL, offset, (short)0, flushCount);
profileBase(EVT_UPDATESQL, offset, (short) 0, flushCount);
}
/**
@@ -77,9 +78,15 @@ public final class PersistRequestUpdateSql extends PersistRequest {
if (control == null) {
control = persistExecute.createBatchControl(transaction);
}
flushQueue = true;
control.addToFlushQueue(this, early);
}
@Override
public boolean isFlushQueue() {
return flushQueue;
}
@Override
public int executeNow() {
return persistExecute.executeSqlUpdate(this);
@@ -30,11 +30,6 @@ public interface Persister {
*/
void update(EntityBean entityBean, Transaction t);
/**
* Update the bean specifying deleteMissingChildren.
*/
void update(EntityBean entityBean, Transaction t, boolean deleteMissingChildren);
/**
* Force an Insert using the given bean.
*/
@@ -109,8 +104,18 @@ public interface Persister {
void executeOrQueue(SpiSqlUpdate update, SpiTransaction t, boolean queue);
/**
* Add the statement to JDBC batch for later execution via executeBatch.
* Queue the SqlUpdate for early execution (with JDBC batch).
*/
void addToFlushQueue(SpiSqlUpdate update, SpiTransaction t);
/**
* Queue the SqlUpdate for late execution (with JDBC batch).
*/
void addToFlushQueueLast(SpiSqlUpdate update, SpiTransaction t);
/**
* Add the statement to JDBC batch for later execution via executeBatch.
*/
void addBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction);
/**
@@ -30,6 +30,7 @@ public class PlatformDdlBuilder {
return new H2Ddl(platform);
case DB2:
return new DB2Ddl(platform);
case MYSQL55:
case MYSQL:
return new MySqlDdl(platform);
case HSQLDB:
@@ -171,4 +171,10 @@ public interface SpiOrmQueryRequest<T> extends BeanQueryRequest<T>, DocQueryRequ
* Return true if delete by statement is allowed for this type given cascade rules etc.
*/
boolean isDeleteByStatement();
/**
* Set when we want to use a single persistence context for all beans returned
* in the query (so all beans are held in memory)
*/
void setIterateSingleContext();
}
@@ -21,9 +21,7 @@ class AssocOneHelpRefInherit extends AssocOneHelp {
@Override
void loadIgnore(DbReadContext ctx) {
property.targetIdBinder.loadIgnore(ctx);
if (inherit.hasChildren()) {
ctx.getDataReader().incrementPos(1);
}
ctx.getDataReader().incrementPos(1);
}
/**
@@ -57,14 +55,13 @@ class AssocOneHelpRefInherit extends AssocOneHelp {
boolean disableLazyLoading = ctx.isDisableLazyLoading();
Object ref = desc.contextRef(pc, ctx.isReadOnly(), disableLazyLoading, id);
if (!disableLazyLoading) {
ctx.register(property.name, ((EntityBean) ref)._ebean_getIntercept());
ctx.registerBeanInherit(property, ((EntityBean) ref)._ebean_getIntercept());
}
return ref;
}
@Override
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
// add join to support the discriminator column
String relativePrefix = ctx.getRelativePrefix(property.name);
property.tableJoin.addJoin(joinType, relativePrefix, ctx);
@@ -76,7 +73,7 @@ class AssocOneHelpRefInherit extends AssocOneHelp {
@Override
void appendSelect(DbSqlContext ctx, boolean subQuery) {
if (!subQuery && inherit.hasChildren()) {
if (!subQuery) {
// add discriminator column
String relativePrefix = ctx.getRelativePrefix(property.getName());
String tableAlias = ctx.getTableAlias(relativePrefix);
@@ -66,7 +66,6 @@ import io.ebeaninternal.server.el.ElPropertyChainBuilder;
import io.ebeaninternal.server.el.ElPropertyDeploy;
import io.ebeaninternal.server.el.ElPropertyValue;
import io.ebeaninternal.server.persist.DeleteMode;
import io.ebeaninternal.server.persist.DmlUtil;
import io.ebeaninternal.server.query.CQueryPlan;
import io.ebeaninternal.server.query.ExtraJoin;
import io.ebeaninternal.server.query.STreeProperty;
@@ -77,6 +76,7 @@ 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.querydefn.OrmQueryProperties;
import io.ebeaninternal.server.rawsql.SpiRawSql;
import io.ebeaninternal.server.type.DataBind;
import io.ebeaninternal.server.type.ScalarType;
@@ -140,6 +140,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
private final short profileBeanId;
private final boolean multiValueSupported;
private boolean batchEscalateOnCascadeInsert;
private boolean batchEscalateOnCascadeDelete;
public enum EntityType {
ORM, EMBEDDED, VIEW, SQL, DOC
@@ -333,6 +335,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* list of properties that are Lists/Sets/Maps (Derived).
*/
private final BeanProperty[] propertiesNonMany;
private final BeanProperty[] propertiesAggregate;
private final BeanPropertyAssocMany<?>[] propertiesMany;
private final BeanPropertyAssocMany<?>[] propertiesManySave;
private final BeanPropertyAssocMany<?>[] propertiesManyDelete;
@@ -405,11 +408,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
private final String baseTableAlias;
/**
* If true then only changed properties get updated.
*/
private final boolean updateChangesOnly;
private final boolean cacheSharableBeans;
private final String docStoreQueueId;
@@ -465,7 +463,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
this.selectLastInsertedId = deploy.getSelectLastInsertedId();
this.selectLastInsertedIdDraft = deploy.getSelectLastInsertedIdDraft();
this.concurrencyMode = deploy.getConcurrencyMode();
this.updateChangesOnly = deploy.isUpdateChangesOnly();
this.indexDefinitions = deploy.getIndexDefinitions();
this.readAuditing = deploy.isReadAuditing();
@@ -515,6 +512,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
this.propertiesMany = listHelper.getMany();
this.propertiesNonMany = listHelper.getNonMany();
this.propertiesAggregate = listHelper.getAggregates();
this.propertiesManySave = listHelper.getManySave();
this.propertiesManyDelete = listHelper.getManyDelete();
this.propertiesManyToMany = listHelper.getManyToMany();
@@ -756,6 +754,24 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
}
}
private boolean hasCircularImportedId() {
for (BeanPropertyAssocOne<?> assocOne : propertiesOneImportedSave) {
if (assocOne.hasCircularImportedId(this)) {
return true;
}
}
return false;
}
boolean hasCircularImportedIdTo(BeanDescriptor sourceDesc) {
for (BeanPropertyAssocOne<?> assocOne : propertiesOneImportedSave) {
if (assocOne.getTargetDescriptor() == sourceDesc) {
return true;
}
}
return false;
}
void registerColumn(String dbColumn, String path) {
String key = dbColumn.toLowerCase();
// check for clash with imported OneToOne PK
@@ -816,6 +832,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
*/
@SuppressWarnings("unchecked")
void initialiseDocMapping() {
batchEscalateOnCascadeInsert = supportBatchEscalateOnInsert();
batchEscalateOnCascadeDelete = supportBatchEscalateOnDelete();
for (BeanPropertyAssocMany<?> many : propertiesMany) {
many.initialisePostTarget();
}
@@ -830,6 +848,30 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
cacheHelp.deriveNotifyFlags();
}
private boolean supportBatchEscalateOnDelete() {
if (softDelete) {
return false;
}
for (BeanPropertyAssocMany<?> assocMany : propertiesManyDelete) {
if (assocMany.isCascadeDeleteEscalate()) {
return true;
}
}
return false;
}
private boolean supportBatchEscalateOnInsert() {
return idType == IdType.IDENTITY || !hasCircularImportedId();
}
/**
* Return false if JDBC batch can't be implicitly escalated to.
* This happens when we have circular import id situation (need to defer setting identity value).
*/
public boolean isBatchEscalateOnCascade(PersistRequest.Type type) {
return type == PersistRequest.Type.INSERT ? batchEscalateOnCascadeInsert : batchEscalateOnCascadeDelete;
}
void initInheritInfo() {
if (inheritInfo != null) {
// need to check every BeanDescriptor in the inheritance hierarchy
@@ -1260,7 +1302,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
/**
* Return the draft dirty boolean property or null if there is not one assigned to this bean type.
*/
public BeanProperty getDraftDirty() {
BeanProperty getDraftDirty() {
return draftDirty;
}
@@ -1701,14 +1743,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return "update " + baseTable + " set " + prop.importedIdClause() + " where " + idBinder.getBindIdSql(null);
}
/**
* Return true if updates should only include changed properties. Otherwise
* all loaded properties are included in the update.
*/
public boolean isUpdateChangesOnly() {
return updateChangesOnly;
}
/**
* Return true if save does not recurse to other beans. That is return true if
* there are no assoc one or assoc many beans that cascade save.
@@ -3073,6 +3107,18 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return idType;
}
/**
* Set the generated Id value if appropriate.
*/
public void setGeneratedId(EntityBean entityBean, Transaction transaction) {
if (idGenerator == null || idProperty == null || idProperty.isEmbedded()) {
return;
}
if (isNullOrZero(idProperty.getValue(entityBean))) {
convertSetId(nextId(transaction), entityBean);
}
}
/**
* Return true if the Id value is marked as a <code>@GeneratedValue</code>.
*/
@@ -3169,6 +3215,27 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return propertiesEmbedded;
}
/**
* Return true if the query detail includes an aggregation property.
*/
public boolean includesAggregation(OrmQueryDetail detail) {
return detail != null && propertiesAggregate.length > 0 && includesAggregation(detail.getChunk(null, false));
}
private boolean includesAggregation(OrmQueryProperties rootProps) {
if (rootProps != null) {
final Set<String> included = rootProps.getIncluded();
if (included != null) {
for (BeanProperty property : propertiesAggregate) {
if (included.contains(property.getName())) {
return true;
}
}
}
}
return false;
}
/**
* Set all properties to be loaded (recurse to embedded beans).
*/
@@ -3494,6 +3561,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
throw new IllegalStateException("Unexpected - expect Element override");
}
public boolean isJsonReadCollection() {
return false;
}
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
jsonHelp.jsonWrite(writeJson, bean, null);
}
@@ -21,6 +21,11 @@ abstract class BeanDescriptorElement<T> extends BeanDescriptor<T> {
this.elementHelp = elementHelp;
}
@Override
public boolean isJsonReadCollection() {
return true;
}
/**
* Find and return the first base scalar type (and we expect only 1).
*/
@@ -35,6 +35,11 @@ class BeanDescriptorElementEmbedded<T> extends BeanDescriptorElement<T> {
}
}
@Override
public boolean isJsonReadCollection() {
return false;
}
@Override
public void initialiseOther(BeanDescriptorInitContext initContext) {
super.initialiseOther(initContext);
@@ -28,10 +28,14 @@ class BeanDescriptorElementEmbeddedMap<T> extends BeanDescriptorElementEmbedded<
this.stringKey = String.class.equals(scalarTypeKey.getType());
}
@Override
public boolean isJsonReadCollection() {
return true;
}
@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();
@@ -43,7 +47,6 @@ class BeanDescriptorElementEmbeddedMap<T> extends BeanDescriptorElementEmbedded<
ctx.writeFieldName("value");
writeJsonElement(ctx, entry.getValue());
}
ctx.writeEndObject();
}
@Override
@@ -52,30 +55,21 @@ class BeanDescriptorElementEmbeddedMap<T> extends BeanDescriptorElementEmbedded<
JsonParser parser = readJson.getParser();
ElementCollector add = elementHelp.createCollector();
do {
JsonToken token = parser.nextToken();
if (token != JsonToken.START_OBJECT) {
String fieldName = parser.nextFieldName();
if (fieldName == null) {
break;
}
if (stringKey) {
String key = parser.nextFieldName();
parser.nextToken();
Object val = readJsonElement(readJson, null);
add.addKeyValue(key, val);
add.addKeyValue(fieldName, 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();
@@ -1,7 +1,6 @@
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;
@@ -35,7 +34,6 @@ class BeanDescriptorElementScalarMap<T> extends BeanDescriptorElement<T> {
@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();
@@ -47,7 +45,6 @@ class BeanDescriptorElementScalarMap<T> extends BeanDescriptorElement<T> {
ctx.writeFieldName("value");
scalarTypeVal.jsonWrite(ctx.gen(), entry.getValue());
}
ctx.writeEndObject();
}
@Override
@@ -56,30 +53,21 @@ class BeanDescriptorElementScalarMap<T> extends BeanDescriptorElement<T> {
JsonParser parser = readJson.getParser();
ElementCollector add = elementHelp.createCollector();
do {
JsonToken token = parser.nextToken();
if (token != JsonToken.START_OBJECT) {
String fieldName = parser.nextFieldName();
if (fieldName == null) {
break;
}
if (stringKey) {
String key = parser.nextFieldName();
parser.nextToken();
Object val = scalarTypeVal.jsonRead(parser);
add.addKeyValue(key, val);
add.addKeyValue(fieldName, 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();
@@ -144,8 +144,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private int entityBeanCount;
private final boolean updateChangesOnly;
private final BootupClasses bootupClasses;
private final String serverName;
@@ -227,11 +225,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
this.deplyInherit = config.getDeployInherit();
this.deployUtil = config.getDeployUtil();
this.typeManager = deployUtil.getTypeManager();
this.beanManagerFactory = new BeanManagerFactory(config.getDatabasePlatform());
this.updateChangesOnly = serverConfig.isUpdateChangesOnly();
this.beanLifecycleAdapterFactory = new BeanLifecycleAdapterFactory(serverConfig);
this.persistControllerManager = new PersistControllerManager(bootupClasses);
this.postLoadManager = new PostLoadManager(bootupClasses);
@@ -1310,9 +1304,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private <T> DeployBeanInfo<T> createDeployBeanInfo(Class<T> beanClass) {
DeployBeanDescriptor<T> desc = new DeployBeanDescriptor<>(this, beanClass, serverConfig);
desc.setUpdateChangesOnly(updateChangesOnly);
beanLifecycleAdapterFactory.addLifecycleMethods(desc);
// set bean controller, finder and listener
@@ -163,11 +163,12 @@ public class BeanMapHelp<T> extends BaseCollectionHelp<T> {
}
if (!map.isEmpty() || ctx.isIncludeEmpty()) {
ctx.beginAssocMany(name);
final boolean elementCollection = many.isElementCollection();
ctx.beginAssocManyMap(name, elementCollection);
for (Entry<?, ?> entry : map.entrySet()) {
many.jsonWriteMapEntry(ctx, entry);
}
ctx.endAssocMany();
ctx.endAssocManyMap(elementCollection);
}
}
@@ -835,6 +835,13 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
return scalarType.format(value);
}
/**
* Return the value from String format into Object value.
*/
public Object parse(String value) {
return scalarType.parse(value);
}
/**
* Read the value for this property from L2 cache entry and set it to the bean.
* <p>
@@ -14,6 +14,7 @@ import io.ebean.text.PathProperties;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.SpiSqlUpdate;
import io.ebeaninternal.api.json.SpiJsonReader;
import io.ebeaninternal.api.json.SpiJsonWriter;
import io.ebeaninternal.server.deploy.id.ImportedId;
@@ -49,6 +50,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
private final TableJoin intersectionJoin;
private final String intersectionPublishTable;
private final String intersectionDraftTable;
private final boolean orphanRemoval;
private IntersectionTable intersectionTable;
@@ -123,6 +125,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
public BeanPropertyAssocMany(BeanDescriptor<?> descriptor, DeployBeanPropertyAssocMany<T> deploy) {
super(descriptor, deploy);
this.unidirectional = deploy.isUnidirectional();
this.orphanRemoval = deploy.isOrphanRemoval();
this.o2mJoinTable = deploy.isO2mJoinTable();
this.hasOrderColumn = deploy.hasOrderColumn();
this.manyToMany = deploy.isManyToMany();
@@ -226,6 +229,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
desc.registerTable(targetDescriptor.getBaseTable(), this);
}
}
/**
* Return the underlying collection of beans.
*/
@@ -315,7 +319,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
// do not add to the selectChain at the top level of the Many bean
}
public SqlUpdate deleteByParentId(Object parentId, List<Object> parentIdist) {
public SpiSqlUpdate deleteByParentId(Object parentId, List<Object> parentIdist) {
if (parentId != null) {
return sqlHelp.deleteByParentId(parentId);
} else {
@@ -345,7 +349,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
/**
* Add the loaded current bean to its associated parent.
*
* <p>
* Helper method used by Elastic integration when loading with a persistence context.
*/
@Override
@@ -509,6 +513,10 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
return hasOrderColumn;
}
public boolean isOrphanRemoval() {
return orphanRemoval;
}
@Override
public boolean isAssocMany() {
return true;
@@ -948,10 +956,14 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
}
public boolean isIncludeCascadeDelete() {
return cascadeInfo.isDelete() || o2mJoinTable || ModifyListenMode.REMOVALS == modifyListenMode;
return cascadeInfo.isDelete() || hasJoinTable() || ModifyListenMode.REMOVALS == modifyListenMode;
}
public String insertElementCollection() {
boolean isCascadeDeleteEscalate() {
return !elementCollection && cascadeInfo.isDelete();
}
public SpiSqlUpdate insertElementCollection() {
return sqlHelp.insertElementCollection();
}
@@ -960,13 +972,24 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
}
void jsonWriteMapEntry(SpiJsonWriter ctx, Map.Entry<?, ?> entry) throws IOException {
elementDescriptor.jsonWriteMapEntry(ctx, entry);
if (elementDescriptor != null) {
elementDescriptor.jsonWriteMapEntry(ctx, entry);
} else {
targetDescriptor.jsonWrite(ctx, (EntityBean)entry.getValue());
}
}
void jsonWriteElementValue(SpiJsonWriter ctx, Object element) {
elementDescriptor.jsonWriteElement(ctx, element);
}
/**
* Only cache Many Ids if the target bean is also cached.
*/
public boolean isUseCache() {
return targetDescriptor.isBeanCaching();
}
/**
* A Many (element collection) property in bean cache to held as JSON.
*/
@@ -1033,10 +1056,9 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
*/
public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException {
if (elementDescriptor != null && manyType.isMap()) {
if (elementDescriptor != null && elementDescriptor.isJsonReadCollection()) {
return elementDescriptor.jsonReadCollection(readJson, parentBean);
}
BeanCollection<?> collection = createEmpty(parentBean);
BeanCollectionAdd add = getBeanCollectionAdd(collection);
do {
@@ -40,22 +40,18 @@ class BeanPropertyAssocManyJsonHelp {
if (!this.many.jsonDeserialize) {
return;
}
JsonParser parser = readJson.getParser();
JsonToken event = parser.nextToken();
if (JsonToken.VALUE_NULL == event) {
return;
}
if (many.isTransient()) {
jsonReadTransientUsingObjectMapper(readJson, parentBean);
return;
}
if (JsonToken.START_ARRAY != event) {
throw new JsonParseException(parser, "Unexpected token " + event + " - expecting start_array");
if (JsonToken.START_ARRAY != event && JsonToken.START_OBJECT != event) {
throw new JsonParseException(parser, "Unexpected token " + event + " - expecting start array or object");
}
many.setValue(parentBean, many.jsonReadCollection(readJson, parentBean));
}
@@ -1,9 +1,9 @@
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.api.SpiSqlUpdate;
import io.ebeaninternal.dbmigration.model.visitor.BaseTablePropertyVisitor;
import io.ebeaninternal.dbmigration.model.visitor.VisitAllUsing;
import io.ebeaninternal.server.core.DefaultSqlUpdate;
@@ -59,8 +59,8 @@ class BeanPropertyAssocManySqlHelp<T> {
return sb.toString();
}
String insertElementCollection() {
return elementCollectionInsertSql;
SpiSqlUpdate insertElementCollection() {
return new DefaultSqlUpdate(elementCollectionInsertSql);
}
private static class Cols extends BaseTablePropertyVisitor {
@@ -165,13 +165,13 @@ class BeanPropertyAssocManySqlHelp<T> {
return server.findIds(q, t);
}
SqlUpdate deleteByParentId(Object parentId) {
SpiSqlUpdate deleteByParentId(Object parentId) {
DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByParentIdSql);
many.bindParentId(sqlDelete, parentId);
return sqlDelete;
}
SqlUpdate deleteByParentIdList(List<Object> parentIds) {
SpiSqlUpdate deleteByParentIdList(List<Object> parentIds) {
StringBuilder sb = new StringBuilder(100);
sb.append(deleteByParentIdInSql);
@@ -39,11 +39,8 @@ import java.util.Map;
public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STreePropertyAssocOne {
private final boolean oneToOne;
private final boolean oneToOneExported;
private final boolean orphanRemoval;
private final boolean primaryKeyExport;
private final boolean primaryKeyJoin;
@@ -89,7 +86,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
for (BeanProperty embeddedProp : embeddedProps) {
embeddedPropsMap.put(embeddedProp.getName(), embeddedProp);
}
} else {
embeddedProps = null;
embeddedPropsMap = null;
@@ -811,4 +807,8 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
}
}
}
public boolean hasCircularImportedId(BeanDescriptor<?> sourceDesc) {
return targetDescriptor.hasCircularImportedIdTo(sourceDesc);
}
}
@@ -60,6 +60,11 @@ public interface DbReadContext {
*/
void register(String path, EntityBeanIntercept ebi);
/**
* Register a reference with inheritance for lazy loading.
*/
void registerBeanInherit(BeanPropertyAssocOne<?> property, EntityBeanIntercept ebi);
/**
* Register a collection for lazy loading.
*/
@@ -10,7 +10,7 @@ public interface DbSqlContext {
/**
* Add a join to the sql query.
*/
void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2, String inheritance);
void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2);
/**
* Push the current table alias onto the stack.
@@ -219,9 +219,6 @@ public class InheritInfo {
* Return the associated InheritInfo for this DB row read.
*/
public InheritInfo readType(DbReadContext ctx) throws SQLException {
if (!hasChildren()) {
return this;
}
return readType(ctx.getDataReader().getString());
}
@@ -154,11 +154,8 @@ public final class TableJoin {
public SqlJoinType addJoin(SqlJoinType joinType, String a1, String a2, DbSqlContext ctx) {
String inheritance = inheritInfo != null ? inheritInfo.getWhere() : null;
String joinLiteral = joinType.getLiteral(type);
ctx.addJoin(joinLiteral, table, columns(), a1, a2, inheritance);
ctx.addJoin(joinLiteral, table, columns(), a1, a2);
return joinType.autoToOuter(type);
}
@@ -27,6 +27,8 @@ public class GeneratedPropertyFactory {
private final HashSet<String> numberTypes = new HashSet<>();
private final UuidGeneratedProperty generatedUuid = new UuidGeneratedProperty();
private final GeneratedWhoModified generatedWhoModified;
private final GeneratedWhoCreated generatedWhoCreated;
@@ -123,6 +125,10 @@ public class GeneratedPropertyFactory {
return idGeneratorMap.get(generatorName);
}
public void setUuid(DeployBeanProperty prop) {
prop.setGeneratedProperty(generatedUuid);
}
/**
* Wraps the custom IdGenerator to implement PlatformIdGenerator.
*/
@@ -0,0 +1,40 @@
package io.ebeaninternal.server.deploy.generatedproperty;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.deploy.BeanProperty;
import java.util.UUID;
public class UuidGeneratedProperty implements GeneratedProperty {
@Override
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
return UUID.randomUUID();
}
@Override
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
return null;
}
@Override
public boolean includeInInsert() {
return true;
}
@Override
public boolean isDDLNotNullable() {
return true;
}
@Override
public boolean includeInUpdate() {
return false;
}
@Override
public boolean includeInAllUpdates() {
return false;
}
}
@@ -263,11 +263,9 @@ public final class IdBinderEmbedded implements IdBinder {
public Object convertIdFromJson(Object value) {
Map<String, Object> map = (Map<String, Object>) value;
EntityBean idValue = idDesc.createEntityBean();
for (BeanProperty prop : props) {
Object val = map.get(prop.getName());
prop.setValue(idValue, val);
prop.setValue(idValue, map.get(prop.getName()));
}
return idValue;
}
@@ -465,7 +463,14 @@ public final class IdBinderEmbedded implements IdBinder {
@Override
public Object convertId(Object idValue) {
// can not cast/convert if it is embedded
if (idValue instanceof String) {
final EntityBean embId = idDesc.createEntityBean();
final String[] rawVals = ((String)idValue).split("\\|");
for (int i = 0; i < props.length; i++) {
props[i].setValue(embId, props[i].parse(rawVals[i]));
}
return embId;
}
return idValue;
}
@@ -131,8 +131,6 @@ public class DeployBeanDescriptor<T> {
*/
private ConcurrencyMode concurrencyMode;
private boolean updateChangesOnly;
private List<IndexDefinition> indexDefinitions;
private String storageEngine;
@@ -523,14 +521,6 @@ public class DeployBeanDescriptor<T> {
this.concurrencyMode = concurrencyMode;
}
public boolean isUpdateChangesOnly() {
return updateChangesOnly;
}
public void setUpdateChangesOnly(boolean updateChangesOnly) {
this.updateChangesOnly = updateChangesOnly;
}
/**
* Add a compound unique constraint.
*/
@@ -45,6 +45,8 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
private PropertyForeignKey foreignKey;
boolean orphanRemoval;
/**
* Construct the property.
*/
@@ -148,6 +150,14 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
}
}
public void setOrphanRemoval() {
orphanRemoval = true;
}
public boolean isOrphanRemoval() {
return orphanRemoval;
}
/**
* Set DocStoreEmbedded deployment information.
*/
@@ -21,8 +21,6 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
private String columnPrefix;
private boolean orphanRemoval;
/**
* Create the property.
*/
@@ -145,14 +143,6 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
}
}
public void setOrphanRemoval(boolean orphanRemoval) {
this.orphanRemoval = orphanRemoval;
}
public boolean isOrphanRemoval() {
return orphanRemoval;
}
public void setJoinType(boolean outerJoin) {
tableJoin.setType(outerJoin ? SqlJoinType.OUTER : SqlJoinType.INNER);
}
@@ -10,7 +10,6 @@ import io.ebeaninternal.server.deploy.BeanPropertyIdClass;
import io.ebeaninternal.server.deploy.BeanPropertyOrderColumn;
import io.ebeaninternal.server.deploy.BeanPropertySimpleCollection;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import io.ebeaninternal.server.properties.BeanPropertySetter;
import io.ebeaninternal.server.type.ScalarTypeString;
@@ -54,6 +53,8 @@ public class DeployBeanPropertyLists {
private final List<BeanProperty> nonManys = new ArrayList<>();
private final List<BeanProperty> aggs = new ArrayList<>();
private final List<BeanPropertyAssocOne<?>> ones = new ArrayList<>();
private final List<BeanPropertyAssocOne<?>> onesImported = new ArrayList<>();
@@ -232,6 +233,9 @@ public class DeployBeanPropertyLists {
} else {
nonManys.add(prop);
if (prop.isAggregation()) {
aggs.add(prop);
}
if (prop.isTenantId()) {
tenant = prop;
}
@@ -331,6 +335,10 @@ public class DeployBeanPropertyLists {
return nonManys.toArray(new BeanProperty[0]);
}
public BeanProperty[] getAggregates() {
return aggs.toArray(new BeanProperty[0]);
}
public BeanPropertyAssocMany<?>[] getMany() {
return manys.toArray(new BeanPropertyAssocMany[0]);
}
@@ -3,7 +3,6 @@ package io.ebeaninternal.server.deploy.parse;
import io.ebean.annotation.DbForeignKey;
import io.ebean.annotation.FetchPreference;
import io.ebean.annotation.HistoryExclude;
import io.ebean.annotation.PrivateOwned;
import io.ebean.annotation.Where;
import io.ebean.bean.BeanCollection.ModifyListenMode;
import io.ebean.config.BeanNotRegisteredException;
@@ -84,14 +83,10 @@ class AnnotationAssocManys extends AnnotationParser {
if (oneToMany != null) {
readToOne(oneToMany, prop);
if (readOrphanRemoval(oneToMany)) {
prop.setOrphanRemoval();
prop.setModifyListenMode(ModifyListenMode.REMOVALS);
prop.getCascadeInfo().setDelete(true);
}
PrivateOwned privateOwned = get(prop, PrivateOwned.class);
if (privateOwned != null) {
prop.setModifyListenMode(ModifyListenMode.REMOVALS);
prop.getCascadeInfo().setDelete(privateOwned.cascadeRemove());
}
OrderColumn orderColumn = get(prop, OrderColumn.class);
if (orderColumn != null) {
// need to cascade as we set the order on cascade
@@ -225,7 +225,9 @@ public class AnnotationAssocOnes extends AnnotationParser {
prop.setNullable(propAnn.optional());
prop.setFetchType(propAnn.fetch());
prop.setMappedBy(propAnn.mappedBy());
prop.setOrphanRemoval(readOrphanRemoval(propAnn));
if (readOrphanRemoval(propAnn)) {
prop.setOrphanRemoval();
}
if (!"".equals(propAnn.mappedBy())) {
prop.setOneToOneExported();
}
@@ -11,7 +11,6 @@ import io.ebean.annotation.Index;
import io.ebean.annotation.InvalidateQueryCache;
import io.ebean.annotation.ReadAudit;
import io.ebean.annotation.StorageEngine;
import io.ebean.annotation.UpdateMode;
import io.ebean.annotation.View;
import io.ebean.config.TableName;
import io.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
@@ -135,12 +134,12 @@ public class AnnotationClass extends AnnotationParser {
}
for (Index index : findAnnotationsRecursive(cls, Index.class)) {
descriptor.addIndex(new IndexDefinition(index.columnNames(), index.name(), index.unique()));
descriptor.addIndex(new IndexDefinition(convertColumnNames(index.columnNames()), index.name(), index.unique()));
}
UniqueConstraint uc = findAnnotationRecursive(cls, UniqueConstraint.class);
if (uc != null) {
descriptor.addIndex(new IndexDefinition(uc.columnNames()));
descriptor.addIndex(new IndexDefinition(convertColumnNames(uc.columnNames())));
}
View view = findAnnotationRecursive(cls, View.class);
@@ -151,7 +150,7 @@ public class AnnotationClass extends AnnotationParser {
if (table != null) {
UniqueConstraint[] uniqueConstraints = table.uniqueConstraints();
for (UniqueConstraint c : uniqueConstraints) {
descriptor.addIndex(new IndexDefinition(c.columnNames()));
descriptor.addIndex(new IndexDefinition(convertColumnNames(c.columnNames())));
}
}
@@ -190,11 +189,6 @@ public class AnnotationClass extends AnnotationParser {
descriptor.setDbComment(comment.value());
}
UpdateMode updateMode = findAnnotationRecursive(cls, UpdateMode.class);
if (updateMode != null) {
descriptor.setUpdateChangesOnly(updateMode.updateChangesOnly());
}
if (!disableL2Cache) {
Cache cache = findAnnotationRecursive(cls, Cache.class);
if (cache != null) {
@@ -5,7 +5,7 @@ import io.ebean.annotation.CreatedTimestamp;
import io.ebean.annotation.DbArray;
import io.ebean.annotation.DbComment;
import io.ebean.annotation.DbDefault;
import io.ebean.annotation.DbHstore;
import io.ebean.annotation.DbMap;
import io.ebean.annotation.DbJson;
import io.ebean.annotation.DbJsonB;
import io.ebean.annotation.DbMigration;
@@ -202,12 +202,11 @@ public class AnnotationFields extends AnnotationParser {
prop.setDbColumn(dbColumn);
}
Id id = get(prop, Id.class);
GeneratedValue gen = get(prop, GeneratedValue.class);
if (gen != null) {
readGenValue(gen, prop);
readGenValue(gen, id, prop);
}
Id id = get(prop, Id.class);
if (id != null) {
readIdScalar(prop);
}
@@ -246,9 +245,9 @@ public class AnnotationFields extends AnnotationParser {
if (comment != null) {
prop.setDbComment(comment.value());
}
DbHstore dbHstore = get(prop, DbHstore.class);
if (dbHstore != null) {
util.setDbHstore(prop, dbHstore);
DbMap dbMap = get(prop, DbMap.class);
if (dbMap != null) {
util.setDbMap(prop, dbMap);
}
DbJson dbJson = get(prop, DbJson.class);
if (dbJson != null) {
@@ -519,8 +518,13 @@ public class AnnotationFields extends AnnotationParser {
return util.createDataEncryptSupport(table, column);
}
private void readGenValue(GeneratedValue gen, DeployBeanProperty prop) {
private void readGenValue(GeneratedValue gen, Id id, DeployBeanProperty prop) {
if (id == null) {
if (UUID.class.equals(prop.getPropertyType())) {
generatedPropFactory.setUuid(prop);
return;
}
}
descriptor.setIdGeneratedValue();
String genName = gen.generator();
@@ -103,8 +103,7 @@ public abstract class AnnotationParser extends AnnotationBase {
void readColumn(Column columnAnn, DeployBeanProperty prop) {
if (!isEmpty(columnAnn.name())) {
String dbColumn = databasePlatform.convertQuotedIdentifiers(columnAnn.name());
prop.setDbColumn(dbColumn);
prop.setDbColumn(databasePlatform.convertQuotedIdentifiers(columnAnn.name()));
}
prop.setDbInsertable(columnAnn.insertable());
@@ -138,4 +137,11 @@ public abstract class AnnotationParser extends AnnotationBase {
}
return groups.length == 0 || groups.length == 1 && Default.class.isAssignableFrom(groups[0]);
}
String[] convertColumnNames(String[] columnNames) {
for (int i = 0; i < columnNames.length; i++) {
columnNames[i] = databasePlatform.convertQuotedIdentifiers(columnNames[i]);
}
return columnNames;
}
}
@@ -2,7 +2,7 @@ package io.ebeaninternal.server.deploy.parse;
import io.ebean.Model;
import io.ebean.annotation.DbArray;
import io.ebean.annotation.DbHstore;
import io.ebean.annotation.DbMap;
import io.ebean.annotation.DbJson;
import io.ebean.annotation.DbJsonB;
import io.ebean.annotation.UnmappedJson;
@@ -274,7 +274,7 @@ public class DeployCreateProperties {
return (AnnotationUtil.findAnnotation(field, DbJson.class) != null)
|| (AnnotationUtil.findAnnotation(field, DbJsonB.class) != null)
|| (AnnotationUtil.findAnnotation(field, DbArray.class) != null)
|| (AnnotationUtil.findAnnotation(field, DbHstore.class) != null)
|| (AnnotationUtil.findAnnotation(field, DbMap.class) != null)
|| (AnnotationUtil.findAnnotation(field, UnmappedJson.class) != null);
}
@@ -1,7 +1,7 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.annotation.DbArray;
import io.ebean.annotation.DbHstore;
import io.ebean.annotation.DbMap;
import io.ebean.annotation.DbJson;
import io.ebean.annotation.DbJsonB;
import io.ebean.annotation.DbJsonType;
@@ -171,15 +171,15 @@ public class DeployUtil {
/**
* Map to Postgres HSTORE type (with fallback to JSON storage in VARCHAR).
*/
void setDbHstore(DeployBeanProperty prop, DbHstore dbHstore) {
void setDbMap(DeployBeanProperty prop, DbMap dbMap) {
ScalarType<?> scalarType = typeManager.getHstoreScalarType();
ScalarType<?> scalarType = typeManager.getDbMapScalarType();
int dbType = scalarType.getJdbcType();
prop.setDbType(dbType);
prop.setScalarType(scalarType);
if (dbType == Types.VARCHAR) {
// this is actually the fallback of JSON storage into VARCHAR
int dbLength = dbHstore.length();
int dbLength = dbMap.length();
int columnLength = (dbLength > 0) ? dbLength : DEFAULT_JSON_VARCHAR_LENGTH;
prop.setDbLength(columnLength);
}
@@ -104,7 +104,7 @@ public class SimpleExpression extends AbstractValueExpression {
return;
}
if (prop.isDbEncrypted()) {
String dsql = prop.getBeanProperty().getDecryptSql();
String dsql = prop.getBeanProperty().getDecryptProperty(propName);
request.append(dsql).append(type.bind());
return;
}
@@ -17,6 +17,7 @@ public class DbExpressionHandlerFactory {
return new H2DbExpression();
case POSTGRES:
return new PostgresDbExpression();
case MYSQL55:
case MYSQL:
return new MySqlDbExpression();
case ORACLE:

Some files were not shown because too many files have changed in this diff Show More