Compare commits

..
Author SHA1 Message Date
Rob Bygrave 4b72924529 [maven-release-plugin] prepare release ebean-11.5.1 2017-11-13 23:17:54 +13:00
Rob Bygrave a75ed676f8 Bump pom version to 11.5.1-SNAPSHOT 2017-11-13 23:16:38 +13:00
Rob Bygrave c3438b888d #995 - @OneToMany with constraints on the many side 2017-11-13 22:43:56 +13:00
Rob Bygrave 56be274acc No effective change - change internal use of ArrayList to List 2017-11-13 22:42:39 +13:00
Rob Bygrave 84bb4ff78c #1207 - Regression added in 11.4.1 with NPE on @transactional(type = TxType.SUPPORTS) 2017-11-13 21:33:39 +13:00
Rob Bygrave 4054eda1f6 #1207 - Regression added in 11.4.1 with NPE on @transactional(type = TxType.SUPPORTS) 2017-11-13 21:32:52 +13:00
Rob Bygrave a4b9354e43 #1206 - NPE when using @Encrypted with Enum property that is null 2017-11-13 19:12:57 +13:00
Rob Bygrave 06e033f23f #1181 - ENH: Add @FetchPreference support - Secondary query generation is based on order of fetch statements 2017-11-08 23:20:08 +13:00
Rob Bygrave 156f505394 #1181 - ENH: Add @FetchPreference support - Secondary query generation is based on order of fetch statements 2017-11-08 23:19:58 +13:00
Rob Bygrave 9b2b2f7787 #1205 - ENH: Add support for raw expression array binding with Postgres - e.g. raw(" foo = any(?) ", collection) 2017-11-08 00:56:13 +13:00
Rob Bygrave e68dae0baa Update ebean-annotation to 3.3 and tests to use 11.5.1 agent 2017-11-07 22:57:04 +13:00
Rob Bygrave 2cf864954f #1204 - Check presence of JAXB and javax @PostConstruct (which may not be present in minimal Java 9) 2017-11-07 20:08:10 +13:00
Rob Bygrave 2b94487573 #1202 - Automatically remove old query plans from Ebean's internal query plan cache 2017-11-06 22:59:42 +13:00
Rob Bygrave fcf730ec39 [maven-release-plugin] prepare for next development iteration 2017-11-06 14:49:08 +13:00
Rob Bygrave 7d343455ac [maven-release-plugin] prepare release ebean-11.4.1 2017-11-06 14:48:55 +13:00
Rob Bygrave 21e0b748a6 #1199 - Remove deprecated findUnique() method - please migrate to findOne() 2017-11-03 03:35:19 +13:00
Rob Bygrave 2fab733e4e Update agent and maven plugin for 11.4.1 enhancement (@Transactional and PropertyChangeListener changes) 2017-11-03 03:30:53 +13:00
Rob Bygrave 7260a1b6eb #1198 - Remove support for PropertyChangeListener from entity beans 2017-11-03 03:08:43 +13:00
Rob Bygrave 434a005366 #1198 - Remove support for PropertyChangeListener from entity beans 2017-11-03 03:05:59 +13:00
Rob Bygrave ff67ef60b0 #1197 - Refactor how @Transactional methods internally managed 2017-11-03 02:13:56 +13:00
Rob Bygrave 0a1747d8e3 #1194 - Change from using Jetbrains @Nullable to ... class retention based javax annotation @Nonnull 2017-11-02 15:56:15 +13:00
Rob Bygrave f36499f71f No effective change - remove use of jetbrains @NotNull in tests 2017-11-02 15:26:35 +13:00
rob bygrave da818f17c2 #1190 - MySql throwing DataIntegrityException when it should throw the more specific DuplicateKeyException
Ah, remove the 23000 SQLState to just use the error codes for MySql.
2017-11-02 01:16:52 +13:00
rob bygrave 10eb2bc21f #1193 - For RawSql ignore the Postgres cast double colon like ::text 2017-11-02 00:03:38 +13:00
rob bygrave 66fe278e3f #1192 - findNative() with @Embedded beans ... the embedded bean isn't loaded properly 2017-11-01 23:03:57 +13:00
rob bygrave 10607385e0 #1191 - Trim logged SQL removing new line chars (for sql from SqlUpdate) 2017-11-01 22:00:55 +13:00
rob bygrave db6562df76 #1190 - MySql throwing DataIntegrityException when it should throw the more specific DuplicateKeyException 2017-11-01 21:54:48 +13:00
rob bygrave c0ec87d4cd #1189 - ExpressionList.isNull and SoftDelete 2017-11-01 21:48:23 +13:00
rob bygrave 0dbba7a211 #1189 - ExpressionList.isNull and SoftDelete 2017-11-01 21:26:15 +13:00
rob bygrave e75670f207 #1188 - When using inheritance getReference() on a leaf type ... should not hit the DB 2017-11-01 20:42:18 +13:00
rob bygrave fb8db14f3a #1186 - Tidy, remove H2MultiValueBind for now 2017-10-31 22:14:01 +13:00
rob bygrave c00b1849ef Merge branch 'feature/1186-MultiValueBind' 2017-10-31 22:11:30 +13:00
rob bygrave 1e1c518641 #1186 - ENH: Support using TableValues / Array for select prop in (?) queries
Use Postgres Array ANY and ALL without select unnest
2017-10-31 22:09:35 +13:00
Rob Bygrave d5b42f9ba1 [maven-release-plugin] prepare for next development iteration 2017-10-31 10:52:44 +13:00
Rob Bygrave bda564d2c8 [maven-release-plugin] prepare release ebean-11.3.1 2017-10-31 10:52:31 +13:00
Rob Bygrave dce124cabd #1187 - Fix connection leak regression introduced in 11.2.3 for findEach() with invalid SQL 2017-10-31 10:50:20 +13:00
Rob Bygrave f5cfcbd56e #1187 - Fix connection leak regression introduced in 11.2.3 for findEach() with invalid SQL 2017-10-31 10:49:35 +13:00
rob bygrave 5bccdccbd6 #1186 - ENH: Support using TableValues / Array for select prop in (?) queries 2017-10-31 02:53:34 +13:00
rob bygrave 6e62b72fb9 #1186 - ENH: Support using TableValues / Array for select prop in (?) queries 2017-10-31 02:53:20 +13:00
rob bygrave 89dac3820b #1182 - Error when applied disjunction to filterMany 2017-10-30 22:06:57 +13:00
rob bygrave ea836a0078 No effective change - rename variable 2017-10-30 21:29:57 +13:00
rob bygrave 1ffd7c7720 git push origin masterMerge branch 'OndrejPacay-feature/1179_bugCollectionL2CacheNotInvalidatedAfterExternalChange' 2017-10-30 21:25:43 +13:00
rob bygrave 387582543c Merge branch 'feature/1179_bugCollectionL2CacheNotInvalidatedAfterExternalChange' of https://github.com/OndrejPacay/ebean into OndrejPacay-feature/1179_bugCollectionL2CacheNotInvalidatedAfterExternalChange 2017-10-30 21:25:30 +13:00
rob bygrave a215878a7c #1183 - ENH: Add support for transaction profiling 2017-10-30 21:21:12 +13:00
rob bygrave dbedb65922 #1183 - ENH: Add support for transaction profiling 2017-10-30 21:19:58 +13:00
Rob Bygrave 1d52b3a818 #1183 - Support transaction profiling, adjust output format 2017-10-27 00:27:21 +13:00
Rob Bygrave 6ce2b91fff #1183 - Support transaction profiling, move away from binary encoding 2017-10-26 23:32:54 +13:00
Rob Bygrave 55d66ae851 #1183 - Support transaction profiling, move away from binary encoding 2017-10-26 23:32:44 +13:00
Rob Bygrave 37d001f754 #1185 - Modify JDBC batch so it does not "auto flush" on non-Id non-generated property 2017-10-26 00:03:58 +13:00
rob bygrave c121618c0c #1183 - ENH: Add support for transaction profiling 2017-10-25 01:31:09 +13:00
rob bygrave 7ebef51859 #1183 - ENH: Add support for transaction profiling 2017-10-25 01:07:00 +13:00
rob bygrave 093f82a87f #1178 - Make the new changeLogAsync property default to true in ServerConfig 2017-10-19 22:26:13 +13:00
Ondrej Pacay b60f1f978f Bug Collection IDs L2 cache not cleared on external sql modification of the table #1179 2017-10-19 01:06:43 +02:00
Roland PramlandRob Bygrave 6ac146cabe Fix beanchange deser (#1178)
Fix / support JSON marshalling of BeanChange + add changeLogAsync option to ServerConfig
2017-10-19 09:40:27 +13:00
Roland PramlandRob Bygrave 2263d31f46 Improved the performance of findAnnotation (#1177) 2017-10-19 09:34:57 +13:00
rob bygrave 4a66d0be6b [maven-release-plugin] prepare for next development iteration 2017-10-19 01:26:28 +13:00
rob bygrave e68e496207 [maven-release-plugin] prepare release ebean-11.2.3 2017-10-19 01:26:12 +13:00
rob bygrave 088110385f Bump ebean-migration dependency to 11.1.1 2017-10-19 01:19:51 +13:00
rob bygrave a355f66561 #1176 - Allow script in extra-ddl.xml ... to not have trailing semi-colon 2017-10-19 01:11:14 +13:00
rob bygrave 00fdb632d7 #1173 - Ebean server should read ebean.mf entity-packages to locate entity beans 2017-10-18 23:58:22 +13:00
rob bygrave a54c4157e6 Refactor as part of - Ensure that Ebean.currentTransaction is available in lifecycle handlers (#1166) #1175 2017-10-18 23:27:59 +13:00
Rob Bygrave a89a9f0202 #1166 - Refactor to use beginServerTransaction() to make Ebean.currentTransaction available in lifecycle handlers 2017-10-17 01:07:48 +13:00
Rob Bygrave 331f49824f #1172 - Refactor - remove internal unused DefaultOrmQuery logSecondaryQuery (as we use LoggedSqlCollector) 2017-10-16 23:49:23 +13:00
Rob Bygrave 94a857d345 #1171 - ENH: Add Finder.update() method ... returning a UpdateQuery<T> 2017-10-16 23:25:56 +13:00
Rob Bygrave 1005f177fd #1170 - ENH: Add isIn() methods as alias to in() on ExpressionList (for use with Kotlin where in is a keyword) 2017-10-16 23:13:49 +13:00
Rob Bygrave 96a4b93c75 #1167 - BUG: query.setId(...).delete() does not work 2017-10-16 22:58:48 +13:00
Rob Bygrave 5869187368 [maven-release-plugin] prepare for next development iteration 2017-10-16 22:26:39 +13:00
Rob Bygrave eac50b8f5b [maven-release-plugin] prepare release ebean-11.2.2 2017-10-16 22:26:21 +13:00
Rob Bygrave 6e2712ea49 #1169 - Memory leak - DefaultTransactionThreadLocal holding reference to transaction 2017-10-16 22:14:48 +13:00
Rob Bygrave f6e5446dab #1169 - Memory leak - DefaultTransactionThreadLocal holding reference to transaction - test case 2017-10-16 22:13:41 +13:00
Koen De GrooteandRob Bygrave 4879d44da4 Bit of cleanup suggestions from intellij. (#1168) 2017-10-16 20:16:30 +13:00
rob bygrave 2c5bf98d0b Merge branch 'master' of github.com:ebean-orm/ebean 2017-10-09 21:13:32 +13:00
rob bygrave 45a571bd97 #1165 - ENH: Add to Finder - currentTransaction() and flush() methods ... (as useful shortcuts) 2017-10-09 21:13:12 +13:00
Roland PramlandRob Bygrave d0ca75c25b FIX: connection leak in query-iterator (#1164) 2017-10-09 20:46:02 +13:00
Roland PramlandRob Bygrave fb1c7667cc FIX: order by must be added in findEach also (#1163) 2017-10-09 20:28:36 +13:00
Roland PramlandRob Bygrave 1909ba1557 Fix like escaping for DB2, Oracle and MySql (#1162)
* Extended test case to show problem

* FIX: Proper escaping of backslash in like/contains queries

* Had to replace "like ?" assertions in tests with "like " as mysql will do a "like binary ? escape'|'" now to be case sensitive
2017-10-06 09:46:56 +13:00
Roland PramlandRob Bygrave e4eca4bbe9 FIX: Oto relations must match datatype (#1161) 2017-10-06 09:44:06 +13:00
Roland PramlandRob Bygrave b2438ecd71 FIX: do not generate unique-constraint for PK as it is useless (and oracle does not accept this) (#1160) 2017-10-06 09:43:30 +13:00
Roland PramlandRob Bygrave ed08567d06 FIX: bundle symbolic name in manifest (#1159) 2017-10-06 09:42:42 +13:00
Roland PramlandRob Bygrave bbe8abcc2e Can handle one to many & many to one relations in docstore only entities (#1158) 2017-10-06 09:42:05 +13:00
Roland PramlandRob Bygrave 1651d5e119 Misc platform fixes (#1157)
* Oracle does not return row count - so we probably cannot detect Optimistic locking

* DB2 wants a varchar null value (other platfroms are happy with this, too)

* FIX Test for DB2 platform
2017-10-05 21:51:09 +13:00
Roland PramlandRob Bygrave 64bf6d6a35 commit transaction only if active (#1156) 2017-10-05 11:04:12 +13:00
Roland PramlandRob Bygrave 47b0a917a2 No effective code change - fix imports and override annotations (#1155) 2017-10-05 10:55:48 +13:00
rob bygrave 2fe1a349c3 [maven-release-plugin] prepare for next development iteration 2017-10-05 01:36:46 +13:00
rob bygrave f168a8fd0d [maven-release-plugin] prepare release ebean-11.2.1 2017-10-05 01:36:30 +13:00
rob bygrave dda98c1dda Bump to 11.2.1-SNAPSHOT 2017-10-05 01:35:30 +13:00
rob bygrave 88a13c5416 #1154 - Remove deprecated TxCallable and TxRunnable - migrate to use Callable and Runnable 2017-10-05 01:24:53 +13:00
rob bygrave 16b8f5da35 Refactor EbeanServerFactory internals - use SpiContainerFactory interface and service loader 2017-10-05 01:12:07 +13:00
rob bygrave 3471f6d206 Refactor EbeanServerFactory internals - use SpiContainerShutdown interface for container shutdown mechanism 2017-10-05 00:57:40 +13:00
rob bygrave 0bf4c84bc8 #1153 - Refactor move SplitName into io.ebean.util 2017-10-05 00:41:32 +13:00
rob bygrave d366336ee5 #1153 - Refactor move ExtraTypeFactory out of io.ebean.plugin into io.ebeaninternal 2017-10-05 00:38:42 +13:00
rob bygrave bf3f8ecff5 Refactor plugin.BeanType addInheritanceWhere() to just take Query and not SpiQuery 2017-10-05 00:35:12 +13:00
rob bygrave 7524903aea #1152 - Refactor EJson internals extracting io.ebean.service.SpiJsonService 2017-10-05 00:29:43 +13:00
rob bygrave 90cf3fddc9 #1151 - Refactor add util AnnotationUtil ... moving common annotation searching methods there 2017-10-05 00:00:44 +13:00
rob bygrave 8e0f9f7591 Refactor - remove unused methods from StringHelper 2017-10-04 23:35:57 +13:00
rob bygrave 24936c0a41 Refactor - remove unused class ClassUtil 2017-10-04 23:34:13 +13:00
rob bygrave f02a552cdf #1150 - Refactor convert RawSql and RawSqlBuilder into interfaces and push the parsers into io.ebeaninternal 2017-10-04 23:03:12 +13:00
rob bygrave 5a5cb7ffbd #1149 - Refactor move PersistenceContextUtil into ... io.ebeaninternal 2017-10-04 21:12:39 +13:00
rob bygrave f85fa772bf #1147 - Refactor move DbMigration internals into io.ebeaninternal - change to use DbMigration.create() - move DbMigrationFactory.create() method 2017-10-04 21:06:55 +13:00
rob bygrave 9f9ec8ef9f #1147 - Refactor move DbMigration internals into io.ebeaninternal - change to use DbMigration.create() - move IdentityType handling into internal from DbIdentity 2017-10-04 20:49:04 +13:00
rob bygrave 62707caf64 #1147 - Refactor move DbMigration internals into io.ebeaninternal - change to use DbMigration.create() - move PlatformDdl into internal 2017-10-04 20:30:57 +13:00
rob bygrave c6fc7a032d #1147 - Refactor move DbMigration internals into io.ebeaninternal - change to use DbMigration.create() - DbOffline & ServerConfig 2017-10-04 19:44:29 +13:00
rob bygrave 10faa1fae6 #1147 - Refactor move DbMigration internals into io.ebeaninternal - change to use DbMigration.create() - initial move 2017-10-04 19:36:27 +13:00
Rob Bygrave 764f5ce4d4 [maven-release-plugin] prepare for next development iteration 2017-10-03 23:20:50 +13:00
474 changed files with 8614 additions and 4558 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ ebean-profiling*.xml
/db
/mydb.db
!src/test/ddl-review/*.sql
profiling/
# Intellij project files
*.iml
+16 -11
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.1.1</version>
<version>11.5.1</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -17,7 +17,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-11.1.1</tag>
<tag>ebean-11.5.1</tag>
</scm>
<profiles>
@@ -72,10 +72,15 @@
</profiles>
<dependencies>
<!--
Class retention Nonnull and Nullable annotations
to assist with IDE auto-completion with Ebean API
-->
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>13.0</version>
<groupId>io.ebean</groupId>
<artifactId>ebean-jsr305</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -87,7 +92,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>3.1</version>
<version>3.4</version>
</dependency>
<dependency>
@@ -117,7 +122,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration</artifactId>
<version>10.3.1</version>
<version>11.1.1</version>
</dependency>
<dependency>
@@ -210,7 +215,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-agent</artifactId>
<version>11.1.1</version>
<version>11.5.1</version>
<scope>test</scope>
</dependency>
@@ -219,7 +224,7 @@
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.193</version>
<version>1.4.196</version>
<scope>provided</scope>
</dependency>
@@ -283,7 +288,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>11.1.1</version>
<version>11.5.1</version>
<executions>
<execution>
<id>test</id>
@@ -340,7 +345,7 @@
<overview>src/main/java/io/ebean/overview.html</overview>
<source>1.8</source>
<doclet>org.avaje.doclet.PygmentsDoclet</doclet>
<excludePackageNames>io.ebeaninternal.*:com.avaje.ebean.util:io.ebean.dbmigration.ddlgeneration:io.ebean.dbmigration.migration:io.ebean.dbmigration.migrationreader:io.ebean.dbmigration.model</excludePackageNames>
<excludePackageNames>io.ebeaninternal.*:com.avaje.ebean.util</excludePackageNames>
<docletArtifact>
<groupId>org.avaje</groupId>
<artifactId>pygments-doclet</artifactId>
-11
View File
@@ -1,6 +1,5 @@
package io.ebean;
import java.beans.PropertyChangeListener;
import java.util.Map;
import java.util.Set;
@@ -99,16 +98,6 @@ public interface BeanState {
*/
void setReadOnly(boolean readOnly);
/**
* Add a propertyChangeListener.
*/
void addPropertyChangeListener(PropertyChangeListener listener);
/**
* Remove a propertyChangeListener.
*/
void removePropertyChangeListener(PropertyChangeListener listener);
/**
* Advanced - Used to programmatically build a partially or fully loaded
* entity bean. First create an entity bean via
+1 -1
View File
@@ -2,7 +2,7 @@ package io.ebean;
import io.ebeanservice.docstore.api.DocQueryRequest;
import io.ebeanservice.docstore.api.RawDoc;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.List;
+1 -17
View File
@@ -5,7 +5,7 @@ import io.ebean.cache.ServerCacheManager;
import io.ebean.config.ServerConfig;
import io.ebean.text.csv.CsvReader;
import io.ebean.text.json.JsonContext;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -1241,14 +1241,6 @@ public final class Ebean {
return serverMgr.getDefaultServer().executeCall(scope, c);
}
/**
* Deprecated - please migrate to executeCall().
*/
@Deprecated
public static <T> T execute(TxScope scope, TxCallable<T> c) {
return serverMgr.getDefaultServer().executeCall(scope, c);
}
/**
* Execute a Callable in a Transaction with the default scope.
* <p>
@@ -1282,14 +1274,6 @@ public final class Ebean {
return serverMgr.getDefaultServer().executeCall(c);
}
/**
* Deprecated - please migrate to executeCall().
*/
@Deprecated
public static <T> T execute(TxCallable<T> c) {
return serverMgr.getDefaultServer().execute(c);
}
/**
* Inform Ebean that tables have been modified externally. These could be the
* result of from calling a stored procedure, other JDBC calls or external
+18 -43
View File
@@ -7,7 +7,9 @@ import io.ebean.meta.MetaInfoManager;
import io.ebean.plugin.SpiServer;
import io.ebean.text.csv.CsvReader;
import io.ebean.text.json.JsonContext;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.NonUniqueResultException;
import javax.persistence.OptimisticLockException;
@@ -732,6 +734,7 @@ public interface EbeanServer {
* @param beanType the type of entity bean to fetch
* @param id the id value
*/
@Nullable
<T> T find(Class<T> beanType, Object id);
/**
@@ -775,6 +778,7 @@ public interface EbeanServer {
* @param beanType the type of entity bean
* @param id the id value
*/
@Nonnull
<T> T getReference(Class<T> beanType, Object id);
/**
@@ -790,6 +794,7 @@ public interface EbeanServer {
*
* @see Query#findIds()
*/
@Nonnull
<A, T> List<A> findIds(Query<T> query, Transaction transaction);
/**
@@ -809,6 +814,7 @@ public interface EbeanServer {
* @see Query#findEach(Consumer)
* @see Query#findEachWhile(Predicate)
*/
@Nonnull
<T> QueryIterator<T> findIterate(Query<T> query, Transaction transaction);
/**
@@ -885,6 +891,7 @@ public interface EbeanServer {
* It will execute the query against the history returning the versions of the bean.
* </p>
*/
@Nonnull
<T> List<Version<T>> findVersions(Query<T> query, Transaction transaction);
/**
@@ -910,6 +917,7 @@ public interface EbeanServer {
* @return the list of fetched beans.
* @see Query#findList()
*/
@Nonnull
<T> List<T> findList(Query<T> query, Transaction transaction);
/**
@@ -925,6 +933,7 @@ public interface EbeanServer {
* @return a Future object for the row count query
* @see Query#findFutureCount()
*/
@Nonnull
<T> FutureRowCount<T> findFutureCount(Query<T> query, Transaction transaction);
/**
@@ -940,6 +949,7 @@ public interface EbeanServer {
* @return a Future object for the list of Id's
* @see Query#findFutureIds()
*/
@Nonnull
<T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction);
/**
@@ -956,6 +966,7 @@ public interface EbeanServer {
* @return a Future object for the list result of the query
* @see Query#findFutureList()
*/
@Nonnull
<T> FutureList<T> findFutureList(Query<T> query, Transaction transaction);
/**
@@ -987,6 +998,7 @@ public interface EbeanServer {
* @return The PagedList
* @see Query#findPagedList()
*/
@Nonnull
<T> PagedList<T> findPagedList(Query<T> query, Transaction transaction);
/**
@@ -1012,6 +1024,7 @@ public interface EbeanServer {
* @return the set of fetched beans.
* @see Query#findSet()
*/
@Nonnull
<T> Set<T> findSet(Query<T> query, Transaction transaction);
/**
@@ -1028,6 +1041,7 @@ public interface EbeanServer {
* @return the map of fetched beans.
* @see Query#findMap()
*/
@Nonnull
<K, T> Map<K, T> findMap(Query<T> query, Transaction transaction);
/**
@@ -1060,6 +1074,7 @@ public interface EbeanServer {
* @return the list of values for the selected property
* @see Query#findSingleAttributeList()
*/
@Nonnull
<A, T> List<A> findSingleAttributeList(Query<T> query, Transaction transaction);
/**
@@ -1087,21 +1102,9 @@ public interface EbeanServer {
/**
* Similar to findOne() but returns an Optional (rather than nullable).
*/
@Nonnull
<T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction);
/**
* Deprecated - please migrate to findOne().
* <p>
* This proceeded findOne which was introduced to better match spring data.
* This will be deprecated at some future point.
* </p>
*/
@Deprecated
@Nullable
default <T> T findUnique(Query<T> query, Transaction transaction) {
return findOne(query, transaction);
}
/**
* Execute as a delete query deleting the 'root level' beans that match the predicates
* in the query.
@@ -1143,6 +1146,7 @@ public interface EbeanServer {
* @return the list of fetched MapBean.
* @see SqlQuery#findList()
*/
@Nonnull
List<SqlRow> findList(SqlQuery query, Transaction transaction);
/**
@@ -1184,19 +1188,6 @@ public interface EbeanServer {
@Nullable
SqlRow findOne(SqlQuery query, Transaction transaction);
/**
* Deprecated - please migrate to findOne().
* <p>
* This proceeded findOne which was introduced to better match spring data.
* This will be deprecated at some future point.
* </p>
*/
@Deprecated
@Nullable
default SqlRow findUnique(SqlQuery query, Transaction transaction) {
return findOne(query, transaction);
}
/**
* Either Insert or Update the bean depending on its state.
* <p>
@@ -1665,14 +1656,6 @@ public interface EbeanServer {
*/
<T> T executeCall(TxScope scope, Callable<T> callable);
/**
* Deprecated - please migrate to executeCall().
*/
@Deprecated
default <T> T execute(TxScope scope, TxCallable<T> callable) {
return executeCall(scope, callable);
}
/**
* Execute a TxCallable in a Transaction with the default scope.
* <p>
@@ -1701,14 +1684,6 @@ public interface EbeanServer {
*/
<T> T executeCall(Callable<T> callable);
/**
* Deprecated - please migrate to executeCall().
*/
@Deprecated
default <T> T execute(TxCallable<T> callable) {
return executeCall(null, callable);
}
/**
* Return the manager of the server cache ("L2" cache).
*/
+9 -14
View File
@@ -1,13 +1,14 @@
package io.ebean;
import io.ebean.common.SpiContainer;
import io.ebean.config.ContainerConfig;
import io.ebean.config.ServerConfig;
import io.ebeaninternal.server.lib.ShutdownManager;
import io.ebean.service.SpiContainer;
import io.ebean.service.SpiContainerFactory;
import javax.persistence.PersistenceException;
import java.lang.reflect.Constructor;
import java.util.Iterator;
import java.util.Properties;
import java.util.ServiceLoader;
/**
* Creates EbeanServer instances.
@@ -30,8 +31,6 @@ import java.util.Properties;
public class EbeanServerFactory {
private static final String DEFAULT_CONTAINER = "io.ebeaninternal.server.core.DefaultContainer";
private static SpiContainer container;
/**
@@ -99,7 +98,7 @@ public class EbeanServerFactory {
* </p>
*/
public static synchronized void shutdown() {
ShutdownManager.shutdown();
container.shutdown();
}
@@ -135,14 +134,10 @@ public class EbeanServerFactory {
*/
protected static SpiContainer createContainer(ContainerConfig containerConfig) {
String implClassName = System.getProperty("ebean.container", DEFAULT_CONTAINER);
try {
Class<?> cls = Class.forName(implClassName);
Constructor<?> constructor = cls.getConstructor(ContainerConfig.class);
return (SpiContainer) constructor.newInstance(containerConfig);
} catch (Exception ex) {
throw new RuntimeException(ex);
Iterator<SpiContainerFactory> factories = ServiceLoader.load(SpiContainerFactory.class).iterator();
if (factories.hasNext()) {
return factories.next().create(containerConfig);
}
throw new IllegalStateException("Service loader didn't find a SpiContainerFactory?");
}
}
+47 -16
View File
@@ -5,8 +5,9 @@ import io.ebean.search.MultiMatch;
import io.ebean.search.TextCommonTerms;
import io.ebean.search.TextQueryString;
import io.ebean.search.TextSimple;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nullable;
import javax.annotation.Nonnull;
import javax.persistence.NonUniqueResultException;
import java.sql.Timestamp;
import java.util.Collection;
@@ -192,6 +193,7 @@ public interface ExpressionList<T> {
*
* @see Query#findList()
*/
@Nonnull
List<T> findList();
/**
@@ -199,6 +201,7 @@ public interface ExpressionList<T> {
*
* @see Query#findIds()
*/
@Nonnull
<A> List<A> findIds();
/**
@@ -214,6 +217,7 @@ public interface ExpressionList<T> {
*
* @see Query#findSet()
*/
@Nonnull
Set<T> findSet();
/**
@@ -221,6 +225,7 @@ public interface ExpressionList<T> {
*
* @see Query#findMap()
*/
@Nonnull
<K> Map<K, T> findMap();
/**
@@ -253,6 +258,7 @@ public interface ExpressionList<T> {
*
* @return the list of values for the selected property
*/
@Nonnull
<A> List<A> findSingleAttributeList();
/**
@@ -272,21 +278,9 @@ public interface ExpressionList<T> {
/**
* Execute the query returning an optional bean.
*/
@Nonnull
Optional<T> findOneOrEmpty();
/**
* Deprecated - please migrate to findOne().
* <p>
* This proceeded findOne which was introduced to better match spring data.
* This will be deprecated at some future point.
* </p>
*/
@Deprecated
@Nullable
default T findUnique() {
return findOne();
}
/**
* Execute find row count query in a background thread.
* <p>
@@ -297,6 +291,7 @@ public interface ExpressionList<T> {
*
* @return a Future object for the row count query
*/
@Nonnull
FutureRowCount<T> findFutureCount();
/**
@@ -309,6 +304,7 @@ public interface ExpressionList<T> {
*
* @return a Future object for the list of Id's
*/
@Nonnull
FutureIds<T> findFutureIds();
/**
@@ -321,6 +317,7 @@ public interface ExpressionList<T> {
*
* @return a Future object for the list result of the query
*/
@Nonnull
FutureList<T> findFutureList();
/**
@@ -352,6 +349,7 @@ public interface ExpressionList<T> {
* @return The PagedList
* @see Query#findPagedList()
*/
@Nonnull
PagedList<T> findPagedList();
/**
@@ -361,6 +359,7 @@ public interface ExpressionList<T> {
* It will execute the query against the history returning the versions of the bean.
* </p>
*/
@Nonnull
List<Version<T>> findVersions();
/**
@@ -370,11 +369,13 @@ public interface ExpressionList<T> {
* It will execute the query against the history returning the versions of the bean.
* </p>
*/
@Nonnull
List<Version<T>> findVersionsBetween(Timestamp start, Timestamp end);
/**
* Add some filter predicate expressions to the many property.
*/
@Nonnull
ExpressionList<T> filterMany(String prop);
/**
@@ -453,13 +454,13 @@ public interface ExpressionList<T> {
/**
* Calls {@link #setUseQueryCache(CacheMode)} with <code>ON</code> or <code>OFF</code>.
*
*
* @see Query#setUseQueryCache(CacheMode)
*/
default Query<T> setUseQueryCache(boolean enabled) {
return setUseQueryCache(enabled ? CacheMode.ON : CacheMode.OFF);
}
/**
* Set to true if this query should execute against the doc store.
* <p>
@@ -807,6 +808,36 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> in(String propertyName, Collection<?> values);
/**
* In - using a subQuery.
*
* This is exactly the same as in() and provided due to "in" being a Kotlin keyword
* (and hence to avoid the slightly ugly escaping when using in() in Kotlin)
*/
default ExpressionList<T> isIn(String propertyName, Query<?> subQuery) {
return in(propertyName, subQuery);
}
/**
* In - property has a value in the array of values.
*
* This is exactly the same as in() and provided due to "in" being a Kotlin keyword
* (and hence to avoid the slightly ugly escaping when using in() in Kotlin)
*/
default ExpressionList<T> isIn(String propertyName, Object... values) {
return in(propertyName, values);
}
/**
* In - property has a value in the collection of values.
*
* This is exactly the same as in() and provided due to "in" being a Kotlin keyword
* (and hence to avoid the slightly ugly escaping when using in() in Kotlin)
*/
default ExpressionList<T> isIn(String propertyName, Collection<?> values) {
return in(propertyName, values);
}
/**
* Not In - property has a value in the array of values.
*/
+41 -1
View File
@@ -1,6 +1,7 @@
package io.ebean;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
@@ -92,6 +93,20 @@ public class Finder<I, T> {
this.serverName = serverName;
}
/**
* Return the current transaction.
*/
public Transaction currentTransaction() {
return db().currentTransaction();
}
/**
* Flush the JDBC batch on the current transaction.
*/
public void flush() {
db().flush();
}
/**
* Return the underlying 'default' EbeanServer.
* <p>
@@ -118,6 +133,7 @@ public class Finder<I, T> {
* <p>
* Equivalent to {@link EbeanServer#getReference(Class, Object)}
*/
@Nonnull
public T ref(I id) {
return db().getReference(type, id);
}
@@ -144,10 +160,33 @@ public class Finder<I, T> {
/**
* Retrieves all entities of the given type.
*/
@Nonnull
public List<T> all() {
return query().findList();
}
/**
* Creates an update query.
*
* <pre>{@code
*
* int rows =
* finder.update()
* .set("status", Customer.Status.ACTIVE)
* .set("updtime", new Timestamp(System.currentTimeMillis()))
* .where()
* .gt("id", 1000)
* .update();
*
* }</pre>
*
* <p>
* Equivalent to {@link EbeanServer#update(Class)}
*/
public UpdateQuery<T> update() {
return db().update(type);
}
/**
* Creates a query.
* <p>
@@ -170,4 +209,5 @@ public class Finder<I, T> {
public Query<T> query(String ormQuery) {
return db().createQuery(type, ormQuery);
}
}
+3
View File
@@ -1,5 +1,6 @@
package io.ebean;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.concurrent.Future;
@@ -109,11 +110,13 @@ public interface PagedList<T> {
*
* }</pre>
*/
@Nonnull
Future<Integer> getFutureCount();
/**
* Return the list of entities for this page.
*/
@Nonnull
List<T> getList();
/**
+28 -22
View File
@@ -1,7 +1,7 @@
package io.ebean;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.NonUniqueResultException;
import java.sql.Timestamp;
import java.util.List;
@@ -215,11 +215,6 @@ public interface Query<T> {
SKIPLOCKED
}
/**
* Return the RawSql that was set to use for this query.
*/
RawSql getRawSql();
/**
* Set RawSql to use for this query.
*/
@@ -602,6 +597,7 @@ public interface Query<T> {
*
* @see EbeanServer#findIds(Query, Transaction)
*/
@Nonnull
<A> List<A> findIds();
/**
@@ -642,6 +638,7 @@ public interface Query<T> {
*
* }</pre>
*/
@Nonnull
QueryIterator<T> findIterate();
/**
@@ -740,6 +737,7 @@ public interface Query<T> {
*
* @see EbeanServer#findList(Query, Transaction)
*/
@Nonnull
List<T> findList();
/**
@@ -758,6 +756,7 @@ public interface Query<T> {
*
* @see EbeanServer#findSet(Query, Transaction)
*/
@Nonnull
Set<T> findSet();
/**
@@ -780,6 +779,7 @@ public interface Query<T> {
*
* @see EbeanServer#findMap(Query, Transaction)
*/
@Nonnull
<K> Map<K, T> findMap();
/**
@@ -812,6 +812,7 @@ public interface Query<T> {
*
* @return the list of values for the selected property
*/
@Nonnull
<A> List<A> findSingleAttributeList();
/**
@@ -860,21 +861,9 @@ public interface Query<T> {
/**
* Execute the query returning an optional bean.
*/
@Nonnull
Optional<T> findOneOrEmpty();
/**
* Deprecated - please migrate to findOne().
* <p>
* This proceeded findOne which was introduced to better match spring data.
* This will be deprecated at some future point.
* </p>
*/
@Deprecated
@Nullable
default T findUnique() {
return findOne();
}
/**
* Return versions of a @History entity bean.
* <p>
@@ -887,6 +876,7 @@ public interface Query<T> {
* It will execute the query against the history returning the versions of the bean.
* </p>
*/
@Nonnull
List<Version<T>> findVersions();
/**
@@ -896,6 +886,7 @@ public interface Query<T> {
* It will execute the query against the history returning the versions of the bean.
* </p>
*/
@Nonnull
List<Version<T>> findVersionsBetween(Timestamp start, Timestamp end);
/**
@@ -933,6 +924,7 @@ public interface Query<T> {
*
* @return a Future object for the row count query
*/
@Nonnull
FutureRowCount<T> findFutureCount();
/**
@@ -945,6 +937,7 @@ public interface Query<T> {
*
* @return a Future object for the list of Id's
*/
@Nonnull
FutureIds<T> findFutureIds();
/**
@@ -956,6 +949,7 @@ public interface Query<T> {
*
* @return a Future object for the list result of the query
*/
@Nonnull
FutureList<T> findFutureList();
/**
@@ -985,6 +979,7 @@ public interface Query<T> {
*
* @return The PagedList
*/
@Nonnull
PagedList<T> findPagedList();
/**
@@ -1328,14 +1323,25 @@ public interface Query<T> {
* Set the {@link CacheMode} to use the query for executing this query.
*/
Query<T> setUseQueryCache(CacheMode useQueryCache);
/**
* Calls {@link #setUseQueryCache(CacheMode)} with <code>ON</code> or <code>OFF</code>.
*/
default Query<T> setUseQueryCache(boolean enabled) {
return setUseQueryCache(enabled ? CacheMode.ON : CacheMode.OFF);
}
/**
* Set an id to identify this query for profiling purposes.
* <p>
* The profileId is expected to be unique for a given bean type.
* </p>
* <p>
* Note that the profileId is treated as a short internally and has a MAX value of 32,767.
* </p>
*/
Query<T> setProfileId(int profileId);
/**
* Set to true if this query should execute against the doc store.
* <p>
+1 -572
View File
@@ -1,16 +1,5 @@
package io.ebean;
import io.ebean.util.CamelCaseHelper;
import java.io.Serializable;
import java.sql.ResultSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Used to build object graphs based on a raw SQL statement (rather than
* generated by Ebean).
@@ -169,566 +158,6 @@ import java.util.Map;
* Note that lazy loading also works with object graphs built with RawSql.
* </p>
*/
public final class RawSql implements Serializable {
public interface RawSql {
private static final long serialVersionUID = 1L;
private final ResultSet resultSet;
private final Sql sql;
private final ColumnMapping columnMapping;
/**
* Construct with a ResultSet and properties that the columns map to.
* <p>
* The properties listed in the propertyNames must be in the same order as the columns in the
* resultSet.
* <p>
* When a query executes this RawSql object then it will close the resultSet.
*/
public RawSql(ResultSet resultSet, String... propertyNames) {
this.resultSet = resultSet;
this.sql = null;
this.columnMapping = new ColumnMapping(propertyNames);
}
protected RawSql(ResultSet resultSet, Sql sql, ColumnMapping columnMapping) {
this.resultSet = resultSet;
this.sql = sql;
this.columnMapping = columnMapping;
}
/**
* Return the Sql either unparsed or in parsed (broken up) form.
*/
public Sql getSql() {
return sql;
}
/**
* Return the key;
*/
public Key getKey() {
boolean parsed = sql != null && sql.parsed;
String unParsedSql = (sql == null) ? "" : sql.unparsedSql;
return new Key(parsed, unParsedSql, columnMapping);
}
/**
* Return the resultSet if this is a ResultSet based RawSql.
*/
public ResultSet getResultSet() {
return resultSet;
}
/**
* Return the column mapping for the SQL columns to bean properties.
*/
public ColumnMapping getColumnMapping() {
return columnMapping;
}
/**
* Represents the sql part of the query. For parsed RawSql the sql is broken
* up so that Ebean can insert extra WHERE and HAVING expressions into the
* SQL.
*/
public static final class Sql implements Serializable {
private static final long serialVersionUID = 1L;
private final boolean parsed;
private final String unparsedSql;
private final String preFrom;
private final String preWhere;
private final boolean andWhereExpr;
private final String preHaving;
private final boolean andHavingExpr;
private final String orderByPrefix;
private final String orderBy;
private final boolean distinct;
/**
* Construct for unparsed SQL.
*/
protected Sql(String unparsedSql) {
this.parsed = false;
this.unparsedSql = unparsedSql;
this.preFrom = null;
this.preHaving = null;
this.preWhere = null;
this.andHavingExpr = false;
this.andWhereExpr = false;
this.orderByPrefix = null;
this.orderBy = null;
this.distinct = false;
}
/**
* Construct for parsed SQL.
*/
protected Sql(String unparsedSql, String preFrom, String preWhere, boolean andWhereExpr,
String preHaving, boolean andHavingExpr, String orderByPrefix, String orderBy, boolean distinct) {
this.unparsedSql = unparsedSql;
this.parsed = true;
this.preFrom = preFrom;
this.preHaving = preHaving;
this.preWhere = preWhere;
this.andHavingExpr = andHavingExpr;
this.andWhereExpr = andWhereExpr;
this.orderByPrefix = orderByPrefix;
this.orderBy = orderBy;
this.distinct = distinct;
}
@Override
public String toString() {
if (!parsed) {
return "unparsed[" + unparsedSql + "]";
}
return "select[" + preFrom + "] preWhere[" + preWhere + "] preHaving[" + preHaving + "] orderBy[" + orderBy + "]";
}
public boolean isDistinct() {
return distinct;
}
/**
* Return true if the SQL is left completely unmodified.
* <p>
* This means Ebean can't add WHERE or HAVING expressions into the query -
* it will be left completely unmodified.
* </p>
*/
public boolean isParsed() {
return parsed;
}
/**
* Return the SQL when it is unparsed.
*/
public String getUnparsedSql() {
return unparsedSql;
}
/**
* Return the SQL prior to FROM clause.
*/
public String getPreFrom() {
return preFrom;
}
/**
* Return the SQL prior to WHERE clause.
*/
public String getPreWhere() {
return preWhere;
}
/**
* Return true if there is already a WHERE clause and any extra where
* expressions start with AND.
*/
public boolean isAndWhereExpr() {
return andWhereExpr;
}
/**
* Return the SQL prior to HAVING clause.
*/
public String getPreHaving() {
return preHaving;
}
/**
* Return true if there is already a HAVING clause and any extra having
* expressions start with AND.
*/
public boolean isAndHavingExpr() {
return andHavingExpr;
}
/**
* Return the 'order by' keywords.
* This can contain additional keywords, for example 'order siblings by' as Oracle syntax.
*/
public String getOrderByPrefix() {
return (orderByPrefix == null) ? "order by" : orderByPrefix;
}
/**
* Return the SQL ORDER BY clause.
*/
public String getOrderBy() {
return orderBy;
}
}
/**
* Defines the column mapping for raw sql DB columns to bean properties.
*/
public static final class ColumnMapping implements Serializable {
private static final long serialVersionUID = 1L;
private final LinkedHashMap<String, Column> dbColumnMap;
private final Map<String, String> propertyMap;
private final Map<String, Column> propertyColumnMap;
private final boolean parsed;
private final boolean immutable;
/**
* Construct from parsed sql where the columns have been identified.
*/
protected ColumnMapping(List<Column> columns) {
this.immutable = false;
this.parsed = true;
this.propertyMap = null;
this.propertyColumnMap = null;
this.dbColumnMap = new LinkedHashMap<>();
for (Column c : columns) {
dbColumnMap.put(c.getDbColumnKey(), c);
}
}
/**
* Construct for unparsed sql.
*/
protected ColumnMapping() {
this.immutable = false;
this.parsed = false;
this.propertyMap = null;
this.propertyColumnMap = null;
this.dbColumnMap = new LinkedHashMap<>();
}
/**
* Construct for ResultSet use.
*/
protected ColumnMapping(String... propertyNames) {
this.immutable = false;
this.parsed = false;
this.propertyMap = null;
this.dbColumnMap = new LinkedHashMap<>();
int pos = 0;
for (String prop : propertyNames) {
dbColumnMap.put(prop, new Column(pos++, prop, null, prop));
}
propertyColumnMap = dbColumnMap;
}
/**
* Construct an immutable ColumnMapping based on collected information.
*/
protected ColumnMapping(boolean parsed, LinkedHashMap<String, Column> dbColumnMap) {
this.immutable = true;
this.parsed = parsed;
this.dbColumnMap = dbColumnMap;
HashMap<String, Column> pcMap = new HashMap<>();
HashMap<String, String> pMap = new HashMap<>();
for (Column c : dbColumnMap.values()) {
pMap.put(c.getPropertyName(), c.getDbColumn());
pcMap.put(c.getPropertyName(), c);
}
this.propertyMap = Collections.unmodifiableMap(pMap);
this.propertyColumnMap = Collections.unmodifiableMap(pcMap);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ColumnMapping that = (ColumnMapping) o;
return dbColumnMap.equals(that.dbColumnMap);
}
@Override
public int hashCode() {
return dbColumnMap.hashCode();
}
/**
* Return true if the property is mapped.
*/
public boolean contains(String property) {
return this.propertyColumnMap.containsKey(property);
}
/**
* Creates an immutable copy of this ColumnMapping.
*
* @throws IllegalStateException when a propertyName has not been defined for a column.
*/
protected ColumnMapping createImmutableCopy() {
for (Column c : dbColumnMap.values()) {
c.checkMapping();
}
return new ColumnMapping(parsed, dbColumnMap);
}
protected void columnMapping(String dbColumn, String propertyName) {
if (immutable) {
throw new IllegalStateException("Should never happen");
}
if (!parsed) {
int pos = dbColumnMap.size();
dbColumnMap.put(dbColumn, new Column(pos, dbColumn, null, propertyName));
} else {
Column column = dbColumnMap.get(dbColumn);
if (column == null) {
String msg = "DB Column [" + dbColumn + "] not found in mapping. Expecting one of [" + dbColumnMap.keySet() + "]";
throw new IllegalArgumentException(msg);
}
column.setPropertyName(propertyName);
}
}
/**
* Returns true if the Columns where supplied by parsing the sql select
* clause.
* <p>
* In the case where the columns where parsed then we can do extra checks on
* the column mapping such as, is the column a valid one in the sql and
* whether all the columns in the sql have been mapped.
* </p>
*/
public boolean isParsed() {
return parsed;
}
/**
* Return the number of columns in this column mapping.
*/
public int size() {
return dbColumnMap.size();
}
/**
* Return the column mapping.
*/
protected Map<String, Column> mapping() {
return dbColumnMap;
}
/**
* Return the mapping by DB column.
*/
public Map<String, String> getMapping() {
return propertyMap;
}
/**
* Return the index position by bean property name.
*/
public int getIndexPosition(String property) {
Column c = propertyColumnMap.get(property);
return c == null ? -1 : c.getIndexPos();
}
/**
* Return an iterator of the Columns.
*/
public Iterator<Column> getColumns() {
return dbColumnMap.values().iterator();
}
/**
* Modify any column mappings with the given table alias to have the path prefix.
* <p>
* For example modify all mappings with table alias "c" to have the path prefix "customer".
* </p>
* <p>
* For the "Root type" you don't need to specify a tableAliasMapping.
* </p>
*/
public void tableAliasMapping(String tableAlias, String path) {
String startMatch = tableAlias + ".";
for (Map.Entry<String, Column> entry : dbColumnMap.entrySet()) {
if (entry.getKey().startsWith(startMatch)) {
entry.getValue().tableAliasMapping(path);
}
}
}
/**
* A Column of the RawSql that is mapped to a bean property (or ignored).
*/
public static class Column implements Serializable {
private static final long serialVersionUID = 1L;
private final int indexPos;
private final String dbColumn;
private final String dbAlias;
private String propertyName;
/**
* Construct a Column.
*/
public Column(int indexPos, String dbColumn, String dbAlias) {
this(indexPos, dbColumn, dbAlias, derivePropertyName(dbAlias, dbColumn));
}
private Column(int indexPos, String dbColumn, String dbAlias, String propertyName) {
this.indexPos = indexPos;
this.dbColumn = dbColumn;
this.dbAlias = dbAlias;
if (propertyName == null && dbAlias != null) {
this.propertyName = dbAlias;
} else {
this.propertyName = propertyName;
}
}
protected static String derivePropertyName(String dbAlias, String dbColumn) {
if (dbAlias != null) {
return CamelCaseHelper.toCamelFromUnderscore(dbAlias);
}
int dotPos = dbColumn.indexOf('.');
if (dotPos > -1) {
dbColumn = dbColumn.substring(dotPos + 1);
}
return CamelCaseHelper.toCamelFromUnderscore(dbColumn);
}
private void checkMapping() {
if (propertyName == null) {
String msg = "No propertyName defined (Column mapping) for dbColumn [" + dbColumn + "]";
throw new IllegalStateException(msg);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Column that = (Column) o;
if (indexPos != that.indexPos) return false;
if (!dbColumn.equals(that.dbColumn)) return false;
if (dbAlias != null ? !dbAlias.equals(that.dbAlias) : that.dbAlias != null) return false;
return propertyName != null ? propertyName.equals(that.propertyName) : that.propertyName == null;
}
@Override
public int hashCode() {
int result = indexPos;
result = 92821 * result + dbColumn.hashCode();
result = 92821 * result + (dbAlias != null ? dbAlias.hashCode() : 0);
result = 92821 * result + (propertyName != null ? propertyName.hashCode() : 0);
return result;
}
@Override
public String toString() {
return dbColumn + "->" + propertyName;
}
/**
* Return the index position of this column.
*/
public int getIndexPos() {
return indexPos;
}
/**
* Return the DB column alias if specified otherwise DB column.
* This is used as the key for mapping a column to a logical property.
*/
public String getDbColumnKey() {
return (dbAlias != null) ? dbAlias : dbColumn;
}
/**
* Return the DB column name including table alias (if it has one).
*/
public String getDbColumn() {
return dbColumn;
}
/**
* Return the bean property this column is mapped to.
*/
public String getPropertyName() {
return propertyName;
}
/**
* Set the property name mapped to this db column.
*/
private void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
/**
* Prepend the path to the property name.
* <p/>
* For example if path is "customer" then "name" becomes "customer.name".
*/
public void tableAliasMapping(String path) {
if (path != null) {
propertyName = path + "." + propertyName;
}
}
}
}
/**
* A key for the RawSql object using for the query plan.
*/
public static final class Key {
private final boolean parsed;
private final ColumnMapping columnMapping;
private final String unParsedSql;
Key(boolean parsed, String unParsedSql, ColumnMapping columnMapping) {
this.parsed = parsed;
this.unParsedSql = unParsedSql;
this.columnMapping = columnMapping;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Key that = (Key) o;
return parsed == that.parsed
&& columnMapping.equals(that.columnMapping)
&& unParsedSql.equals(that.unParsedSql);
}
@Override
public int hashCode() {
int result = (parsed ? 1 : 0);
result = 92821 * result + columnMapping.hashCode();
result = 92821 * result + unParsedSql.hashCode();
return result;
}
}
}
+11 -55
View File
@@ -1,8 +1,5 @@
package io.ebean;
import io.ebean.RawSql.ColumnMapping;
import io.ebean.RawSql.Sql;
import java.sql.ResultSet;
/**
@@ -14,18 +11,7 @@ import java.sql.ResultSet;
*
* @see RawSql
*/
public class RawSqlBuilder {
/**
* Special property name assigned to a DB column that should be ignored.
*/
public static final String IGNORE_COLUMN = "$$_IGNORE_COLUMN_$$";
private final ResultSet resultSet;
private final Sql sql;
private final ColumnMapping columnMapping;
public interface RawSqlBuilder {
/**
* Create and return a RawSql object based on the resultSet and list of properties the columns in
@@ -34,8 +20,8 @@ public class RawSqlBuilder {
* The properties listed in the propertyNames must be in the same order as the columns in the
* resultSet.
*/
public static RawSql resultSet(ResultSet resultSet, String... propertyNames) {
return new RawSql(resultSet, propertyNames);
static RawSql resultSet(ResultSet resultSet, String... propertyNames) {
return XServiceProvider.get().resultSet(resultSet, propertyNames);
}
/**
@@ -43,10 +29,8 @@ public class RawSqlBuilder {
* modified - so no additional WHERE or HAVING expressions can be added to
* this query.
*/
public static RawSqlBuilder unparsed(String sql) {
Sql s = new Sql(sql);
return new RawSqlBuilder(s, new ColumnMapping());
static RawSqlBuilder unparsed(String sql) {
return XServiceProvider.get().unparsed(sql);
}
/**
@@ -62,19 +46,8 @@ public class RawSqlBuilder {
* correct column names are entered into the mapping.
* </p>
*/
public static RawSqlBuilder parse(String sql) {
Sql sql2 = DRawSqlParser.parse(sql);
String select = sql2.getPreFrom();
ColumnMapping mapping = DRawSqlColumnsParser.parse(select);
return new RawSqlBuilder(sql2, mapping);
}
private RawSqlBuilder(Sql sql, ColumnMapping columnMapping) {
this.sql = sql;
this.columnMapping = columnMapping;
this.resultSet = null;
static RawSqlBuilder parse(String sql) {
return XServiceProvider.get().parsed(sql);
}
/**
@@ -87,17 +60,12 @@ public class RawSqlBuilder {
* @param dbColumn the DB column that we are mapping to a bean property
* @param propertyName the bean property that we are mapping the DB column to.
*/
public RawSqlBuilder columnMapping(String dbColumn, String propertyName) {
columnMapping.columnMapping(dbColumn, propertyName);
return this;
}
RawSqlBuilder columnMapping(String dbColumn, String propertyName);
/**
* Ignore this DB column. It is not mapped to any bean property.
*/
public RawSqlBuilder columnMappingIgnore(String dbColumn) {
return columnMapping(dbColumn, IGNORE_COLUMN);
}
RawSqlBuilder columnMappingIgnore(String dbColumn);
/**
* Modify any column mappings with the given table alias to have the path prefix.
@@ -108,24 +76,12 @@ public class RawSqlBuilder {
* For the "Root type" you don't need to specify a tableAliasMapping.
* </p>
*/
public RawSqlBuilder tableAliasMapping(String tableAlias, String path) {
columnMapping.tableAliasMapping(tableAlias, path);
return this;
}
RawSqlBuilder tableAliasMapping(String tableAlias, String path);
/**
* Create the immutable RawSql object. Do this after all the column mapping
* has been defined.
*/
public RawSql create() {
return new RawSql(resultSet, sql, columnMapping.createImmutableCopy());
}
/**
* Return the internal parsed Sql object (for testing).
*/
protected Sql getSql() {
return sql;
}
RawSql create();
}
+5 -8
View File
@@ -1,5 +1,7 @@
package io.ebean;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.Serializable;
import java.util.List;
import java.util.Optional;
@@ -43,6 +45,7 @@ public interface SqlQuery extends Serializable {
/**
* Execute the query returning a list.
*/
@Nonnull
List<SqlRow> findList();
/**
@@ -71,21 +74,15 @@ public interface SqlQuery extends Serializable {
* PersistenceException.
* </p>
*/
@Nullable
SqlRow findOne();
/**
* Execute the query returning an optional row.
*/
@Nonnull
Optional<SqlRow> findOneOrEmpty();
/**
* Deprecated - please migrate to findOne().
*/
@Deprecated
default SqlRow findUnique() {
return findOne();
}
/**
* The same as bind for named parameters.
*/
-51
View File
@@ -1,51 +0,0 @@
package io.ebean;
import java.util.concurrent.Callable;
/**
* Deprecated - please migrate to just using Callable instead with executeCall().
*
* Execute a TxCallable in a Transaction scope.
* <p>
* Use this with the {@link Ebean#executeCall(Callable)} method.
* </p>
* <p>
* Note that this is basically the same as TxRunnable except that it returns an
* Object (and you specify the return type via generics).
* </p>
* <p>
* See also {@link TxRunnable}.
* </p>
* <p>
* <pre>{@code
*
* Ebean.execute(new TxCallable<String>() {
* public String call() {
* User u1 = Ebean.find(User.class, 1);
* User u2 = Ebean.find(User.class, 2);
*
* u1.setName("u1 mod");
* u2.setName("u2 mod");
*
* Ebean.save(u1);
* Ebean.save(u2);
*
* return u1.getEmail();
* }
* });
*
* }</pre>
*/
@Deprecated
public interface TxCallable<T> extends Callable<T> {
/**
* Execute the method within a transaction scope returning the result.
* <p>
* If you do not want to return a result you should look to use TxRunnable
* instead.
* </p>
*/
@Override
T call();
}
-42
View File
@@ -1,42 +0,0 @@
package io.ebean;
/**
* Deprecated - please migrate to using just Runnable.
*
*
* Execute a TxRunnable in a Transaction scope.
* <p>
* Use this with the {@link Ebean#execute(Runnable)} method.
* </p>
* <p>
* <pre>{@code
*
* // this run method runs in a transaction scope
* // which by default is TxScope.REQUIRED
*
* Ebean.execute(new TxRunnable() {
* public void run() {
* User u1 = Ebean.find(User.class, 1);
* User u2 = Ebean.find(User.class, 2);
*
* u1.setName("u1 mod");
* u2.setName("u2 mod");
*
* Ebean.save(u1);
* Ebean.save(u2);
* }
* });
*
* }</pre>
*
* @see TxCallable
*/
@Deprecated
public interface TxRunnable extends Runnable {
/**
* Run the method in a transaction sope.
*/
@Override
void run();
}
+35 -11
View File
@@ -25,30 +25,32 @@ import java.util.concurrent.Callable;
*/
public final class TxScope {
TxType type;
private int profileId;
String serverName;
private TxType type;
TxIsolation isolation;
private String serverName;
PersistBatch batch;
private TxIsolation isolation;
PersistBatch batchOnCascade;
private PersistBatch batch;
int batchSize;
private PersistBatch batchOnCascade;
boolean skipGeneratedKeys;
private int batchSize;
boolean readOnly;
private boolean skipGeneratedKeys;
private boolean readOnly;
/**
* Set this to false if the JDBC batch should not be automatically be flushed when a query is executed.
*/
boolean flushOnQuery = true;
private boolean flushOnQuery = true;
ArrayList<Class<? extends Throwable>> rollbackFor;
private ArrayList<Class<? extends Throwable>> rollbackFor;
ArrayList<Class<? extends Throwable>> noRollbackFor;
private ArrayList<Class<? extends Throwable>> noRollbackFor;
/**
* Helper method to create a TxScope with REQUIRES.
@@ -168,6 +170,21 @@ public final class TxScope {
return this;
}
/**
* Return the transaction profile id.
*/
public int getProfileId() {
return profileId;
}
/**
* Set the transaction profile id.
*/
public TxScope setProfileId(int profileId) {
this.profileId = profileId;
return this;
}
/**
* Return the batch mode.
*/
@@ -258,6 +275,13 @@ public final class TxScope {
return this;
}
/**
* Return the isolation level.
*/
public int getIsolationLevel() {
return isolation != null ? isolation.getLevel() : -1;
}
/**
* Return the Isolation level this transaction should run with.
*/
@@ -0,0 +1,30 @@
package io.ebean;
import io.ebean.service.SpiRawSqlService;
import java.util.Iterator;
import java.util.ServiceLoader;
/**
* Lookup internal services.
*/
class XServiceProvider {
private static SpiRawSqlService builder = init();
private static SpiRawSqlService init() {
Iterator<SpiRawSqlService> loader = ServiceLoader.load(SpiRawSqlService.class).iterator();
if (loader.hasNext()) {
return loader.next();
}
throw new IllegalStateException("No service implementation found for SpiRawSqlService?");
}
/**
* Return the RawSqlService implementation.
*/
static SpiRawSqlService get() {
return builder;
}
}
@@ -1,6 +1,5 @@
package io.ebean.bean;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
/**
@@ -32,16 +31,6 @@ public interface EntityBean extends Serializable {
*/
Object _ebean_newInstance();
/**
* Add a PropertyChangeListener to this bean.
*/
void addPropertyChangeListener(PropertyChangeListener listener);
/**
* Remove a PropertyChangeListener from this bean.
*/
void removePropertyChangeListener(PropertyChangeListener listener);
/**
* Generated method that sets the loaded state on all the embedded beans on
* this entity bean by using EntityBeanIntercept.setEmbeddedLoaded(Object o);
@@ -3,15 +3,12 @@ package io.ebean.bean;
import io.ebean.Ebean;
import io.ebean.ValuePair;
import java.util.Arrays;
import javax.persistence.EntityNotFoundException;
import javax.persistence.PersistenceException;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.Serializable;
import java.math.BigDecimal;
import java.net.URL;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
@@ -34,8 +31,6 @@ public final class EntityBeanIntercept implements Serializable {
private transient NodeUsageCollector nodeUsageCollector;
private transient PropertyChangeSupport pcs;
private transient PersistenceContext persistenceContext;
private transient BeanLoader beanLoader;
@@ -128,46 +123,6 @@ public final class EntityBeanIntercept implements Serializable {
this.persistenceContext = persistenceContext;
}
/**
* Add a property change listener for this entity bean.
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
if (pcs == null) {
pcs = new PropertyChangeSupport(owner);
}
pcs.addPropertyChangeListener(listener);
}
/**
* Add a property change listener for this entity bean for a specific
* property.
*/
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
if (pcs == null) {
pcs = new PropertyChangeSupport(owner);
}
pcs.addPropertyChangeListener(propertyName, listener);
}
/**
* Remove a property change listener for this entity bean.
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
if (pcs != null) {
pcs.removePropertyChangeListener(listener);
}
}
/**
* Remove a property change listener for this entity bean for a specific
* property.
*/
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
if (pcs != null) {
pcs.removePropertyChangeListener(propertyName, listener);
}
}
/**
* Turn on profile collection.
*/
@@ -891,9 +846,9 @@ public final class EntityBeanIntercept implements Serializable {
loadedProps[propertyIndex] = true;
}
private void preGetterCallback() {
private void preGetterCallback(int propertyIndex) {
if (preGetterCallback != null) {
preGetterCallback.preGetterTrigger();
preGetterCallback.preGetterTrigger(propertyIndex);
}
}
@@ -901,14 +856,14 @@ public final class EntityBeanIntercept implements Serializable {
* Called prior to Id property getter.
*/
public void preGetId() {
preGetterCallback();
preGetterCallback(-1);
}
/**
* Method that is called prior to a getter method on the actual entity.
*/
public void preGetter(int propertyIndex) {
preGetterCallback();
preGetterCallback(propertyIndex);
if (state == STATE_NEW || disableLazyLoad) {
return;
}
@@ -920,49 +875,17 @@ public final class EntityBeanIntercept implements Serializable {
}
}
/**
* Called for "enhancement" postSetter processing. This is around a PUTFIELD
* so no need to check the newValue afterwards.
*/
public void postSetter(PropertyChangeEvent event) {
if (pcs != null && event != null) {
pcs.firePropertyChange(event);
}
}
/**
* Called for "subclassed" postSetter processing. Here the newValue has to be
* re-fetched (and passed into this method) in case there is code inside the
* setter that further mutates the value.
*/
public void postSetter(PropertyChangeEvent event, Object newValue) {
if (pcs != null && event != null) {
if (newValue != null && newValue.equals(event.getNewValue())) {
pcs.firePropertyChange(event);
} else {
pcs.firePropertyChange(event.getPropertyName(), event.getOldValue(), newValue);
}
}
}
/**
* OneToMany and ManyToMany don't have any interception so just check for
* PropertyChangeSupport.
*/
public PropertyChangeEvent preSetterMany(boolean interceptField, int propertyIndex, Object oldValue, Object newValue) {
public void preSetterMany(boolean interceptField, int propertyIndex, Object oldValue, Object newValue) {
if (readOnly) {
throw new IllegalStateException("This bean is readOnly");
}
setLoadedProperty(propertyIndex);
// Bean itself not considered dirty when many changed
if (pcs != null) {
return new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
} else {
return null;
}
}
private void setChangedPropertyValue(int propertyIndex, boolean setDirtyState, Object origValue) {
@@ -991,175 +914,140 @@ public final class EntityBeanIntercept implements Serializable {
* Check to see if the values are not equal. If they are not equal then create
* the old values for use with ConcurrencyMode.ALL.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, Object oldValue, Object newValue) {
public void preSetter(boolean intercept, int propertyIndex, Object oldValue, Object newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (!areEqual(oldValue, newValue)) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* Check for primitive boolean.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, boolean oldValue, boolean newValue) {
public void preSetter(boolean intercept, int propertyIndex, boolean oldValue, boolean newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* Check for primitive int.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, int oldValue, int newValue) {
public void preSetter(boolean intercept, int propertyIndex, int oldValue, int newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* long.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, long oldValue, long newValue) {
public void preSetter(boolean intercept, int propertyIndex, long oldValue, long newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* double.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, double oldValue, double newValue) {
public void preSetter(boolean intercept, int propertyIndex, double oldValue, double newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (Double.compare(oldValue, newValue) != 0) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* float.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, float oldValue, float newValue) {
public void preSetter(boolean intercept, int propertyIndex, float oldValue, float newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (Float.compare(oldValue, newValue) != 0) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* short.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, short oldValue, short newValue) {
public void preSetter(boolean intercept, int propertyIndex, short oldValue, short newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* char.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, char oldValue, char newValue) {
public void preSetter(boolean intercept, int propertyIndex, char oldValue, char newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* byte.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, byte oldValue, byte newValue) {
public void preSetter(boolean intercept, int propertyIndex, byte oldValue, byte newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* char[].
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, char[] oldValue, char[] newValue) {
public void preSetter(boolean intercept, int propertyIndex, char[] oldValue, char[] newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (!Arrays.equals(oldValue, newValue)) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* byte[].
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, byte[] oldValue, byte[] newValue) {
public void preSetter(boolean intercept, int propertyIndex, byte[] oldValue, byte[] newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (!Arrays.equals(oldValue, newValue)) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* Explicitly set an old value.
*/
public void setOldValue(int propertyIndex,Object oldValue) {
public void setOldValue(int propertyIndex, Object oldValue) {
setChangedPropertyValue(propertyIndex, true, oldValue);
}
}
@@ -7,7 +7,9 @@ package io.ebean.bean;
public interface PreGetterCallback {
/**
* Trigger the callback.
* Trigger the callback based on a getter on a property.
*
* @param propertyIndex The index of the property
*/
void preGetterTrigger();
void preGetterTrigger(int propertyIndex);
}
@@ -1,7 +1,7 @@
package io.ebean.config;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebeaninternal.server.deploy.parse.AnnotationBase;
import io.ebean.util.AnnotationUtil;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -219,8 +219,8 @@ public abstract class AbstractNamingConvention implements NamingConvention {
* Return true if this class is part of entity inheritance.
*/
protected boolean hasInheritance(Class<?> supCls) {
return AnnotationBase.findAnnotationRecursive(supCls, Inheritance.class) != null
|| AnnotationBase.findAnnotation(supCls, DiscriminatorValue.class) != null;
return AnnotationUtil.findAnnotationRecursive(supCls, Inheritance.class) != null
|| AnnotationUtil.findAnnotation(supCls, DiscriminatorValue.class) != null;
}
@@ -253,7 +253,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
*/
protected TableName getTableNameFromAnnotation(Class<?> beanClass) {
final Table t = AnnotationBase.findAnnotationRecursive(beanClass, Table.class);
final Table t = AnnotationUtil.findAnnotationRecursive(beanClass, Table.class);
// Take the annotation if defined
if (t != null && !isEmpty(t.name())) {
@@ -54,6 +54,22 @@ public class ClassLoadConfig {
return isPresent("javax.validation.constraints.NotNull");
}
/**
* Return true if javax PostConstruct annotation is present (maybe not in java9).
* If not we don't support PostConstruct lifecycle events.
*/
public boolean isJavaxPostConstructPresent() {
return isPresent("javax.annotation.PostConstruct");
}
/**
* Return true if javax JAXB is present (maybe not in java9).
* If not we don't try to parse or support 'extra ddl'.
*/
public boolean isJavaxJAXBPresent() {
return isPresent("javax.xml.bind.JAXBException");
}
/**
* Return true if Jackson annotations like JsonIgnore are present.
*/
@@ -0,0 +1,148 @@
package io.ebean.config;
/**
* Configuration for transaction profiling.
*/
public class ProfilingConfig {
/**
* When true transaction profiling is enabled.
*/
private boolean enabled;
/**
* Set true for verbose mode.
*/
private boolean verbose;
/**
* The minimum transaction execution time to be included in profiling.
*/
private long minimumMicros;
/**
* A specific set of profileIds to include in profiling.
*/
private int[] includeProfileIds = {};
/**
* The number of profiles to write per file.
*/
private long profilesPerFile = 1000;
private String directory = "profiling";
/**
* Return true if transaction profiling is enabled.
*/
public boolean isEnabled() {
return enabled;
}
/**
* Set to true to enable transaction profiling.
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
/**
* Return true if verbose mode is used.
*/
public boolean isVerbose() {
return verbose;
}
/**
* Set to true to use verbose mode.
*/
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
/**
* Return the minimum transaction execution to be included in profiling.
*/
public long getMinimumMicros() {
return minimumMicros;
}
/**
* Set the minimum transaction execution to be included in profiling.
*/
public void setMinimumMicros(long minimumMicros) {
this.minimumMicros = minimumMicros;
}
/**
* Return the specific set of profileIds to include in profiling.
* When not set all transactions with profileIds are included.
*/
public int[] getIncludeProfileIds() {
return includeProfileIds;
}
/**
* Set a specific set of profileIds to include in profiling.
* When not set all transactions with profileIds are included.
*/
public void setIncludeProfileIds(int[] includeProfileIds) {
this.includeProfileIds = includeProfileIds;
}
/**
* Return the number of profiles to write to a single file.
*/
public long getProfilesPerFile() {
return profilesPerFile;
}
/**
* Set the number of profiles to write to a single file.
*/
public void setProfilesPerFile(long profilesPerFile) {
this.profilesPerFile = profilesPerFile;
}
/**
* Return the directory profiling files are put into.
*/
public String getDirectory() {
return directory;
}
/**
* Set the directory profiling files are put into.
*/
public void setDirectory(String directory) {
this.directory = directory;
}
/**
* Load setting from properties.
*/
public void loadSettings(PropertiesWrapper p, String name) {
enabled = p.getBoolean("profiling", enabled);
verbose = p.getBoolean("profiling.verbose", verbose);
directory = p.get("profiling.directory", directory);
profilesPerFile = p.getLong("profiling.profilesPerFile", profilesPerFile);
minimumMicros = p.getLong("profiling.minimumMicros", minimumMicros);
String includeIds = p.get("profiling.includeProfileIds");
if (includeIds != null) {
includeProfileIds = parseIds(includeIds);
}
}
private int[] parseIds(String includeIds) {
String[] ids = includeIds.split(",");
int[] vals = new int[ids.length];
for (int i = 0; i < ids.length; i++) {
vals[i] = Integer.parseInt(ids[i]);
}
return vals;
}
}
+66 -22
View File
@@ -2,18 +2,16 @@ package io.ebean.config;
import com.fasterxml.jackson.core.JsonFactory;
import io.ebean.EbeanServerFactory;
import io.ebean.annotation.PersistBatch;
import io.ebean.PersistenceContextScope;
import io.ebean.annotation.Platform;
import io.ebean.Query;
import io.ebean.Transaction;
import io.ebean.annotation.Encrypted;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.cache.ServerCachePlugin;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbEncrypt;
import io.ebean.config.dbplatform.DbType;
import io.ebean.dbmigration.DbOffline;
import io.ebean.migration.MigrationRunner;
import io.ebean.event.BeanFindController;
import io.ebean.event.BeanPersistController;
import io.ebean.event.BeanPersistListener;
@@ -28,12 +26,13 @@ import io.ebean.event.changelog.ChangeLogRegister;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.meta.MetaInfoManager;
import io.ebean.migration.MigrationRunner;
import io.ebean.util.StringHelper;
import org.avaje.datasource.DataSourceConfig;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -369,6 +368,8 @@ public class ServerConfig {
private ChangeLogRegister changeLogRegister;
private boolean changeLogAsync = true;
private ReadAuditLogger readAuditLogger;
private ReadAuditPrepare readAuditPrepare;
@@ -434,6 +435,11 @@ public class ServerConfig {
private String jodaLocalTimeMode;
/**
* Time to live for query plans - defaults to 5 minutes.
*/
private int queryPlanTTLSeconds = 60 * 5;
/**
* Set to true to globally disable L2 caching (typically for performance testing).
*/
@@ -449,6 +455,9 @@ public class ServerConfig {
*/
private SlowQueryListener slowQueryListener;
private ProfilingConfig profilingConfig = new ProfilingConfig();
/**
* Construct a Server Configuration for programmatically creating an EbeanServer.
*/
@@ -971,6 +980,20 @@ public class ServerConfig {
this.changeLogIncludeInserts = changeLogIncludeInserts;
}
/**
* Return true (default) if the changelog should be written async.
*/
public boolean isChangeLogAsync() {
return changeLogAsync;
}
/**
* Sets if the changelog should be written async (default = true).
*/
public void setChangeLogAsync(boolean changeLogAsync) {
this.changeLogAsync = changeLogAsync;
}
/**
* Return the ReadAuditLogger to use.
*/
@@ -1006,6 +1029,20 @@ public class ServerConfig {
this.readAuditPrepare = readAuditPrepare;
}
/**
* Return the configuration for profiling.
*/
public ProfilingConfig getProfilingConfig() {
return profilingConfig;
}
/**
* Set the configuration for profiling.
*/
public void setProfilingConfig(ProfilingConfig profilingConfig) {
this.profilingConfig = profilingConfig;
}
/**
* Return the DB migration configuration.
*/
@@ -1670,16 +1707,6 @@ public class ServerConfig {
this.encryptor = encryptor;
}
/**
* Return true if the instance is coming up in offline mode.
* <p>
* Offline mode is mostly used when generating DB migration.
* </p>
*/
public boolean isOfflineMode() {
return dbOffline || DbOffline.isSet();
}
/**
* Return true if the EbeanServer instance should be created in offline mode.
*/
@@ -2541,6 +2568,7 @@ public class ServerConfig {
*/
protected void loadSettings(PropertiesWrapper p) {
profilingConfig.loadSettings(p, name);
migrationConfig.loadSettings(p, name);
boolean quotedIdentifiers = p.getBoolean("allQuotedIdentifiers", allQuotedIdentifiers);
@@ -2572,6 +2600,7 @@ public class ServerConfig {
dbTypeConfig.setGeometrySRID(srid);
}
queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds);
slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis);
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
@@ -2618,6 +2647,7 @@ public class ServerConfig {
persistenceContextScope = PersistenceContextScope.valueOf(p.get("persistenceContextScope", "TRANSACTION"));
changeLogAsync = p.getBoolean("changeLogAsync", changeLogAsync);
changeLogIncludeInserts = p.getBoolean("changeLogIncludeInserts", changeLogIncludeInserts);
expressionEqualsWithNullAsNoop = p.getBoolean("expressionEqualsWithNullAsNoop", expressionEqualsWithNullAsNoop);
expressionNativeIlike = p.getBoolean("expressionNativeIlike", expressionNativeIlike);
@@ -2701,16 +2731,16 @@ public class ServerConfig {
private List<String> getSearchJarsPackages(String searchPackages) {
List<String> hitList = new ArrayList<>();
if (searchPackages != null) {
String[] entries = StringHelper.splitNames(searchPackages);
for (String entry : entries) {
hitList.add(entry);
}
List<String> hitList = new ArrayList<>(entries.length);
Collections.addAll(hitList, entries);
return hitList;
} else {
return new ArrayList<>();
}
return hitList;
}
/**
@@ -2793,6 +2823,20 @@ public class ServerConfig {
this.disableL2Cache = disableL2Cache;
}
/**
* Return the query plan time to live.
*/
public int getQueryPlanTTLSeconds() {
return queryPlanTTLSeconds;
}
/**
* Set the query plan time to live.
*/
public void setQueryPlanTTLSeconds(int queryPlanTTLSeconds) {
this.queryPlanTTLSeconds = queryPlanTTLSeconds;
}
/**
* Run the DB migration against the DataSource.
*/
@@ -2,13 +2,10 @@ package io.ebean.config.dbplatform;
import io.ebean.BackgroundExecutor;
import io.ebean.Query;
import io.ebean.config.CustomDbTypeMapping;
import io.ebean.config.DbTypeConfig;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.config.ServerConfig;
import io.ebean.dbmigration.ddlgeneration.DdlHandler;
import io.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
import io.ebean.config.CustomDbTypeMapping;
import io.ebean.config.DbTypeConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -162,8 +159,6 @@ public class DatabasePlatform {
*/
protected PersistBatch persistBatchOnCascade = PersistBatch.ALL;
protected PlatformDdl platformDdl;
/**
* The maximum length of table names - used specifically when derived
* default table names for intersection tables.
@@ -180,7 +175,7 @@ public class DatabasePlatform {
protected SqlExceptionTranslator exceptionTranslator = new SqlCodeTranslator();
protected char[] specialLikeCharacters = { '%', '_' };
protected char[] specialLikeCharacters = { '%', '_', '\\' };
/**
* Instantiates a new database platform.
@@ -276,23 +271,6 @@ public class DatabasePlatform {
return maxConstraintNameLength;
}
/**
* Return the platform specific DDL.
*/
public PlatformDdl getPlatformDdl() {
return platformDdl;
}
/**
* Create and return a DDL handler for generating DDL scripts.
*/
public DdlHandler createDdlHandler(ServerConfig serverConfig) {
if (platformDdl == null) {
throw new IllegalStateException("Platform " + getName() + " has no DDL Handler");
}
return platformDdl.createDdlHandler(serverConfig);
}
/**
* Return true if the JDBC driver does not allow additional queries to execute
* when a resultSet is being 'streamed' as is the case with findEach() etc.
@@ -1,6 +1,5 @@
package io.ebean.config.dbplatform;
import io.ebean.dbmigration.migration.IdentityType;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -121,28 +120,4 @@ public class DbIdentity {
this.idType = idType;
}
/**
* Determine the id type to use based on requested identityType and
* the support for that in the database platform.
*/
public IdType useIdentityType(IdentityType identityType) {
if (identityType == null) {
// use the default
return idType;
}
switch (identityType) {
case GENERATOR:
return IdType.GENERATOR;
case EXTERNAL:
return IdType.EXTERNAL;
case SEQUENCE:
return supportsSequence ? IdType.SEQUENCE : idType;
case IDENTITY:
return supportsIdentity ? IdType.IDENTITY : idType;
default:
return idType;
}
}
}
@@ -34,6 +34,10 @@ public class SqlCodeTranslator implements SqlExceptionTranslator {
public PersistenceException translate(String message, SQLException e) {
DataErrorType errorType = map.get(e.getSQLState());
if (errorType == null) {
// fall back to error code
errorType = map.get(String.valueOf(e.getErrorCode()));
}
if (errorType != null) {
switch (errorType) {
case AcquireLock:
@@ -8,7 +8,6 @@ import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.config.dbplatform.SqlErrorCodes;
import io.ebean.dbmigration.ddlgeneration.platform.DB2Ddl;
import javax.sql.DataSource;
import java.sql.Types;
@@ -24,11 +23,13 @@ public class DB2Platform extends DatabasePlatform {
this.maxTableNameLength = 18;
this.maxConstraintNameLength = 18;
this.sqlLimiter = new Db2SqlLimiter();
this.platformDdl = new DB2Ddl(this);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
this.likeClause = "like ? escape '|'";
this.specialLikeCharacters = new char[] { '%', '_', '|' };
this.exceptionTranslator =
new SqlErrorCodes()
.addAcquireLock("40001","57033") // key -911/-913
@@ -48,6 +49,10 @@ public class DB2Platform extends DatabasePlatform {
persistBatchOnCascade = PersistBatch.NONE;
}
@Override
protected void escapeLikeCharacter(char ch, StringBuilder sb) {
sb.append('|').append(ch);
}
/**
* Return a DB2 specific sequence IdGenerator that supports batch fetching
* sequence values.
@@ -1,15 +1,14 @@
package io.ebean.config.dbplatform.h2;
import io.ebean.BackgroundExecutor;
import io.ebean.annotation.Platform;
import io.ebean.Query;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.config.dbplatform.SqlErrorCodes;
import io.ebean.dbmigration.ddlgeneration.platform.H2Ddl;
import javax.sql.DataSource;
@@ -22,7 +21,6 @@ public class H2Platform extends DatabasePlatform {
super();
this.platform = Platform.H2;
this.dbEncrypt = new H2DbEncrypt();
this.platformDdl = new H2Ddl(this);
this.historySupport = new H2HistorySupport();
this.nativeUuidType = true;
this.dbDefaultValue.setNow("now()");
@@ -9,7 +9,6 @@ import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.config.dbplatform.h2.H2DbEncrypt;
import io.ebean.config.dbplatform.h2.H2SequenceIdGenerator;
import io.ebean.dbmigration.ddlgeneration.platform.HsqldbDdl;
import javax.sql.DataSource;
@@ -22,7 +21,6 @@ public class HsqldbPlatform extends DatabasePlatform {
super();
this.platform = Platform.HSQLDB;
this.dbEncrypt = new H2DbEncrypt();
this.platformDdl = new HsqldbDdl(this);
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
@@ -1,15 +1,14 @@
package io.ebean.config.dbplatform.mysql;
import io.ebean.BackgroundExecutor;
import io.ebean.annotation.Platform;
import io.ebean.Query;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.config.dbplatform.SqlErrorCodes;
import io.ebean.dbmigration.ddlgeneration.platform.MySqlDdl;
import javax.sql.DataSource;
import java.sql.Types;
@@ -32,7 +31,6 @@ public class MySqlPlatform extends DatabasePlatform {
this.useExtraTransactionOnIterateSecondaryQueries = true;
this.selectCountWithAlias = true;
this.dbEncrypt = new MySqlDbEncrypt();
this.platformDdl = new MySqlDdl(this);
this.historySupport = new MySqlHistorySupport();
this.columnAliasPrefix = null;
@@ -44,12 +42,15 @@ public class MySqlPlatform extends DatabasePlatform {
this.exceptionTranslator =
new SqlErrorCodes()
.addAcquireLock("1205")
.addDuplicateKey("1062")
.addDataIntegrity("630","839","840","893","1169","1215","1216","1217","1364","1451","1452","1557","23000")
.addDuplicateKey("1062", "1169")
.addDataIntegrity("630", "839", "840", "893", "1215", "1216", "1217", "1364", "1451", "1452", "1557")
.build();
this.openQuote = "`";
this.closeQuote = "`";
// use pipe for escaping as it depends if mysql runs in no_backslash_escapes or not.
this.likeClause = "like binary ? escape '|'";
this.specialLikeCharacters = new char[] { '%', '_', '|' };
this.forwardOnlyHintOnFindIterate = true;
this.booleanDbType = Types.BIT;
@@ -78,4 +79,9 @@ public class MySqlPlatform extends DatabasePlatform {
// NOWAIT and SKIP LOCKED currently not supported with MySQL
return sql + " for update";
}
@Override
protected void escapeLikeCharacter(char ch, StringBuilder sb) {
sb.append('|').append(ch);
}
}
@@ -1,8 +1,8 @@
package io.ebean.config.dbplatform.oracle;
import io.ebean.BackgroundExecutor;
import io.ebean.annotation.Platform;
import io.ebean.Query;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.BasicSqlAnsiLimiter;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
@@ -10,7 +10,6 @@ import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.config.dbplatform.RownumSqlLimiter;
import io.ebean.dbmigration.ddlgeneration.platform.Oracle10Ddl;
import javax.sql.DataSource;
import java.sql.Types;
@@ -28,7 +27,6 @@ public class OraclePlatform extends DatabasePlatform {
this.dbEncrypt = new OracleDbEncrypt();
this.sqlLimiter = new RownumSqlLimiter();
this.basicSqlLimiter = new BasicSqlAnsiLimiter();
this.platformDdl = new Oracle10Ddl(this);
this.historySupport = new OracleDbHistorySupport();
// Not using getGeneratedKeys as instead we will
@@ -39,6 +37,9 @@ public class OraclePlatform extends DatabasePlatform {
this.treatEmptyStringsAsNull = true;
this.likeClause = "like ? escape '|'";
this.specialLikeCharacters = new char[] { '%', '_', '|' };
this.openQuote = "\"";
this.closeQuote = "\"";
@@ -80,4 +81,9 @@ public class OraclePlatform extends DatabasePlatform {
return sql + " for update";
}
}
@Override
protected void escapeLikeCharacter(char ch, StringBuilder sb) {
sb.append('|').append(ch);
}
}
@@ -1,17 +1,14 @@
package io.ebean.config.dbplatform.postgres;
import io.ebean.BackgroundExecutor;
import io.ebean.annotation.Platform;
import io.ebean.Query;
import io.ebean.config.ServerConfig;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.config.dbplatform.SqlErrorCodes;
import io.ebean.dbmigration.ddlgeneration.DdlHandler;
import io.ebean.dbmigration.ddlgeneration.platform.PostgresDdl;
import javax.sql.DataSource;
import java.sql.Types;
@@ -36,7 +33,6 @@ public class PostgresPlatform extends DatabasePlatform {
this.dbEncrypt = new PostgresDbEncrypt();
this.historySupport = new PostgresHistorySupport();
this.platformDdl = new PostgresDdl(this);
// Use Identity and getGeneratedKeys
this.dbIdentity.setIdType(IdType.IDENTITY);
@@ -90,14 +86,6 @@ public class PostgresPlatform extends DatabasePlatform {
return new DbPlatformType("geometry(" + type + "," + srid + ")");
}
/**
* Return a DdlHandler instance for generating DDL for the specific platform.
*/
@Override
public DdlHandler createDdlHandler(ServerConfig serverConfig) {
return this.platformDdl.createDdlHandler(serverConfig);
}
/**
* Create a Postgres specific sequence IdGenerator.
*/
@@ -5,7 +5,6 @@ import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.dbmigration.ddlgeneration.platform.SQLiteDdl;
import java.sql.Types;
@@ -14,8 +13,6 @@ public class SQLitePlatform extends DatabasePlatform {
public SQLitePlatform() {
super();
this.platform = Platform.SQLITE;
this.platformDdl = new SQLiteDdl(this);
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(false);
this.dbIdentity.setSupportsSequence(false);
@@ -7,7 +7,7 @@ import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.SqlErrorCodes;
import io.ebean.dbmigration.ddlgeneration.platform.SqlServerDdl;
import java.sql.Types;
/**
@@ -25,7 +25,6 @@ public class SqlServerPlatform extends DatabasePlatform {
this.selectCountWithAlias = true;
this.sqlLimiter = new SqlServerSqlLimiter();
this.basicSqlLimiter = new SqlServerBasicSqlLimiter();
this.platformDdl = new SqlServerDdl(this);
this.historySupport = new SqlServerHistorySupport();
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
@@ -1,105 +1,52 @@
package io.ebean.dbmigration;
import io.ebean.Ebean;
import io.ebean.EbeanServer;
import io.ebean.config.DbConstraintNaming;
import io.ebean.config.DbMigrationConfig;
import io.ebean.annotation.Platform;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.db2.DB2Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
import io.ebean.config.dbplatform.sqlserver.SqlServerPlatform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.oracle.OraclePlatform;
import io.ebean.config.dbplatform.postgres.PostgresPlatform;
import io.ebean.config.dbplatform.sqlanywhere.SqlAnywherePlatform;
import io.ebean.config.dbplatform.sqlite.SQLitePlatform;
import io.ebean.dbmigration.ddlgeneration.DdlWrite;
import io.ebean.dbmigration.migration.Migration;
import io.ebean.dbmigration.migrationreader.MigrationXmlWriter;
import io.ebean.dbmigration.model.CurrentModel;
import io.ebean.dbmigration.model.MConfiguration;
import io.ebean.dbmigration.model.MigrationModel;
import io.ebean.dbmigration.model.MigrationVersion;
import io.ebean.dbmigration.model.ModelContainer;
import io.ebean.dbmigration.model.ModelDiff;
import io.ebean.dbmigration.model.PlatformDdlWriter;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.extraddl.model.DdlScript;
import io.ebeaninternal.extraddl.model.ExtraDdl;
import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import java.util.ServiceLoader;
/**
* Generates DB Migration xml and sql scripts.
* Generates DDL migration scripts based on changes to the current model.
*
* <p>
* Reads the prior migrations and compares with the current model of the EbeanServer
* and generates a migration 'diff' in the form of xml document with the logical schema
* changes and a series of sql scripts to apply, rollback the applied changes if necessary
* and drop objects (drop tables, drop columns).
* </p>
* <p>
* This does not run the migration or ddl scripts but just generates them.
* Typically this is run as a main method in src/test once a developer is happy
* with the next set of changes to the model.
* </p>
*
* <h3>Example: Run for a single specific platform</h3>
*
* <pre>{@code
*
* DbMigration migration = new DbMigration();
* migration.setPathToResources("src/main/resources");
* migration.setPlatform(DbPlatformName.ORACLE);
* // optionally specify the version and name
* //System.setProperty("ddl.migration.version", "1.1");
* //System.setProperty("ddl.migration.name", "add bars");
*
* migration.generateMigration();
* // generate a migration using drops from a prior version
* //System.setProperty("ddl.migration.pendingDropsFor", "1.2");
*
* DbMigration migration = DbMigration.create();
*
* migration.setPlatform(Platform.POSTGRES);
* migration.generateMigration();
*
* }</pre>
*/
public class DbMigration {
protected static final Logger logger = LoggerFactory.getLogger(DbMigration.class);
private static final String initialVersion = "1.0";
private static final String GENERATED_COMMENT = "THIS IS A GENERATED FILE - DO NOT MODIFY";
public interface DbMigration {
/**
* Set to true if DbMigration run with online EbeanServer instance.
* Create a DbMigration implementation to use.
*/
protected final boolean online;
static DbMigration create() {
protected SpiEbeanServer server;
protected DbMigrationConfig migrationConfig;
protected String pathToResources = "src/main/resources";
protected DatabasePlatform databasePlatform;
protected List<Pair> platforms = new ArrayList<>();
protected ServerConfig serverConfig;
protected DbConstraintNaming constraintNaming;
/**
* Create for offline migration generation.
*/
public DbMigration() {
this.online = false;
}
/**
* Create using online EbeanServer.
*/
public DbMigration(EbeanServer server) {
this.online = true;
setServer(server);
Iterator<DbMigration> loader = ServiceLoader.load(DbMigration.class).iterator();
if (loader.hasNext()) {
return loader.next();
}
throw new IllegalStateException("No service implementation found for DbMigration?");
}
/**
@@ -107,33 +54,18 @@ public class DbMigration {
* <p>
* This defaults to maven style 'src/main/resources'.
*/
public void setPathToResources(String pathToResources) {
this.pathToResources = pathToResources;
}
void setPathToResources(String pathToResources);
/**
* Set the server to use to determine the current model.
* Typically this is not called explicitly.
*/
public void setServer(EbeanServer ebeanServer) {
this.server = (SpiEbeanServer) ebeanServer;
setServerConfig(server.getServerConfig());
}
void setServer(EbeanServer ebeanServer);
/**
* Set the serverConfig to use. Typically this is not called explicitly.
*/
public void setServerConfig(ServerConfig config) {
if (this.serverConfig == null) {
this.serverConfig = config;
}
if (migrationConfig == null) {
this.migrationConfig = serverConfig.getMigrationConfig();
}
if (constraintNaming == null) {
this.constraintNaming = serverConfig.getConstraintNaming();
}
}
void setServerConfig(ServerConfig config);
/**
* Set the specific platform to generate DDL for.
@@ -141,9 +73,7 @@ public class DbMigration {
* If not set this defaults to the platform of the default server.
* </p>
*/
public void setPlatform(Platform platform) {
setPlatform(getPlatform(platform));
}
void setPlatform(Platform platform);
/**
* Set the specific platform to generate DDL for.
@@ -151,12 +81,7 @@ public class DbMigration {
* If not set this defaults to the platform of the default server.
* </p>
*/
public void setPlatform(DatabasePlatform databasePlatform) {
this.databasePlatform = databasePlatform;
if (!online) {
DbOffline.setPlatform(databasePlatform.getPlatform());
}
}
void setPlatform(DatabasePlatform databasePlatform);
/**
* Add an additional platform to write the migration DDL.
@@ -165,9 +90,7 @@ public class DbMigration {
* from the migration (e.g. generate migration sql for MySql, Postgres and Oracle).
* </p>
*/
public void addPlatform(Platform platform, String prefix) {
platforms.add(new Pair(getPlatform(platform), prefix));
}
void addPlatform(Platform platform, String prefix);
/**
* Generate the next migration xml file and associated apply and rollback sql scripts.
@@ -177,394 +100,30 @@ public class DbMigration {
* <h3>Example: Run for a single specific platform</h3>
* <pre>{@code
*
* DbMigration migration = new DbMigration();
* migration.setPathToResources("src/main/resources");
* migration.setPlatform(DbPlatformName.ORACLE);
* DbMigration migration = DbMigration.create();
*
* migration.generateMigration();
* migration.setPlatform(Platform.POSTGRES);
*
* migration.generateMigration();
*
* }</pre>
* <p>
* <h3>Example: Run migration generating DDL for multiple platforms</h3>
* <pre>{@code
*
* DbMigration migration = new DbMigration();
* migration.setPathToResources("src/main/resources");
* DbMigration migration = DbMigration.create();
*
* migration.addPlatform(DbPlatformName.POSTGRES, "pg");
* migration.addPlatform(DbPlatformName.MYSQL, "mysql");
* migration.addPlatform(DbPlatformName.ORACLE, "mysql");
* migration.setPathToResources("src/main/resources");
*
* migration.generateMigration();
* migration.addPlatform(Platform.POSTGRES, "pg");
* migration.addPlatform(Platform.MYSQL, "mysql");
* migration.addPlatform(Platform.ORACLE, "oracle");
*
* migration.generateMigration();
*
* }</pre>
* @return the generated migration or null
*
* @return the version of the generated migration or null
*/
public String generateMigration() throws IOException {
// use this flag to stop other plugins like full DDL generation
if (!online) {
DbOffline.setGenerateMigration();
if (databasePlatform == null && !platforms.isEmpty()) {
// for multiple platform generation set the general platform
// to H2 so that it runs offline without DB connection
setPlatform(platforms.get(0).platform);
}
}
setDefaults();
try {
Request request = createRequest();
if (platforms.isEmpty()) {
generateExtraDdl(request.migrationDir, databasePlatform);
}
String pendingVersion = generatePendingDrop();
if (pendingVersion != null) {
return generatePendingDrop(request, pendingVersion);
} else {
return generateDiff(request);
}
} finally {
if (!online) {
DbOffline.reset();
}
}
}
/**
* Generate "repeatable" migration scripts.
* <p>
* These take scrips from extra-dll.xml (typically views) and outputs "repeatable"
* migration scripts (starting with "R__") to be run by FlywayDb or Ebean's own
* migration runner.
* </p>
*/
private void generateExtraDdl(File migrationDir, DatabasePlatform dbPlatform) throws IOException {
if (dbPlatform != null) {
ExtraDdl extraDdl = ExtraDdlXmlReader.read("/extra-ddl.xml");
if (extraDdl != null) {
List<DdlScript> ddlScript = extraDdl.getDdlScript();
for (DdlScript script : ddlScript) {
if (ExtraDdlXmlReader.matchPlatform(dbPlatform.getName(), script.getPlatforms())) {
writeExtraDdl(migrationDir, script);
}
}
}
}
}
/**
* Write (or override) the "repeatable" migration script.
*/
private void writeExtraDdl(File migrationDir, DdlScript script) throws IOException {
String fullName = repeatableMigrationName(script.getName());
logger.info("writing repeatable script {}", fullName);
File file = new File(migrationDir, fullName);
try (FileWriter writer = new FileWriter(file)) {
writer.write(script.getValue());
writer.flush();
}
}
private String repeatableMigrationName(String scriptName) {
return "R__" + scriptName.replace(' ', '_') + migrationConfig.getApplySuffix();
}
/**
* Generate the diff migration.
*/
private String generateDiff(Request request) throws IOException {
List<String> pendingDrops = request.getPendingDrops();
if (!pendingDrops.isEmpty()) {
logger.info("Pending un-applied drops in versions {}", pendingDrops);
}
Migration migration = request.createDiffMigration();
if (migration == null) {
logger.info("no changes detected - no migration written");
return null;
} else {
// there were actually changes to write
return generateMigration(request, migration, null);
}
}
/**
* Generate the migration based on the pendingDrops from a prior version.
*/
private String generatePendingDrop(Request request, String pendingVersion) throws IOException {
Migration migration = request.migrationForPendingDrop(pendingVersion);
String version = generateMigration(request, migration, pendingVersion);
List<String> pendingDrops = request.getPendingDrops();
if (!pendingDrops.isEmpty()) {
logger.info("... remaining pending un-applied drops in versions {}", pendingDrops);
}
return version;
}
private Request createRequest() {
return new Request();
}
private class Request {
final File migrationDir;
final File modelDir;
final MigrationModel migrationModel;
final CurrentModel currentModel;
final ModelContainer migrated;
final ModelContainer current;
private Request() {
this.migrationDir = getMigrationDirectory();
this.modelDir = getModelDirectory(migrationDir);
this.migrationModel = new MigrationModel(modelDir, migrationConfig.getModelSuffix());
this.migrated = migrationModel.read();
this.currentModel = new CurrentModel(server, constraintNaming);
this.current = currentModel.read();
}
/**
* Return the migration for the pending drops for a given version.
*/
public Migration migrationForPendingDrop(String pendingVersion) {
Migration migration = migrated.migrationForPendingDrop(pendingVersion);
// register any remaining pending drops
migrated.registerPendingHistoryDropColumns(current);
return migration;
}
/**
* Return the list of versions that have pending un-applied drops.
*/
public List<String> getPendingDrops() {
return migrated.getPendingDrops();
}
/**
* Create and return the diff of the current model to the migration model.
*/
public Migration createDiffMigration() {
ModelDiff diff = new ModelDiff(migrated);
diff.compareTo(current);
return diff.isEmpty() ? null : diff.getMigration();
}
}
private String generateMigration(Request request, Migration dbMigration, String dropsFor) throws IOException {
String fullVersion = getFullVersion(request.migrationModel, dropsFor);
logger.info("generating migration:{}", fullVersion);
if (!writeMigrationXml(dbMigration, request.modelDir, fullVersion)) {
logger.warn("migration already exists, not generating DDL");
return null;
} else {
if (!platforms.isEmpty()) {
writeExtraPlatformDdl(fullVersion, request.currentModel, dbMigration, request.migrationDir);
} 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);
PlatformDdlWriter writer = createDdlWriter(databasePlatform);
writer.processMigration(dbMigration, write, request.migrationDir, fullVersion);
}
return fullVersion;
}
}
/**
* Return true if the next pending drop changeSet should be generated as the next migration.
*/
private String generatePendingDrop() {
String nextDrop = System.getProperty("ddl.migration.pendingDropsFor");
if (nextDrop != null) {
return nextDrop;
}
return migrationConfig.getGeneratePendingDrop();
}
/**
* Return the full version for the migration being generated.
* <p>
* The full version can contain a comment suffix after a "__" double underscore.
*/
private String getFullVersion(MigrationModel migrationModel, String dropsFor) {
String version = migrationConfig.getVersion();
if (version == null) {
version = migrationModel.getNextVersion(initialVersion);
}
String fullVersion = migrationConfig.getApplyPrefix() + version;
if (migrationConfig.getName() != null) {
fullVersion += "__" + toUnderScore(migrationConfig.getName());
} else if (dropsFor != null) {
fullVersion += "__" + toUnderScore("dropsFor_" + MigrationVersion.trim(dropsFor));
} else if (version.equals(initialVersion)) {
fullVersion += "__initial";
}
return fullVersion;
}
/**
* Replace spaces with underscores.
*/
private String toUnderScore(String name) {
return name.replace(' ', '_');
}
/**
* Write any extra platform ddl.
*/
protected void writeExtraPlatformDdl(String fullVersion, CurrentModel currentModel, Migration dbMigration, File writePath) throws IOException {
for (Pair pair : platforms) {
DdlWrite platformBuffer = new DdlWrite(new MConfiguration(), currentModel.read());
PlatformDdlWriter platformWriter = createDdlWriter(pair.platform);
File subPath = platformWriter.subPath(writePath, pair.prefix);
platformWriter.processMigration(dbMigration, platformBuffer, subPath, fullVersion);
generateExtraDdl(subPath, pair.platform);
}
}
private PlatformDdlWriter createDdlWriter(DatabasePlatform platform) {
return new PlatformDdlWriter(platform, serverConfig, migrationConfig);
}
/**
* Write the migration xml.
*/
protected boolean writeMigrationXml(Migration dbMigration, File resourcePath, String fullVersion) {
String modelFile = fullVersion + migrationConfig.getModelSuffix();
File file = new File(resourcePath, modelFile);
if (file.exists()) {
return false;
}
String comment = migrationConfig.isIncludeGeneratedFileComment() ? GENERATED_COMMENT : null;
MigrationXmlWriter xmlWriter = new MigrationXmlWriter(comment);
xmlWriter.write(dbMigration, file);
return true;
}
/**
* Set default server and platform if necessary.
*/
protected void setDefaults() {
if (server == null) {
setServer(Ebean.getDefaultServer());
}
if (databasePlatform == null && platforms.isEmpty()) {
// not explicitly set not set a list of platforms so
// default to the platform of the default server
databasePlatform = server.getDatabasePlatform();
logger.debug("set platform to {}", databasePlatform.getName());
}
}
/**
* Return the file path to write the xml and sql to.
*/
protected File getMigrationDirectory() {
// path to src/main/resources in typical maven project
File resourceRootDir = new File(pathToResources);
String resourcePath = migrationConfig.getMigrationPath();
// expect to be a path to something like - src/main/resources/dbmigration/model
File path = new File(resourceRootDir, resourcePath);
if (!path.exists()) {
if (!path.mkdirs()) {
logger.debug("Unable to ensure migration directory exists at {}", path.getAbsolutePath());
}
}
return path;
}
/**
* Return the model directory (relative to the migration directory).
*/
protected File getModelDirectory(File migrationDirectory) {
String modelPath = migrationConfig.getModelPath();
if (modelPath == null || modelPath.isEmpty()) {
return migrationDirectory;
}
File modelDir = new File(migrationDirectory, migrationConfig.getModelPath());
if (!modelDir.exists() && !modelDir.mkdirs()) {
logger.debug("Unable to ensure migration model directory exists at {}", modelDir.getAbsolutePath());
}
return modelDir;
}
/**
* Return the DatabasePlatform given the platform key.
*/
protected DatabasePlatform getPlatform(Platform platform) {
switch (platform) {
case H2:
return new H2Platform();
case HSQLDB:
return new HsqldbPlatform();
case POSTGRES:
return new PostgresPlatform();
case MYSQL:
return new MySqlPlatform();
case ORACLE:
return new OraclePlatform();
case SQLANYWHERE:
return new SqlAnywherePlatform();
case SQLSERVER:
return new SqlServerPlatform();
case DB2:
return new DB2Platform();
case SQLITE:
return new SQLitePlatform();
case GENERIC:
return new DatabasePlatform();
default:
throw new IllegalArgumentException("Platform missing? " + platform);
}
}
/**
* Holds a platform and prefix. Used to generate multiple platform specific DDL
* for a single migration.
*/
public static class Pair {
/**
* The platform to generate the DDL for.
*/
public final DatabasePlatform platform;
/**
* A prefix included into the file/resource names indicating the platform.
*/
public final String prefix;
public Pair(DatabasePlatform platform, String prefix) {
this.platform = platform;
this.prefix = prefix;
}
}
String generateMigration() throws IOException;
}
@@ -1,8 +1,4 @@
/**
* Generation DDL migration scripts based on changes to the model.
* <p>
* You can use <code>DbMigration</code> to compare the entity bean model to its prior state
* and generate DDL for the differences - adding tables, columns etc.
* </p>
* Generates DDL migration scripts based on changes to the model.
*/
package io.ebean.dbmigration;
@@ -24,4 +24,13 @@ public interface BeanQueryRequest<T> {
*/
Query<T> getQuery();
/**
* Return true if multi-value binding using Array or Table Values is supported.
*/
boolean isMultiValueIdSupported();
/**
* Return true if multi-value binding is supported for this value type.
*/
boolean isMultiValueSupported(Class<?> valueType);
}
@@ -8,37 +8,43 @@ public class BeanChange {
/**
* The underling base table name.
*/
private final String type;
private String type;
/**
* The tenantId value.
*/
private final Object tenantId;
private Object tenantId;
/**
* The id value.
*/
private final Object id;
private Object id;
/**
* The INSERT, UPDATE or DELETE change type.
*/
private final ChangeType event;
private ChangeType event;
/**
* The time the bean change was created.
*/
private final long eventTime;
private long eventTime;
/**
* The change in JSON form.
*/
private final String data;
private String data;
/**
* The change in JSON form.
*/
private final String oldData;
private String oldData;
/**
* Constructor for JSON tools.
*/
public BeanChange() {
}
/**
* Construct with change as JSON.
+7 -2
View File
@@ -1,11 +1,11 @@
package io.ebean.plugin;
import io.ebean.Query;
import io.ebean.config.dbplatform.IdType;
import io.ebean.event.BeanFindController;
import io.ebean.event.BeanPersistController;
import io.ebean.event.BeanPersistListener;
import io.ebean.event.BeanQueryAdapter;
import io.ebeaninternal.api.SpiQuery;
import io.ebeanservice.docstore.api.mapping.DocumentMapping;
import java.util.Collection;
@@ -20,6 +20,11 @@ public interface BeanType<T> {
*/
String getName();
/**
* Return the profileId of the bean type.
*/
short getProfileId();
/**
* Return the full name of the bean type.
*/
@@ -180,7 +185,7 @@ public interface BeanType<T> {
/**
* Add the discriminator value to the query if needed.
*/
void addInheritanceWhere(SpiQuery<?> query);
void addInheritanceWhere(Query<?> query);
/**
* Return the root bean type for an inheritance hierarchy.
@@ -1,4 +1,4 @@
package io.ebean.common;
package io.ebean.service;
import io.ebean.EbeanServer;
import io.ebean.config.ServerConfig;
@@ -0,0 +1,14 @@
package io.ebean.service;
import io.ebean.config.ContainerConfig;
/**
* Provides shutdown of the entire container.
*/
public interface SpiContainerFactory {
/**
* Create the Container that builds EbeanServer instances.
*/
SpiContainer create(ContainerConfig containerConfig);
}
@@ -0,0 +1,125 @@
package io.ebean.service;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* JSON service that Ebean is expected to provide.
*
* Supports converting between JSON content and simple java Maps/Lists.
*/
public interface SpiJsonService {
/**
* Write the nested Map/List as json.
*/
String write(Object object) throws IOException;
/**
* Write the nested Map/List as json to the writer.
*/
void write(Object object, Writer writer) throws IOException;
/**
* Write the nested Map/List as json to the jsonGenerator.
*/
void write(Object object, JsonGenerator jsonGenerator) throws IOException;
/**
* Write the collection as json array to the jsonGenerator.
*/
void writeCollection(Collection<Object> collection, JsonGenerator jsonGenerator) throws IOException;
/**
* Parse the json and return as a Map additionally specifying if the returned map should
* be modify aware meaning that it can detect when it has been modified.
*/
Map<String, Object> parseObject(String json, boolean modifyAware) throws IOException;
/**
* Parse the json and return as a Map.
*/
Map<String, Object> parseObject(String json) throws IOException;
/**
* Parse the json and return as a Map taking a reader.
*/
Map<String, Object> parseObject(Reader reader, boolean modifyAware) throws IOException;
/**
* Parse the json and return as a Map taking a reader.
*/
Map<String, Object> parseObject(Reader reader) throws IOException;
/**
* Parse the json and return as a Map taking a JsonParser.
*/
Map<String, Object> parseObject(JsonParser parser) throws IOException;
/**
* Parse the json and return as a Map taking a JsonParser and a starting token.
* <p>
* Used when the first token is checked to see if the value is null prior to calling this.
* </p>
*/
Map<String, Object> parseObject(JsonParser parser, JsonToken token) throws IOException;
/**
* Parse the json and return as a modify aware List.
*/
<T> List<T> parseList(String json, boolean modifyAware) throws IOException;
/**
* Parse the json and return as a List.
*/
List<Object> parseList(String json) throws IOException;
/**
* Parse the json and return as a List taking a Reader.
*/
List<Object> parseList(Reader reader) throws IOException;
/**
* Parse the json and return as a List taking a JsonParser.
*/
List<Object> parseList(JsonParser parser) throws IOException;
/**
* Parse the json returning as a List taking into account the current token.
*/
<T> List<T> parseList(JsonParser parser, JsonToken currentToken) throws IOException;
/**
* Parse the json and return as a List or Map.
*/
Object parse(String json) throws IOException;
/**
* Parse the json and return as a List or Map.
*/
Object parse(Reader reader) throws IOException;
/**
* Parse the json and return as a List or Map.
*/
Object parse(JsonParser parser) throws IOException;
/**
* Parse the json returning a Set that might be modify aware.
*/
<T> Set<T> parseSet(String json, boolean modifyAware) throws IOException;
/**
* Parse the json returning as a Set taking into account the current token.
*/
<T> Set<T> parseSet(JsonParser parser, JsonToken currentToken) throws IOException;
}
@@ -0,0 +1,27 @@
package io.ebean.service;
import io.ebean.RawSql;
import io.ebean.RawSqlBuilder;
import java.sql.ResultSet;
/**
* Service provided by Ebean for parsing and column mapping raw SQL queries.
*/
public interface SpiRawSqlService {
/**
* Create based on a JDBC ResultSet.
*/
RawSql resultSet(ResultSet resultSet, String... propertyNames);
/**
* Parse the SQL determining column mapping.
*/
RawSqlBuilder parsed(String sql);
/**
* Unparsed SQL so explicit column mapping expected.
*/
RawSqlBuilder unparsed(String sql);
}
@@ -2,7 +2,7 @@ package io.ebean.text;
import io.ebean.FetchPath;
import io.ebean.Query;
import io.ebeaninternal.server.query.SplitName;
import io.ebean.util.SplitName;
import java.util.Collection;
import java.util.Iterator;
+34 -31
View File
@@ -1,17 +1,18 @@
package io.ebean.text.json;
import io.ebeaninternal.server.type.ModifyAwareList;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import io.ebean.service.SpiJsonService;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
/**
@@ -19,32 +20,43 @@ import java.util.Set;
*/
public class EJson {
private static SpiJsonService plugin = init();
private static SpiJsonService init() {
Iterator<SpiJsonService> loader = ServiceLoader.load(SpiJsonService.class).iterator();
if (loader.hasNext()) {
return loader.next();
}
throw new IllegalStateException("No service implementation found for SpiJsonService?");
}
/**
* Write the nested Map/List as json.
*/
public static String write(Object object) throws IOException {
return EJsonWriter.write(object);
return plugin.write(object);
}
/**
* Write the nested Map/List as json to the writer.
*/
public static void write(Object object, Writer writer) throws IOException {
EJsonWriter.write(object, writer);
plugin.write(object, writer);
}
/**
* Write the nested Map/List as json to the jsonGenerator.
*/
public static void write(Object object, JsonGenerator jsonGenerator) throws IOException {
EJsonWriter.write(object, jsonGenerator);
plugin.write(object, jsonGenerator);
}
/**
* Write the collection as json array to the jsonGenerator.
*/
public static void writeCollection(Collection<Object> collection, JsonGenerator jsonGenerator) throws IOException {
EJsonWriter.writeCollection(collection, jsonGenerator);
plugin.writeCollection(collection, jsonGenerator);
}
/**
@@ -52,35 +64,35 @@ public class EJson {
* be modify aware meaning that it can detect when it has been modified.
*/
public static Map<String, Object> parseObject(String json, boolean modifyAware) throws IOException {
return EJsonReader.parseObject(json, modifyAware);
return plugin.parseObject(json, modifyAware);
}
/**
* Parse the json and return as a Map.
*/
public static Map<String, Object> parseObject(String json) throws IOException {
return EJsonReader.parseObject(json);
return plugin.parseObject(json);
}
/**
* Parse the json and return as a Map taking a reader.
*/
public static Map<String, Object> parseObject(Reader reader, boolean modifyAware) throws IOException {
return EJsonReader.parseObject(reader, modifyAware);
return plugin.parseObject(reader, modifyAware);
}
/**
* Parse the json and return as a Map taking a reader.
*/
public static Map<String, Object> parseObject(Reader reader) throws IOException {
return EJsonReader.parseObject(reader);
return plugin.parseObject(reader);
}
/**
* Parse the json and return as a Map taking a JsonParser.
*/
public static Map<String, Object> parseObject(JsonParser parser) throws IOException {
return EJsonReader.parseObject(parser);
return plugin.parseObject(parser);
}
/**
@@ -90,35 +102,35 @@ public class EJson {
* </p>
*/
public static Map<String, Object> parseObject(JsonParser parser, JsonToken token) throws IOException {
return EJsonReader.parseObject(parser, token);
return plugin.parseObject(parser, token);
}
/**
* Parse the json and return as a modify aware List.
*/
public static <T> List<T> parseList(String json, boolean modifyAware) throws IOException {
return EJsonReader.parseList(json, modifyAware);
return plugin.parseList(json, modifyAware);
}
/**
* Parse the json and return as a List.
*/
public static List<Object> parseList(String json) throws IOException {
return EJsonReader.parseList(json);
return plugin.parseList(json);
}
/**
* Parse the json and return as a List taking a Reader.
*/
public static List<Object> parseList(Reader reader) throws IOException {
return EJsonReader.parseList(reader);
return plugin.parseList(reader);
}
/**
* Parse the json and return as a List taking a JsonParser.
*/
public static List<Object> parseList(JsonParser parser) throws IOException {
return EJsonReader.parseList(parser, false);
return plugin.parseList(parser);
}
/**
@@ -126,50 +138,41 @@ public class EJson {
*/
@SuppressWarnings("unchecked")
public static <T> List<T> parseList(JsonParser parser, JsonToken currentToken) throws IOException {
return (List<T>) EJsonReader.parse(parser, currentToken, false);
return plugin.parseList(parser, currentToken);
}
/**
* Parse the json and return as a List or Map.
*/
public static Object parse(String json) throws IOException {
return EJsonReader.parse(json);
return plugin.parse(json);
}
/**
* Parse the json and return as a List or Map.
*/
public static Object parse(Reader reader) throws IOException {
return EJsonReader.parse(reader);
return plugin.parse(reader);
}
/**
* Parse the json and return as a List or Map.
*/
public static Object parse(JsonParser parser) throws IOException {
return EJsonReader.parse(parser);
return plugin.parse(parser);
}
/**
* Parse the json returning a Set that might be modify aware.
*/
public static <T> Set<T> parseSet(String json, boolean modifyAware) throws IOException {
List<T> list = parseList(json, modifyAware);
if (list == null) {
return null;
}
if (modifyAware) {
return ((ModifyAwareList<T>) list).asSet();
} else {
return new LinkedHashSet<T>(list);
}
return plugin.parseSet(json, modifyAware);
}
/**
* Parse the json returning as a Set taking into account the current token.
*/
public static <T> Set<T> parseSet(JsonParser parser, JsonToken currentToken) throws IOException {
return new LinkedHashSet<T>(parseList(parser, currentToken));
return plugin.parseSet(parser, currentToken);
}
}
@@ -0,0 +1,288 @@
package io.ebean.util;
import io.ebean.annotation.Formula;
import io.ebean.annotation.Platform;
import io.ebean.annotation.Where;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Annotation utility methods to find annotations recursively - taken from the spring framework.
*/
public class AnnotationUtil {
/**
* Determine if the supplied {@link Annotation} is defined in the core JDK {@code java.lang.annotation} package.
*/
public static boolean isInJavaLangAnnotationPackage(Annotation annotation) {
return annotation.annotationType().getName().startsWith("java.lang.annotation");
}
/**
* Find a single {@link Annotation} of {@code annotationType} on the supplied {@link AnnotatedElement}.
* <p>
* Meta-annotations will be searched if the annotation is not <em>directly present</em> on the supplied element.
* <p>
* <strong>Warning</strong>: this method operates generically on annotated elements. In other words, this method
* does not execute specialized search algorithms for classes or methods. It only traverses through Annotations!
* It also does not filter out platform dependent annotations!
*/
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
if (annotationType == null) {
return null;
}
// check if directly present, if not, start search for meta-annotations.
Annotation[] anns = annotatedElement.getAnnotations();
if (anns.length == 0) {
return null; // no annotations present, so searching for meta annotations not required
}
// As we need the anns array anyway, we iterate over this instead
// of using annotatedElement.getAnnotation(...) which is synchronized internally
for (Annotation ann : anns) {
if (ann.annotationType() == annotationType) {
return (A) ann;
}
}
return findAnnotation(anns, annotationType, new HashSet<>());
}
/**
* Find a single {@link Annotation} of {@code annotationType} on the supplied class.
* <p>Meta-annotations will be searched if the annotation is not directly present on
* the supplied element.
* <p><strong>Note</strong>: this method searches for annotations at class & superClass(es)!
*/
public static <A extends Annotation> A findAnnotationRecursive(Class<?> clazz, Class<A> annotationType) {
if (annotationType == null) {
return null;
}
while (clazz != null && clazz != Object.class) {
// check if directly present, if not, start search for meta-annotations.
Annotation[] anns = clazz.getAnnotations();
if (anns.length != 0) {
for (Annotation ann : anns) {
if (ann.annotationType() == annotationType) {
return (A) ann;
}
}
A ann = findAnnotation(anns, annotationType, new HashSet<>());
if (ann != null) {
return ann;
}
}
// no meta-annotation present at this class - traverse to superclass
clazz = clazz.getSuperclass();
}
return null;
}
/**
* Finds the first annotation of a type for this platform. (if annotation is platform specific, otherwise first
* found annotation is returned)
*/
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType, Platform platform) {
if (annotationType == null) {
return null;
}
Set<A> anns = findAnnotations(annotatedElement, annotationType);
return getPlatformMatchingAnnotation(anns, platform);
}
/**
* Finds all annotations recusively for a class and its superclasses.
*/
public static <A extends Annotation> Set<A> findAnnotationsRecursive(Class<?> clazz, Class<A> annotationType) {
if (annotationType == null) {
return null;
}
Set<A> ret = new LinkedHashSet<>();
Set<Annotation> visited = new HashSet<>();
while (clazz != null && clazz != Object.class) {
findMetaAnnotations(clazz, annotationType, ret, visited);
clazz = clazz.getSuperclass();
}
return ret;
}
/**
* Perform the search algorithm avoiding endless recursion by tracking which
* annotations have already been visited.
*/
@SuppressWarnings("unchecked")
private static <A extends Annotation> A findAnnotation(Annotation[] anns, Class<A> annotationType, Set<Annotation> visited) {
for (Annotation ann : anns) {
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
Annotation[] metaAnns = ann.annotationType().getAnnotations();
for (Annotation metaAnn : metaAnns) {
if (metaAnn.annotationType() == annotationType) {
return (A) metaAnn;
}
}
if (metaAnns.length > 0) {
A annotation = findAnnotation(metaAnns, annotationType, visited);
if (annotation != null) {
return annotation;
}
}
}
}
return null;
}
/**
* Find all {@link Annotation}s of {@code annotationType} on the supplied {@link AnnotatedElement}.
* <p>
* Meta-annotations will be searched if the annotation is not <em>directly present</em> on the supplied element.
* <p>
* <strong>Warning</strong>: this method operates generically on annotated elements. In other words, this method
* does not execute specialized search algorithms for classes or methods. It only traverses through Annotations!
*/
public static <A extends Annotation> Set<A> findAnnotations(AnnotatedElement annotatedElement, Class<A> annotationType) {
if (annotationType == null) {
return null;
}
Set<A> ret = new LinkedHashSet<>();
findMetaAnnotations(annotatedElement, annotationType, ret, new HashSet<>());
return ret;
}
/**
* Perform the search algorithm avoiding endless recursion by tracking which
* annotations have already been visited.
*/
@SuppressWarnings("unchecked")
private static <A extends Annotation> void findMetaAnnotations(AnnotatedElement annotatedElement, Class<A> annotationType, Set<A> ret, Set<Annotation> visited) {
Annotation[] anns = annotatedElement.getAnnotations();
for (Annotation ann : anns) {
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
if (ann.annotationType() == annotationType) {
ret.add((A) ann);
} else {
Method repeatableValueMethod = getRepeatableValueMethod(ann, annotationType);
if (repeatableValueMethod != null) {
try {
A[] repeatedAnns = (A[]) repeatableValueMethod.invoke(ann);
for (Annotation repeatedAnn : repeatedAnns) {
ret.add((A) repeatedAnn);
findMetaAnnotations(repeatedAnn.annotationType(), annotationType, ret, visited);
}
} catch (Exception e) { // catch all exceptions (thrown by invoke)
throw new RuntimeException(e);
}
} else {
findMetaAnnotations(ann.annotationType(), annotationType, ret, visited);
}
}
}
}
}
// caches for getRepeatableValueMethod
private static Method getNullMethod() {
try {
return AnnotationUtil.class.getDeclaredMethod("getNullMethod");
} catch (NoSuchMethodException e) {
return null;
}
}
private static final ConcurrentMap<Annotation, Method> valueMethods = new ConcurrentHashMap<>();
// only a non-null-marker the valueMethods - Cache
private static final Method nullMethod = getNullMethod();
/**
* Returns the <code>value()</code> method for a possible containerAnnotation.
* Method is retuned only, if its signature is <code>array of containingType</code>.
*/
private static <A extends Annotation> Method getRepeatableValueMethod(
Annotation containerAnnotation, Class<A> containingType) {
Method method = valueMethods.get(containerAnnotation);
if (method == null) {
try {
method = containerAnnotation.annotationType().getMethod("value");
} catch (NoSuchMethodException e) {
method = nullMethod;
} catch (Exception e) {
throw new RuntimeException(e);
}
Method prev = valueMethods.putIfAbsent(containerAnnotation, method);
method = prev == null ? method : prev;
}
if (method != nullMethod) {
Class<?> retType = method.getReturnType();
if (retType.isArray() && retType.getComponentType() == containingType) {
return method;
}
}
return null;
}
/**
* Finds a suitable annotation from <code>Set<T> anns</code> for this platform.
* To distinguish between platforms, annotation type <code>T</code> must define
* a method withthis signature:
* <p>
* <code>Class<? extends DatabasePlatform>[] platforms() default {};</code>
* </p>
* The finding rules are:
* <ol>
* <li>Check if T has method "platforms" if not, return <code>ann[0]</code></code>
* <li>find the annotation that is defined for <code>databasePlatform</code></li>
* <li>otherwise return the annotation for default platform (platforms = {})</li>
* <li>return null
* </ol>
* (This mechanism is currently used by {@link Where} and {@link Formula})
*/
public static <T extends Annotation> T getPlatformMatchingAnnotation(Set<T> anns, Platform matchPlatform) {
if (anns.isEmpty()) {
return null;
}
Method getPlatformsMethod = null;
T fallback = null;
for (T ann : anns) {
try {
if (getPlatformsMethod == null) {
getPlatformsMethod = ann.getClass().getMethod("platforms");
}
if (!Platform[].class.isAssignableFrom(getPlatformsMethod.getReturnType())) {
return ann;
}
Platform[] platforms = (Platform[]) getPlatformsMethod.invoke(ann);
if (platforms.length == 0) {
fallback = ann;
} else {
for (Platform platform : platforms) {
if (matchPlatform == platform) {
return ann;
}
}
}
} catch (NoSuchMethodException e) {
return ann; // not platform specific - return first one
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return fallback;
}
}
@@ -1,47 +0,0 @@
package io.ebean.util;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
/**
* Helper to find classes taking into account the context class loader.
*/
public class ClassUtil {
/**
* Returns the raw type for the 2nd generic parameter for a subclass.
*/
public static Class<?> getSecondArgumentType(Class<?> subclass) {
Type[] typeArguments = getSuperclassTypeParameter(subclass);
if (typeArguments.length != 2) {
throw new IllegalArgumentException("Expected type with 2 generic argument types but got "
+ typeArguments.length + " - " + Arrays.toString(typeArguments));
}
return getRawType(typeArguments[1]);
}
static Type[] getSuperclassTypeParameter(Class<?> subclass) {
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class) {
throw new RuntimeException("Missing generics type parameters on subclass " + subclass);
}
return ((ParameterizedType) superclass).getActualTypeArguments();
}
private static Class<?> getRawType(Type type) {
if (type instanceof Class<?>) {
return (Class<?>) type;
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Type rawType = parameterizedType.getRawType();
if (rawType instanceof Class<?>) {
return (Class<?>) rawType;
}
}
throw new RuntimeException("Unable to obtain raw class type from " + type);
}
}
@@ -1,4 +1,4 @@
package io.ebeaninternal.server.query;
package io.ebean.util;
/**
* Helper for dot notation property paths.
+2 -107
View File
@@ -13,9 +13,9 @@ public class StringHelper {
private static final char SINGLE_QUOTE = '\'';
private static final char DOUBLE_QUOTE = '"';
private static final Pattern SPLIT_NAMES = Pattern.compile("[\\s,;]+");
private static final String[] EMPTY_STRING_ARRAY = new String[0];
/**
@@ -270,66 +270,6 @@ public class StringHelper {
}
}
/**
* This returns the FIRST string in str that is bounded on the left by
* leftBound, and bounded on the right by rightBound. This will return null if
* the leftBound is not found within str.
* <p>
* If leftBound can't be found this returns null.
* <p>
* This rightBound can't be found then this throws a
* StringIndexOutOfBoundsException.
*
* @param str the base string that we will search for the bounded string.
* @param leftBound the left bound of the string.
* @param rightBound the right bound of the string.
*/
public static String getBoundedString(String str, String leftBound, String rightBound)
throws RuntimeException {
if (str == null) {
throw new RuntimeException("string to parse is null?");
}
int startPos = str.indexOf(leftBound);
if (startPos > -1) {
startPos += leftBound.length();
int endPos = str.indexOf(rightBound, startPos);
// dp(str+" start:"+startPos+" end:"+endPos);
if (endPos == -1) {
throw new RuntimeException("Can't find rightBound: " + rightBound);
}
return str.substring(startPos, endPos);
} else {
// if no leftBound can be found.. return null... could be in a
// search n parse type loop?
// this keeps "no tag"==null different from "tag not formed
// properly"==StringParsingException
return null;
}
}
/**
* Takes the String bounded by leftBound & rightBound, and replaces it with
* replaceString. Actually removes the left and right bound strings aswell.
*/
public static String setBoundedString(String str, String leftBound, String rightBound,
String replaceString) {
int startPos = str.indexOf(leftBound);
if (startPos > -1) {
// startPos = startPos;
int endPos = str.indexOf(rightBound, startPos + leftBound.length());
if (endPos > -1) {
String toReplace = str.substring(startPos, endPos + 1);
return replaceString(str, toReplace, replaceString);
} else {
return str;
}
} else {
return str;
}
}
/**
* This method takes a String and will replace all occurrences of the match
* String with that of the replace String.
@@ -512,51 +452,6 @@ public class StringHelper {
return sb.toString();
}
/**
* This method takes a String as an argument and removes all occurrences of
* the supplied Char. It returns the resulting String.
*/
public static String removeChar(String s, char chr) {
StringBuilder sb = new StringBuilder(s.length());
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c != chr) {
sb.append(c);
}
}
return sb.toString();
}
/**
* This method takes a String as an argument and removes all occurrences of
* the supplied Chars. It returns the resulting String.
*/
public static String removeChars(String s, char[] chr) {
StringBuilder sb = new StringBuilder(s.length());
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (!charMatch(c, chr)) {
sb.append(c);
}
}
return sb.toString();
}
private static boolean charMatch(int iChr, char[] chr) {
for (char aChr : chr) {
if (iChr == aChr) {
return true;
}
}
return false;
}
/**
* Splits at any whitespace "," or ";" and trims the result.
* It does not return empty entries.
@@ -1,4 +1,4 @@
package io.ebean.plugin;
package io.ebeaninternal.api;
import io.ebean.config.ServerConfig;
import io.ebeaninternal.server.type.ScalarType;
@@ -1,7 +1,6 @@
package io.ebeaninternal.api;
import io.ebean.Ebean;
import io.ebean.EbeanServer;
import io.ebean.TxScope;
/**
@@ -10,28 +9,20 @@ import io.ebean.TxScope;
public class HelpScopeTrans {
/**
* Create a ScopeTrans for a given methods TxScope.
* Entering an enhanced transactional method.
*/
public static ScopeTrans createScopeTrans(TxScope txScope) {
EbeanServer server = Ebean.getServer(txScope.getServerName());
SpiEbeanServer iserver = (SpiEbeanServer) server;
return iserver.createScopeTrans(txScope);
public static void enter(TxScope txScope) {
server().scopedTransactionEnter(txScope);
}
/**
* Exiting the method in an expected fashion.
* <p>
* That is returning successfully or via a caught exception.
* Unexpected exceptions are caught via the Thread uncaughtExceptionHandler.
* </p>
*
* @param returnOrThrowable the return or throwable object
* @param opCode the opcode for ATHROW or ARETURN etc
* @param scopeTrans the scoped transaction the method was run with.
* Exiting an enhanced transactional method.
*/
public static void onExitScopeTrans(Object returnOrThrowable, int opCode, ScopeTrans scopeTrans) {
public static void exit(Object returnOrThrowable, int opCode) {
server().scopedTransactionExit(returnOrThrowable, opCode);
}
scopeTrans.onExit(returnOrThrowable, opCode);
private static SpiEbeanServer server() {
return (SpiEbeanServer) Ebean.getDefaultServer();
}
}
@@ -103,7 +103,8 @@ public class LoadBeanRequest extends LoadRequest {
idList.add(desc.getId(bean));
}
if (!idList.isEmpty()) {
if (!desc.isMultiValueIdSupported() && !idList.isEmpty()) {
int extraIds = batchSize - batch.size();
if (extraIds > 0) {
// for performance make up the Id's to the batch size
@@ -107,11 +107,13 @@ public class LoadManyRequest extends LoadRequest {
for (BeanCollection<?> bc : batch) {
idList.add(many.getParentId(bc.getOwnerBean()));
}
int extraIds = batchSize - batch.size();
if (extraIds > 0) {
Object firstId = idList.get(0);
for (int i = 0; i < extraIds; i++) {
idList.add(firstId);
if (!many.getTargetDescriptor().isMultiValueIdSupported()) {
int extraIds = batchSize - batch.size();
if (extraIds > 0) {
Object firstId = idList.get(0);
for (int i = 0; i < extraIds; i++) {
idList.add(firstId);
}
}
}
@@ -26,16 +26,6 @@ public abstract class LoadRequest {
*/
public abstract Class<?> getBeanType();
/**
* Log the just executed secondary query with the 'root' query if 'logSecondaryQuery' is set to
* true. This is for testing purposes to confirm the secondary query executes etc.
*/
public void logSecondaryQuery(SpiQuery<?> query) {
if (parentRequest != null && parentRequest.isLogSecondaryQuery()) {
parentRequest.getQuery().logSecondaryQuery(query);
}
}
/**
* Return true if this is a lazy load and false if it is a secondary query.
*/
@@ -3,7 +3,7 @@ package io.ebeaninternal.api;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.el.ElPropertyDeploy;
import io.ebeaninternal.server.query.SplitName;
import io.ebean.util.SplitName;
import io.ebeaninternal.server.query.SqlJoinType;
import java.io.Serializable;
@@ -8,17 +8,10 @@ import java.util.ArrayList;
/**
* Used internally to handle the scoping of transactions for methods.
*/
public class ScopeTrans implements Thread.UncaughtExceptionHandler {
public class ScopeTrans {
private static final int OPCODE_ATHROW = 191;
private final SpiTransactionScopeManager scopeMgr;
/**
* The suspended transaction (can be null).
*/
private final SpiTransaction suspendedTransaction;
/**
* The transaction in scope (can be null).
*/
@@ -61,15 +54,11 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
private boolean rolledBack;
public ScopeTrans(boolean rollbackOnChecked, boolean created, SpiTransaction transaction, TxScope txScope,
SpiTransaction suspendedTransaction, SpiTransactionScopeManager scopeMgr) {
public ScopeTrans(boolean rollbackOnChecked, boolean created, SpiTransaction transaction, TxScope txScope) {
this.rollbackOnChecked = rollbackOnChecked;
this.created = created;
this.transaction = transaction;
this.suspendedTransaction = suspendedTransaction;
this.scopeMgr = scopeMgr;
this.noRollbackFor = txScope.getNoRollbackFor();
this.rollbackFor = txScope.getRollbackFor();
@@ -100,6 +89,10 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
}
public String toString() {
return "ScopeTrans[" + transaction + "]";
}
/**
* Return the current/active transaction.
*/
@@ -108,55 +101,30 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
}
/**
* Called when the Thread catches any uncaught exception.
* For example, an unexpected NullPointerException or Error.
* Complete the transaction from enhanced transactional. Try to commit.
*/
@Override
public void uncaughtException(Thread thread, Throwable e) {
// rollback transaction if required
caughtThrowable(e);
// reinstate suspended transaction
onFinally();
}
/**
* Returned via RETURN or expected Exception from the method.
*
* @param returnOrThrowable the return value or Throwable
* @param opCode indicates
*/
public void onExit(Object returnOrThrowable, int opCode) {
void complete(Object returnOrThrowable, int opCode) {
if (opCode == OPCODE_ATHROW) {
// exited with a Throwable
caughtThrowable((Throwable) returnOrThrowable);
}
onFinally();
complete();
}
/**
* Commit if the transaction exists and has not already been rolled back.
* Also reinstate the suspended transaction if there was one.
* Complete the transaction programmatically. Try to commit.
*/
public void onFinally() {
try {
if (!rolledBack) {
commitTransaction();
}
} finally {
restoreSuspended();
public void complete() {
if (!rolledBack) {
commitTransaction();
}
}
protected void restoreSuspended() {
if (suspendedTransaction != null) {
// put the previously suspended transaction
// back onto the ThreadLocal or equivalent
scopeMgr.replace(suspendedTransaction);
public void end() {
if (created) {
transaction.end();
}
}
@@ -247,17 +215,10 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
}
}
if (e instanceof RuntimeException) {
return true;
} else {
// checked exceptions...
// EJB defaults this to false which is not intuitive IMO
// Ebean makes this configurable (default to true)
return rollbackOnChecked;
}
// checked exceptions...
// EJB defaults this to false which is not intuitive IMO
// Ebean makes this configurable (default to true)
return e instanceof RuntimeException || rollbackOnChecked;
}
}
@@ -1,421 +1,121 @@
package io.ebeaninternal.api;
import io.ebean.annotation.PersistBatch;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.bean.PersistenceContext;
import io.ebean.event.changelog.BeanChange;
import io.ebean.event.changelog.ChangeSet;
import io.ebeaninternal.server.core.PersistDeferredRelationship;
import io.ebeaninternal.server.core.PersistRequest;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.persist.BatchControl;
import io.ebeanservice.docstore.api.DocStoreTransaction;
import io.ebeaninternal.server.transaction.TransactionScopeManager;
import io.ebeaninternal.server.util.ArrayStack;
import javax.persistence.PersistenceException;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Wrapper of a ScopeTrans request and it's underlying transaction.
* Manage scoped (typically thread local) transactions.
*
* These can be nested and internally they are pushed and popped from a stack.
*/
public class ScopedTransaction implements SpiTransaction {
public class ScopedTransaction extends SpiTransactionProxy {
private final ScopeTrans scopeTrans;
private final TransactionScopeManager manager;
private final SpiTransaction transaction;
/**
* Stack of 'nested' transactions.
*/
private ArrayStack<ScopeTrans> stack = new ArrayStack<>();
private boolean committed;
private ScopeTrans current;
public ScopedTransaction(ScopeTrans scopeTrans) {
this.scopeTrans = scopeTrans;
this.transaction = scopeTrans.getTransaction();
public ScopedTransaction(TransactionScopeManager manager) {
this.manager = manager;
}
@Override
public PersistenceException translate(String message, SQLException cause) {
return transaction.translate(message, cause);
public String toString() {
return "ScopedTransaction[" + current + "]";
}
@Override
public void commitAndContinue() {
transaction.commitAndContinue();
/**
* Push the scope transaction.
*/
public void push(ScopeTrans scopeTrans) {
if (current != null) {
stack.push(current);
}
current = scopeTrans;
transaction = scopeTrans.getTransaction();
}
@Override
public void commit() {
scopeTrans.commitTransaction();
committed = true;
/**
* Exiting an enhanced transactional method.
*/
public void complete(Object returnOrThrowable, int opCode) {
current.complete(returnOrThrowable, opCode);
pop();
}
@Override
public void rollback() throws PersistenceException {
scopeTrans.rollback(null);
/**
* Programmatic complete - finally block, try to commit.
*/
public void complete() {
current.complete();
pop();
}
@Override
public void rollback(Throwable e) throws PersistenceException {
scopeTrans.rollback(e);
}
@Override
public void rollbackIfActive() {
transaction.rollbackIfActive();
}
@Override
public void setRollbackOnly() {
scopeTrans.setRollbackOnly();
}
@Override
public boolean isRollbackOnly() {
return transaction.isRollbackOnly();
}
@Override
public void end() throws PersistenceException {
try {
if (!committed) {
scopeTrans.rollback(null);
}
} finally {
scopeTrans.restoreSuspended();
private void pop() {
if (!stack.isEmpty()) {
current = stack.pop();
transaction = current.getTransaction();
} else {
manager.set(null);
}
}
@Override
public void setTenantId(Object tenantId) {
transaction.setTenantId(tenantId);
}
@Override
public Object getTenantId() {
return transaction.getTenantId();
}
@Override
public DocStoreTransaction getDocStoreTransaction() {
return transaction.getDocStoreTransaction();
}
@Override
public DocStoreMode getDocStoreMode() {
return transaction.getDocStoreMode();
}
@Override
public void setDocStoreMode(DocStoreMode mode) {
transaction.setDocStoreMode(mode);
}
@Override
public int getDocStoreBatchSize() {
return transaction.getDocStoreBatchSize();
}
@Override
public void setDocStoreBatchSize(int batchSize) {
transaction.setDocStoreBatchSize(batchSize);
}
@Override
public String getLogPrefix() {
return transaction.getLogPrefix();
}
@Override
public boolean isLogSql() {
return transaction.isLogSql();
}
@Override
public boolean isLogSummary() {
return transaction.isLogSummary();
}
@Override
public void logSql(String msg) {
transaction.logSql(msg);
}
@Override
public void logSummary(String msg) {
transaction.logSummary(msg);
}
@Override
public void setSkipCache(boolean skipCache) {
transaction.setSkipCache(skipCache);
}
@Override
public boolean isSkipCache() {
return transaction.isSkipCache();
}
@Override
public void addBeanChange(BeanChange beanChange) {
transaction.addBeanChange(beanChange);
}
@Override
public void sendChangeLog(ChangeSet changes) {
transaction.sendChangeLog(changes);
}
@Override
public void registerDeferred(PersistDeferredRelationship derived) {
transaction.registerDeferred(derived);
}
@Override
public void registerDeleteBean(Integer hash) {
transaction.registerDeleteBean(hash);
}
@Override
public void unregisterDeleteBean(Integer hash) {
transaction.unregisterDeleteBean(hash);
}
@Override
public boolean isRegisteredDeleteBean(Integer hash) {
return transaction.isRegisteredDeleteBean(hash);
}
@Override
public void unregisterBean(Object bean) {
transaction.unregisterBean(bean);
}
@Override
public boolean isRegisteredBean(Object bean) {
return transaction.isRegisteredBean(bean);
}
@Override
public String getId() {
return transaction.getId();
}
@Override
public void register(TransactionCallback callback) {
transaction.register(callback);
}
@Override
public boolean isReadOnly() {
return transaction.isReadOnly();
}
@Override
public void setReadOnly(boolean readOnly) {
transaction.setReadOnly(readOnly);
}
@Override
public boolean isActive() {
return transaction.isActive();
}
@Override
public void setPersistCascade(boolean persistCascade) {
transaction.setPersistCascade(persistCascade);
}
@Override
public void setUpdateAllLoadedProperties(boolean updateAllLoaded) {
transaction.setUpdateAllLoadedProperties(updateAllLoaded);
}
@Override
public Boolean isUpdateAllLoadedProperties() {
return transaction.isUpdateAllLoadedProperties();
}
@Override
public void setBatchMode(boolean useBatch) {
transaction.setBatchMode(useBatch);
}
@Override
public void setBatch(PersistBatch persistBatchMode) {
transaction.setBatch(persistBatchMode);
}
@Override
public PersistBatch getBatch() {
return transaction.getBatch();
}
@Override
public void setBatchOnCascade(PersistBatch batchOnCascadeMode) {
transaction.setBatchOnCascade(batchOnCascadeMode);
}
@Override
public PersistBatch getBatchOnCascade() {
return transaction.getBatchOnCascade();
}
@Override
public void setBatchSize(int batchSize) {
transaction.setBatchSize(batchSize);
}
@Override
public int getBatchSize() {
return transaction.getBatchSize();
}
@Override
public void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
transaction.setBatchGetGeneratedKeys(getGeneratedKeys);
}
@Override
public Boolean getBatchGetGeneratedKeys() {
return transaction.getBatchGetGeneratedKeys();
}
@Override
public void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
transaction.setBatchFlushOnMixed(batchFlushOnMixed);
}
@Override
public void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
transaction.setBatchFlushOnQuery(batchFlushOnQuery);
}
@Override
public boolean isBatchFlushOnQuery() {
return transaction.isBatchFlushOnQuery();
}
@Override
public void flush() throws PersistenceException {
transaction.flush();
}
@Override
public void flushBatch() throws PersistenceException {
flush();
}
@Override
public Connection getConnection() {
return transaction.getConnection();
}
@Override
public void addModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
transaction.addModification(tableName, inserts, updates, deletes);
}
@Override
public void putUserObject(String name, Object value) {
transaction.putUserObject(name, value);
}
@Override
public Object getUserObject(String name) {
return transaction.getUserObject(name);
}
@Override
public void depth(int diff) {
transaction.depth();
}
@Override
public int depth() {
return transaction.depth();
}
@Override
public boolean isExplicit() {
return transaction.isExplicit();
}
@Override
public TransactionEvent getEvent() {
return transaction.getEvent();
}
@Override
public boolean isPersistCascade() {
return transaction.isPersistCascade();
}
@Override
public boolean isBatchThisRequest(PersistRequest.Type type) {
return transaction.isBatchThisRequest(type);
}
@Override
public BatchControl getBatchControl() {
return transaction.getBatchControl();
}
@Override
public void setBatchControl(BatchControl control) {
transaction.setBatchControl(control);
}
@Override
public PersistenceContext getPersistenceContext() {
return transaction.getPersistenceContext();
}
@Override
public void setPersistenceContext(PersistenceContext context) {
transaction.setPersistenceContext(context);
}
@Override
public Connection getInternalConnection() {
return transaction.getInternalConnection();
}
@Override
public boolean isSaveAssocManyIntersection(String intersectionTable, String beanName) {
return transaction.isSaveAssocManyIntersection(intersectionTable, beanName);
}
@Override
public boolean checkBatchEscalationOnCascade(PersistRequestBean<?> request) {
return transaction.checkBatchEscalationOnCascade(request);
}
@Override
public void flushBatchOnCascade() {
transaction.flushBatchOnCascade();
}
@Override
public void flushBatchOnRollback() {
transaction.flushBatchOnRollback();
}
@Override
public void markNotQueryOnly() {
transaction.markNotQueryOnly();
}
@Override
public void checkBatchEscalationOnCollection() {
transaction.checkBatchEscalationOnCollection();
}
@Override
public void flushBatchOnCollection() {
transaction.flushBatchOnCollection();
public void end() throws PersistenceException {
current.end();
pop();
}
@Override
public void close() {
transaction.close();
end();
}
@Override
public void commit() {
current.commitTransaction();
}
@Override
public void rollback() throws PersistenceException {
current.rollback(null);
}
@Override
public void rollback(Throwable e) throws PersistenceException {
current.rollback(e);
}
@Override
public void setRollbackOnly() {
current.setRollbackOnly();
}
/**
* Return the current transaction.
*/
public SpiTransaction current() {
return transaction;
}
/**
* Rollback for Error.
*/
public Error caughtError(Error e) {
return current.caughtError(e);
}
/**
* Maybe rollback based on TxScope rollback on settings.
*/
public Exception caughtThrowable(Exception e) {
return current.caughtThrowable(e);
}
}
@@ -13,6 +13,7 @@ import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
import io.ebeaninternal.server.core.timezone.DataTimeZone;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.query.CQuery;
@@ -115,22 +116,14 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
void externalModification(TransactionEventTable event);
/**
* Create a ServerTransaction.
* <p>
* To specify to use the default transaction isolation use a value of -1.
* </p>
* Begin a managed transaction (Uses scope manager / ThreadLocal).
*/
SpiTransaction createServerTransaction(boolean isExplicit, int isolationLevel);
SpiTransaction beginServerTransaction();
/**
* Return the current transaction or null if there is no current transaction.
*/
SpiTransaction getCurrentServerTransaction();
/**
* Create a ScopeTrans for a method for the given scope definition.
*/
ScopeTrans createScopeTrans(TxScope txScope);
SpiTransaction currentServerTransaction();
/**
* Create a ServerTransaction for query purposes.
@@ -210,4 +203,20 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
* Check for slow query event.
*/
void slowQueryCheck(long executionTimeMicros, int rowCount, SpiQuery<?> query);
/**
* Create DDL handler given the platform and configuration of the server.
*/
DdlHandler createDdlHandler();
/**
* Start an enhanced transactional method.
*/
void scopedTransactionEnter(TxScope txScope);
/**
* Handle the end of an enhanced Transactional method.
*/
void scopedTransactionExit(Object returnOrThrowable, int opCode);
}
@@ -70,4 +70,9 @@ public interface SpiExpressionRequest {
* Escapes a string to use it as exact match in Like clause.
*/
String escapeLikeString(String value);
/**
* Append IN expression taking into account platform and type support for Multi-value.
*/
void appendInExpression(boolean not, Object[] bindValues);
}
@@ -0,0 +1,30 @@
package io.ebeaninternal.api;
import io.ebeaninternal.server.transaction.ProfileStream;
import io.ebeaninternal.server.transaction.TransactionProfile;
/**
* Handle the logging or processing of transaction profiling information that is collected.
*/
public interface SpiProfileHandler {
/**
* Process the collected transaction profiling information.
* <p>
* Note that profileId and totalMicros are part of the profilingData but passed separately as the handler
* may filter what it processed based on this information (ignore short transactions, only process specific
* profileId transactions etc).
* </p>
*
* @param transactionProfile The transaction profile that has just been collected
*/
void collectTransactionProfile(TransactionProfile transactionProfile);
/**
* Create a profiling stream if we are profiling this transaction.
* Return null if we are not profiling this transaction.
*
* @param profileId The transaction profileId
*/
ProfileStream createProfileStream(int profileId);
}
@@ -0,0 +1,12 @@
package io.ebeaninternal.api;
/**
* Event that adds to a profiling transaction.
*/
public interface SpiProfileTransactionEvent {
/**
* Add the event information to the profiling transaction.
*/
void profile();
}
@@ -20,6 +20,7 @@ import io.ebeaninternal.server.query.CancelableQuery;
import io.ebeaninternal.server.querydefn.NaturalKeyBindParam;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
import io.ebeaninternal.server.querydefn.OrmUpdateProperties;
import io.ebeaninternal.server.rawsql.SpiRawSql;
import java.sql.Timestamp;
import java.util.List;
@@ -28,7 +29,7 @@ import java.util.Set;
/**
* Object Relational query - Internal extension to Query object.
*/
public interface SpiQuery<T> extends Query<T> {
public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
enum Mode {
NORMAL(false), LAZYLOAD_MANY(false), LAZYLOAD_BEAN(true), REFRESH_BEAN(true);
@@ -52,57 +53,67 @@ public interface SpiQuery<T> extends Query<T> {
/**
* Find by Id or unique returning a single bean.
*/
BEAN,
/**
* Find iterate type query - findEach(), findIterate() etc.
*/
ITERATE,
BEAN(FIND_ONE),
/**
* Find returning a List.
*/
LIST,
LIST(FIND_MANY),
/**
* Find returning a Set.
*/
SET,
SET(FIND_MANY),
/**
* Find returning a Map.
*/
MAP,
MAP(FIND_MANY),
/**
* Find iterate type query - findEach(), findIterate() etc.
*/
ITERATE(FIND_ITERATE),
/**
* Find the Id's.
*/
ID_LIST,
ID_LIST(FIND_ID_LIST),
/**
* Find single attribute.
*/
ATTRIBUTE,
ATTRIBUTE(FIND_ATTRIBUTE),
/**
* Find rowCount.
*/
COUNT,
COUNT(FIND_COUNT),
/**
* A subquery used as part of a where clause.
*/
SUBQUERY,
SUBQUERY(FIND_SUBQUERY),
/**
* Delete query.
*/
DELETE,
DELETE(FIND_DELETE),
/**
* Update query.
*/
UPDATE,
UPDATE(FIND_UPDATE);
String profileEventId;
Type(String profileEventId) {
this.profileEventId = profileEventId;
}
public String profileEventId() {
return profileEventId;
}
}
enum TemporalMode {
@@ -139,6 +150,16 @@ public interface SpiQuery<T> extends Query<T> {
}
}
/**
* Return the profile event id based on query mode and type.
*/
String profileEventId();
/**
* Return the id used to identify a particular query for the given bean type.
*/
short getProfileId();
/**
* Check for a single "equal to" expression for the Id.
*/
@@ -170,6 +191,11 @@ public interface SpiQuery<T> extends Query<T> {
*/
BeanDescriptor<T> getBeanDescriptor();
/**
* Return the RawSql that was set to use for this query.
*/
SpiRawSql getRawSql();
/**
* Return true if this query should be executed against the doc store.
*/
@@ -434,26 +460,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
Boolean isAutoTune();
/**
* Set to true if you want to capture executed secondary queries.
*/
void setLogSecondaryQuery(boolean logSecondaryQuery);
/**
* Return true if executed secondary queries should be captured.
*/
boolean isLogSecondaryQuery();
/**
* Return the list of secondary queries that were executed.
*/
List<SpiQuery<?>> getLoggedSecondaryQueries();
/**
* Log an executed secondary query.
*/
void logSecondaryQuery(SpiQuery<?> query);
/**
* If return null then no profiling for this query. If a ProfilingListener is
* returned this implies that profiling is turned on for this query (and all
@@ -9,6 +9,7 @@ import io.ebeaninternal.server.core.PersistDeferredRelationship;
import io.ebeaninternal.server.core.PersistRequest;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.persist.BatchControl;
import io.ebeaninternal.server.transaction.ProfileStream;
import io.ebeanservice.docstore.api.DocStoreTransaction;
import javax.persistence.PersistenceException;
@@ -218,12 +219,6 @@ public interface SpiTransaction extends Transaction {
*/
Connection getInternalConnection();
/**
* Rollback if the transaction is active. This provides an internal
* mechanism for rollback failures occur on commit().
*/
void rollbackIfActive();
/**
* Return true if the manyToMany intersection should be persisted for this particular relationship direction.
*/
@@ -288,4 +283,19 @@ public interface SpiTransaction extends Transaction {
* Return the current Tenant Id.
*/
Object getTenantId();
/**
* Return the offset time from the start of the transaction.
*/
long profileOffset();
/**
* Check if the event should be added to a profiling transaction.
*/
void profileEvent(SpiProfileTransactionEvent event);
/**
* Return the stream that profiling events are written to.
*/
ProfileStream profileStream();
}
@@ -0,0 +1,387 @@
package io.ebeaninternal.api;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PersistBatch;
import io.ebean.bean.PersistenceContext;
import io.ebean.event.changelog.BeanChange;
import io.ebean.event.changelog.ChangeSet;
import io.ebeaninternal.server.core.PersistDeferredRelationship;
import io.ebeaninternal.server.core.PersistRequest;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.persist.BatchControl;
import io.ebeaninternal.server.transaction.ProfileStream;
import io.ebeanservice.docstore.api.DocStoreTransaction;
import javax.persistence.PersistenceException;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Proxy for an underlying SpiTransaction (most of the API).
*/
abstract class SpiTransactionProxy implements SpiTransaction {
protected SpiTransaction transaction;
@Override
public PersistenceException translate(String message, SQLException cause) {
return transaction.translate(message, cause);
}
@Override
public void commitAndContinue() {
transaction.commitAndContinue();
}
@Override
public boolean isRollbackOnly() {
return transaction.isRollbackOnly();
}
@Override
public long profileOffset() {
return transaction.profileOffset();
}
@Override
public void profileEvent(SpiProfileTransactionEvent event) {
transaction.profileEvent(event);
}
@Override
public ProfileStream profileStream() {
return transaction.profileStream();
}
@Override
public void setTenantId(Object tenantId) {
transaction.setTenantId(tenantId);
}
@Override
public Object getTenantId() {
return transaction.getTenantId();
}
@Override
public DocStoreTransaction getDocStoreTransaction() {
return transaction.getDocStoreTransaction();
}
@Override
public DocStoreMode getDocStoreMode() {
return transaction.getDocStoreMode();
}
@Override
public void setDocStoreMode(DocStoreMode mode) {
transaction.setDocStoreMode(mode);
}
@Override
public int getDocStoreBatchSize() {
return transaction.getDocStoreBatchSize();
}
@Override
public void setDocStoreBatchSize(int batchSize) {
transaction.setDocStoreBatchSize(batchSize);
}
@Override
public String getLogPrefix() {
return transaction.getLogPrefix();
}
@Override
public boolean isLogSql() {
return transaction.isLogSql();
}
@Override
public boolean isLogSummary() {
return transaction.isLogSummary();
}
@Override
public void logSql(String msg) {
transaction.logSql(msg);
}
@Override
public void logSummary(String msg) {
transaction.logSummary(msg);
}
@Override
public void setSkipCache(boolean skipCache) {
transaction.setSkipCache(skipCache);
}
@Override
public boolean isSkipCache() {
return transaction.isSkipCache();
}
@Override
public void addBeanChange(BeanChange beanChange) {
transaction.addBeanChange(beanChange);
}
@Override
public void sendChangeLog(ChangeSet changes) {
transaction.sendChangeLog(changes);
}
@Override
public void registerDeferred(PersistDeferredRelationship derived) {
transaction.registerDeferred(derived);
}
@Override
public void registerDeleteBean(Integer hash) {
transaction.registerDeleteBean(hash);
}
@Override
public void unregisterDeleteBean(Integer hash) {
transaction.unregisterDeleteBean(hash);
}
@Override
public boolean isRegisteredDeleteBean(Integer hash) {
return transaction.isRegisteredDeleteBean(hash);
}
@Override
public void unregisterBean(Object bean) {
transaction.unregisterBean(bean);
}
@Override
public boolean isRegisteredBean(Object bean) {
return transaction.isRegisteredBean(bean);
}
@Override
public String getId() {
return transaction.getId();
}
@Override
public void register(TransactionCallback callback) {
transaction.register(callback);
}
@Override
public boolean isReadOnly() {
return transaction.isReadOnly();
}
@Override
public void setReadOnly(boolean readOnly) {
transaction.setReadOnly(readOnly);
}
@Override
public boolean isActive() {
return transaction.isActive();
}
@Override
public void setPersistCascade(boolean persistCascade) {
transaction.setPersistCascade(persistCascade);
}
@Override
public void setUpdateAllLoadedProperties(boolean updateAllLoaded) {
transaction.setUpdateAllLoadedProperties(updateAllLoaded);
}
@Override
public Boolean isUpdateAllLoadedProperties() {
return transaction.isUpdateAllLoadedProperties();
}
@Override
public void setBatchMode(boolean useBatch) {
transaction.setBatchMode(useBatch);
}
@Override
public void setBatch(PersistBatch persistBatchMode) {
transaction.setBatch(persistBatchMode);
}
@Override
public PersistBatch getBatch() {
return transaction.getBatch();
}
@Override
public void setBatchOnCascade(PersistBatch batchOnCascadeMode) {
transaction.setBatchOnCascade(batchOnCascadeMode);
}
@Override
public PersistBatch getBatchOnCascade() {
return transaction.getBatchOnCascade();
}
@Override
public void setBatchSize(int batchSize) {
transaction.setBatchSize(batchSize);
}
@Override
public int getBatchSize() {
return transaction.getBatchSize();
}
@Override
public void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
transaction.setBatchGetGeneratedKeys(getGeneratedKeys);
}
@Override
public Boolean getBatchGetGeneratedKeys() {
return transaction.getBatchGetGeneratedKeys();
}
@Override
public void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
transaction.setBatchFlushOnMixed(batchFlushOnMixed);
}
@Override
public void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
transaction.setBatchFlushOnQuery(batchFlushOnQuery);
}
@Override
public boolean isBatchFlushOnQuery() {
return transaction.isBatchFlushOnQuery();
}
@Override
public void flush() throws PersistenceException {
transaction.flush();
}
@Override
public void flushBatch() throws PersistenceException {
flush();
}
@Override
public Connection getConnection() {
return transaction.getConnection();
}
@Override
public void addModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
transaction.addModification(tableName, inserts, updates, deletes);
}
@Override
public void putUserObject(String name, Object value) {
transaction.putUserObject(name, value);
}
@Override
public Object getUserObject(String name) {
return transaction.getUserObject(name);
}
@Override
public void depth(int diff) {
transaction.depth();
}
@Override
public int depth() {
return transaction.depth();
}
@Override
public boolean isExplicit() {
return transaction.isExplicit();
}
@Override
public TransactionEvent getEvent() {
return transaction.getEvent();
}
@Override
public boolean isPersistCascade() {
return transaction.isPersistCascade();
}
@Override
public boolean isBatchThisRequest(PersistRequest.Type type) {
return transaction.isBatchThisRequest(type);
}
@Override
public BatchControl getBatchControl() {
return transaction.getBatchControl();
}
@Override
public void setBatchControl(BatchControl control) {
transaction.setBatchControl(control);
}
@Override
public PersistenceContext getPersistenceContext() {
return transaction.getPersistenceContext();
}
@Override
public void setPersistenceContext(PersistenceContext context) {
transaction.setPersistenceContext(context);
}
@Override
public Connection getInternalConnection() {
return transaction.getInternalConnection();
}
@Override
public boolean isSaveAssocManyIntersection(String intersectionTable, String beanName) {
return transaction.isSaveAssocManyIntersection(intersectionTable, beanName);
}
@Override
public boolean checkBatchEscalationOnCascade(PersistRequestBean<?> request) {
return transaction.checkBatchEscalationOnCascade(request);
}
@Override
public void flushBatchOnCascade() {
transaction.flushBatchOnCascade();
}
@Override
public void flushBatchOnRollback() {
transaction.flushBatchOnRollback();
}
@Override
public void markNotQueryOnly() {
transaction.markNotQueryOnly();
}
@Override
public void checkBatchEscalationOnCollection() {
transaction.checkBatchEscalationOnCollection();
}
@Override
public void flushBatchOnCollection() {
transaction.flushBatchOnCollection();
}
}
@@ -0,0 +1,36 @@
package io.ebeaninternal.api;
/**
* Event codes used in transaction profiling.
*
* These appear in verbose transaction profile logs.
*/
public interface TxnProfileEventCodes {
String EVT_COMMIT = "c";
String EVT_ROLLBACK = "r";
String EVT_INSERT = "i";
String EVT_UPDATE = "u";
String EVT_DELETE = "d";
String EVT_SOFT_DELETE = "ds";
String EVT_DELETE_PERMANENT = "dp";
String EVT_ORMUPDATE = "uo";
String FIND_UPDATE = "uq";
String FIND_DELETE = "dq";
String EVT_UPDATESQL = "su";
String EVT_CALLABLESQL = "sc";
String FIND_ONE = "fo";
String FIND_MANY = "fm";
String FIND_ITERATE = "fe";
String FIND_ID_LIST = "fi";
String FIND_ATTRIBUTE = "fa";
String FIND_COUNT = "fc";
String FIND_SUBQUERY = "fs";
String FIND_MANY_LAZY = "lm";
String FIND_ONE_LAZY = "lo";
}
@@ -1,4 +1,4 @@
package io.ebean.dbmigration;
package io.ebeaninternal.dbmigration;
import io.ebean.annotation.Platform;
import org.slf4j.Logger;
@@ -1,8 +1,8 @@
package io.ebean.dbmigration;
package io.ebeaninternal.dbmigration;
import io.ebean.Transaction;
import io.ebean.config.ServerConfig;
import io.ebean.dbmigration.model.CurrentModel;
import io.ebeaninternal.dbmigration.model.CurrentModel;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
import io.ebean.migration.ddl.DdlRunner;
@@ -36,6 +36,7 @@ public class DdlGenerator {
private final boolean generateDdl;
private final boolean runDdl;
private final boolean createOnly;
private final boolean jaxbPresent;
private CurrentModel currentModel;
private String dropAllContent;
@@ -43,6 +44,7 @@ public class DdlGenerator {
public DdlGenerator(SpiEbeanServer server, ServerConfig serverConfig) {
this.server = server;
this.jaxbPresent = serverConfig.getClassLoadConfig().isJavaxJAXBPresent();
this.generateDdl = serverConfig.isDdlGenerate();
this.createOnly = serverConfig.isDdlCreateOnly();
if (serverConfig.getTenantMode().isDynamicDataSource() && serverConfig.isDdlRun()) {
@@ -139,7 +141,7 @@ public class DdlGenerator {
runScript(false, createAllContent, getCreateFileName());
String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl");
if (!"true".equalsIgnoreCase(ignoreExtraDdl)) {
if (!"true".equalsIgnoreCase(ignoreExtraDdl) && jaxbPresent) {
String extraApply = ExtraDdlXmlReader.buildExtra(server.getDatabasePlatform().getName());
if (extraApply != null) {
runScript(false, extraApply, "extra-dll");
@@ -0,0 +1,578 @@
package io.ebeaninternal.dbmigration;
import io.ebean.Ebean;
import io.ebean.EbeanServer;
import io.ebean.dbmigration.DbMigration;
import io.ebean.config.DbConstraintNaming;
import io.ebean.config.DbMigrationConfig;
import io.ebean.annotation.Platform;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.db2.DB2Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
import io.ebean.config.dbplatform.sqlserver.SqlServerPlatform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.oracle.OraclePlatform;
import io.ebean.config.dbplatform.postgres.PostgresPlatform;
import io.ebean.config.dbplatform.sqlanywhere.SqlAnywherePlatform;
import io.ebean.config.dbplatform.sqlite.SQLitePlatform;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.Migration;
import io.ebeaninternal.dbmigration.migrationreader.MigrationXmlWriter;
import io.ebeaninternal.dbmigration.model.CurrentModel;
import io.ebeaninternal.dbmigration.model.MConfiguration;
import io.ebeaninternal.dbmigration.model.MigrationModel;
import io.ebeaninternal.dbmigration.model.MigrationVersion;
import io.ebeaninternal.dbmigration.model.ModelContainer;
import io.ebeaninternal.dbmigration.model.ModelDiff;
import io.ebeaninternal.dbmigration.model.PlatformDdlWriter;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.extraddl.model.DdlScript;
import io.ebeaninternal.extraddl.model.ExtraDdl;
import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Generates DB Migration xml and sql scripts.
* <p>
* Reads the prior migrations and compares with the current model of the EbeanServer
* and generates a migration 'diff' in the form of xml document with the logical schema
* changes and a series of sql scripts to apply, rollback the applied changes if necessary
* and drop objects (drop tables, drop columns).
* </p>
* <p>
* This does not run the migration or ddl scripts but just generates them.
* </p>
* <pre>{@code
*
* DefaultDbMigration migration = new DefaultDbMigration();
* migration.setPathToResources("src/main/resources");
* migration.setPlatform(DbPlatformName.ORACLE);
*
* migration.generateMigration();
*
* }</pre>
*/
public class DefaultDbMigration implements DbMigration {
protected static final Logger logger = LoggerFactory.getLogger(DefaultDbMigration.class);
private static final String initialVersion = "1.0";
private static final String GENERATED_COMMENT = "THIS IS A GENERATED FILE - DO NOT MODIFY";
/**
* Set to true if DefaultDbMigration run with online EbeanServer instance.
*/
protected final boolean online;
protected SpiEbeanServer server;
protected DbMigrationConfig migrationConfig;
protected String pathToResources = "src/main/resources";
protected DatabasePlatform databasePlatform;
protected List<Pair> platforms = new ArrayList<>();
protected ServerConfig serverConfig;
protected DbConstraintNaming constraintNaming;
/**
* Create for offline migration generation.
*/
public DefaultDbMigration() {
this.online = false;
}
/**
* Create using online EbeanServer.
*/
public DefaultDbMigration(EbeanServer server) {
this.online = true;
setServer(server);
}
/**
* Set the path from the current working directory to the application resources.
* <p>
* This defaults to maven style 'src/main/resources'.
*/
@Override
public void setPathToResources(String pathToResources) {
this.pathToResources = pathToResources;
}
/**
* Set the server to use to determine the current model.
* Typically this is not called explicitly.
*/
@Override
public void setServer(EbeanServer ebeanServer) {
this.server = (SpiEbeanServer) ebeanServer;
setServerConfig(server.getServerConfig());
}
/**
* Set the serverConfig to use. Typically this is not called explicitly.
*/
@Override
public void setServerConfig(ServerConfig config) {
if (this.serverConfig == null) {
this.serverConfig = config;
}
if (migrationConfig == null) {
this.migrationConfig = serverConfig.getMigrationConfig();
}
if (constraintNaming == null) {
this.constraintNaming = serverConfig.getConstraintNaming();
}
}
/**
* Set the specific platform to generate DDL for.
* <p>
* If not set this defaults to the platform of the default server.
* </p>
*/
@Override
public void setPlatform(Platform platform) {
setPlatform(getPlatform(platform));
}
/**
* Set the specific platform to generate DDL for.
* <p>
* If not set this defaults to the platform of the default server.
* </p>
*/
@Override
public void setPlatform(DatabasePlatform databasePlatform) {
this.databasePlatform = databasePlatform;
if (!online) {
DbOffline.setPlatform(databasePlatform.getPlatform());
}
}
/**
* Add an additional platform to write the migration DDL.
* <p>
* Use this when you want to generate sql scripts for multiple database platforms
* from the migration (e.g. generate migration sql for MySql, Postgres and Oracle).
* </p>
*/
@Override
public void addPlatform(Platform platform, String prefix) {
platforms.add(new Pair(getPlatform(platform), prefix));
}
/**
* Generate the next migration xml file and associated apply and rollback sql scripts.
* <p>
* This does not run the migration or ddl scripts but just generates them.
* </p>
* <h3>Example: Run for a single specific platform</h3>
* <pre>{@code
*
* DefaultDbMigration migration = new DefaultDbMigration();
* migration.setPathToResources("src/main/resources");
* migration.setPlatform(DbPlatformName.ORACLE);
*
* migration.generateMigration();
*
* }</pre>
* <p>
* <h3>Example: Run migration generating DDL for multiple platforms</h3>
* <pre>{@code
*
* DefaultDbMigration migration = new DefaultDbMigration();
* migration.setPathToResources("src/main/resources");
*
* migration.addPlatform(DbPlatformName.POSTGRES, "pg");
* migration.addPlatform(DbPlatformName.MYSQL, "mysql");
* migration.addPlatform(DbPlatformName.ORACLE, "mysql");
*
* migration.generateMigration();
*
* }</pre>
* @return the generated migration or null
*/
@Override
public String generateMigration() throws IOException {
// use this flag to stop other plugins like full DDL generation
if (!online) {
DbOffline.setGenerateMigration();
if (databasePlatform == null && !platforms.isEmpty()) {
// for multiple platform generation set the general platform
// to H2 so that it runs offline without DB connection
setPlatform(platforms.get(0).platform);
}
}
setDefaults();
try {
Request request = createRequest();
if (platforms.isEmpty()) {
generateExtraDdl(request.migrationDir, databasePlatform);
}
String pendingVersion = generatePendingDrop();
if (pendingVersion != null) {
return generatePendingDrop(request, pendingVersion);
} else {
return generateDiff(request);
}
} finally {
if (!online) {
DbOffline.reset();
}
}
}
/**
* Generate "repeatable" migration scripts.
* <p>
* These take scrips from extra-dll.xml (typically views) and outputs "repeatable"
* migration scripts (starting with "R__") to be run by FlywayDb or Ebean's own
* migration runner.
* </p>
*/
private void generateExtraDdl(File migrationDir, DatabasePlatform dbPlatform) throws IOException {
if (dbPlatform != null) {
ExtraDdl extraDdl = ExtraDdlXmlReader.read("/extra-ddl.xml");
if (extraDdl != null) {
List<DdlScript> ddlScript = extraDdl.getDdlScript();
for (DdlScript script : ddlScript) {
if (ExtraDdlXmlReader.matchPlatform(dbPlatform.getName(), script.getPlatforms())) {
writeExtraDdl(migrationDir, script);
}
}
}
}
}
/**
* Write (or override) the "repeatable" migration script.
*/
private void writeExtraDdl(File migrationDir, DdlScript script) throws IOException {
String fullName = repeatableMigrationName(script.getName());
logger.info("writing repeatable script {}", fullName);
File file = new File(migrationDir, fullName);
try (FileWriter writer = new FileWriter(file)) {
writer.write(script.getValue());
writer.flush();
}
}
private String repeatableMigrationName(String scriptName) {
return "R__" + scriptName.replace(' ', '_') + migrationConfig.getApplySuffix();
}
/**
* Generate the diff migration.
*/
private String generateDiff(Request request) throws IOException {
List<String> pendingDrops = request.getPendingDrops();
if (!pendingDrops.isEmpty()) {
logger.info("Pending un-applied drops in versions {}", pendingDrops);
}
Migration migration = request.createDiffMigration();
if (migration == null) {
logger.info("no changes detected - no migration written");
return null;
} else {
// there were actually changes to write
return generateMigration(request, migration, null);
}
}
/**
* Generate the migration based on the pendingDrops from a prior version.
*/
private String generatePendingDrop(Request request, String pendingVersion) throws IOException {
Migration migration = request.migrationForPendingDrop(pendingVersion);
String version = generateMigration(request, migration, pendingVersion);
List<String> pendingDrops = request.getPendingDrops();
if (!pendingDrops.isEmpty()) {
logger.info("... remaining pending un-applied drops in versions {}", pendingDrops);
}
return version;
}
private Request createRequest() {
return new Request();
}
private class Request {
final File migrationDir;
final File modelDir;
final MigrationModel migrationModel;
final CurrentModel currentModel;
final ModelContainer migrated;
final ModelContainer current;
private Request() {
this.migrationDir = getMigrationDirectory();
this.modelDir = getModelDirectory(migrationDir);
this.migrationModel = new MigrationModel(modelDir, migrationConfig.getModelSuffix());
this.migrated = migrationModel.read();
this.currentModel = new CurrentModel(server, constraintNaming);
this.current = currentModel.read();
}
/**
* Return the migration for the pending drops for a given version.
*/
public Migration migrationForPendingDrop(String pendingVersion) {
Migration migration = migrated.migrationForPendingDrop(pendingVersion);
// register any remaining pending drops
migrated.registerPendingHistoryDropColumns(current);
return migration;
}
/**
* Return the list of versions that have pending un-applied drops.
*/
public List<String> getPendingDrops() {
return migrated.getPendingDrops();
}
/**
* Create and return the diff of the current model to the migration model.
*/
public Migration createDiffMigration() {
ModelDiff diff = new ModelDiff(migrated);
diff.compareTo(current);
return diff.isEmpty() ? null : diff.getMigration();
}
}
private String generateMigration(Request request, Migration dbMigration, String dropsFor) throws IOException {
String fullVersion = getFullVersion(request.migrationModel, dropsFor);
logger.info("generating migration:{}", fullVersion);
if (!writeMigrationXml(dbMigration, request.modelDir, fullVersion)) {
logger.warn("migration already exists, not generating DDL");
return null;
} else {
if (!platforms.isEmpty()) {
writeExtraPlatformDdl(fullVersion, request.currentModel, dbMigration, request.migrationDir);
} 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);
PlatformDdlWriter writer = createDdlWriter(databasePlatform);
writer.processMigration(dbMigration, write, request.migrationDir, fullVersion);
}
return fullVersion;
}
}
/**
* Return true if the next pending drop changeSet should be generated as the next migration.
*/
private String generatePendingDrop() {
String nextDrop = System.getProperty("ddl.migration.pendingDropsFor");
if (nextDrop != null) {
return nextDrop;
}
return migrationConfig.getGeneratePendingDrop();
}
/**
* Return the full version for the migration being generated.
* <p>
* The full version can contain a comment suffix after a "__" double underscore.
*/
private String getFullVersion(MigrationModel migrationModel, String dropsFor) {
String version = migrationConfig.getVersion();
if (version == null) {
version = migrationModel.getNextVersion(initialVersion);
}
String fullVersion = migrationConfig.getApplyPrefix() + version;
if (migrationConfig.getName() != null) {
fullVersion += "__" + toUnderScore(migrationConfig.getName());
} else if (dropsFor != null) {
fullVersion += "__" + toUnderScore("dropsFor_" + MigrationVersion.trim(dropsFor));
} else if (version.equals(initialVersion)) {
fullVersion += "__initial";
}
return fullVersion;
}
/**
* Replace spaces with underscores.
*/
private String toUnderScore(String name) {
return name.replace(' ', '_');
}
/**
* Write any extra platform ddl.
*/
protected void writeExtraPlatformDdl(String fullVersion, CurrentModel currentModel, Migration dbMigration, File writePath) throws IOException {
for (Pair pair : platforms) {
DdlWrite platformBuffer = new DdlWrite(new MConfiguration(), currentModel.read());
PlatformDdlWriter platformWriter = createDdlWriter(pair.platform);
File subPath = platformWriter.subPath(writePath, pair.prefix);
platformWriter.processMigration(dbMigration, platformBuffer, subPath, fullVersion);
generateExtraDdl(subPath, pair.platform);
}
}
private PlatformDdlWriter createDdlWriter(DatabasePlatform platform) {
return new PlatformDdlWriter(platform, serverConfig, migrationConfig);
}
/**
* Write the migration xml.
*/
protected boolean writeMigrationXml(Migration dbMigration, File resourcePath, String fullVersion) {
String modelFile = fullVersion + migrationConfig.getModelSuffix();
File file = new File(resourcePath, modelFile);
if (file.exists()) {
return false;
}
String comment = migrationConfig.isIncludeGeneratedFileComment() ? GENERATED_COMMENT : null;
MigrationXmlWriter xmlWriter = new MigrationXmlWriter(comment);
xmlWriter.write(dbMigration, file);
return true;
}
/**
* Set default server and platform if necessary.
*/
protected void setDefaults() {
if (server == null) {
setServer(Ebean.getDefaultServer());
}
if (databasePlatform == null && platforms.isEmpty()) {
// not explicitly set not set a list of platforms so
// default to the platform of the default server
databasePlatform = server.getDatabasePlatform();
logger.debug("set platform to {}", databasePlatform.getName());
}
}
/**
* Return the file path to write the xml and sql to.
*/
protected File getMigrationDirectory() {
// path to src/main/resources in typical maven project
File resourceRootDir = new File(pathToResources);
String resourcePath = migrationConfig.getMigrationPath();
// expect to be a path to something like - src/main/resources/dbmigration/model
File path = new File(resourceRootDir, resourcePath);
if (!path.exists()) {
if (!path.mkdirs()) {
logger.debug("Unable to ensure migration directory exists at {}", path.getAbsolutePath());
}
}
return path;
}
/**
* Return the model directory (relative to the migration directory).
*/
protected File getModelDirectory(File migrationDirectory) {
String modelPath = migrationConfig.getModelPath();
if (modelPath == null || modelPath.isEmpty()) {
return migrationDirectory;
}
File modelDir = new File(migrationDirectory, migrationConfig.getModelPath());
if (!modelDir.exists() && !modelDir.mkdirs()) {
logger.debug("Unable to ensure migration model directory exists at {}", modelDir.getAbsolutePath());
}
return modelDir;
}
/**
* Return the DatabasePlatform given the platform key.
*/
protected DatabasePlatform getPlatform(Platform platform) {
switch (platform) {
case H2:
return new H2Platform();
case HSQLDB:
return new HsqldbPlatform();
case POSTGRES:
return new PostgresPlatform();
case MYSQL:
return new MySqlPlatform();
case ORACLE:
return new OraclePlatform();
case SQLANYWHERE:
return new SqlAnywherePlatform();
case SQLSERVER:
return new SqlServerPlatform();
case DB2:
return new DB2Platform();
case SQLITE:
return new SQLitePlatform();
case GENERIC:
return new DatabasePlatform();
default:
throw new IllegalArgumentException("Platform missing? " + platform);
}
}
/**
* Holds a platform and prefix. Used to generate multiple platform specific DDL
* for a single migration.
*/
public static class Pair {
/**
* The platform to generate the DDL for.
*/
public final DatabasePlatform platform;
/**
* A prefix included into the file/resource names indicating the platform.
*/
public final String prefix;
public Pair(DatabasePlatform platform, String prefix) {
this.platform = platform;
this.prefix = prefix;
}
}
}
@@ -1,19 +1,19 @@
package io.ebean.dbmigration.ddlgeneration;
package io.ebeaninternal.dbmigration.ddlgeneration;
import io.ebean.config.ServerConfig;
import io.ebean.dbmigration.ddlgeneration.platform.BaseTableDdl;
import io.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
import io.ebean.dbmigration.migration.AddColumn;
import io.ebean.dbmigration.migration.AddHistoryTable;
import io.ebean.dbmigration.migration.AddTableComment;
import io.ebean.dbmigration.migration.AlterColumn;
import io.ebean.dbmigration.migration.ChangeSet;
import io.ebean.dbmigration.migration.CreateIndex;
import io.ebean.dbmigration.migration.CreateTable;
import io.ebean.dbmigration.migration.DropColumn;
import io.ebean.dbmigration.migration.DropHistoryTable;
import io.ebean.dbmigration.migration.DropIndex;
import io.ebean.dbmigration.migration.DropTable;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.BaseTableDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.PlatformDdl;
import io.ebeaninternal.dbmigration.migration.AddColumn;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
import io.ebeaninternal.dbmigration.migration.AddTableComment;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import io.ebeaninternal.dbmigration.migration.ChangeSet;
import io.ebeaninternal.dbmigration.migration.CreateIndex;
import io.ebeaninternal.dbmigration.migration.CreateTable;
import io.ebeaninternal.dbmigration.migration.DropColumn;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.migration.DropIndex;
import io.ebeaninternal.dbmigration.migration.DropTable;
import java.io.IOException;
import java.util.List;
@@ -1,8 +1,8 @@
package io.ebean.dbmigration.ddlgeneration;
package io.ebeaninternal.dbmigration.ddlgeneration;
import io.ebean.dbmigration.migration.AddColumn;
import io.ebean.dbmigration.migration.AlterColumn;
import io.ebean.dbmigration.migration.DropColumn;
import io.ebeaninternal.dbmigration.migration.AddColumn;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import io.ebeaninternal.dbmigration.migration.DropColumn;
import java.io.IOException;
@@ -1,6 +1,6 @@
package io.ebean.dbmigration.ddlgeneration;
package io.ebeaninternal.dbmigration.ddlgeneration;
import io.ebean.dbmigration.model.MConfiguration;
import io.ebeaninternal.dbmigration.model.MConfiguration;
import java.io.IOException;
@@ -1,16 +1,16 @@
package io.ebean.dbmigration.ddlgeneration;
package io.ebeaninternal.dbmigration.ddlgeneration;
import io.ebean.dbmigration.migration.AddColumn;
import io.ebean.dbmigration.migration.AddHistoryTable;
import io.ebean.dbmigration.migration.AddTableComment;
import io.ebean.dbmigration.migration.AlterColumn;
import io.ebean.dbmigration.migration.ChangeSet;
import io.ebean.dbmigration.migration.CreateIndex;
import io.ebean.dbmigration.migration.CreateTable;
import io.ebean.dbmigration.migration.DropColumn;
import io.ebean.dbmigration.migration.DropHistoryTable;
import io.ebean.dbmigration.migration.DropIndex;
import io.ebean.dbmigration.migration.DropTable;
import io.ebeaninternal.dbmigration.migration.AddColumn;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
import io.ebeaninternal.dbmigration.migration.AddTableComment;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import io.ebeaninternal.dbmigration.migration.ChangeSet;
import io.ebeaninternal.dbmigration.migration.CreateIndex;
import io.ebeaninternal.dbmigration.migration.CreateTable;
import io.ebeaninternal.dbmigration.migration.DropColumn;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.migration.DropIndex;
import io.ebeaninternal.dbmigration.migration.DropTable;
import java.io.IOException;
@@ -1,9 +1,9 @@
package io.ebean.dbmigration.ddlgeneration;
package io.ebeaninternal.dbmigration.ddlgeneration;
import io.ebean.dbmigration.ddlgeneration.platform.BaseDdlBuffer;
import io.ebean.dbmigration.model.MConfiguration;
import io.ebean.dbmigration.model.MTable;
import io.ebean.dbmigration.model.ModelContainer;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.BaseDdlBuffer;
import io.ebeaninternal.dbmigration.model.MConfiguration;
import io.ebeaninternal.dbmigration.model.MTable;
import io.ebeaninternal.dbmigration.model.ModelContainer;
/**
* Write context holding the buffers for both apply and rollback DDL.
@@ -1,15 +1,15 @@
package io.ebean.dbmigration.ddlgeneration;
package io.ebeaninternal.dbmigration.ddlgeneration;
import io.ebean.dbmigration.migration.AddColumn;
import io.ebean.dbmigration.migration.AddHistoryTable;
import io.ebean.dbmigration.migration.AddTableComment;
import io.ebean.dbmigration.migration.AlterColumn;
import io.ebean.dbmigration.migration.CreateIndex;
import io.ebean.dbmigration.migration.CreateTable;
import io.ebean.dbmigration.migration.DropColumn;
import io.ebean.dbmigration.migration.DropHistoryTable;
import io.ebean.dbmigration.migration.DropIndex;
import io.ebean.dbmigration.migration.DropTable;
import io.ebeaninternal.dbmigration.migration.AddColumn;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
import io.ebeaninternal.dbmigration.migration.AddTableComment;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import io.ebeaninternal.dbmigration.migration.CreateIndex;
import io.ebeaninternal.dbmigration.migration.CreateTable;
import io.ebeaninternal.dbmigration.migration.DropColumn;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.migration.DropIndex;
import io.ebeaninternal.dbmigration.migration.DropTable;
import java.io.IOException;
@@ -1,7 +1,7 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.dbmigration.ddlgeneration.DdlBuffer;
import io.ebean.dbmigration.model.MConfiguration;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.model.MConfiguration;
import java.io.IOException;
import java.io.StringWriter;
@@ -1,29 +1,29 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.DbConstraintNaming;
import io.ebean.config.NamingConvention;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DbHistorySupport;
import io.ebean.config.dbplatform.IdType;
import io.ebean.dbmigration.ddlgeneration.DdlBuffer;
import io.ebean.dbmigration.ddlgeneration.DdlWrite;
import io.ebean.dbmigration.ddlgeneration.TableDdl;
import io.ebean.dbmigration.ddlgeneration.platform.util.IndexSet;
import io.ebean.dbmigration.migration.AddColumn;
import io.ebean.dbmigration.migration.AddHistoryTable;
import io.ebean.dbmigration.migration.AddTableComment;
import io.ebean.dbmigration.migration.AlterColumn;
import io.ebean.dbmigration.migration.Column;
import io.ebean.dbmigration.migration.CreateIndex;
import io.ebean.dbmigration.migration.CreateTable;
import io.ebean.dbmigration.migration.DdlScript;
import io.ebean.dbmigration.migration.DropColumn;
import io.ebean.dbmigration.migration.DropHistoryTable;
import io.ebean.dbmigration.migration.DropIndex;
import io.ebean.dbmigration.migration.DropTable;
import io.ebean.dbmigration.migration.ForeignKey;
import io.ebean.dbmigration.migration.UniqueConstraint;
import io.ebean.dbmigration.model.MTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.ddlgeneration.TableDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.IndexSet;
import io.ebeaninternal.dbmigration.migration.AddColumn;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
import io.ebeaninternal.dbmigration.migration.AddTableComment;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import io.ebeaninternal.dbmigration.migration.Column;
import io.ebeaninternal.dbmigration.migration.CreateIndex;
import io.ebeaninternal.dbmigration.migration.CreateTable;
import io.ebeaninternal.dbmigration.migration.DdlScript;
import io.ebeaninternal.dbmigration.migration.DropColumn;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.migration.DropIndex;
import io.ebeaninternal.dbmigration.migration.DropTable;
import io.ebeaninternal.dbmigration.migration.ForeignKey;
import io.ebeaninternal.dbmigration.migration.UniqueConstraint;
import io.ebeaninternal.dbmigration.model.MTable;
import io.ebean.util.StringHelper;
import java.io.IOException;
@@ -58,7 +58,7 @@ public class BaseTableDdl implements TableDdl {
* Used when unique constraints specifically for OneToOne can't be created normally (MsSqlServer).
*/
protected List<Column> externalUnique = new ArrayList<>();
protected List<UniqueConstraint> externalCompoundUnique = new ArrayList<>();
// counters used when constraint names are truncated due to maximum length
@@ -77,7 +77,7 @@ public class BaseTableDdl implements TableDdl {
private boolean strict;
private final boolean sql2011History;
/**
* Helper class that is used to execute the migration ddl before and after the migration action.
*/
@@ -100,7 +100,7 @@ public class BaseTableDdl implements TableDdl {
if (column.getBefore().isEmpty() && alterNotNull && defaultValue == null) {
handleStrictError("non-null column has no default value: " + tableName + "." + columnName);
}
before = getScriptsForPlatform(column.getBefore(), platformDdl.getPlatform().getName());
after = getScriptsForPlatform(column.getAfter(), platformDdl.getPlatform().getName());
@@ -112,10 +112,10 @@ public class BaseTableDdl implements TableDdl {
DdlMigrationHelp(AlterColumn alter) throws IOException {
this.tableName = alter.getTableName();
this.columnName = alter.getColumnName();
String tmp = alter.getDefaultValue() != null ? alter.getDefaultValue() : alter.getCurrentDefaultValue();
this.defaultValue = platformDdl.convertDefaultValue(tmp);
boolean alterNotNull = Boolean.TRUE.equals(alter.isNotnull());
// here we add the platform's default update script
if (alter.getBefore().isEmpty() && alterNotNull) {
@@ -126,11 +126,11 @@ public class BaseTableDdl implements TableDdl {
} else {
before = getScriptsForPlatform(alter.getBefore(), platformDdl.getPlatform().getName());
}
after = getScriptsForPlatform(alter.getAfter(), platformDdl.getPlatform().getName());
}
public void writeBefore(DdlBuffer buffer) throws IOException {
if (!before.isEmpty()) {
buffer.end();
@@ -140,7 +140,7 @@ public class BaseTableDdl implements TableDdl {
buffer.endOfStatement();
}
}
public void writeAfter(DdlBuffer buffer) throws IOException {
// here we run postmigration scripts
for (String ddlScript : after) {
@@ -182,7 +182,7 @@ public class BaseTableDdl implements TableDdl {
System.err.println("Error in DDL: " + message);
}
}
public String getDefaultValue() {
return defaultValue;
}
@@ -341,7 +341,7 @@ public class BaseTableDdl implements TableDdl {
.append(platformDdl.dropIndex(uqName, tableName))
.endOfStatement();
}
for (UniqueConstraint constraint : externalCompoundUnique) {
String uqName = constraint.getName();
String[] columnNames = StringHelper.delimitedToArray(constraint.getColumnNames(), ",", false);
@@ -524,7 +524,7 @@ public class BaseTableDdl implements TableDdl {
buffer.append(platformDdl.dropSequence(sequenceName)).endOfStatement();
}
/**
* Write all the check constraints.
*/
@@ -577,7 +577,8 @@ public class BaseTableDdl implements TableDdl {
List<Column> columns = createTable.getColumn();
for (Column column : columns) {
if (hasValue(column.getUnique()) || hasValue(column.getUniqueOneToOne())) {
if (!Boolean.TRUE.equals(column.isPrimaryKey())
&& (hasValue(column.getUnique()) || hasValue(column.getUniqueOneToOne()))) {
if (Boolean.TRUE.equals(column.isNotnull()) || inlineUniqueWhenNullable) {
// normal mechanism for adding unique constraint
inlineUniqueConstraintSingle(apply, column);
@@ -790,7 +791,7 @@ public class BaseTableDdl implements TableDdl {
public void generate(DdlWrite writer, AlterColumn alterColumn) throws IOException {
DdlMigrationHelp ddlHelp = new DdlMigrationHelp(alterColumn);
ddlHelp.writeBefore(writer.apply());
if (isTrue(alterColumn.isHistoryExclude())) {
regenerateHistoryTriggers(alterColumn.getTableName(), HistoryTableUpdate.Change.EXCLUDE, alterColumn.getColumnName());
} else if (isFalse(alterColumn.isHistoryExclude())) {
@@ -996,13 +997,13 @@ public class BaseTableDdl implements TableDdl {
}
protected void alterTableAddColumn(DdlBuffer buffer, String tableName, Column column, boolean onHistoryTable) throws IOException {
DdlMigrationHelp help = new DdlMigrationHelp(tableName, column);
DdlMigrationHelp help = new DdlMigrationHelp(tableName, column);
if (!onHistoryTable) {
help.writeBefore(buffer);
}
platformDdl.alterTableAddColumn(buffer, tableName, column, onHistoryTable, help.getDefaultValue());
if (!onHistoryTable) {
help.writeAfter(buffer);
}
@@ -1,4 +1,4 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.DatabasePlatform;
@@ -16,7 +16,7 @@ public class DB2Ddl extends PlatformDdl {
this.identitySuffix = " generated by default as identity";
this.inlineUniqueWhenNullable = false;
}
@Override
public String alterTableAddUniqueConstraint(String tableName, String uqName, String[] columns, boolean notNull) {
if (notNull) {
@@ -1,13 +1,13 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.DbConstraintNaming;
import io.ebean.config.ServerConfig;
import io.ebean.dbmigration.ddlgeneration.DdlBuffer;
import io.ebean.dbmigration.ddlgeneration.DdlWrite;
import io.ebean.dbmigration.migration.AddHistoryTable;
import io.ebean.dbmigration.migration.DropHistoryTable;
import io.ebean.dbmigration.model.MColumn;
import io.ebean.dbmigration.model.MTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.model.MColumn;
import io.ebeaninternal.dbmigration.model.MTable;
import java.io.IOException;
import java.util.Collection;
@@ -1,7 +1,7 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.dbmigration.ddlgeneration.DdlBuffer;
import io.ebean.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import java.util.List;
@@ -1,22 +1,22 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
public class DdlHelp {
public static final String DROP_DEFAULT = "DROP DEFAULT";
public static final String DROP_COMMENT = "DROP COMMENT";
/**
* Return true if the default value is the special DROP DEFAULT value.
*/
public static boolean isDropDefault(String defaultValue) {
return DROP_DEFAULT.equals(defaultValue);
}
/**
* Return true if the default value is the special DROP DEFAULT value.
*/
public static boolean isDropComment(String comment) {
return DROP_COMMENT.equals(comment);
}
}
@@ -1,7 +1,7 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.DbConstraintNaming;
import io.ebean.dbmigration.ddlgeneration.platform.util.VowelRemover;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.VowelRemover;
/**
* Default implementation used to truncate or shorten db constraint names as required.
@@ -1,4 +1,4 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.DatabasePlatform;
@@ -1,9 +1,9 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.h2.H2HistoryTrigger;
import io.ebean.dbmigration.ddlgeneration.DdlBuffer;
import io.ebean.dbmigration.ddlgeneration.DdlWrite;
import io.ebean.dbmigration.model.MTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.model.MTable;
import java.io.IOException;
@@ -1,4 +1,4 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import java.util.ArrayList;
import java.util.List;
@@ -1,4 +1,4 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.DatabasePlatform;
@@ -1,10 +1,10 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.util.StringHelper;
import io.ebean.dbmigration.ddlgeneration.DdlBuffer;
import io.ebean.dbmigration.migration.AlterColumn;
import io.ebean.dbmigration.migration.Column;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import io.ebeaninternal.dbmigration.migration.Column;
import java.io.IOException;
@@ -1,8 +1,8 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.dbmigration.ddlgeneration.DdlBuffer;
import io.ebean.dbmigration.ddlgeneration.DdlWrite;
import io.ebean.dbmigration.model.MTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.model.MTable;
import java.io.IOException;
@@ -1,10 +1,10 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.ServerConfig;
import io.ebean.dbmigration.ddlgeneration.DdlWrite;
import io.ebean.dbmigration.migration.AddHistoryTable;
import io.ebean.dbmigration.migration.DropHistoryTable;
import io.ebean.dbmigration.model.MTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.model.MTable;
import java.io.IOException;
@@ -1,4 +1,4 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.DatabasePlatform;
@@ -1,4 +1,4 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.DbConstraintNaming;
import io.ebean.config.ServerConfig;
@@ -6,17 +6,17 @@ import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbDefaultValue;
import io.ebean.config.dbplatform.DbIdentity;
import io.ebean.config.dbplatform.IdType;
import io.ebean.dbmigration.ddlgeneration.BaseDdlHandler;
import io.ebean.dbmigration.ddlgeneration.DdlBuffer;
import io.ebean.dbmigration.ddlgeneration.DdlHandler;
import io.ebean.dbmigration.ddlgeneration.DdlWrite;
import io.ebean.dbmigration.ddlgeneration.platform.util.PlatformTypeConverter;
import io.ebean.dbmigration.migration.AddHistoryTable;
import io.ebean.dbmigration.migration.AlterColumn;
import io.ebean.dbmigration.migration.Column;
import io.ebean.dbmigration.migration.DropHistoryTable;
import io.ebean.dbmigration.migration.IdentityType;
import io.ebean.dbmigration.model.MTable;
import io.ebeaninternal.dbmigration.ddlgeneration.BaseDdlHandler;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.PlatformTypeConverter;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import io.ebeaninternal.dbmigration.migration.Column;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.migration.IdentityType;
import io.ebeaninternal.dbmigration.model.MTable;
import io.ebean.util.StringHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -136,9 +136,33 @@ public class PlatformDdl {
* Return the identity type to use given the support in the underlying database
* platform for sequences and identity/autoincrement.
*/
public IdType useIdentityType(IdentityType modelIdentityType) {
public IdType useIdentityType(IdentityType modelIdentity) {
return dbIdentity.useIdentityType(modelIdentityType);
if (modelIdentity == null) {
// use the default
return dbIdentity.getIdType();
}
return identityType(modelIdentity, dbIdentity.getIdType(), dbIdentity.isSupportsSequence(), dbIdentity.isSupportsIdentity());
}
/**
* Determine the id type to use based on requested identityType and
* the support for that in the database platform.
*/
private IdType identityType(IdentityType modelIdentity, IdType platformIdType, boolean supportsSequence, boolean supportsIdentity) {
switch (modelIdentity) {
case GENERATOR:
return IdType.GENERATOR;
case EXTERNAL:
return IdType.EXTERNAL;
case SEQUENCE:
return supportsSequence ? IdType.SEQUENCE : platformIdType;
case IDENTITY:
return supportsIdentity ? IdType.IDENTITY : platformIdType;
default:
return platformIdType;
}
}
/**
@@ -418,7 +442,7 @@ public class PlatformDdl {
}
}
public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException {
buffer.append("alter table ").append(tableName).append(" drop column ").append(columnName)
.endOfStatement();
@@ -552,7 +576,7 @@ public class PlatformDdl {
}
apply.append(String.format("comment on table %s is '%s'", tableName, tableComment)).endOfStatement();
}
/**
* Add column comment as a separate statement.
*/
@@ -1,10 +1,10 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.ServerConfig;
import io.ebean.dbmigration.ddlgeneration.DdlWrite;
import io.ebean.dbmigration.migration.AddHistoryTable;
import io.ebean.dbmigration.migration.DropHistoryTable;
import io.ebean.dbmigration.model.MTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.model.MTable;
import java.io.IOException;
@@ -1,4 +1,4 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.DatabasePlatform;
@@ -1,8 +1,8 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.dbmigration.ddlgeneration.DdlBuffer;
import io.ebean.dbmigration.ddlgeneration.DdlWrite;
import io.ebean.dbmigration.model.MTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.model.MTable;
import java.io.IOException;
import java.util.List;
@@ -1,7 +1,7 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import java.io.IOException;
@@ -1,8 +1,8 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.dbmigration.ddlgeneration.DdlBuffer;
import io.ebean.dbmigration.migration.AlterColumn;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import java.io.IOException;
@@ -39,13 +39,13 @@ public class SqlServerDdl extends PlatformDdl {
String objectId = fkName;
if (pos != -1) {
objectId = tableName.substring(0, pos + 1) + fkName;
}
}
return "IF OBJECT_ID('" + objectId + "', 'F') IS NOT NULL " + super.alterTableDropForeignKey(tableName, fkName);
}
@Override
public String dropSequence(String sequenceName) {
return "IF OBJECT_ID('" + sequenceName + "', 'SO') IS NOT NULL drop sequence " + sequenceName;
return "IF OBJECT_ID('" + sequenceName + "', 'SO') IS NOT NULL drop sequence " + sequenceName;
}
@Override
@@ -81,7 +81,7 @@ public class SqlServerDdl extends PlatformDdl {
}
return sb.toString();
}
@Override
public String alterTableDropConstraint(String tableName, String constraintName) {
StringBuilder sb = new StringBuilder();
@@ -122,7 +122,7 @@ public class SqlServerDdl extends PlatformDdl {
sb.append(";");
return sb.toString();
}
@Override
public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) {
// Unfortunately, the SqlServer creates default values with a random name.
@@ -191,7 +191,7 @@ public class SqlServerDdl extends PlatformDdl {
// do nothing for MS SQL Server (cause it requires stored procedures etc)
}
/**
* It is rather complex to delete a column on SqlServer as there must not exist any references
* (constraints, default values, indices and foreign keys). The list is not yet complete, as
@@ -201,7 +201,7 @@ public class SqlServerDdl extends PlatformDdl {
@Override
public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException {
buffer.append("-- drop column ").append(tableName).append(".").append(columnName).endOfStatement();
buffer.append(alterTableDropUniqueConstraint(tableName, naming.uniqueConstraintName(tableName, columnName)));
buffer.endOfStatement();
buffer.append(alterColumnDefaultValue(tableName, columnName, DdlHelp.DROP_DEFAULT));
@@ -214,5 +214,5 @@ public class SqlServerDdl extends PlatformDdl {
buffer.endOfStatement();
super.alterTableDropColumn(buffer, tableName, columnName);
}
}
@@ -1,11 +1,11 @@
package io.ebean.dbmigration.ddlgeneration.platform;
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.ServerConfig;
import io.ebean.dbmigration.ddlgeneration.DdlBuffer;
import io.ebean.dbmigration.ddlgeneration.DdlWrite;
import io.ebean.dbmigration.migration.AddHistoryTable;
import io.ebean.dbmigration.migration.DropHistoryTable;
import io.ebean.dbmigration.model.MTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.model.MTable;
import java.io.IOException;
@@ -44,7 +44,7 @@ public class SqlServerHistoryDdl implements PlatformHistoryDdl {
historyTable = "dbo." +historyTable; // so add the default schema, if none was specified.
}
apply.append("alter table ").append(baseTable).append(" set (system_versioning = on (history_table=").append(historyTable).append("))").endOfStatement();
DdlBuffer drop = writer.dropAll();
drop.append("IF OBJECT_ID('").append(baseTable).append("', 'U') IS NOT NULL alter table ").append(baseTable).append(" set (system_versioning = off)").endOfStatement();
drop.append("IF OBJECT_ID('").append(baseTable).append("_history', 'U') IS NOT NULL drop table ").append(baseTable).append("_history").endOfStatement();
@@ -56,7 +56,7 @@ public class SqlServerHistoryDdl implements PlatformHistoryDdl {
DdlBuffer apply = writer.applyHistory();
apply.append("-- dropping history support for ").append(baseTable).endOfStatement();
// drop default constraints
apply.append(platformDdl.alterColumnDefaultValue(baseTable, systemPeriodStart, DdlHelp.DROP_DEFAULT)).endOfStatement();
apply.append(platformDdl.alterColumnDefaultValue(baseTable, systemPeriodEnd, DdlHelp.DROP_DEFAULT)).endOfStatement();
// switch of versioning & period
@@ -1,4 +1,4 @@
package io.ebean.dbmigration.ddlgeneration.platform.util;
package io.ebeaninternal.dbmigration.ddlgeneration.platform.util;
import java.util.ArrayList;
import java.util.Collections;
@@ -1,4 +1,4 @@
package io.ebean.dbmigration.ddlgeneration.platform.util;
package io.ebeaninternal.dbmigration.ddlgeneration.platform.util;
import java.util.ArrayList;
import java.util.List;

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