diff --git a/.github/workflows/jdk-18-ea.yml b/.github/workflows/jdk-18-ea.yml index 73121f1d4..b814c77c9 100644 --- a/.github/workflows/jdk-18-ea.yml +++ b/.github/workflows/jdk-18-ea.yml @@ -3,8 +3,8 @@ name: JDK 18-ea on: workflow_dispatch: -# schedule: -# - cron: '30 6 * * *' + schedule: + - cron: '30 6 * * 1,3,5' jobs: build: diff --git a/.github/workflows/mariadb-10.6.yml b/.github/workflows/mariadb-10.6.yml index 797e948ff..cae97ac27 100644 --- a/.github/workflows/mariadb-10.6.yml +++ b/.github/workflows/mariadb-10.6.yml @@ -1,7 +1,10 @@ name: MariaDB 10.6 -on: [workflow_dispatch] +on: + workflow_dispatch: + schedule: + - cron: '10 7 * * 1,4' jobs: build: diff --git a/.github/workflows/sqlserver-2017-latest.yml b/.github/workflows/sqlserver-2017-latest.yml index 83649d080..6c72d8980 100644 --- a/.github/workflows/sqlserver-2017-latest.yml +++ b/.github/workflows/sqlserver-2017-latest.yml @@ -1,7 +1,10 @@ name: SqlServer 2017 latest -on: [workflow_dispatch] +on: + workflow_dispatch: + schedule: + - cron: '10 7 * * 2,5' jobs: build: diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index 872acf7d4..56fd5d994 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT ebean api @@ -27,7 +27,7 @@ io.avaje avaje-config - 1.5 + 1.7 @@ -26,7 +26,7 @@ io.ebean ebean-core - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT provided diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml index de70b12e4..6d9110a91 100644 --- a/ebean-bom/pom.xml +++ b/ebean-bom/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT ebean bom @@ -71,88 +71,88 @@ io.ebean ebean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT io.ebean ebean-api - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT io.ebean ebean-core - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT io.ebean ebean-core-type - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT io.ebean ebean-ddl-generator - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT io.ebean ebean-externalmapping-api - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT io.ebean ebean-externalmapping-xml - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT io.ebean ebean-autotune - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT io.ebean ebean-querybean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT io.ebean querybean-generator - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT provided io.ebean kotlin-querybean-generator - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT provided io.ebean ebean-test - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT test io.ebean ebean-postgis - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT io.ebean ebean-redis - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT diff --git a/ebean-core-type/pom.xml b/ebean-core-type/pom.xml index 0e4062b6f..0dd19215f 100644 --- a/ebean-core-type/pom.xml +++ b/ebean-core-type/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT ebean-core-type @@ -16,7 +16,7 @@ io.ebean ebean-api - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index 2c3ff4ec4..d77e38d62 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -3,7 +3,7 @@ ebean-parent io.ebean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT ebean-core @@ -41,19 +41,19 @@ io.ebean ebean-api - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT io.ebean ebean-core-type - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT io.ebean ebean-externalmapping-api - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DScriptRunner.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DScriptRunner.java index 2b4c81f0b..fb2fbd0a2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DScriptRunner.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DScriptRunner.java @@ -3,13 +3,13 @@ package io.ebeaninternal.server.core; import io.ebean.ScriptRunner; import io.ebean.ddlrunner.DdlRunner; import io.ebean.ddlrunner.ScriptTransform; +import io.ebean.util.IOUtils; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.util.UrlHelper; import javax.persistence.PersistenceException; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.LineNumberReader; import java.io.Reader; import java.net.URL; @@ -66,8 +66,9 @@ final class DScriptRunner implements ScriptRunner { throw new IllegalArgumentException("resource is null?"); } - try (InputStream inputStream = UrlHelper.openNoCache(resource)) { - return readContent(new InputStreamReader(inputStream)); + try (InputStream inputStream = UrlHelper.openNoCache(resource); + Reader reader = IOUtils.newReader(inputStream)) { + return readContent(reader); } catch (IOException e) { throw new PersistenceException("Failed to read script content", e); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java index dcfca1310..96600609a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java @@ -13,6 +13,7 @@ import io.ebean.config.dbplatform.mysql.MySql55Platform; import io.ebean.config.dbplatform.mysql.MySqlPlatform; import io.ebean.config.dbplatform.nuodb.NuoDbPlatform; import io.ebean.config.dbplatform.oracle.Oracle11Platform; +import io.ebean.config.dbplatform.oracle.Oracle12Platform; import io.ebean.config.dbplatform.oracle.OraclePlatform; import io.ebean.config.dbplatform.postgres.Postgres9Platform; import io.ebean.config.dbplatform.postgres.PostgresPlatform; @@ -25,11 +26,7 @@ import io.ebeaninternal.api.DbOffline; import javax.persistence.PersistenceException; import javax.sql.DataSource; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; +import java.sql.*; /** * Create a DatabasePlatform from the configuration. @@ -87,6 +84,9 @@ public class DatabasePlatformFactory { if (dbName.equals("oracle11") || dbName.equals("oracle10") || dbName.equals("oracle9")) { return new Oracle11Platform(); } + if (dbName.equals("oracle12")) { + return new Oracle12Platform(); + } if (dbName.equals("oracle")) { return new OraclePlatform(); } @@ -173,7 +173,13 @@ public class DatabasePlatformFactory { } private DatabasePlatform oracleVersion(int majorVersion) { - return majorVersion < 12 ? new Oracle11Platform() : new OraclePlatform(); + if (majorVersion < 12) { + return new Oracle11Platform(); + } + if (majorVersion < 13) { + return new Oracle12Platform(); + } + return new OraclePlatform(); } private DatabasePlatform mysqlVersion(int majorVersion, int minorVersion) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java index 73765d43d..09851725f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -226,7 +226,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST */ @Override public void addBeanToCollectionWithCreate(EntityBean parentBean, EntityBean detailBean, boolean withCheck) { - BeanCollection bc = (BeanCollection) super.getValue(parentBean); + BeanCollection bc = beanCollection(parentBean); if (bc == null) { bc = help.createEmpty(parentBean); setValue(parentBean, bc); @@ -234,6 +234,15 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST help.add(bc, detailBean, withCheck); } + private BeanCollection beanCollection(EntityBean parentBean) { + try { + return (BeanCollection) super.getValue(parentBean); + } catch (ClassCastException e) { + // fetching element collection, ok for now + return null; + } + } + /** * Return true if this is considered 'empty' from a save perspective. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java index 6f773b270..c555e33c5 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java @@ -10,23 +10,18 @@ public final class DbExpressionHandlerFactory { * Create and return the appropriate platform specific handing of expressions. */ public static DbExpressionHandler from(DatabasePlatform databasePlatform) { - Platform platform = databasePlatform.getPlatform(); + Platform platform = databasePlatform.getPlatform().base(); switch (platform) { case H2: return new H2DbExpression(); case POSTGRES: - case POSTGRES9: return new PostgresDbExpression(); case MARIADB: return new MariaDbExpression(); - case MYSQL55: case MYSQL: return new MySqlDbExpression(); case ORACLE: - case ORACLE11: return new OracleDbExpression(); - case SQLSERVER16: - case SQLSERVER17: case SQLSERVER: return new SqlServerDbExpression(); case HANA: diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java index f2c56db74..701b76402 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java @@ -333,6 +333,13 @@ public final class DefaultPersister implements Persister { * Recursively delete the bean. This calls back to the EbeanServer. */ private int deleteRecurse(EntityBean detailBean, Transaction t, DeleteMode deleteMode) { + return deleteRequest(createDeleteCascade(detailBean, t, deleteMode.persistType())); + } + + /** + * Delete without being a cascade. + */ + private int delete(EntityBean detailBean, Transaction t, DeleteMode deleteMode) { return deleteRequest(createDeleteRequest(detailBean, t, deleteMode.persistType())); } @@ -560,7 +567,7 @@ public final class DefaultPersister implements Persister { } } - private void deleteList(List beanList, SpiTransaction t, DeleteMode deleteMode, boolean children) { + private void deleteCascade(List beanList, SpiTransaction t, DeleteMode deleteMode, boolean children) { if (children) { t.depth(-1); t.checkBatchEscalationOnCollection(); @@ -604,7 +611,7 @@ public final class DefaultPersister implements Persister { for (Object id : ids) { EntityBean bean = descriptor.createEntityBean(); descriptor.convertSetId(id, bean); - int rowCount = deleteRecurse(bean, transaction, deleteMode); + int rowCount = delete(bean, transaction, deleteMode); if (rowCount == -1) { total = -1; } else if (total != -1) { @@ -656,7 +663,7 @@ public final class DefaultPersister implements Persister { t.logSummary("-- DeleteById of " + descriptor.name() + " ids[" + idList + "] requires fetch of foreign key values"); } List beanList = server.findList(q, t); - deleteList(beanList, t, deleteMode, false); + deleteCascade(beanList, t, deleteMode, false); return beanList.size(); } else { @@ -994,6 +1001,8 @@ public final class DefaultPersister implements Persister { executeSqlUpdate(sqlDelete, t); } else { + // TODO: Review first checking if many property is loaded and using the loaded beans + // ... and only using findIdsByParentId() when the many property isn't loaded // Delete recurse using the Id values of the children Object parentId = desc.getId(parentBean); List idsByParentId = many.findIdsByParentId(parentId, null, t, excludeDetailIds, deleteMode.isHard()); @@ -1017,7 +1026,7 @@ public final class DefaultPersister implements Persister { for (Object id : childIds) { refList.add(targetDesc.createReference(id, null)); } - deleteList(refList, t, deleteMode, true); + deleteCascade(refList, t, deleteMode, true); } else { // perform delete by statement if possible delete(targetDesc, null, childIds, t, deleteMode); @@ -1157,6 +1166,10 @@ public final class DefaultPersister implements Persister { return createDeleteRequest(bean, t, type, Flags.ZERO); } + private PersistRequestBean createDeleteCascade(EntityBean bean, Transaction t, Type type) { + return createDeleteRequest(bean, t, type, Flags.RECURSE); + } + @SuppressWarnings({"unchecked"}) private PersistRequestBean createDeleteRequest(Object bean, Transaction t, PersistRequest.Type type, int flags) { BeanManager mgr = beanManager(bean.getClass()); @@ -1167,7 +1180,7 @@ public final class DefaultPersister implements Persister { type = Type.DELETE_SOFT; } - PersistRequestBean request = new PersistRequestBean<>(server, (T)bean, null, mgr, (SpiTransaction) t, persistExecute, type, flags); + PersistRequestBean request = new PersistRequestBean<>(server, (T) bean, null, mgr, (SpiTransaction) t, persistExecute, type, flags); request.initForSoftDelete(); return request; } @@ -1186,7 +1199,7 @@ public final class DefaultPersister implements Persister { */ @SuppressWarnings("unchecked") private BeanManager beanManager(Class cls) { - BeanManager mgr = (BeanManager)beanDescriptorManager.beanManager(cls); + BeanManager mgr = (BeanManager) beanDescriptorManager.beanManager(cls); if (mgr == null) { throw new PersistenceException(errNotRegistered(cls)); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/InsertMeta.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/InsertMeta.java index 64b7d6f3a..2ebba4d05 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/InsertMeta.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/InsertMeta.java @@ -13,11 +13,8 @@ import io.ebeaninternal.server.persist.dmlbind.BindableList; import java.sql.SQLException; -import static io.ebean.annotation.Platform.MARIADB; -import static io.ebean.annotation.Platform.MYSQL; - /** - * Meta data for insert handler. The meta data is for a particular bean type. It + * Metadata for insert handler. The metadata is for a particular bean type. It * is considered immutable and is thread safe. */ final class InsertMeta { @@ -153,31 +150,25 @@ final class InsertMeta { private String genSql(boolean nullId, String table, boolean draftTable) { GenerateDmlRequest request = new GenerateDmlRequest(); request.setInsertSetMode(); - request.append("insert into ").append(table); if (nullId && noColumnsForInsert(draftTable)) { return request.append(defaultValues()).toString(); } - request.append(" ("); if (!nullId) { id.dmlAppend(request); } - if (shadowFKey != null) { shadowFKey.dmlAppend(request); } - if (discriminator != null) { discriminator.dmlAppend(request); } - if (draftTable) { all.dmlAppend(request); } else { allExcludeDraftOnly.dmlAppend(request); } - request.append(") values ("); request.append(request.getInsertBindBuffer()); request.append(")"); @@ -185,7 +176,14 @@ final class InsertMeta { } private String defaultValues() { - return platform.base() == MYSQL || platform.base() == MARIADB ? " values (default)" : " default values"; + switch (platform.base()) { + case MYSQL: + case MARIADB: + case ORACLE: + return " values (default)"; + default: + return " default values"; + } } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/DefaultProfileHandler.java b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/DefaultProfileHandler.java index 87c7401e2..8ac394a8c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/DefaultProfileHandler.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/DefaultProfileHandler.java @@ -4,11 +4,13 @@ import io.ebean.ProfileLocation; import io.ebean.config.ProfilingConfig; import io.ebean.plugin.Plugin; import io.ebean.plugin.SpiServer; +import io.ebean.util.IOUtils; import io.ebeaninternal.api.CoreLog; import io.ebeaninternal.api.SpiProfileHandler; import org.slf4j.Logger; import java.io.*; +import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; @@ -123,7 +125,7 @@ public final class DefaultProfileHandler implements SpiProfileHandler, Plugin { try { String now = DTF.format(LocalDateTime.now()); File file = new File(dir, "txprofile-" + now + ".tprofile"); - out = new BufferedWriter(new FileWriter(file)); + out = IOUtils.newWriter(file); } catch (IOException e) { log.error("Not expected", e); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java index a5b3b35c0..81d6a1ea2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java @@ -193,7 +193,7 @@ public final class DefaultTypeManager implements TypeManager { List> types = plugin.createTypes(config, objectMapper); for (ScalarType type : types) { log.debug("adding ScalarType {}", type.getClass()); - addCustomType(type); + add(type); } } } @@ -211,25 +211,6 @@ public final class DefaultTypeManager implements TypeManager { logAdd(scalarType); } - /** - * Register the ScalarType for an enum. This is special in the sense that an Enum - * can have many classes if it uses method overrides and we need to register all - * the variations/classes for the enum. - */ - @SuppressWarnings({"rawtypes", "unchecked"}) - @Override - public void addEnumType(ScalarType scalarType, Class enumClass) { - Set> mappedClasses = new HashSet<>(); - mappedClasses.add(enumClass); - for (Object value : EnumSet.allOf(enumClass).toArray()) { - mappedClasses.add(value.getClass()); - } - for (Class cls : mappedClasses) { - typeMap.put(cls, scalarType); - } - logAdd(scalarType); - } - private void logAdd(ScalarType scalarType) { if (log.isTraceEnabled()) { String msg = "ScalarType register [" + scalarType.getClass().getName() + "]"; @@ -275,16 +256,43 @@ public final class DefaultTypeManager implements TypeManager { "ScalarType of Joda LocalTime not defined. You need to set DatabaseConfig.jodaLocalTimeMode to" + " either 'normal' or 'utc'. UTC is the old mode using UTC timezone but local time zone is now preferred as 'normal' mode."); } - found = checkInterfaceTypes(type); + found = checkInheritedTypes(type); } - return found; + return found != ScalarTypeNotFound.INSTANCE ? found : null; // Do not return ScalarTypeNotFound, otherwise checks will fail } - private ScalarType checkInterfaceTypes(Class type) { - if (java.nio.file.Path.class.isAssignableFrom(type)) { - return typeMap.get(java.nio.file.Path.class); + /** + * Checks the typeMap for inherited types. + * + * If e.g. type is a GregorianCalendar, then this method + * will check the class hierarchy and will probably return a + * ScalarTypeCalendar To speed up a second lookup, it will write + * back the found scalarType to typeMap. + * + * @param type the for which to search for a ScalarType + * @return either a valid ScalarType if one could be found or {@link ScalarTypeNotFound#INSTANCE} if not + */ + private ScalarType checkInheritedTypes(Class type) { + // first step loop through inheritance chain + Class parent = type; + while (parent != null && parent != Object.class) { + ScalarType found = typeMap.get(parent); + if (found != null && found != ScalarTypeNotFound.INSTANCE) { + typeMap.put(type, found); // store type for next lookup + return found; + } + // second step - loop through interfaces of this type + for (Class iface: parent.getInterfaces()) { + found = checkInheritedTypes(iface); + if (found != null && found != ScalarTypeNotFound.INSTANCE) { + typeMap.put(type, found); // store type for next lookup + return found; + } + } + parent = parent.getSuperclass(); } - return null; + typeMap.put(type, ScalarTypeNotFound.INSTANCE); + return ScalarTypeNotFound.INSTANCE; // no success } @Override @@ -389,7 +397,7 @@ public final class DefaultTypeManager implements TypeManager { private DocPropertyType getDocType(Type genericType) { if (genericType instanceof Class) { - ScalarType found = typeMap.get(genericType); + ScalarType found = getScalarType((Class)genericType); if (found != null) { return found.getDocType(); } @@ -447,7 +455,7 @@ public final class DefaultTypeManager implements TypeManager { return scalarType; } - scalarType = typeMap.get(type); + scalarType = getScalarType(type); if (scalarType != null) { if (jdbcType == 0 || scalarType.getJdbcType() == jdbcType) { // matching type @@ -565,7 +573,7 @@ public final class DefaultTypeManager implements TypeManager { // use JPA normal Enum type (without mapping) scalarEnum = createEnumScalarTypePerSpec(enumType, type); } - addEnumType(scalarEnum, enumType); + add(scalarEnum); return scalarEnum; } @@ -655,16 +663,13 @@ public final class DefaultTypeManager implements TypeManager { scalarType = cls.getDeclaredConstructor().newInstance(); } } - addCustomType(scalarType); + add(scalarType); } catch (Exception e) { log.error("Error loading ScalarType [" + cls.getName() + "]", e); } } } - private void addCustomType(ScalarType scalarType) { - add(scalarType); - } private Object initObjectMapper(DatabaseConfig config) { Object objectMapper = config.getObjectMapper(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonMap.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonMap.java index 93ecdab79..b5942289e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonMap.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonMap.java @@ -8,6 +8,7 @@ import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; import io.ebean.text.TextException; import io.ebean.text.json.EJson; +import io.ebean.util.IOUtils; import java.io.*; import java.nio.charset.StandardCharsets; @@ -75,14 +76,14 @@ abstract class ScalarTypeJsonMap extends ScalarTypeBase { try { if (keepSource) { StringWriter jsonBuffer = new StringWriter(); - try (InputStreamReader streamReader = new InputStreamReader(is, StandardCharsets.UTF_8)) { + try (Reader streamReader = IOUtils.newReader(is)) { transferTo(streamReader, jsonBuffer); } String rawJson = jsonBuffer.toString(); reader.pushJson(rawJson); return parse(rawJson); } else { - try (InputStreamReader streamReader = new InputStreamReader(is, StandardCharsets.UTF_8)) { + try (Reader streamReader = IOUtils.newReader(is)) { return parse(streamReader); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonNode.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonNode.java index 675328de5..8d2501f04 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonNode.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonNode.java @@ -8,12 +8,12 @@ import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; import io.ebean.text.TextException; +import io.ebean.util.IOUtils; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.StandardCharsets; import java.sql.SQLException; @@ -68,7 +68,7 @@ abstract class ScalarTypeJsonNode extends ScalarTypeBase { if (is == null) { return null; } - try (InputStreamReader reader = new InputStreamReader(is)) { + try (Reader reader = IOUtils.newReader(is)) { return parse(reader); } catch (IOException e) { throw new SQLException("Error reading Blob stream from DB", e); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeNotFound.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeNotFound.java new file mode 100644 index 000000000..7fbb5b5e9 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeNotFound.java @@ -0,0 +1,140 @@ +package io.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; + +import io.ebean.core.type.DataBinder; +import io.ebean.core.type.DataReader; +import io.ebean.core.type.DocPropertyType; +import io.ebean.core.type.ScalarType; + +/** + * Class is required as "null" key in ConcurrentHashMap. + * @author Roland Praml, FOCONIS AG + * + */ +class ScalarTypeNotFound implements ScalarType { + + public static final ScalarTypeNotFound INSTANCE = new ScalarTypeNotFound(); + private ScalarTypeNotFound() { } + @Override + public boolean isBinaryType() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isMutable() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isDirty(Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public int getLength() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isJdbcNative() { + throw new UnsupportedOperationException(); + } + + @Override + public int getJdbcType() { + throw new UnsupportedOperationException(); + } + + @Override + public Class getType() { + throw new UnsupportedOperationException(); + } + + @Override + public Void read(DataReader reader) throws SQLException { + throw new UnsupportedOperationException(); + } + + @Override + public void loadIgnore(DataReader reader) { + throw new UnsupportedOperationException(); + } + + @Override + public void bind(DataBinder binder, Void value) throws SQLException { + throw new UnsupportedOperationException(); + } + + @Override + public Object toJdbcType(Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public Void toBeanType(Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public String formatValue(Void value) { + throw new UnsupportedOperationException(); + } + + @Override + public String format(Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public Void parse(String value) { + throw new UnsupportedOperationException(); + } + + @Override + public DocPropertyType getDocType() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isDateTimeCapable() { + throw new UnsupportedOperationException(); + } + + @Override + public long asVersion(Void value) { + throw new UnsupportedOperationException(); + } + + @Override + public Void convertFromMillis(long dateTime) { + throw new UnsupportedOperationException(); + } + + @Override + public Void readData(DataInput dataInput) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void writeData(DataOutput dataOutput, Void v) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public Void jsonRead(JsonParser parser) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void jsonWrite(JsonGenerator writer, Void value) throws IOException { + throw new UnsupportedOperationException(); + } + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/SimpleAesEncryptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/SimpleAesEncryptor.java index 0dfd3f999..0b16967c7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/SimpleAesEncryptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/SimpleAesEncryptor.java @@ -32,7 +32,7 @@ public final class SimpleAesEncryptor implements Encryptor { } private IvParameterSpec getIvParameterSpec(String initialVector) { - return new IvParameterSpec(initialVector.getBytes()); + return new IvParameterSpec(initialVector.getBytes(StandardCharsets.UTF_8)); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/TypeManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/TypeManager.java index 692e65681..079782972 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/TypeManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/TypeManager.java @@ -17,12 +17,6 @@ public interface TypeManager { */ void add(ScalarType scalarType); - /** - * Register a ScalarType for an Enum with can have multiple classes. - */ - @SuppressWarnings("rawtypes") - void addEnumType(ScalarType type, Class myEnumClass); - /** * Return the scalar type for the given logical type. */ diff --git a/ebean-ddl-generator/pom.xml b/ebean-ddl-generator/pom.xml index 4a189ed69..b0e8138dc 100644 --- a/ebean-ddl-generator/pom.xml +++ b/ebean-ddl-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT ebean ddl generation @@ -22,20 +22,20 @@ io.ebean ebean-migration - 12.12.1 + 12.13.0 io.ebean ebean-core-type - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT provided io.ebean ebean-core - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT provided diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java index 69a4af033..b9fead35b 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java @@ -5,6 +5,7 @@ import io.ebean.config.DatabaseConfig; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.ddlrunner.DdlRunner; import io.ebean.ddlrunner.ScriptTransform; +import io.ebean.util.IOUtils; import io.ebean.util.JdbcClose; import io.ebeaninternal.api.SpiDdlGenerator; import io.ebeaninternal.api.SpiEbeanServer; @@ -17,13 +18,11 @@ import org.slf4j.LoggerFactory; import javax.persistence.PersistenceException; import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.LineNumberReader; import java.io.Reader; +import java.io.Writer; import java.sql.Connection; import java.sql.SQLException; @@ -50,6 +49,7 @@ public class DdlGenerator implements SpiDdlGenerator { private final ScriptTransform scriptTransform; private final Platform platform; private final String platformName; + private final boolean useMigrationStoredProcedures; private CurrentModel currentModel; private String dropAllContent; @@ -71,9 +71,11 @@ public class DdlGenerator implements SpiDdlGenerator { log.warn("DDL can't be run on startup with TenantMode " + config.getTenantMode()); this.runDdl = false; this.ddlAutoCommit = false; + this.useMigrationStoredProcedures = false; } else { this.runDdl = config.isDdlRun(); this.ddlAutoCommit = databasePlatform.isDdlAutoCommit(); + this.useMigrationStoredProcedures = config.getDatabasePlatform().isUseMigrationStoredProcedures(); } this.scriptTransform = createScriptTransform(config); this.baseDir = initBaseDir(); @@ -187,7 +189,7 @@ public class DdlGenerator implements SpiDdlGenerator { protected void runDropSql(Connection connection) throws IOException { if (!createOnly) { - if (extraDdl && jaxbPresent) { + if (extraDdl && jaxbPresent && useMigrationStoredProcedures) { String extraApply = ExtraDdlXmlReader.buildExtra(platform, true); if (extraApply != null) { runScript(connection, false, extraApply, "extra-ddl"); @@ -268,7 +270,7 @@ public class DdlGenerator implements SpiDdlGenerator { if (is == null) { log.warn("sql script {} was not found as a resource", sqlScript); } else { - String content = readContent(new InputStreamReader(is)); + String content = readContent(IOUtils.newReader(is)); // 'is' is closed runScript(connection, false, content, sqlScript); } } @@ -337,7 +339,7 @@ public class DdlGenerator implements SpiDdlGenerator { protected void writeFile(String fileName, String fileContent) throws IOException { File f = new File(baseDir, fileName); - try (FileWriter fw = new FileWriter(f)) { + try (Writer fw = IOUtils.newWriter(f)) { fw.write(fileContent); fw.flush(); } @@ -348,7 +350,9 @@ public class DdlGenerator implements SpiDdlGenerator { if (!f.exists()) { return null; } - return readContent(new FileReader(f)); + try (Reader reader = IOUtils.newReader(f)) { + return readContent(reader); + } } protected String readContent(Reader reader) throws IOException { diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java index a7a3703d7..4add6fe13 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java @@ -1,8 +1,22 @@ package io.ebeaninternal.dbmigration; +import static io.ebeaninternal.api.PlatformMatch.matchPlatform; + +import java.io.File; +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.StringJoiner; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import io.ebean.DB; import io.ebean.Database; import io.ebean.annotation.Platform; +import io.ebean.config.ClassLoadConfig; import io.ebean.config.DatabaseConfig; import io.ebean.config.DbConstraintNaming; import io.ebean.config.PlatformConfig; @@ -27,6 +41,8 @@ import io.ebean.config.dbplatform.sqlite.SQLitePlatform; import io.ebean.config.dbplatform.sqlserver.SqlServer16Platform; import io.ebean.config.dbplatform.sqlserver.SqlServer17Platform; import io.ebean.dbmigration.DbMigration; +import io.ebean.util.IOUtils; +import io.ebean.util.StringHelper; import io.ebeaninternal.api.DbOffline; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions; @@ -42,17 +58,6 @@ import io.ebeaninternal.dbmigration.model.PlatformDdlWriter; 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.Properties; - -import static io.ebeaninternal.api.PlatformMatch.matchPlatform; /** * Generates DB Migration xml and sql scripts. @@ -392,19 +397,22 @@ public class DefaultDbMigration implements DbMigration { private void generateExtraDdl(File migrationDir, DatabasePlatform dbPlatform, boolean tablePartitioning) throws IOException { if (dbPlatform != null) { if (tablePartitioning && includeBuiltInPartitioning) { - generateExtraDdlFor(migrationDir, dbPlatform, ExtraDdlXmlReader.readBuiltinTablePartitioning()); + generateExtraDdlFor(migrationDir, dbPlatform, ExtraDdlXmlReader.readBuiltinTablePartitioning(), false); } - generateExtraDdlFor(migrationDir, dbPlatform, ExtraDdlXmlReader.readBuiltin()); - generateExtraDdlFor(migrationDir, dbPlatform, ExtraDdlXmlReader.read()); + // skip built-in migration stored procedures based on isUseMigrationStoredProcedures + generateExtraDdlFor(migrationDir, dbPlatform, ExtraDdlXmlReader.readBuiltin(), true); + generateExtraDdlFor(migrationDir, dbPlatform, ExtraDdlXmlReader.read(), false); } } - private void generateExtraDdlFor(File migrationDir, DatabasePlatform dbPlatform, ExtraDdl extraDdl) throws IOException { + private void generateExtraDdlFor(File migrationDir, DatabasePlatform dbPlatform, ExtraDdl extraDdl, boolean checkSkip) throws IOException { if (extraDdl != null) { List ddlScript = extraDdl.getDdlScript(); for (DdlScript script : ddlScript) { if (!script.isDrop() && matchPlatform(dbPlatform.getPlatform(), script.getPlatforms())) { - writeExtraDdl(migrationDir, script); + if (!checkSkip || dbPlatform.isUseMigrationStoredProcedures()) { + writeExtraDdl(migrationDir, script); + } } } } @@ -417,7 +425,7 @@ public class DefaultDbMigration implements DbMigration { String fullName = repeatableMigrationName(script.isInit(), script.getName()); logger.debug("writing repeatable script {}", fullName); File file = new File(migrationDir, fullName); - try (FileWriter writer = new FileWriter(file)) { + try (Writer writer = IOUtils.newWriter(file)) { writer.write(script.getValue()); writer.flush(); } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/IndexMigration.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/IndexMigration.java index baedb77b9..c0f6221d7 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/IndexMigration.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/IndexMigration.java @@ -2,10 +2,11 @@ package io.ebeaninternal.dbmigration; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.migration.MigrationVersion; +import io.ebean.util.IOUtils; import java.io.File; -import java.io.FileWriter; import java.io.IOException; +import java.io.Writer; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -50,17 +51,17 @@ class IndexMigration { private void generateIndex() throws IOException { Collections.sort(all); - FileWriter writer = new FileWriter(indexFile); - for (Entry entry : all) { - writeChecksumPadded(writer, entry.checksum); - writer.write(entry.fileName); + try (Writer writer = IOUtils.newWriter(indexFile)) { + for (Entry entry : all) { + writeChecksumPadded(writer, entry.checksum); + writer.write(entry.fileName); + writer.write(eol); + } writer.write(eol); } - writer.write(eol); - writer.close(); } - private void writeChecksumPadded(FileWriter writer, int checksum) throws IOException { + private void writeChecksumPadded(Writer writer, int checksum) throws IOException { final String asStr = String.valueOf(checksum); writer.write(asStr); writer.write(','); diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/MChecksum.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/MChecksum.java index 9c533992e..9ce34a7ba 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/MChecksum.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/MChecksum.java @@ -4,6 +4,8 @@ import java.io.*; import java.nio.charset.StandardCharsets; import java.util.zip.CRC32; +import io.ebean.util.IOUtils; + /** * Calculates the checksum for the given file content. */ @@ -13,9 +15,8 @@ class MChecksum { * Returns the checksum of the file. Agnostic of encoding and new line character. */ static int calculate(File file) { - try { + try (BufferedReader bufferedReader = IOUtils.newReader(file)) { final CRC32 crc32 = new CRC32(); - BufferedReader bufferedReader = new BufferedReader(new FileReader(file)); String line; while ((line = bufferedReader.readLine()) != null) { final byte[] lineBytes = line.getBytes(StandardCharsets.UTF_8); diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MySqlDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MySqlDdl.java index f5658c3ec..c772a30aa 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MySqlDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MySqlDdl.java @@ -18,12 +18,15 @@ public class MySqlDdl extends PlatformDdl { // this flag is for compatibility. Use it with care. private static final boolean USE_CHECK_CONSTRAINT = Boolean.getBoolean("ebean.mysql.useCheckConstraint"); + private final boolean useMigrationStoredProcedures; + public MySqlDdl(DatabasePlatform platform) { super(platform); this.alterColumn = "modify"; this.dropUniqueConstraint = "drop index"; this.historyDdl = new MySqlHistoryDdl(); this.inlineComments = true; + this.useMigrationStoredProcedures = platform.isUseMigrationStoredProcedures(); } /** @@ -34,6 +37,15 @@ public class MySqlDdl extends PlatformDdl { return "drop index " + maxConstraintName(indexName) + " on " + tableName; } + @Override + public void alterTableDropColumn(final DdlBuffer buffer, final String tableName, final String columnName) throws IOException { + if (this.useMigrationStoredProcedures) { + buffer.append("CALL usp_ebean_drop_column('").append(tableName).append("', '").append(columnName).append("')").endOfStatement(); + } else { + super.alterTableDropColumn(buffer, tableName, columnName); + } + } + /** * Return the drop foreign key clause. */ diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/migrationreader/MigrationXmlWriter.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/migrationreader/MigrationXmlWriter.java index 43d389b56..c887fc2b0 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/migrationreader/MigrationXmlWriter.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/migrationreader/MigrationXmlWriter.java @@ -1,14 +1,15 @@ package io.ebeaninternal.dbmigration.migrationreader; +import io.ebean.util.IOUtils; import io.ebeaninternal.dbmigration.migration.Migration; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import java.io.File; -import java.io.FileWriter; import java.io.IOException; +import java.io.Writer; /** * Simple writer for output of the Migration/ChangeSet as an XML document. @@ -26,7 +27,7 @@ public class MigrationXmlWriter { */ public void write(Migration migration, File file) { - try (FileWriter writer = new FileWriter(file)) { + try (Writer writer = IOUtils.newWriter(file)) { writer.write("\n"); if (comment != null) { diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/PlatformDdlWriter.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/PlatformDdlWriter.java index 59da934e4..ec55dd9a9 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/PlatformDdlWriter.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/PlatformDdlWriter.java @@ -2,6 +2,7 @@ package io.ebeaninternal.dbmigration.model; import io.ebean.config.DatabaseConfig; import io.ebean.config.dbplatform.DatabasePlatform; +import io.ebean.util.IOUtils; import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler; import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; @@ -14,7 +15,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; -import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.util.List; @@ -70,16 +70,16 @@ public class PlatformDdlWriter { */ protected void writePlatformDdl(DdlWrite write, File resourcePath, String fullVersion) throws IOException { if (!write.isApplyEmpty()) { - try (FileWriter applyWriter = createWriter(resourcePath, fullVersion, ".sql")) { + try (Writer applyWriter = createWriter(resourcePath, fullVersion, ".sql")) { writeApplyDdl(applyWriter, write); applyWriter.flush(); } } } - protected FileWriter createWriter(File path, String fullVersion, String suffix) throws IOException { + protected Writer createWriter(File path, String fullVersion, String suffix) throws IOException { File applyFile = new File(path, fullVersion + suffix); - return new FileWriter(applyFile); + return IOUtils.newWriter(applyFile); } /** diff --git a/ebean-ddl-generator/src/main/resources/io/ebeaninternal/dbmigration/builtin-extra-ddl.xml b/ebean-ddl-generator/src/main/resources/io/ebeaninternal/dbmigration/builtin-extra-ddl.xml index 66d23b702..402c51f94 100644 --- a/ebean-ddl-generator/src/main/resources/io/ebeaninternal/dbmigration/builtin-extra-ddl.xml +++ b/ebean-ddl-generator/src/main/resources/io/ebeaninternal/dbmigration/builtin-extra-ddl.xml @@ -112,5 +112,88 @@ BEGIN END GO +-- Inital script to create stored procedures etc for mysql platform +DROP PROCEDURE IF EXISTS usp_ebean_drop_foreign_keys; +delimiter $$ +-- +-- PROCEDURE: usp_ebean_drop_foreign_keys TABLE, COLUMN +-- deletes all constraints and foreign keys referring to TABLE.COLUMN +-- +CREATE PROCEDURE usp_ebean_drop_foreign_keys(IN p_table_name VARCHAR(255), IN p_column_name VARCHAR(255)) +BEGIN +DECLARE done INT DEFAULT FALSE; +DECLARE c_fk_name CHAR(255); +DECLARE curs CURSOR FOR SELECT CONSTRAINT_NAME from information_schema.KEY_COLUMN_USAGE +WHERE TABLE_SCHEMA = DATABASE() and TABLE_NAME = p_table_name and COLUMN_NAME = p_column_name +AND REFERENCED_TABLE_NAME IS NOT NULL; +DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + +OPEN curs; + +read_loop: LOOP +FETCH curs INTO c_fk_name; +IF done THEN +LEAVE read_loop; +END IF; +SET @sql = CONCAT('ALTER TABLE ', p_table_name, ' DROP FOREIGN KEY ', c_fk_name); +PREPARE stmt FROM @sql; +EXECUTE stmt; +END LOOP; + +CLOSE curs; +END +$$ + +DROP PROCEDURE IF EXISTS usp_ebean_drop_column; + +delimiter $$ +-- +-- PROCEDURE: usp_ebean_drop_column TABLE, COLUMN +-- deletes the column and ensures that all indices and constraints are dropped first +-- +CREATE PROCEDURE usp_ebean_drop_column(IN p_table_name VARCHAR(255), IN p_column_name VARCHAR(255)) +BEGIN +CALL usp_ebean_drop_foreign_keys(p_table_name, p_column_name); +SET @sql = CONCAT('ALTER TABLE ', p_table_name, ' DROP COLUMN ', p_column_name); +PREPARE stmt FROM @sql; +EXECUTE stmt; +END +$$ + + +-- Inital script to create stored procedures etc for the hana platform +delimiter $$ +-- +-- PROCEDURE: usp_ebean_drop_foreign_keys TABLE, COLUMN +-- deletes all constraints and foreign keys referring to TABLE.COLUMN +-- +CREATE OR REPLACE PROCEDURE usp_ebean_drop_foreign_keys(IN table_name NVARCHAR(256), IN column_name NVARCHAR(256)) +AS +BEGIN +DECLARE foreign_key_names TABLE(CONSTRAINT_NAME NVARCHAR(256), TABLE_NAME NVARCHAR(256)); +DECLARE i INT; + +foreign_key_names = SELECT CONSTRAINT_NAME, TABLE_NAME FROM SYS.REFERENTIAL_CONSTRAINTS WHERE SCHEMA_NAME=CURRENT_SCHEMA AND TABLE_NAME=UPPER(:table_name) AND COLUMN_NAME=UPPER(:column_name); + +FOR I IN 1 .. RECORD_COUNT(:foreign_key_names) DO +EXEC 'ALTER TABLE "' || ESCAPE_DOUBLE_QUOTES(:foreign_key_names.TABLE_NAME[i]) || '" DROP CONSTRAINT "' || ESCAPE_DOUBLE_QUOTES(:foreign_key_names.CONSTRAINT_NAME[i]) || '"'; +END FOR; + +END; +$$ + +delimiter $$ +-- +-- PROCEDURE: usp_ebean_drop_column TABLE, COLUMN +-- deletes the column and ensures that all indices and constraints are dropped first +-- +CREATE OR REPLACE PROCEDURE usp_ebean_drop_column(IN table_name NVARCHAR(256), IN column_name NVARCHAR(256)) +AS +BEGIN +CALL usp_ebean_drop_foreign_keys(table_name, column_name); +EXEC 'ALTER TABLE "' || UPPER(ESCAPE_DOUBLE_QUOTES(table_name)) || '" DROP ("' || UPPER(ESCAPE_DOUBLE_QUOTES(column_name)) || '")'; +END; +$$ + diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/MChecksumTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/MChecksumTest.java index 0f41e7ba0..3bd677a7e 100644 --- a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/MChecksumTest.java +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/MChecksumTest.java @@ -7,7 +7,7 @@ import java.io.File; import static org.assertj.core.api.Assertions.assertThat; public class MChecksumTest { - + @Test public void calculate() { File file = new File("src/test/resources/dbmigration/index/1.0__hello.sql"); @@ -15,4 +15,12 @@ public class MChecksumTest { assertThat(MChecksum.calculate(file)).isEqualTo(907060870); } + + @Test + public void calculateWithSpecialChars() { + File file = new File("src/test/resources/dbmigration/index-special-chars/1.0__hello.sql"); + assertThat(file).exists(); + + assertThat(MChecksum.calculate(file)).isEqualTo(1859426839); + } } diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/MysqlGenerateMigrationTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/MysqlGenerateMigrationTest.java new file mode 100644 index 000000000..2601582f0 --- /dev/null +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/MysqlGenerateMigrationTest.java @@ -0,0 +1,85 @@ +package io.ebeaninternal.dbmigration; + +import io.ebean.Database; +import io.ebean.DatabaseFactory; +import io.ebean.annotation.Platform; +import io.ebean.config.DatabaseConfig; +import io.ebean.config.PlatformConfig; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Class to test the alternative drop behaviour using stored procedures for MySql databases . + * + * @author Jonas Pöhler, FOCONIS AG + */ +public class MysqlGenerateMigrationTest { + + @AfterEach + public void resetPendingDropsProperty() { + System.clearProperty("ddl.migration.pendingDropsFor"); + } + + @Test + public void testMysqlStoredProcedures() throws Exception { + DefaultDbMigration migration = new DefaultDbMigration(); + migration.setIncludeIndex(true); + // We use src/test/resources as output directory (so we see in GIT if files will change) + migration.setPathToResources("src/test/resources"); + + migration.addPlatform(Platform.MYSQL, "mysql"); + + final PlatformConfig platformConfig = new PlatformConfig(); + platformConfig.setUseMigrationStoredProcedures(true); + + DatabaseConfig config = new DatabaseConfig(); + config.setName("migrationtest"); + config.loadFromProperties(); + config.setPlatformConfig(platformConfig); + config.setRegister(false); + config.setDefaultServer(false); + config.getProperties().put("ebean.migration.migrationPath", "db/migration/mysql"); + + config.setPackages(Arrays.asList("misc.migration.mysql_v1_0")); + Database server = DatabaseFactory.create(config); + migration.setServer(server); + migration.setMigrationPath("mysql/procedures"); + + // First, we clean up the output-directory + assertThat(migration.migrationDirectory().getAbsolutePath()).contains("procedures"); + Files.walk(migration.migrationDirectory().toPath()) + .filter(Files::isRegularFile) + .map(Path::toFile).forEach(File::delete); + + // then we generate migration scripts for v1_0 + assertThat(migration.generateMigration()).isEqualTo("1.0__initial"); + + config.setPackages(Arrays.asList("misc.migration.mysql_v1_1")); + server.shutdown(); + server = DatabaseFactory.create(config); + migration.setServer(server); + migration.setMigrationPath("mysql/procedures"); + assertThat(migration.generateMigration()).isEqualTo("1.1"); + + System.setProperty("ddl.migration.pendingDropsFor", "1.1"); + assertThat(migration.generateMigration()).isEqualTo("1.2__dropsFor_1.1"); + + final Path sqlFile = migration.migrationDirectory().toPath() + .resolve("mysql/1.2__dropsFor_1.1.sql"); + + assertThat(sqlFile).isNotEmptyFile(); + assertThat(Files.readAllLines(sqlFile, StandardCharsets.UTF_8)) + .contains("CALL usp_ebean_drop_column('migtest_e_basic', 'status2');") + .contains("CALL usp_ebean_drop_column('migtest_e_basic', 'description');"); + + } +} diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/Helper.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/Helper.java index b85bd6fb7..9ce850323 100644 --- a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/Helper.java +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/Helper.java @@ -1,5 +1,6 @@ package io.ebeaninternal.dbmigration.ddlgeneration; +import io.ebean.util.IOUtils; import io.ebeaninternal.dbmigration.migration.AddColumn; import io.ebeaninternal.dbmigration.migration.ChangeSet; import io.ebeaninternal.dbmigration.migration.CreateTable; @@ -9,8 +10,8 @@ import io.ebeaninternal.dbmigration.migrationreader.MigrationXmlReader; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.LineNumberReader; +import java.io.Reader; import java.util.List; /** @@ -89,8 +90,7 @@ public class Helper { public static String asText(InputStream in) throws IOException { - try { - InputStreamReader reader = new InputStreamReader(in); + try (Reader reader = IOUtils.newReader(in)) { LineNumberReader lineNumberReader = new LineNumberReader(reader); diff --git a/ebean-ddl-generator/src/test/java/misc/migration/mysql_v1_0/EBasic.java b/ebean-ddl-generator/src/test/java/misc/migration/mysql_v1_0/EBasic.java new file mode 100644 index 000000000..b11fb4a8c --- /dev/null +++ b/ebean-ddl-generator/src/test/java/misc/migration/mysql_v1_0/EBasic.java @@ -0,0 +1,89 @@ +package misc.migration.mysql_v1_0; + +import io.ebean.annotation.DbDefault; +import io.ebean.annotation.EnumValue; +import io.ebean.annotation.NotNull; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.validation.constraints.Size; + +@Entity +@Table(name = "migtest_e_basic") +public class EBasic { + + public enum Status { + @EnumValue("N") + NEW, + + @EnumValue("A") + ACTIVE, + + @EnumValue("I") + INACTIVE, + } + + @Id + Integer id; + + Status status; + + @DbDefault("N") + @NotNull + Status status2; + + @Size(max=127) + String name; + + @Size(max=127) + String description; + + public EBasic() { + + } + + public EBasic(String name) { + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Status getStatus2() { + return status2; + } + + public void setStatus2(final Status status2) { + this.status2 = status2; + } +} diff --git a/ebean-ddl-generator/src/test/java/misc/migration/mysql_v1_1/EBasic.java b/ebean-ddl-generator/src/test/java/misc/migration/mysql_v1_1/EBasic.java new file mode 100644 index 000000000..739332226 --- /dev/null +++ b/ebean-ddl-generator/src/test/java/misc/migration/mysql_v1_1/EBasic.java @@ -0,0 +1,64 @@ +package misc.migration.mysql_v1_1; + +import io.ebean.annotation.EnumValue; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.validation.constraints.Size; + +@Entity +@Table(name = "migtest_e_basic") +public class EBasic { + + public enum Status { + @EnumValue("N") + NEW, + + @EnumValue("A") + ACTIVE, + + @EnumValue("I") + INACTIVE, + } + + @Id + Integer id; + + Status status; + + @Size(max=127) + String name; + + public EBasic() { + + } + + public EBasic(String name) { + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/index-special-chars/1.0__hello.sql b/ebean-ddl-generator/src/test/resources/dbmigration/index-special-chars/1.0__hello.sql new file mode 100644 index 000000000..6814e2a28 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/index-special-chars/1.0__hello.sql @@ -0,0 +1 @@ +helloäüü diff --git a/ebean-ddl-generator/src/test/resources/mysql/procedures/model/1.0__initial.model.xml b/ebean-ddl-generator/src/test/resources/mysql/procedures/model/1.0__initial.model.xml new file mode 100644 index 000000000..4cb2aea38 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/mysql/procedures/model/1.0__initial.model.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/ebean-ddl-generator/src/test/resources/mysql/procedures/model/1.1.model.xml b/ebean-ddl-generator/src/test/resources/mysql/procedures/model/1.1.model.xml new file mode 100644 index 000000000..27ec6c42a --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/mysql/procedures/model/1.1.model.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/ebean-ddl-generator/src/test/resources/mysql/procedures/model/1.2__dropsFor_1.1.model.xml b/ebean-ddl-generator/src/test/resources/mysql/procedures/model/1.2__dropsFor_1.1.model.xml new file mode 100644 index 000000000..7b89fb0e0 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/mysql/procedures/model/1.2__dropsFor_1.1.model.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/ebean-ddl-generator/src/test/resources/mysql/procedures/mysql/1.0__initial.sql b/ebean-ddl-generator/src/test/resources/mysql/procedures/mysql/1.0__initial.sql new file mode 100644 index 000000000..9e44107be --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/mysql/procedures/mysql/1.0__initial.sql @@ -0,0 +1,11 @@ +-- Migrationscripts for ebean unittest +-- apply changes +create table migtest_e_basic ( + id integer auto_increment not null, + status varchar(1), + status2 varchar(1) default 'N' not null, + name varchar(127), + description varchar(127), + constraint pk_migtest_e_basic primary key (id) +); + diff --git a/ebean-ddl-generator/src/test/resources/mysql/procedures/mysql/1.2__dropsFor_1.1.sql b/ebean-ddl-generator/src/test/resources/mysql/procedures/mysql/1.2__dropsFor_1.1.sql new file mode 100644 index 000000000..38b133086 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/mysql/procedures/mysql/1.2__dropsFor_1.1.sql @@ -0,0 +1,6 @@ +-- Migrationscripts for ebean unittest +-- apply changes +CALL usp_ebean_drop_column('migtest_e_basic', 'status2'); + +CALL usp_ebean_drop_column('migtest_e_basic', 'description'); + diff --git a/ebean-ddl-generator/src/test/resources/mysql/procedures/mysql/I__create_procs.sql b/ebean-ddl-generator/src/test/resources/mysql/procedures/mysql/I__create_procs.sql new file mode 100644 index 000000000..d13ec2708 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/mysql/procedures/mysql/I__create_procs.sql @@ -0,0 +1,48 @@ +-- Inital script to create stored procedures etc for mysql platform +DROP PROCEDURE IF EXISTS usp_ebean_drop_foreign_keys; + +delimiter $$ +-- +-- PROCEDURE: usp_ebean_drop_foreign_keys TABLE, COLUMN +-- deletes all constraints and foreign keys referring to TABLE.COLUMN +-- +CREATE PROCEDURE usp_ebean_drop_foreign_keys(IN p_table_name VARCHAR(255), IN p_column_name VARCHAR(255)) +BEGIN +DECLARE done INT DEFAULT FALSE; +DECLARE c_fk_name CHAR(255); +DECLARE curs CURSOR FOR SELECT CONSTRAINT_NAME from information_schema.KEY_COLUMN_USAGE +WHERE TABLE_SCHEMA = DATABASE() and TABLE_NAME = p_table_name and COLUMN_NAME = p_column_name +AND REFERENCED_TABLE_NAME IS NOT NULL; +DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + +OPEN curs; + +read_loop: LOOP +FETCH curs INTO c_fk_name; +IF done THEN +LEAVE read_loop; +END IF; +SET @sql = CONCAT('ALTER TABLE ', p_table_name, ' DROP FOREIGN KEY ', c_fk_name); +PREPARE stmt FROM @sql; +EXECUTE stmt; +END LOOP; + +CLOSE curs; +END +$$ + +DROP PROCEDURE IF EXISTS usp_ebean_drop_column; + +delimiter $$ +-- +-- PROCEDURE: usp_ebean_drop_column TABLE, COLUMN +-- deletes the column and ensures that all indices and constraints are dropped first +-- +CREATE PROCEDURE usp_ebean_drop_column(IN p_table_name VARCHAR(255), IN p_column_name VARCHAR(255)) +BEGIN +CALL usp_ebean_drop_foreign_keys(p_table_name, p_column_name); +SET @sql = CONCAT('ALTER TABLE ', p_table_name, ' DROP COLUMN ', p_column_name); +PREPARE stmt FROM @sql; +EXECUTE stmt; +END +$$ diff --git a/ebean-ddl-generator/src/test/resources/mysql/procedures/mysql/idx_mysql.migrations b/ebean-ddl-generator/src/test/resources/mysql/procedures/mysql/idx_mysql.migrations new file mode 100644 index 000000000..1cb8a087c --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/mysql/procedures/mysql/idx_mysql.migrations @@ -0,0 +1,4 @@ +1835064798, I__create_procs.sql +1968521526, 1.0__initial.sql +-728933533, 1.2__dropsFor_1.1.sql + diff --git a/ebean-externalmapping-api/pom.xml b/ebean-externalmapping-api/pom.xml index 0566534a9..d9c3b9e62 100644 --- a/ebean-externalmapping-api/pom.xml +++ b/ebean-externalmapping-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT ebean external mapping api diff --git a/ebean-externalmapping-xml/pom.xml b/ebean-externalmapping-xml/pom.xml index 5a651b9dc..94047ec2e 100644 --- a/ebean-externalmapping-xml/pom.xml +++ b/ebean-externalmapping-xml/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT @@ -33,7 +33,7 @@ io.ebean ebean-externalmapping-api - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT @@ -59,14 +59,14 @@ io.ebean ebean-core - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT test io.ebean ebean-ddl-generator - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT test diff --git a/ebean-kotlin/pom.xml b/ebean-kotlin/pom.xml index 22fafea6c..35923973c 100644 --- a/ebean-kotlin/pom.xml +++ b/ebean-kotlin/pom.xml @@ -5,7 +5,7 @@ ebean-parent io.ebean - 12.13.2-SNAPSHOT + 12.14.2-SNAPSHOT 4.0.0 @@ -29,7 +29,7 @@ io.ebean ebean-core - 12.13.2-SNAPSHOT + 12.14.2-SNAPSHOT provided @@ -51,7 +51,7 @@ io.ebean ebean-test - 12.13.2-SNAPSHOT + 12.14.2-SNAPSHOT test diff --git a/ebean-postgis/pom.xml b/ebean-postgis/pom.xml index 0862a5696..ac4553b77 100644 --- a/ebean-postgis/pom.xml +++ b/ebean-postgis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT ebean postgis @@ -23,7 +23,7 @@ io.ebean ebean-core - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT provided @@ -74,7 +74,7 @@ io.ebean ebean-test - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT test diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml index a6e2507d7..6ecbfafad 100644 --- a/ebean-querybean/pom.xml +++ b/ebean-querybean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT ebean querybean @@ -17,7 +17,7 @@ io.ebean ebean-core - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT provided @@ -56,21 +56,21 @@ io.ebean ebean-ddl-generator - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT test io.ebean querybean-generator - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT test io.ebean ebean-test - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT test diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml index dca810bd2..e811c97ad 100644 --- a/ebean-redis/pom.xml +++ b/ebean-redis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT ebean-redis @@ -16,41 +16,41 @@ redis.clients jedis - 3.6.3 + 3.8.0 io.ebean ebean-api - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT provided io.ebean ebean-core - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT provided io.ebean ebean-querybean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT test io.ebean querybean-generator - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT test io.ebean ebean-test - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT test diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml index b5940bab0..e5c80afb8 100644 --- a/ebean-test/pom.xml +++ b/ebean-test/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT ebean test @@ -29,14 +29,14 @@ io.ebean ebean-core - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT provided io.ebean ebean-ddl-generator - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT @@ -56,7 +56,7 @@ io.ebean ebean-test-docker - 4.3 + 4.4 @@ -91,7 +91,7 @@ io.ebean ebean-migration - 12.12.1 + 12.13.0 test @@ -226,7 +226,7 @@ ch.qos.logback logback-classic - 1.2.3 + 1.2.9 test diff --git a/ebean-test/src/main/java/io/ebean/test/config/platform/ClickHouseSetup.java b/ebean-test/src/main/java/io/ebean/test/config/platform/ClickHouseSetup.java index 4dcb472b4..a2a6ca0e0 100644 --- a/ebean-test/src/main/java/io/ebean/test/config/platform/ClickHouseSetup.java +++ b/ebean-test/src/main/java/io/ebean/test/config/platform/ClickHouseSetup.java @@ -11,7 +11,7 @@ class ClickHouseSetup implements PlatformSetup { config.setDefaultPort(8123); config.setUsername("default"); config.setPassword(""); - config.setUrl("jdbc:clickhouse://localhost:${port}/${databaseName}"); + config.setUrl("jdbc:clickhouse://${host}:${port}/${databaseName}"); config.setDriver("ru.yandex.clickhouse.ClickHouseDriver"); config.datasourceDefaults(); diff --git a/ebean-test/src/main/java/io/ebean/test/config/platform/CockroachSetup.java b/ebean-test/src/main/java/io/ebean/test/config/platform/CockroachSetup.java index 879f895ee..b68ca912e 100644 --- a/ebean-test/src/main/java/io/ebean/test/config/platform/CockroachSetup.java +++ b/ebean-test/src/main/java/io/ebean/test/config/platform/CockroachSetup.java @@ -11,7 +11,7 @@ class CockroachSetup implements PlatformSetup { config.setDefaultPort(26257); config.setUsername("root"); config.setPassword(""); - config.setUrl("jdbc:postgresql://localhost:${port}/${databaseName}?sslmode=disable"); + config.setUrl("jdbc:postgresql://${host}:${port}/${databaseName}?sslmode=disable"); config.setDriver("org.postgresql.Driver"); config.datasourceDefaults(); diff --git a/ebean-test/src/main/java/io/ebean/test/config/platform/Config.java b/ebean-test/src/main/java/io/ebean/test/config/platform/Config.java index cefae4ce4..117c04a18 100644 --- a/ebean-test/src/main/java/io/ebean/test/config/platform/Config.java +++ b/ebean-test/src/main/java/io/ebean/test/config/platform/Config.java @@ -4,8 +4,8 @@ import io.ebean.config.DatabaseConfig; import io.ebean.datasource.DataSourceConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import java.io.File; +import java.util.Locale; import java.util.Properties; /** @@ -26,11 +26,9 @@ class Config { private final String db; private final String platform; private String dockerPlatform; - private String databaseName; private final Properties properties; - private int port; private String url; @@ -38,12 +36,10 @@ class Config { private String schema; private String username; private String password; - private final DatabaseConfig config; - private boolean containerDropCreate; - private final Properties dockerProperties = new Properties(); + private final DockerHost dockerHost = new DockerHost(); Config(String db, String platform, String databaseName, DatabaseConfig config) { this.db = db; @@ -231,11 +227,17 @@ class Config { void setUrl(String urlPattern) { String val = getPlatformKey("url", urlPattern); + val = val.replace("${host}", host()); val = val.replace("${port}", String.valueOf(port)); val = val.replace("${databaseName}", databaseName); this.url = val; } + String host() { + String explicitDockerHost = getKey("dockerHost", null); + return getKey("host", dockerHost.dockerHost(explicitDockerHost)); + } + /** * Append to the connection URL. */ @@ -322,7 +324,6 @@ class Config { void setDockerVersion(String version) { String val = getPlatformKey("version", version); dockerProperties.setProperty(dockerKey("version"), val); - if (containerDropCreate) { dockerProperties.setProperty(dockerKey("startMode"), "dropCreate"); } @@ -369,7 +370,10 @@ class Config { } private void initDockerProperties() { - + if (dockerHost.runningInDocker()) { + // tell ebean-docker-test we are not using localhost (for jdbc DB setup commands) + dockerProperties.setProperty(dockerKey("host"), dockerHost.dockerHost()); + } dockerProperties.setProperty(dockerKey("port"), String.valueOf(port)); dockerProperties.setProperty(dockerKey("dbName"), databaseName); if (schema != null) { diff --git a/ebean-test/src/main/java/io/ebean/test/config/platform/Db2Setup.java b/ebean-test/src/main/java/io/ebean/test/config/platform/Db2Setup.java index fd65472ef..70fa4fc2c 100644 --- a/ebean-test/src/main/java/io/ebean/test/config/platform/Db2Setup.java +++ b/ebean-test/src/main/java/io/ebean/test/config/platform/Db2Setup.java @@ -11,7 +11,7 @@ class Db2Setup implements PlatformSetup { config.setDefaultPort(50000); config.setUsernameDefault(); config.setPasswordDefault(); - config.setUrl("jdbc:db2://localhost:${port}/${databaseName}"); + config.setUrl("jdbc:db2://${host}:${port}/${databaseName}"); config.setDriver("com.ibm.db2.jcc.DB2Driver"); config.datasourceDefaults(); diff --git a/ebean-test/src/main/java/io/ebean/test/config/platform/DockerHost.java b/ebean-test/src/main/java/io/ebean/test/config/platform/DockerHost.java new file mode 100644 index 000000000..5e3f0ce94 --- /dev/null +++ b/ebean-test/src/main/java/io/ebean/test/config/platform/DockerHost.java @@ -0,0 +1,55 @@ +package io.ebean.test.config.platform; + +import java.io.File; +import java.util.Locale; + +/** + * Helper to detect if running inside docker and determine host name for that case. + */ +class DockerHost { + + private final boolean runningInDocker; + private String dockerHost; + + DockerHost() { + runningInDocker = initInDocker(); + } + + boolean runningInDocker() { + return runningInDocker; + } + + String dockerHost() { + return dockerHost; + } + + String dockerHost(String explicitHost) { + if (!runningInDocker) { + return "localhost"; + } + dockerHost = explicitHost != null ? explicitHost : defaultDockerHost(); + return dockerHost; + } + + /** + * Return true if running inside a docker container (we are using docker in docker). + */ + boolean initInDocker() { + return new File("/.dockerenv").exists(); + } + + /** + * Return the default host name to use when running in docker. + *

+ * Can instead be explicitly specified via ebean.test.dockerHost. + */ + String defaultDockerHost() { + String os = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH); + if (os.contains("mac") || os.contains("darwin") || os.contains("win")) { + return "host.docker.internal"; + } else { + return "172.17.0.1"; + } + } + +} diff --git a/ebean-test/src/main/java/io/ebean/test/config/platform/HanaSetup.java b/ebean-test/src/main/java/io/ebean/test/config/platform/HanaSetup.java index 8ad014b70..a5137686f 100644 --- a/ebean-test/src/main/java/io/ebean/test/config/platform/HanaSetup.java +++ b/ebean-test/src/main/java/io/ebean/test/config/platform/HanaSetup.java @@ -20,7 +20,7 @@ class HanaSetup implements PlatformSetup { config.setUsername("SYSTEM"); config.setPassword("HXEHana1"); config.setDatabaseName("HXE"); - config.setUrl("jdbc:sap://localhost:${port}/?databaseName=${databaseName}"); + config.setUrl("jdbc:sap://${host}:${port}/?databaseName=${databaseName}"); String schema = config.getSchema(); if (schema != null && !schema.equals(config.getUsername())) { config.urlAppend("¤tSchema=" + schema); diff --git a/ebean-test/src/main/java/io/ebean/test/config/platform/MariaDBSetup.java b/ebean-test/src/main/java/io/ebean/test/config/platform/MariaDBSetup.java index 4e242d4b0..f2ff22cc4 100644 --- a/ebean-test/src/main/java/io/ebean/test/config/platform/MariaDBSetup.java +++ b/ebean-test/src/main/java/io/ebean/test/config/platform/MariaDBSetup.java @@ -13,7 +13,7 @@ class MariaDBSetup implements PlatformSetup { config.setDefaultPort(defaultPort); config.setUsernameDefault(); config.setPasswordDefault(); - config.setUrl("jdbc:mariadb://localhost:${port}/${databaseName}?useLegacyDatetimeCode=false"); + config.setUrl("jdbc:mariadb://${host}:${port}/${databaseName}?useLegacyDatetimeCode=false"); config.datasourceDefaults(); return dockerProperties(config); diff --git a/ebean-test/src/main/java/io/ebean/test/config/platform/MySqlSetup.java b/ebean-test/src/main/java/io/ebean/test/config/platform/MySqlSetup.java index 3939ffbec..c865c41b7 100644 --- a/ebean-test/src/main/java/io/ebean/test/config/platform/MySqlSetup.java +++ b/ebean-test/src/main/java/io/ebean/test/config/platform/MySqlSetup.java @@ -13,7 +13,7 @@ class MySqlSetup implements PlatformSetup { config.setDefaultPort(defaultPort); config.setUsernameDefault(); config.setPasswordDefault(); - config.setUrl("jdbc:mysql://localhost:${port}/${databaseName}"); + config.setUrl("jdbc:mysql://${host}:${port}/${databaseName}"); config.setDriver(defaultDriver()); config.datasourceDefaults(); diff --git a/ebean-test/src/main/java/io/ebean/test/config/platform/PostgisSetup.java b/ebean-test/src/main/java/io/ebean/test/config/platform/PostgisSetup.java index 8180dc425..bd5a38387 100644 --- a/ebean-test/src/main/java/io/ebean/test/config/platform/PostgisSetup.java +++ b/ebean-test/src/main/java/io/ebean/test/config/platform/PostgisSetup.java @@ -20,7 +20,7 @@ class PostgisSetup implements PlatformSetup { config.setUsernameDefault(); config.setPasswordDefault(); config.setDriver("org.postgis.DriverWrapperLW"); - config.setUrl("jdbc:postgresql_lwgis://localhost:${port}/${databaseName}"); + config.setUrl("jdbc:postgresql_lwgis://${host}:${port}/${databaseName}"); String schema = config.getSchema(); if (schema != null && !schema.equals(config.getUsername())) { diff --git a/ebean-test/src/main/java/io/ebean/test/config/platform/PostgresSetup.java b/ebean-test/src/main/java/io/ebean/test/config/platform/PostgresSetup.java index 8e9045408..e5d1206f7 100644 --- a/ebean-test/src/main/java/io/ebean/test/config/platform/PostgresSetup.java +++ b/ebean-test/src/main/java/io/ebean/test/config/platform/PostgresSetup.java @@ -13,7 +13,7 @@ class PostgresSetup implements PlatformSetup { config.setDefaultPort(defaultPort); config.setUsernameDefault(); config.setPasswordDefault(); - config.setUrl("jdbc:postgresql://localhost:${port}/${databaseName}"); + config.setUrl("jdbc:postgresql://${host}:${port}/${databaseName}"); String schema = config.getSchema(); if (schema != null && !schema.equals(config.getUsername())) { @@ -44,7 +44,7 @@ class PostgresSetup implements PlatformSetup { config.setDefaultPort(defaultPort); config.setExtraUsernameDefault(); config.setExtraDbPasswordDefault(); - config.setUrl("jdbc:postgresql://localhost:${port}/${databaseName}"); + config.setUrl("jdbc:postgresql://${host}:${port}/${databaseName}"); config.setDriver("org.postgresql.Driver"); config.extraDatasourceDefaults(); } diff --git a/ebean-test/src/main/java/io/ebean/test/config/platform/RedisSetup.java b/ebean-test/src/main/java/io/ebean/test/config/platform/RedisSetup.java index 70580bd81..c8fc01671 100644 --- a/ebean-test/src/main/java/io/ebean/test/config/platform/RedisSetup.java +++ b/ebean-test/src/main/java/io/ebean/test/config/platform/RedisSetup.java @@ -8,10 +8,14 @@ import java.util.Properties; class RedisSetup { static void run(Properties properties) { - String version = properties.getProperty("ebean.test.redis"); version = properties.getProperty("ebean.test.redis.version", version); if (version != null) { + DockerHost dockerHost = new DockerHost(); + if (dockerHost.runningInDocker()) { + String host = dockerHost.dockerHost(properties.getProperty("ebean.test.dockerHost")); + properties.setProperty("redis.host", host); + } RedisConfig redisConfig = new RedisConfig(version, properties); RedisContainer container = new RedisContainer(redisConfig); container.start(); diff --git a/ebean-test/src/main/java/io/ebean/test/config/platform/SqlServerSetup.java b/ebean-test/src/main/java/io/ebean/test/config/platform/SqlServerSetup.java index 2dc68b5d7..3428fb43e 100644 --- a/ebean-test/src/main/java/io/ebean/test/config/platform/SqlServerSetup.java +++ b/ebean-test/src/main/java/io/ebean/test/config/platform/SqlServerSetup.java @@ -13,7 +13,7 @@ class SqlServerSetup implements PlatformSetup { config.setDefaultPort(1433); config.setUsernameDefault(); config.setPassword("SqlS3rv#r"); - config.setUrl("jdbc:sqlserver://localhost:${port};databaseName=${databaseName}"); + config.setUrl("jdbc:sqlserver://${host}:${port};databaseName=${databaseName};sendTimeAsDateTime=false"); config.setDriver("com.microsoft.sqlserver.jdbc.SQLServerDriver"); config.datasourceDefaults(); diff --git a/ebean-test/src/test/java/io/ebean/config/DatabaseConfigTest.java b/ebean-test/src/test/java/io/ebean/config/DatabaseConfigTest.java index 143e3b19e..7ef7cc0f7 100644 --- a/ebean-test/src/test/java/io/ebean/config/DatabaseConfigTest.java +++ b/ebean-test/src/test/java/io/ebean/config/DatabaseConfigTest.java @@ -27,16 +27,17 @@ public class DatabaseConfigTest { @Test public void evalPropertiesInput() { - String home = System.getenv("HOME"); + String home = System.getProperty("user.home"); + String fileSeparator = System.getProperty("file.separator"); Properties props = new Properties(); - props.setProperty("ddl.initSql", "${HOME}/initSql"); + props.setProperty("ddl.initSql", "${user.home}" + fileSeparator + "initSql"); DatabaseConfig config = new DatabaseConfig(); config.loadFromProperties(props); String ddlInitSql = config.getDdlInitSql(); - assertThat(ddlInitSql).isEqualTo(home+"/initSql"); + assertThat(ddlInitSql).isEqualTo(home + fileSeparator + "initSql"); } @Test diff --git a/ebean-test/src/test/java/io/ebean/config/PropertiesWrapperTest.java b/ebean-test/src/test/java/io/ebean/config/PropertiesWrapperTest.java index 98a64ad0b..5301d0173 100644 --- a/ebean-test/src/test/java/io/ebean/config/PropertiesWrapperTest.java +++ b/ebean-test/src/test/java/io/ebean/config/PropertiesWrapperTest.java @@ -50,30 +50,31 @@ public class PropertiesWrapperTest { @Test public void testGetProperties() { - String home = System.getenv("HOME"); + String home = System.getProperty("user.home"); String tmpDir = System.getProperty("java.io.tmpdir"); + String fileSeparator = System.getProperty("file.separator"); Properties properties = new Properties(); properties.put("someBasic", "hello"); properties.put("someInt", "42"); properties.put("someDouble", "5.5"); - properties.put("somePath", "${HOME}/hello"); - properties.put("someSystemProp", "/aaa/${java.io.tmpdir}/bbb"); + properties.put("somePath", "${user.home}" + fileSeparator + "hello"); + properties.put("someSystemProp", fileSeparator + "aaa" + fileSeparator + "${java.io.tmpdir}" + fileSeparator + "bbb"); Properties evalCopy = Config.asConfiguration().eval(properties); PropertiesWrapper pw = new PropertiesWrapper("pref", "myserver", evalCopy, null); assertEquals(42, pw.getInt("someInt", 99)); assertEquals(Double.valueOf(5.5D), (Double.valueOf(pw.getDouble("someDouble", 99.9D)))); - assertEquals(home + "/hello", pw.get("somePath", null)); - assertEquals("/aaa/" + tmpDir + "/bbb", pw.get("someSystemProp")); + assertEquals(home + fileSeparator + "hello", pw.get("somePath", null)); + assertEquals(fileSeparator + "aaa" + fileSeparator + tmpDir + fileSeparator + "bbb", pw.get("someSystemProp")); pw = new PropertiesWrapper(evalCopy, null); assertEquals(42, pw.getInt("someInt", 99)); assertEquals(Double.valueOf(5.5D), (Double.valueOf(pw.getDouble("someDouble", 99.9D)))); - assertEquals(home + "/hello", pw.get("somePath", null)); - assertEquals("/aaa/" + tmpDir + "/bbb", pw.get("someSystemProp")); + assertEquals(home + fileSeparator + "hello", pw.get("somePath", null)); + assertEquals(fileSeparator + "aaa" + fileSeparator + tmpDir + fileSeparator + "bbb", pw.get("someSystemProp")); } } diff --git a/ebean-test/src/test/java/io/ebean/config/dbplatform/OraclePlatformTest.java b/ebean-test/src/test/java/io/ebean/config/dbplatform/OraclePlatformTest.java index cf6b513ee..47d8fa0dd 100644 --- a/ebean-test/src/test/java/io/ebean/config/dbplatform/OraclePlatformTest.java +++ b/ebean-test/src/test/java/io/ebean/config/dbplatform/OraclePlatformTest.java @@ -2,28 +2,34 @@ package io.ebean.config.dbplatform; import io.ebean.config.PlatformConfig; import io.ebean.config.dbplatform.oracle.Oracle11Platform; +import io.ebean.config.dbplatform.oracle.Oracle12Platform; import io.ebean.config.dbplatform.oracle.OraclePlatform; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -public class OraclePlatformTest { +class OraclePlatformTest { @Test - public void columnAliasPrefix_Oracle11Platform() { + void columnAliasPrefix_Oracle11Platform() { Oracle11Platform platform11 = new Oracle11Platform(); assertThat(platform11.columnAliasPrefix).isEqualTo("c"); } @Test - public void columnAliasPrefix_OraclePlatform() { - OraclePlatform platform = new OraclePlatform(); - assertThat(platform.columnAliasPrefix).isEqualTo("c"); + void columnAliasPrefix_Oracle12Platform() { + Oracle12Platform platform12 = new Oracle12Platform(); + assertThat(platform12.columnAliasPrefix).isEqualTo("c"); } @Test - public void uuid_default() { + void columnAliasPrefix_OraclePlatform() { + OraclePlatform platform = new OraclePlatform(); + assertThat(platform.columnAliasPrefix).isNull(); + } + @Test + void uuid_default() { OraclePlatform platform = new OraclePlatform(); platform.configure(new PlatformConfig(), false); DbPlatformType dbType = platform.getDbTypeMap().get(DbPlatformType.UUID); @@ -31,8 +37,7 @@ public class OraclePlatformTest { } @Test - public void uuid_as_binary() { - + void uuid_as_binary() { OraclePlatform platform = new OraclePlatform(); PlatformConfig config = new PlatformConfig(); config.setDbUuid(PlatformConfig.DbUuid.AUTO_BINARY); diff --git a/ebean-test/src/test/java/io/ebean/json/EJsonTests.java b/ebean-test/src/test/java/io/ebean/json/EJsonTests.java index 929c6582c..1063e4d36 100644 --- a/ebean-test/src/test/java/io/ebean/json/EJsonTests.java +++ b/ebean-test/src/test/java/io/ebean/json/EJsonTests.java @@ -4,24 +4,22 @@ import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; import io.ebean.ModifyAwareType; import io.ebean.text.json.EJson; +import io.ebean.util.IOUtils; import io.ebeaninternal.json.ModifyAwareMap; import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.io.File; -import java.io.FileWriter; import java.io.IOException; import java.io.StringReader; +import java.io.Writer; import java.nio.file.Files; import java.util.*; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.*; public class EJsonTests { - private static final Logger log = LoggerFactory.getLogger(EJsonTests.class); - @Test public void test_map_simple() throws IOException { @@ -46,15 +44,13 @@ public class EJsonTests { public void write_withWriter_expect_writerNotClosed() throws IOException { File temp = Files.createTempFile("some", ".json").toFile(); - FileWriter writer = new FileWriter(temp); - Map map = new LinkedHashMap<>(); - map.put("foo", "bar"); - EJson.write(map, writer); - writer.write("The end."); - writer.flush(); - writer.close(); - - log.info("write to file {}", temp.getAbsolutePath()); + try (Writer writer = IOUtils.newWriter(temp)) { + Map map = new LinkedHashMap<>(); + map.put("foo", "bar"); + EJson.write(map, writer); + writer.write("The end."); + } + assertThat(temp).hasContent("{\"foo\":\"bar\"}The end."); } @Test diff --git a/ebean-test/src/test/java/io/ebean/test/config/platform/DockerHostTest.java b/ebean-test/src/test/java/io/ebean/test/config/platform/DockerHostTest.java new file mode 100644 index 000000000..0be8a5d43 --- /dev/null +++ b/ebean-test/src/test/java/io/ebean/test/config/platform/DockerHostTest.java @@ -0,0 +1,87 @@ +package io.ebean.test.config.platform; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class DockerHostTest { + + @Test + void runningInDocker_when_false_alwaysUseLocalhost() { + DockerHost dockerHost = new DockerHost(); + assertFalse(dockerHost.runningInDocker()); + assertEquals("localhost", dockerHost.dockerHost("foo")); + } + + @Test + void runningInDocker_when_true_useExplicit() { + TDDockerHost dockerHost = new TDDockerHost(); + assertTrue(dockerHost.runningInDocker()); + + assertEquals("my-host", dockerHost.dockerHost("my-host")); + } + + @Disabled + @Test + void runningInDocker_when_trueAndLinux_useDefault() { + TDDockerHost dockerHost = new TDDockerHost(); + assertTrue(dockerHost.runningInDocker()); + + assertEquals("172.17.0.1", dockerHost.dockerHost(null)); + } + + @Test + void runningInDocker_when_windowsDefault() { + TDDockerHost dockerHost = new TDDockerHost(); + assertTrue(dockerHost.runningInDocker()); + + String origName = System.getProperty("os.name"); + System.setProperty("os.name", "win"); + try { + assertEquals("host.docker.internal",dockerHost.defaultDockerHost()); + assertEquals("host.docker.internal", dockerHost.dockerHost(null)); + } finally { + System.setProperty("os.name", origName); + } + } + + @Test + void runningInDocker_when_macDefault() { + TDDockerHost dockerHost = new TDDockerHost(); + assertTrue(dockerHost.runningInDocker()); + + String origName = System.getProperty("os.name"); + System.setProperty("os.name", "mac"); + try { + assertEquals("host.docker.internal",dockerHost.defaultDockerHost()); + assertEquals("host.docker.internal", dockerHost.dockerHost(null)); + } finally { + System.setProperty("os.name", origName); + } + } + + + @Test + void runningInDocker_when_linuxDefault() { + TDDockerHost dockerHost = new TDDockerHost(); + assertTrue(dockerHost.runningInDocker()); + + String origName = System.getProperty("os.name"); + System.setProperty("os.name", "linux"); + try { + assertEquals("172.17.0.1",dockerHost.defaultDockerHost()); + assertEquals("172.17.0.1", dockerHost.dockerHost(null)); + } finally { + System.setProperty("os.name", origName); + } + } + + static class TDDockerHost extends DockerHost { + + @Override + boolean initInDocker() { + return true; + } + } +} diff --git a/ebean-test/src/test/java/io/ebeaninternal/server/cache/CachedBeanDataSerializeTest.java b/ebean-test/src/test/java/io/ebeaninternal/server/cache/CachedBeanDataSerializeTest.java index 53bf0c21d..08988cfce 100644 --- a/ebean-test/src/test/java/io/ebeaninternal/server/cache/CachedBeanDataSerializeTest.java +++ b/ebean-test/src/test/java/io/ebeaninternal/server/cache/CachedBeanDataSerializeTest.java @@ -11,6 +11,7 @@ import org.tests.model.basic.ResetBasicData; import org.tests.model.basic.TBytesOnly; import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; @@ -94,7 +95,7 @@ public class CachedBeanDataSerializeTest extends BaseTestCase { TBytesOnly bean = new TBytesOnly(); bean.setId(42); - bean.setContent(stringContent.getBytes("UTF-8")); + bean.setContent(stringContent.getBytes(StandardCharsets.UTF_8)); BeanDescriptor desc = getBeanDescriptor(TBytesOnly.class); CachedBeanData extract = CachedBeanDataFromBean.extract(desc, (EntityBean) bean); diff --git a/ebean-test/src/test/java/io/ebeaninternal/server/type/TestSimpleEncryptor.java b/ebean-test/src/test/java/io/ebeaninternal/server/type/TestSimpleEncryptor.java index 7ea6d2b3c..74c4fcd7a 100644 --- a/ebean-test/src/test/java/io/ebeaninternal/server/type/TestSimpleEncryptor.java +++ b/ebean-test/src/test/java/io/ebeaninternal/server/type/TestSimpleEncryptor.java @@ -5,6 +5,7 @@ import io.ebean.config.EncryptKey; import org.junit.jupiter.api.Test; import org.tests.basic.encrypt.BasicEncryptKey; +import java.nio.charset.StandardCharsets; import java.sql.Timestamp; import static org.assertj.core.api.Assertions.assertThat; @@ -19,7 +20,7 @@ public class TestSimpleEncryptor extends BaseTestCase { EncryptKey key = new BasicEncryptKey("hello"); - byte[] data = "test123".getBytes(); + byte[] data = "test123".getBytes(StandardCharsets.UTF_8); byte[] ecData = e.encrypt(data, key); diff --git a/ebean-test/src/test/java/io/ebeaninternal/server/type/TestTypeManager.java b/ebean-test/src/test/java/io/ebeaninternal/server/type/TestTypeManager.java index c28da92a7..383671363 100644 --- a/ebean-test/src/test/java/io/ebeaninternal/server/type/TestTypeManager.java +++ b/ebean-test/src/test/java/io/ebeaninternal/server/type/TestTypeManager.java @@ -15,6 +15,8 @@ import org.tests.model.ivo.converter.MoneyTypeConverter; import javax.persistence.EnumType; import java.sql.SQLException; import java.sql.Types; +import java.util.Calendar; +import java.util.GregorianCalendar; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; @@ -29,7 +31,6 @@ public class TestTypeManager extends BaseTestCase { DefaultTypeManager typeManager = createTypeManager(); ScalarType type = typeManager.createEnumScalarType(MyEnum.class, null); - typeManager.addEnumType(type, MyEnum.class); Object val = type.read(new DummyDataReader("A")); assertThat(val).isEqualTo(MyEnum.Aval); @@ -131,6 +132,13 @@ public class TestTypeManager extends BaseTestCase { return new DefaultTypeManager(config, bootupClasses); } + @Test + public void testCalendar() throws SQLException { + + DefaultTypeManager typeManager = createTypeManager(); + ScalarType typeB = typeManager.getScalarType(GregorianCalendar.class); + assertThat(typeB).isInstanceOf(ScalarTypeCalendar.class); + } /** * Test double DataReader implementation. */ diff --git a/ebean-test/src/test/java/org/tests/basic/TestDeleteImportedPartial.java b/ebean-test/src/test/java/org/tests/basic/TestDeleteImportedPartial.java index 16ba29c32..4729929c9 100644 --- a/ebean-test/src/test/java/org/tests/basic/TestDeleteImportedPartial.java +++ b/ebean-test/src/test/java/org/tests/basic/TestDeleteImportedPartial.java @@ -8,12 +8,14 @@ import org.tests.model.basic.PFileContent; import static org.junit.jupiter.api.Assertions.assertNull; +import java.nio.charset.StandardCharsets; + public class TestDeleteImportedPartial extends BaseTestCase { @Test public void test() { - PFile persistentFile = new PFile("test.txt", new PFileContent("test".getBytes())); + PFile persistentFile = new PFile("test.txt", new PFileContent("test".getBytes(StandardCharsets.UTF_8))); DB.save(persistentFile); Integer id = persistentFile.getId(); diff --git a/ebean-test/src/test/java/org/tests/basic/TestDeleteOneToOne.java b/ebean-test/src/test/java/org/tests/basic/TestDeleteOneToOne.java index dfe431246..98b837db2 100644 --- a/ebean-test/src/test/java/org/tests/basic/TestDeleteOneToOne.java +++ b/ebean-test/src/test/java/org/tests/basic/TestDeleteOneToOne.java @@ -8,13 +8,15 @@ import org.tests.model.basic.PersistentFileContent; import static org.junit.jupiter.api.Assertions.assertNull; +import java.nio.charset.StandardCharsets; + public class TestDeleteOneToOne extends BaseTestCase { @Test public void testCreateDeletePersistentFile() { PersistentFile persistentFile = new PersistentFile("test.txt", new PersistentFileContent( - "test".getBytes())); + "test".getBytes(StandardCharsets.UTF_8))); DB.save(persistentFile); Integer id = persistentFile.getId(); diff --git a/ebean-test/src/test/java/org/tests/basic/TestDeleteOneToOneMultiple.java b/ebean-test/src/test/java/org/tests/basic/TestDeleteOneToOneMultiple.java index 433fb270c..af51fdec5 100644 --- a/ebean-test/src/test/java/org/tests/basic/TestDeleteOneToOneMultiple.java +++ b/ebean-test/src/test/java/org/tests/basic/TestDeleteOneToOneMultiple.java @@ -8,16 +8,18 @@ import org.tests.model.basic.PFileContent; import static org.junit.jupiter.api.Assertions.assertNull; +import java.nio.charset.StandardCharsets; + public class TestDeleteOneToOneMultiple extends BaseTestCase { @Test public void testCreateDeletePersistentFile() { - PFile persistentFile = new PFile("test.txt", new PFileContent("test".getBytes())); + PFile persistentFile = new PFile("test.txt", new PFileContent("test".getBytes(StandardCharsets.UTF_8))); // PFile persistentFile = new PFile(); // persistentFile.setName("test.txt"); // PFileContent content = new PFileContent(); -// content.setContent("test".getBytes()); +// content.setContent("test".getBytes(StandardCharsets.UTF_8)); // persistentFile.setFileContent(content); DB.save(persistentFile); diff --git a/ebean-test/src/test/java/org/tests/basic/TestSaveDeleteOneToOne.java b/ebean-test/src/test/java/org/tests/basic/TestSaveDeleteOneToOne.java index 75208a534..f17c9862a 100644 --- a/ebean-test/src/test/java/org/tests/basic/TestSaveDeleteOneToOne.java +++ b/ebean-test/src/test/java/org/tests/basic/TestSaveDeleteOneToOne.java @@ -8,12 +8,14 @@ import org.tests.model.basic.PersistentFileContent; import static org.junit.jupiter.api.Assertions.assertNotNull; +import java.nio.charset.StandardCharsets; + public class TestSaveDeleteOneToOne extends BaseTestCase { @Test public void testCreateDeletePersistentFile() { PersistentFile persistentFile = new PersistentFile("test.txt", - new PersistentFileContent("test".getBytes())); + new PersistentFileContent("test".getBytes(StandardCharsets.UTF_8))); DB.save(persistentFile); DB.delete(persistentFile); @@ -22,7 +24,7 @@ public class TestSaveDeleteOneToOne extends BaseTestCase { @Test public void testCreateLoadDeletePersistentFile() { PersistentFile persistentFile = new PersistentFile("test.txt", - new PersistentFileContent("test".getBytes())); + new PersistentFileContent("test".getBytes(StandardCharsets.UTF_8))); DB.save(persistentFile); diff --git a/ebean-test/src/test/java/org/tests/basic/TestSaveDeleteOneToOneMultiple.java b/ebean-test/src/test/java/org/tests/basic/TestSaveDeleteOneToOneMultiple.java index e7e23283d..b357445e5 100644 --- a/ebean-test/src/test/java/org/tests/basic/TestSaveDeleteOneToOneMultiple.java +++ b/ebean-test/src/test/java/org/tests/basic/TestSaveDeleteOneToOneMultiple.java @@ -8,11 +8,13 @@ import org.tests.model.basic.PFileContent; import static org.junit.jupiter.api.Assertions.assertNotNull; +import java.nio.charset.StandardCharsets; + public class TestSaveDeleteOneToOneMultiple extends BaseTestCase { // public void testCreateDeletePFile() { // PFile persistentFile = new PFile("test.txt", -// new PFileContent("test".getBytes())); +// new PFileContent("test".getBytes(StandardCharsets.UTF_8))); // // DB.save(persistentFile); // DB.delete(persistentFile); @@ -21,7 +23,7 @@ public class TestSaveDeleteOneToOneMultiple extends BaseTestCase { @Test public void testCreateLoadDeletePFile() { PFile persistentFile = new PFile("test.txt", - new PFileContent("test".getBytes())); + new PFileContent("test".getBytes(StandardCharsets.UTF_8))); DB.save(persistentFile); diff --git a/ebean-test/src/test/java/org/tests/basic/event/TestPreInsertValidation.java b/ebean-test/src/test/java/org/tests/basic/event/TestPreInsertValidation.java index c67e63c4a..8b86b1f87 100644 --- a/ebean-test/src/test/java/org/tests/basic/event/TestPreInsertValidation.java +++ b/ebean-test/src/test/java/org/tests/basic/event/TestPreInsertValidation.java @@ -6,6 +6,7 @@ import io.ebean.DB; import org.junit.jupiter.api.Test; import org.tests.model.basic.TWithPreInsert; import org.tests.model.basic.TWithPreInsertChild; +import org.tests.model.basic.event.TWithPreInsertPersistAdapter; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -27,6 +28,7 @@ class TestPreInsertValidation extends BaseTestCase { assertThat(e.requestCascadeState()).isEqualTo(2); TWithPreInsert e1 = DB.find(TWithPreInsert.class, e.getId()); + assert e1 != null; e1.setTitle("Missus"); DB.save(e1); @@ -54,11 +56,21 @@ class TestPreInsertValidation extends BaseTestCase { assert e1 != null; e1.setTitle("ParentCascading-changed"); - e1.children().get(0).setName("Child0-changed"); + TWithPreInsertChild childBean = e1.children().get(0); + childBean.setName("Child0-changed"); DB.save(e1); assertThat(e1.requestCascadeState()).isEqualTo(12); - assertThat(e1.children().get(0).requestCascadeState()).isEqualTo(11); + assertThat(childBean.requestCascadeState()).isEqualTo(11); + + DB.delete(e1); + + assertThat(e1.requestCascadeState()).isEqualTo(22); + + // assert that isCascade() was true for the child bean + assertThat(TWithPreInsertPersistAdapter.cascadeDelete).hasSize(1); + String deleteCascade = TWithPreInsertPersistAdapter.cascadeDelete.get(0); + assertThat(deleteCascade).isEqualTo("class org.tests.model.basic.TWithPreInsertChild:1"); } @Test diff --git a/ebean-test/src/test/java/org/tests/defaultvalues/TestDefaults.java b/ebean-test/src/test/java/org/tests/defaultvalues/TestDefaults.java index 828fc2b35..170584d5b 100644 --- a/ebean-test/src/test/java/org/tests/defaultvalues/TestDefaults.java +++ b/ebean-test/src/test/java/org/tests/defaultvalues/TestDefaults.java @@ -26,7 +26,7 @@ public class TestDefaults extends BaseTestCase { final List current = LoggedSql.collect(); assertThat(current).isNotEmpty(); - if (isMySql() || isMariaDB()) { + if (isMySql() || isMariaDB() || isOracle()) { assertThat(current.get(0)).contains("insert into defaults_model_draft values (default);"); } else if (isSqlServer()) { assertThat(current.get(0)).contains("insert into defaults_model_draft (id) values (?)"); diff --git a/ebean-test/src/test/java/org/tests/enhancement/TestConstructorPutfieldReplacement.java b/ebean-test/src/test/java/org/tests/enhancement/TestConstructorPutfieldReplacement.java index 3a945c8e7..0b82b7c33 100644 --- a/ebean-test/src/test/java/org/tests/enhancement/TestConstructorPutfieldReplacement.java +++ b/ebean-test/src/test/java/org/tests/enhancement/TestConstructorPutfieldReplacement.java @@ -9,12 +9,14 @@ import org.tests.model.basic.PFileContent; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.nio.charset.StandardCharsets; + public class TestConstructorPutfieldReplacement extends BaseTestCase { @Test public void test() { - PFile persistentFile = new PFile("test.txt", new PFileContent("test".getBytes())); + PFile persistentFile = new PFile("test.txt", new PFileContent("test".getBytes(StandardCharsets.UTF_8))); EntityBean eb = (EntityBean) persistentFile; EntityBeanIntercept ebi = eb._ebean_getIntercept(); diff --git a/ebean-test/src/test/java/org/tests/idkeys/TestGeneratedKeys.java b/ebean-test/src/test/java/org/tests/idkeys/TestGeneratedKeys.java index 56245930c..8f57569a0 100644 --- a/ebean-test/src/test/java/org/tests/idkeys/TestGeneratedKeys.java +++ b/ebean-test/src/test/java/org/tests/idkeys/TestGeneratedKeys.java @@ -14,6 +14,8 @@ import org.tests.idkeys.db.GenKeySeqB; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -24,6 +26,7 @@ public class TestGeneratedKeys extends BaseTestCase { @ForPlatform(Platform.H2) // readSequenceValue is H2 specific public void testGenKeySeqA() throws SQLException { assumeTrue(idType() == IdType.SEQUENCE); + SpiEbeanServer server = spiEbeanServer(); try (Transaction tx = server.beginTransaction()) { @@ -69,20 +72,34 @@ public class TestGeneratedKeys extends BaseTestCase { } private long readSequenceValue(Transaction tx, String sequence) throws SQLException { - Statement stm = null; - try { - stm = tx.connection().createStatement(); - ResultSet rs = stm.executeQuery("select currval('" + sequence + "')"); - rs.next(); + String sql; + switch (spiEbeanServer().databasePlatform().getPlatform().base()) { + case H2 : + sql = "select currval('" + sequence + "')"; + break; + case DB2 : + sql = "values previous value for " + sequence; + + break; + case SQLSERVER : + sql = "select current_value from sys.sequences where name = '" + sequence + "'"; + break; + + case MARIADB : + throw new UnsupportedOperationException("reading sequence value outside of the current connection is not supported. " + + "See https://mariadb.com/kb/en/previous-value-for-sequence_name/#description"); + + default : + throw new UnsupportedOperationException("reading sequence value from " + + spiEbeanServer().databasePlatform().getPlatform() + + " is not supported."); + + } + try (Statement stm = tx.connection().createStatement()) { + ResultSet rs = stm.executeQuery(sql); + rs.next(); return rs.getLong(1); - } finally { - if (stm != null) { - try { - stm.close(); - } catch (SQLException e) { - } - } } } @@ -109,5 +126,25 @@ public class TestGeneratedKeys extends BaseTestCase { assertNotNull(al.getId()); } } + + @Test + @ForPlatform({Platform.H2, Platform.MARIADB, Platform.SQLSERVER, Platform.DB2}) + public void testGeneratedKeys() throws SQLException { + assumeTrue(idType() == IdType.SEQUENCE); + + SpiEbeanServer server = spiEbeanServer(); + List idList = new ArrayList<>(52); + + try (Transaction tx = server.beginTransaction()) { + // bigger than increment + for (int i = 1; i < 52; i++) { + GenKeySeqA gks = new GenKeySeqA(); + gks.setDescription("my description " + i); + server.save(gks); + assertFalse(idList.contains(gks.getId())); + idList.add(gks.getId()); + } + } + } } diff --git a/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsert.java b/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsert.java index 21398bc7a..acfffce20 100644 --- a/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsert.java +++ b/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsert.java @@ -7,7 +7,7 @@ import javax.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; -import static javax.persistence.CascadeType.PERSIST; +import static javax.persistence.CascadeType.ALL; @Entity public class TWithPreInsert implements TWithPreInsertCommon { @@ -20,7 +20,7 @@ public class TWithPreInsert implements TWithPreInsertCommon { private String title; - @OneToMany(cascade = PERSIST) + @OneToMany(cascade = ALL) private List children = new ArrayList<>(); /** diff --git a/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsertCommon.java b/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsertCommon.java index dc61e28ba..632c7ade9 100644 --- a/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsertCommon.java +++ b/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsertCommon.java @@ -2,6 +2,8 @@ package org.tests.model.basic; public interface TWithPreInsertCommon { + Integer getId(); + String getName(); void setName(String name); diff --git a/ebean-test/src/test/java/org/tests/model/basic/event/TWithPreInsertPersistAdapter.java b/ebean-test/src/test/java/org/tests/model/basic/event/TWithPreInsertPersistAdapter.java index 03143d385..ffc87e00a 100644 --- a/ebean-test/src/test/java/org/tests/model/basic/event/TWithPreInsertPersistAdapter.java +++ b/ebean-test/src/test/java/org/tests/model/basic/event/TWithPreInsertPersistAdapter.java @@ -4,8 +4,13 @@ import io.ebean.event.BeanPersistAdapter; import io.ebean.event.BeanPersistRequest; import org.tests.model.basic.TWithPreInsertCommon; +import java.util.ArrayList; +import java.util.List; + public class TWithPreInsertPersistAdapter extends BeanPersistAdapter { + public static List cascadeDelete = new ArrayList<>(); + @Override public boolean isRegisterFor(Class cls) { return TWithPreInsertCommon.class.isAssignableFrom(cls); @@ -32,4 +37,14 @@ public class TWithPreInsertPersistAdapter extends BeanPersistAdapter { return super.preUpdate(request); } + @Override + public boolean preDelete(BeanPersistRequest request) { + TWithPreInsertCommon bean = (TWithPreInsertCommon) request.bean(); + if (request.isCascade()) { + cascadeDelete.add(bean.getClass() + ":" + bean.getId()); + } else { + bean.requestCascadeState(22); + } + return true; + } } diff --git a/ebean-test/src/test/java/org/tests/model/elementcollection/EcblPerson2.java b/ebean-test/src/test/java/org/tests/model/elementcollection/EcblPerson2.java new file mode 100644 index 000000000..7ce82c393 --- /dev/null +++ b/ebean-test/src/test/java/org/tests/model/elementcollection/EcblPerson2.java @@ -0,0 +1,61 @@ +package org.tests.model.elementcollection; + +import io.ebean.annotation.Cache; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; + +@Cache +@Entity +public class EcblPerson2 { + + @Id + long id; + + String name; + + @ElementCollection + @CollectionTable(joinColumns = @JoinColumn(name = "person_id")) + List phoneNumbers = new ArrayList<>(); + + @Version + long version; + + @Override + public String toString() { + return "person id:" + id + " name:" + name + " phs:" + phoneNumbers; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getPhoneNumbers() { + return phoneNumbers; + } + + public void setPhoneNumbers(List phoneNumbers) { + this.phoneNumbers = phoneNumbers; + } + + public long getVersion() { + return version; + } + + public void setVersion(long version) { + this.version = version; + } +} diff --git a/ebean-test/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache.java b/ebean-test/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache.java index f3ec4b24b..80e2d9bd1 100644 --- a/ebean-test/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache.java +++ b/ebean-test/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache.java @@ -9,10 +9,10 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThat; -public class TestElementCollectionEmbeddedListCache extends BaseTestCase { +class TestElementCollectionEmbeddedListCache extends BaseTestCase { @Test - public void test() { + void test() { EcblPerson person = new EcblPerson("CacheL"); person.getPhoneNumbers().add(new EcPhone("64", "021","1234")); @@ -86,12 +86,10 @@ public class TestElementCollectionEmbeddedListCache extends BaseTestCase { assertThat(four.getPhoneNumbers().toString()).contains("61-07-11"); assertThat(four.getPhoneNumbers()).hasSize(1); - DB.delete(four); sql = LoggedSql.collect(); assertThat(sql).hasSize(2); - LoggedSql.stop(); } } diff --git a/ebean-test/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache2.java b/ebean-test/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache2.java new file mode 100644 index 000000000..1a361c33b --- /dev/null +++ b/ebean-test/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache2.java @@ -0,0 +1,99 @@ +package org.tests.model.elementcollection; + +import io.ebean.BaseTestCase; +import io.ebean.DB; +import io.ebean.test.LoggedSql; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class TestElementCollectionEmbeddedListCache2 extends BaseTestCase { + + @Test + void test() { + + EcblPerson2 person = new EcblPerson2(); + person.setName("CacheL"); + person.getPhoneNumbers().add(new EcPhone("64", "021","1234")); + person.getPhoneNumbers().add(new EcPhone("64","021","4321")); + DB.save(person); + + EcblPerson2 one = DB.find(EcblPerson2.class) + .setId(person.getId()) + .fetch("phoneNumbers") + .findOne(); + + LoggedSql.start(); + + one.getPhoneNumbers().size(); + + List sql = LoggedSql.collect(); + assertThat(sql).isEmpty(); + + EcblPerson2 two = DB.find(EcblPerson2.class ) + .setId(person.getId()) + .findOne(); + + two.getPhoneNumbers().size(); + assertThat(two.getPhoneNumbers().toString()).contains("64-021-1234", "64-021-4321"); + + sql = LoggedSql.collect(); + assertThat(sql).isEmpty(); // cache hit + + two.getPhoneNumbers().add(new EcPhone("61", "07", "11")); + two.getPhoneNumbers().remove(1); + + DB.save(two); + + sql = LoggedSql.collect(); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(5); // update of collection only + assertSql(sql.get(0)).contains("delete from ecbl_person2_phone_numbers where person_id=?"); + assertSqlBind(sql.get(1)); + assertSql(sql.get(2)).contains("insert into ecbl_person2_phone_numbers (person_id,country_code,area,phnum) values (?,?,?,?)"); + assertSqlBind(sql, 3, 4); + } else { + assertThat(sql).hasSize(3); // update of collection only + assertSql(sql.get(0)).contains("delete from ecbl_person2_phone_numbers where person_id=?"); + assertSql(sql.get(1)).contains("insert into ecbl_person2_phone_numbers (person_id,country_code,area,phnum) values (?,?,?,?)"); + assertSql(sql.get(2)).contains("insert into ecbl_person2_phone_numbers (person_id,country_code,area,phnum) values (?,?,?,?)"); + } + + EcblPerson2 three = DB.find(EcblPerson2.class ) + .setId(person.getId()) + .findOne(); + + assertThat(three.getPhoneNumbers().toString()).contains("61-07-11", "64-021-1234"); + assertThat(three.getPhoneNumbers()).hasSize(2); + + sql = LoggedSql.collect(); + assertThat(sql).isEmpty(); // cache hit + + + three.setName("mod-3"); + three.getPhoneNumbers().remove(0); + + DB.save(three); + + sql = LoggedSql.collect(); + assertThat(sql).hasSize(5); + + EcblPerson2 four = DB.find(EcblPerson2.class ) + .setId(person.getId()) + .findOne(); + + assertThat(four.getPhoneNumbers().toString()).contains("61-07-11"); + assertThat(four.getPhoneNumbers()).hasSize(1); + + + DB.delete(four); + sql = LoggedSql.collect(); + assertThat(sql).hasSize(2); + + + LoggedSql.stop(); + } +} diff --git a/ebean-test/src/test/java/org/tests/model/embedded/EmbArrayMaster.java b/ebean-test/src/test/java/org/tests/model/embedded/EmbArrayMaster.java new file mode 100644 index 000000000..09ff8312f --- /dev/null +++ b/ebean-test/src/test/java/org/tests/model/embedded/EmbArrayMaster.java @@ -0,0 +1,31 @@ +package org.tests.model.embedded; + +import io.ebean.annotation.DbArray; + +import javax.persistence.*; +import java.util.List; + +@Entity +public class EmbArrayMaster { + + @Embeddable + public static class EmbArrayDetail { + @DbArray + List vals; + + public EmbArrayDetail(List vals) { + this.vals = vals; + } + } + + @Id + int id; + + @ElementCollection + @CollectionTable(name = "test_array_detail", joinColumns = {@JoinColumn(name = "master_id")}) + List details; + + public EmbArrayMaster(List details) { + this.details = details; + } +} diff --git a/ebean-test/src/test/java/org/tests/model/embedded/TestEmbeddedDbArray.java b/ebean-test/src/test/java/org/tests/model/embedded/TestEmbeddedDbArray.java new file mode 100644 index 000000000..6bf121032 --- /dev/null +++ b/ebean-test/src/test/java/org/tests/model/embedded/TestEmbeddedDbArray.java @@ -0,0 +1,21 @@ +package org.tests.model.embedded; + +import io.ebean.DB; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; + +class TestEmbeddedDbArray { + + /** + * Failing test case for #2477 + */ + @Disabled + @Test + void testArrayInsert() { + EmbArrayMaster t = new EmbArrayMaster(singletonList(new EmbArrayMaster.EmbArrayDetail(emptyList()))); + DB.insert(t); + } +} diff --git a/ebean-test/src/test/java/org/tests/model/m2m/MnyNode.java b/ebean-test/src/test/java/org/tests/model/m2m/MnyNode.java index edbd87cc0..fabb58040 100644 --- a/ebean-test/src/test/java/org/tests/model/m2m/MnyNode.java +++ b/ebean-test/src/test/java/org/tests/model/m2m/MnyNode.java @@ -33,7 +33,7 @@ public class MnyNode { joinColumns = @JoinColumn(name = "from_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "to_id", referencedColumnName = "id")) @Where(clause = "${mta}.flags & 1 != 0") - @Where(clause = "BITAND(${mta}.flags, 1) != 0", platforms = Platform.H2) + @Where(clause = "BITAND(${mta}.flags, 1) != 0", platforms = {Platform.H2, Platform.ORACLE}) List bit1Relations; @ManyToMany @@ -41,7 +41,7 @@ public class MnyNode { joinColumns = @JoinColumn(name = "to_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "from_id", referencedColumnName = "id")) @Where(clause = "${mta}.flags & 1 != 0") - @Where(clause = "BITAND(${mta}.flags, 1) != 0", platforms = Platform.H2) + @Where(clause = "BITAND(${mta}.flags, 1) != 0", platforms = {Platform.H2, Platform.ORACLE}) List bit1ReverseRelations; @ManyToMany @@ -49,7 +49,7 @@ public class MnyNode { joinColumns = @JoinColumn(name = "from_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "to_id", referencedColumnName = "id")) @Where(clause = "${mta}.flags & 2 != 0") - @Where(clause = "BITAND(${mta}.flags, 2) != 0", platforms = Platform.H2) + @Where(clause = "BITAND(${mta}.flags, 2) != 0", platforms = {Platform.H2, Platform.ORACLE}) List bit2Relations; @ManyToMany @@ -57,7 +57,7 @@ public class MnyNode { joinColumns = @JoinColumn(name = "to_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "from_id", referencedColumnName = "id")) @Where(clause = "${mta}.flags & 2 != 0") - @Where(clause = "BITAND(${mta}.flags, 2) != 0", platforms = Platform.H2) + @Where(clause = "BITAND(${mta}.flags, 2) != 0", platforms = {Platform.H2, Platform.ORACLE}) List bit2ReverseRelations; @ManyToMany diff --git a/ebean-test/src/test/java/org/tests/model/types/SomeNewTypesBean.java b/ebean-test/src/test/java/org/tests/model/types/SomeNewTypesBean.java index 2596dbcba..1a0fee0bf 100644 --- a/ebean-test/src/test/java/org/tests/model/types/SomeNewTypesBean.java +++ b/ebean-test/src/test/java/org/tests/model/types/SomeNewTypesBean.java @@ -7,6 +7,7 @@ import javax.persistence.Version; import java.nio.file.Path; import java.sql.Date; import java.time.*; +import java.util.Calendar; @Entity public class SomeNewTypesBean { @@ -58,6 +59,8 @@ public class SomeNewTypesBean { Duration duration; + Calendar calendar; + public Long getId() { return id; } @@ -217,4 +220,12 @@ public class SomeNewTypesBean { public void setDuration(Duration duration) { this.duration = duration; } + + public Calendar getCalendar() { + return calendar; + } + + public void setCalendar(final Calendar calendar) { + this.calendar = calendar; + } } diff --git a/ebean-test/src/test/java/org/tests/rawsql/inherit/ParentQueryTest.java b/ebean-test/src/test/java/org/tests/rawsql/inherit/ParentQueryTest.java index b3e879e71..5e45b8130 100644 --- a/ebean-test/src/test/java/org/tests/rawsql/inherit/ParentQueryTest.java +++ b/ebean-test/src/test/java/org/tests/rawsql/inherit/ParentQueryTest.java @@ -12,14 +12,12 @@ import java.util.ArrayList; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -public class ParentQueryTest extends BaseTestCase { +class ParentQueryTest extends BaseTestCase { @Test - public void QueryParentCollectionFetch() { - + void queryParentCollectionFetch() { List exampleData = new ArrayList<>(); exampleData.add(new Data(0)); exampleData.add(new Data(1)); @@ -40,7 +38,7 @@ public class ParentQueryTest extends BaseTestCase { List partial = DB.find(Parent.class).where().ge("val", 1001).findList(); assertNotNull(partial.get(0).getData()); assertThat(partial.get(0).getMore()).startsWith("PQT-"); - assertEquals(partial.get(0).getData().get(0).getVal().intValue(), 0); + assertThat(partial.get(0).getData()).hasSize(3); } } diff --git a/ebean-test/src/test/java/org/tests/text/csv/TestCsvReader.java b/ebean-test/src/test/java/org/tests/text/csv/TestCsvReader.java index 313f5017c..e8cb48160 100644 --- a/ebean-test/src/test/java/org/tests/text/csv/TestCsvReader.java +++ b/ebean-test/src/test/java/org/tests/text/csv/TestCsvReader.java @@ -3,26 +3,25 @@ package org.tests.text.csv; import io.ebean.DB; import io.ebean.TransactionalTestCase; import io.ebean.text.csv.CsvReader; +import io.ebean.util.IOUtils; + import org.junit.jupiter.api.Test; import org.tests.model.basic.Customer; import org.tests.model.basic.ResetBasicData; -import java.io.File; -import java.io.FileReader; +import java.io.Reader; import java.net.URL; +import java.util.Locale; public class TestCsvReader extends TransactionalTestCase { @Test - public void test() { + public void test() throws Exception { ResetBasicData.reset(); - try { - URL resource = TestCsvReaderWithCallback.class.getResource("/test1.csv"); - File f = new File(resource.getFile()); - - FileReader reader = new FileReader(f); + URL resource = TestCsvReaderWithCallback.class.getResource("/test1.csv"); + try (Reader reader = IOUtils.newReader(resource.openStream())){ CsvReader csvReader = DB.getDefault().createCsvReader(Customer.class); @@ -32,15 +31,13 @@ public class TestCsvReader extends TransactionalTestCase { // csvReader.addProperty("id"); csvReader.addProperty("status"); csvReader.addProperty("name"); - csvReader.addDateTime("anniversary", "dd-MMM-yyyy"); + csvReader.addDateTime("anniversary", "dd-MMM-yyyy", Locale.ENGLISH); csvReader.addProperty("billingAddress.line1"); csvReader.addProperty("billingAddress.city"); csvReader.addProperty("billingAddress.country.code"); csvReader.process(reader); - - } catch (Exception e) { - throw new RuntimeException(e); + } } diff --git a/ebean-test/src/test/java/org/tests/text/csv/TestCsvReaderWithCallback.java b/ebean-test/src/test/java/org/tests/text/csv/TestCsvReaderWithCallback.java index ef6bfd8c7..efa424d07 100644 --- a/ebean-test/src/test/java/org/tests/text/csv/TestCsvReaderWithCallback.java +++ b/ebean-test/src/test/java/org/tests/text/csv/TestCsvReaderWithCallback.java @@ -4,12 +4,14 @@ import io.ebean.DB; import io.ebean.TransactionalTestCase; import io.ebean.text.csv.CsvReader; import io.ebean.text.csv.DefaultCsvCallback; +import io.ebean.util.IOUtils; + import org.junit.jupiter.api.Test; import org.tests.model.basic.Customer; -import java.io.File; -import java.io.FileReader; +import java.io.Reader; import java.net.URL; +import java.util.Locale; import static org.assertj.core.api.Assertions.assertThat; @@ -19,41 +21,40 @@ public class TestCsvReaderWithCallback extends TransactionalTestCase { public void test() throws Throwable { URL resource = TestCsvReaderWithCallback.class.getResource("/test1.csv"); - File f = new File(resource.getFile()); + try (Reader reader = IOUtils.newReader(resource.openStream())) { - FileReader reader = new FileReader(f); + CsvReader csvReader = DB.getDefault().createCsvReader(Customer.class); - CsvReader csvReader = DB.getDefault().createCsvReader(Customer.class); + csvReader.setPersistBatchSize(2); + csvReader.setLogInfoFrequency(3); - csvReader.setPersistBatchSize(2); - csvReader.setLogInfoFrequency(3); + csvReader.addIgnore(); + // csvReader.addProperty("id"); + csvReader.addProperty("status"); + csvReader.addProperty("name"); + csvReader.addDateTime("anniversary", "dd-MMM-yyyy", Locale.ENGLISH); + csvReader.addProperty("billingAddress.line1"); + csvReader.addProperty("billingAddress.city"); + // processor.addReference("billingAddress.country.code"); + csvReader.addProperty("billingAddress.country.code"); - csvReader.addIgnore(); - // csvReader.addProperty("id"); - csvReader.addProperty("status"); - csvReader.addProperty("name"); - csvReader.addDateTime("anniversary", "dd-MMM-yyyy"); - csvReader.addProperty("billingAddress.line1"); - csvReader.addProperty("billingAddress.city"); - // processor.addReference("billingAddress.country.code"); - csvReader.addProperty("billingAddress.country.code"); + int before = DB.find(Customer.class).findCount(); - int before = DB.find(Customer.class).findCount(); + csvReader.process(reader, new DefaultCsvCallback() { - csvReader.process(reader, new DefaultCsvCallback() { + @Override + public void processBean(int row, String[] lineContent, Customer cust) { - @Override - public void processBean(int row, String[] lineContent, Customer cust) { + server.save(cust.getBillingAddress(), transaction); + server.save(cust, transaction); - server.save(cust.getBillingAddress(), transaction); - server.save(cust, transaction); + } - } + }); - }); - - int after = DB.find(Customer.class).findCount(); - assertThat(after).isEqualTo(before + 9); + int after = DB.find(Customer.class).findCount(); + assertThat(after).isEqualTo(before + 9); + } } } diff --git a/ebean-test/src/test/java/org/tests/text/json/TestJsonSimple.java b/ebean-test/src/test/java/org/tests/text/json/TestJsonSimple.java index f635d926b..c462a80cc 100644 --- a/ebean-test/src/test/java/org/tests/text/json/TestJsonSimple.java +++ b/ebean-test/src/test/java/org/tests/text/json/TestJsonSimple.java @@ -4,9 +4,12 @@ import io.ebean.BaseTestCase; import io.ebean.DB; import io.ebean.text.json.EJson; import io.ebean.text.json.JsonContext; +import io.ebean.util.IOUtils; + import org.junit.jupiter.api.Test; import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.LinkedHashMap; import java.util.Map; @@ -20,19 +23,20 @@ public class TestJsonSimple extends BaseTestCase { public void test() throws IOException { InputStream is = this.getClass().getResourceAsStream("/example1.json"); + String jsonText; + try (final Reader reader = IOUtils.newReader(is)) { + LineNumberReader lineReader = new LineNumberReader(reader); - final Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); - LineNumberReader lineReader = new LineNumberReader(reader); + String readLine; - String readLine; + StringBuilder sb = new StringBuilder(); + while ((readLine = lineReader.readLine()) != null) { + sb.append(readLine); + } - StringBuilder sb = new StringBuilder(); - while ((readLine = lineReader.readLine()) != null) { - sb.append(readLine); + jsonText = sb.toString(); } - String jsonText = sb.toString(); - Object el = EJson.parse(jsonText); assertThat(el).isNotNull(); diff --git a/ebean-test/src/test/java/org/tests/types/TestNewTypes.java b/ebean-test/src/test/java/org/tests/types/TestNewTypes.java index 35732801e..90ffd842a 100644 --- a/ebean-test/src/test/java/org/tests/types/TestNewTypes.java +++ b/ebean-test/src/test/java/org/tests/types/TestNewTypes.java @@ -10,6 +10,7 @@ import org.tests.model.types.SomeNewTypesBean; import java.io.File; import java.nio.file.Paths; import java.time.*; +import java.util.Calendar; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -44,6 +45,7 @@ public class TestNewTypes extends BaseTestCase { bean.setPath(Paths.get(TEMP_PATH)); bean.setPeriod(Period.of(4,3,2)); bean.setDuration(Duration.ofMinutes(5)); + bean.setCalendar(Calendar.getInstance()); DB.save(bean); @@ -104,6 +106,10 @@ public class TestNewTypes extends BaseTestCase { list = DB.find(SomeNewTypesBean.class).where().eq("duration", Duration.ofMinutes(5)).findList(); assertThat(list).isNotEmpty(); + // Calendar.getInstance() returns an implementation, which then has to be remapped to ScalarTypeCalendar + list = DB.find(SomeNewTypesBean.class).where().le("calendar", Calendar.getInstance()).findList(); + assertThat(list).isNotEmpty(); + SomeNewTypesBean fetched = DB.find(SomeNewTypesBean.class, bean.getId()); assertEquals(bean.getZoneId(), fetched.getZoneId()); @@ -122,6 +128,7 @@ public class TestNewTypes extends BaseTestCase { assertEquals(bean.getPath(), fetched.getPath()); assertEquals(bean.getPeriod(), fetched.getPeriod()); assertEquals(bean.getDuration(), fetched.getDuration()); + assertEquals(bean.getCalendar(), fetched.getCalendar()); String asJson = DB.json().toJson(fetched); @@ -139,10 +146,10 @@ public class TestNewTypes extends BaseTestCase { assertThat(toBean.getOffsetDateTime()).isEqualToIgnoringNanos(bean.getOffsetDateTime()); assertEquals(bean.getLocalTime().toSecondOfDay(), toBean.getLocalTime().toSecondOfDay()); assertEquals(bean.getInstant().toEpochMilli() / 1000, toBean.getInstant().toEpochMilli() / 1000); - // FIXME: This test fails on Windows with: expected:<\tmp> but was: assertEquals(bean.getPath(), toBean.getPath()); assertEquals(bean.getPeriod(), toBean.getPeriod()); assertEquals(bean.getDuration(), toBean.getDuration()); + assertEquals(bean.getCalendar(), toBean.getCalendar()); } diff --git a/ebean/pom.xml b/ebean/pom.xml index 2c3dacdf5..e399daa25 100644 --- a/ebean/pom.xml +++ b/ebean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT ebean composite @@ -22,20 +22,20 @@ io.ebean ebean-api - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT io.ebean ebean-core - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT io.ebean ebean-querybean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml index e897804e2..cb1fcce36 100644 --- a/kotlin-querybean-generator/pom.xml +++ b/kotlin-querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT kotlin querybean generator @@ -29,7 +29,7 @@ io.ebean ebean-querybean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT test @@ -43,7 +43,7 @@ io.ebean ebean-core - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT test @@ -64,7 +64,7 @@ io.ebean ebean-ddl-generator - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT test @@ -106,7 +106,7 @@ io.ebean kotlin-querybean-generator - 12.8.2 + 12.13.0 diff --git a/pom.xml b/pom.xml index e7de38588..49c958dde 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ io.ebean ebean-parent - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT pom ebean parent @@ -42,11 +42,11 @@ 2.12.1 1.2 1.1 - 12.12.1 + 12.13.1 4.2 7.3 - 12.13.0 - 12.13.0 + 12.14.1 + 12.14.1 @@ -61,7 +61,7 @@ ch.qos.logback logback-classic - 1.2.8 + 1.2.9 test diff --git a/querybean-generator/pom.xml b/querybean-generator/pom.xml index ef25adaa8..98d196530 100644 --- a/querybean-generator/pom.xml +++ b/querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.13.3-SNAPSHOT + 12.14.2-SNAPSHOT querybean generator diff --git a/tests/test-java16/pom.xml b/tests/test-java16/pom.xml index 0f4927da5..7ac83574a 100644 --- a/tests/test-java16/pom.xml +++ b/tests/test-java16/pom.xml @@ -20,19 +20,19 @@ io.ebean ebean - 12.13.2-SNAPSHOT + 12.14.2-SNAPSHOT ch.qos.logback logback-classic - 1.2.3 + 1.2.9 io.ebean ebean-test - 12.13.2-SNAPSHOT + 12.14.2-SNAPSHOT test @@ -58,7 +58,7 @@ io.ebean querybean-generator - 12.13.2-SNAPSHOT + 12.14.2-SNAPSHOT diff --git a/tests/test-kotlin/pom.xml b/tests/test-kotlin/pom.xml index 3ab10cdf5..ab8706f74 100644 --- a/tests/test-kotlin/pom.xml +++ b/tests/test-kotlin/pom.xml @@ -36,21 +36,21 @@ io.ebean ebean-test - 12.13.2-SNAPSHOT + 12.14.2-SNAPSHOT test io.ebean ebean-core - 12.13.2-SNAPSHOT + 12.14.2-SNAPSHOT test ch.qos.logback logback-classic - 1.2.3 + 1.2.9 test