Merge remote-tracking branch 'upstream/master'

# Conflicts:
#	ebean-bom/pom.xml
This commit is contained in:
Noemi Praml
2022-07-01 10:03:03 +02:00
71 changed files with 553 additions and 653 deletions
+1 -1
View File
@@ -36,5 +36,5 @@ jobs:
# - name: Maven single test
# run: mvn --batch-mode clean verify -Dtest="io.ebeaninternal.server.core.DefaultServer_getReferenceTest" -DfailIfNoTests=false
- name: Build with Maven
run: mvn clean package
run: mvn -T 8 clean test
+1 -1
View File
@@ -35,4 +35,4 @@ jobs:
~/.m2
key: build-${{ env.cache-name }}
- name: db2
run: mvn clean test -Dprops.file=testconfig/ebean-db2.properties
run: mvn -T 8 clean test -Dprops.file=testconfig/ebean-db2.properties
+1 -1
View File
@@ -37,5 +37,5 @@ jobs:
- name: Maven version
run: mvn --version
- name: H2Database
run: mvn clean package
run: mvn -T 8 clean package
+1 -1
View File
@@ -35,5 +35,5 @@ jobs:
~/.m2
key: build-${{ env.cache-name }}
- name: Build with Maven
run: mvn package
run: mvn -T 8 test
+1 -1
View File
@@ -39,5 +39,5 @@ jobs:
- name: Maven version
run: mvn --version
- name: Build with Maven
run: mvn package
run: mvn -T 8 test
+1 -1
View File
@@ -35,4 +35,4 @@ jobs:
~/.m2
key: build-${{ env.cache-name }}
- name: mariadb 10.6
run: mvn clean test -Dprops.file=testconfig/ebean-mariadb.properties
run: mvn -T 8 clean test -Dprops.file=testconfig/ebean-mariadb.properties
+1 -1
View File
@@ -35,4 +35,4 @@ jobs:
~/.m2
key: build-${{ env.cache-name }}
- name: mysql
run: mvn clean test -Dprops.file=testconfig/ebean-mysql.properties
run: mvn -T 8 clean test -Dprops.file=testconfig/ebean-mysql.properties
@@ -1,5 +1,5 @@
name: Oracle18
name: Oracle
on:
workflow_dispatch:
@@ -35,4 +35,4 @@ jobs:
~/.m2
key: build-${{ env.cache-name }}
- name: oracle
run: mvn clean test -Dprops.file=testconfig/ebean-oracle.properties
run: mvn -T 8 clean test -Dprops.file=testconfig/ebean-oracle.properties
+1 -1
View File
@@ -35,4 +35,4 @@ jobs:
~/.m2
key: build-${{ env.cache-name }}
- name: postgres
run: mvn clean test -Dprops.file=testconfig/ebean-postgres.properties
run: mvn -T 8 clean test -Dprops.file=testconfig/ebean-postgres.properties
+1 -1
View File
@@ -35,4 +35,4 @@ jobs:
~/.m2
key: build-${{ env.cache-name }}
- name: sqlserver 2017
run: mvn clean test -Dprops.file=testconfig/ebean-sqlserver17.properties
run: mvn -T 8 clean test -Dprops.file=testconfig/ebean-sqlserver17.properties
+1
View File
@@ -8,6 +8,7 @@
[![Postgres](https://github.com/ebean-orm/ebean/actions/workflows/postgres.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/postgres.yml)
[![MySql](https://github.com/ebean-orm/ebean/actions/workflows/mysql.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/mysql.yml)
[![MariaDB](https://github.com/ebean-orm/ebean/actions/workflows/mariadb.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/mariadb.yml)
[![Oracle](https://github.com/ebean-orm/ebean/actions/workflows/oracle.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/oracle.yml)
[![SqlServer](https://github.com/ebean-orm/ebean/actions/workflows/sqlserver.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/sqlserver.yml)
[![Yugabyte](https://github.com/ebean-orm/ebean/actions/workflows/yugabyte.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/yugabyte.yml)
@@ -802,20 +802,6 @@ public class DatabasePlatform {
}
}
/**
* Return true if partitions exist for the given table.
*/
public boolean tablePartitionsExist(Connection connection, String table) throws SQLException {
return true;
}
/**
* Return the SQL to create an initial partition for the given table.
*/
public String tablePartitionInit(String tableName, PartitionMode mode) {
return null;
}
/**
* Escapes the like string for this DB-Platform
*/
@@ -16,25 +16,24 @@ import java.sql.SQLException;
* <p>
* Scalar in the sense that the types are not compound types. Scalar types only
* map to a single database column.
* </p>
* <p>
* These types fall into two categories. Types that are mapped natively to JDBC
* types and the rest. Types that map to native JDBC types do not require any
* data type conversion to be persisted to the database. These are java types
* that map via java.sql.Types.
* </p>
* <p>
* Types that are not native to JDBC require some conversion. These include some
* common java types such as java.util.Date, java.util.Calendar,
* java.math.BigInteger.
* </p>
* <p>
* Note that Booleans may be native for some databases and require conversion on
* other databases.
* </p>
*/
public interface ScalarType<T> extends StringParser, StringFormatter, ScalarDataReader<T> {
/**
* Return true for types that do mutation detection based on json content.
*/
default boolean isJsonMapper() {
return false;
}
@@ -43,38 +42,40 @@ public interface ScalarType<T> extends StringParser, StringFormatter, ScalarData
* Return true if this is a binary type and can not support parse() and format() from/to string.
* This allows Ebean to optimise marshalling types to string.
*/
boolean isBinaryType();
default boolean isBinaryType() {
return false;
}
/**
* Return true if this is a mutable scalar type (like hstore).
*/
boolean isMutable();
default boolean isMutable() {
return false;
}
/**
* For mutable scalarType's return true if the value is dirty.
* Non-dirty properties may be excluded from updates.
*/
boolean isDirty(Object value);
default boolean isDirty(Object value) {
return false;
}
/**
* Return the default DB column length for this type.
* <p>
* If a BeanProperty has no explicit length defined then this length should
* be assigned.
* </p>
* <p>
* This is primarily to support defining a length on Enum types (to
* supplement defining the length on the BeanProperty directly).
* </p>
*/
int getLength();
default int getLength() {
return 0;
}
/**
* Return true if the type is native to JDBC.
* <p>
* If it is native to JDBC then its values/instances do not need to be
* converted to and from an associated JDBC type.
* </p>
*/
boolean isJdbcNative();
@@ -83,16 +84,13 @@ public interface ScalarType<T> extends StringParser, StringFormatter, ScalarData
* <p>
* This type should be consistent with the toJdbcType() method in converting
* the type to the appropriate type for binding to preparedStatements.
* </p>
*/
int getJdbcType();
/**
* Return the type that matches the bean property type.
* <p>
* This represents the 'logical' type rather than the JDBC type this maps
* to.
* </p>
* This represents the 'logical' type rather than the JDBC type this maps to.
*/
Class<T> getType();
@@ -104,7 +102,7 @@ public interface ScalarType<T> extends StringParser, StringFormatter, ScalarData
T read(DataReader reader) throws SQLException;
/**
* Ignore the reading of this value. Typically this means moving the index
* Ignore the reading of this value. Typically, this means moving the index
* position in the ResultSet.
*/
void loadIgnore(DataReader reader);
@@ -114,20 +112,16 @@ public interface ScalarType<T> extends StringParser, StringFormatter, ScalarData
* <p>
* value may need to be converted from the logical bean property type to the
* JDBC type.
* </p>
*/
void bind(DataBinder binder, T value) throws SQLException;
/**
* Convert the value as necessary to the JDBC type.
* <p>
* Note that this should also match the type as per the getJdbcType()
* method.
* </p>
* Note that this should also match the type as per the getJdbcType() method.
* <p>
* This is typically used when the matching type is used in a where clause
* and we use this to ensure it is an appropriate jdbc type.
* </p>
*/
Object toJdbcType(Object value);
@@ -135,19 +129,14 @@ public interface ScalarType<T> extends StringParser, StringFormatter, ScalarData
* Convert the value as necessary to the logical Bean type.
* <p>
* The type as per the bean property.
* </p>
* <p>
* This is used to automatically convert id values (typically from a string
* to a int, long or UUID).
* </p>
*/
T toBeanType(Object value);
/**
* Convert the type into a string representation.
* <p>
* Reciprocal of parse().
* </p>
*/
String formatValue(T value);
@@ -155,10 +144,8 @@ public interface ScalarType<T> extends StringParser, StringFormatter, ScalarData
* Convert the type into a string representation.
* <p>
* This assumes the value is of the correct type.
* </p>
* <p>
* This is so that ScalarType also implements the StringFormatter interface.
* </p>
*/
@Override
String format(Object value);
@@ -167,10 +154,6 @@ public interface ScalarType<T> extends StringParser, StringFormatter, ScalarData
* Convert the string value to the appropriate java object.
* <p>
* Mostly used to support CSV, JSON and XML parsing.
* </p>
* <p>
* Reciprocal of formatValue().
* </p>
*/
@Override
T parse(String value);
@@ -183,29 +166,32 @@ public interface ScalarType<T> extends StringParser, StringFormatter, ScalarData
/**
* Return true if the type can accept long systemTimeMillis input.
* <p>
* This is used to determine if is is sensible to use the
* This is used to determine if it is sensible to use the
* {@link #convertFromMillis(long)} method.
* </p>
* <p>
* This includes the Date, Calendar, sql Date, Time, Timestamp, JODA types
* as well as Long, BigDecimal and String (although it generally is not
* expected to parse systemTimeMillis to a String or BigDecimal).
* </p>
*/
boolean isDateTimeCapable();
default boolean isDateTimeCapable() {
return false;
}
/**
* Convert the value into a long version value.
*/
long asVersion(T value);
default long asVersion(T value) {
throw new UnsupportedOperationException();
}
/**
* Convert the systemTimeMillis into the appropriate java object.
* <p>
* For non dateTime types this will throw an exception.
* </p>
*/
T convertFromMillis(long dateTime);
default T convertFromMillis(long dateTime) {
throw new UnsupportedOperationException();
}
/**
* Read the value from binary input.
@@ -215,7 +201,7 @@ public interface ScalarType<T> extends StringParser, StringFormatter, ScalarData
/**
* Write the value to binary output.
*/
void writeData(DataOutput dataOutput, T v) throws IOException;
void writeData(DataOutput dataOutput, T value) throws IOException;
/**
* Read the value from JsonParser.
@@ -77,8 +77,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
*/
private List<BeanPropertyAssocMany<?>> updatedManys;
/**
* Need to get and store the updated properties because the persist listener is notified
* later on a different thread and the bean has been reset at that point.
* Store the updated properties to notify persist listener.
*/
private Set<String> updatedProperties;
/**
@@ -122,7 +121,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
*/
private boolean complete;
/**
* Many to many intersection table changes that are held for later batch processing.
* Many-to-many intersection table changes that are held for later batch processing.
*/
private List<SaveMany> saveMany;
@@ -148,9 +147,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
// 'stateless update' - set loaded properties as dirty
intercept.setNewBeanForUpdate();
statelessUpdate = true;
} else if (!intercept.isDirty()) {
// check if any mutable scalar properties are dirty
beanDescriptor.checkAnyMutableProperties(intercept);
}
// Mark Mutable scalar properties (like Hstore) as dirty where necessary
beanDescriptor.checkMutableProperties(intercept);
}
this.concurrencyMode = beanDescriptor.concurrencyMode(intercept);
this.publish = Flags.isPublish(flags);
@@ -726,10 +726,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
executeInsert();
return -1;
case UPDATE:
if (beanPersistListener != null) {
// store the updated properties for sending later
updatedProperties = updatedProperties();
}
executeUpdate();
return -1;
case DELETE_SOFT:
@@ -1208,6 +1204,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
private void executeUpdate() {
setTenantId();
if (controller == null || controller.preUpdate(this)) {
// check dirty state for all mutable scalar properties (like DbJson, Hstore)
beanDescriptor.checkAllMutableProperties(intercept);
if (beanPersistListener != null) {
updatedProperties = updatedProperties();
}
postControllerPrepareUpdate();
beanManager.getBeanPersister().update(this);
}
@@ -3140,9 +3140,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
}
/**
* Check for mutable scalar types and mark as dirty if necessary.
* Check all mutable scalar types and mark as dirty if necessary.
*/
public void checkMutableProperties(EntityBeanIntercept ebi) {
public void checkAllMutableProperties(EntityBeanIntercept ebi) {
for (BeanProperty beanProperty : propertiesMutable) {
int propertyIndex = beanProperty.propertyIndex();
if (ebi.isLoadedProperty(propertyIndex)) {
@@ -3156,6 +3156,23 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
}
}
/**
* Return true if any mutable properties are dirty.
*/
public boolean checkAnyMutableProperties(EntityBeanIntercept ebi) {
for (BeanProperty beanProperty : propertiesMutable) {
int propertyIndex = beanProperty.propertyIndex();
if (ebi.isLoadedProperty(propertyIndex)) {
Object value = beanProperty.getValue(ebi.getOwner());
if (beanProperty.checkMutable(value, ebi.isDirtyProperty(propertyIndex), ebi)) {
ebi.markPropertyAsChanged(propertyIndex);
return true;
}
}
}
return false;
}
public ConcurrencyMode concurrencyMode(EntityBeanIntercept ebi) {
if (!hasVersionProperty(ebi)) {
return ConcurrencyMode.NONE;
@@ -359,6 +359,9 @@ public final class DefaultTypeManager implements TypeManager {
Type genericType = prop.getGenericType();
boolean hasJacksonAnnotations = objectMapperPresent && checkJacksonAnnotations(prop);
if (type.equals(String.class)) {
return ScalarTypeJsonString.typeFor(postgres, dbType);
}
if (type.equals(List.class)) {
DocPropertyType docType = getDocType(genericType);
if (!hasJacksonAnnotations && isValueTypeSimple(genericType)) {
@@ -18,41 +18,6 @@ abstract class ScalarTypeBase<T> implements ScalarType<T> {
this.jdbcType = jdbcType;
}
@Override
public long asVersion(T value) {
throw new RuntimeException("not supported");
}
@Override
public boolean isBinaryType() {
// override for binary/byte based types
return false;
}
/**
* Default implementation of mutable false.
*/
@Override
public boolean isMutable() {
return false;
}
/**
* Default to true.
*/
@Override
public boolean isDirty(Object value) {
return true;
}
/**
* Just return 0.
*/
@Override
public int getLength() {
return 0;
}
@Override
public boolean isJdbcNative() {
return jdbcNative;
@@ -88,16 +88,6 @@ abstract class ScalarTypeBaseVarchar<T> extends ScalarTypeBase<T> {
return format(value);
}
@Override
public T convertFromMillis(long systemTimeMillis) {
throw new TextException("Not Supported");
}
@Override
public boolean isDateTimeCapable() {
return false;
}
@Override
@SuppressWarnings("unchecked")
public String format(Object value) {
@@ -234,7 +234,6 @@ public final class ScalarTypeBoolean {
@Override
public int getLength() {
// typically this will return 1
return Math.max(trueValue.length(), falseValue.length());
}
@@ -329,16 +328,6 @@ public final class ScalarTypeBoolean {
return Boolean.valueOf(value);
}
@Override
public Boolean convertFromMillis(long systemTimeMillis) {
throw new TextException("Not Supported");
}
@Override
public boolean isDateTimeCapable() {
return false;
}
@Override
public Boolean readData(DataInput dataInput) throws IOException {
if (!dataInput.readBoolean()) {
@@ -85,16 +85,6 @@ final class ScalarTypeByte extends ScalarTypeBase<Byte> {
throw new TextException("Not supported");
}
@Override
public Byte convertFromMillis(long systemTimeMillis) {
throw new TextException("Not Supported");
}
@Override
public boolean isDateTimeCapable() {
return false;
}
@Override
public Byte readData(DataInput dataInput) throws IOException {
if (!dataInput.readBoolean()) {
@@ -59,16 +59,6 @@ public abstract class ScalarTypeBytesBase extends ScalarTypeBase<byte[]> {
throw new TextException("Not supported");
}
@Override
public byte[] convertFromMillis(long systemTimeMillis) {
throw new TextException("Not supported");
}
@Override
public boolean isDateTimeCapable() {
return false;
}
@Override
public byte[] readData(DataInput dataInput) throws IOException {
if (!dataInput.readBoolean()) {
@@ -26,26 +26,11 @@ public final class ScalarTypeBytesEncrypted implements ScalarType<byte[]> {
this.dataEncryptSupport = dataEncryptSupport;
}
@Override
public long asVersion(byte[] value) {
throw new RuntimeException("not supported");
}
@Override
public boolean isBinaryType() {
return true;
}
@Override
public boolean isMutable() {
return false;
}
@Override
public boolean isDirty(Object value) {
return false;
}
@Override
public void bind(DataBinder binder, byte[] value) throws SQLException {
value = dataEncryptSupport.encrypt(value);
@@ -93,16 +93,6 @@ class ScalarTypeDuration extends ScalarTypeBase<Duration> {
return Duration.parse(value);
}
@Override
public boolean isDateTimeCapable() {
return false;
}
@Override
public Duration convertFromMillis(long systemTimeMillis) {
throw new TextException("Not Supported");
}
@Override
public Duration jsonRead(JsonParser parser) throws IOException {
return Duration.parse(parser.getValueAsString());
@@ -30,11 +30,6 @@ public final class ScalarTypeEncryptedWrapper<T> implements ScalarType<T>, Local
return dataEncryptSupport.encryptObject(formatValue);
}
@Override
public long asVersion(T value) {
throw new RuntimeException("not supported");
}
@Override
public boolean isBinaryType() {
return wrapped.isBinaryType();
@@ -229,16 +229,6 @@ final class ScalarTypeEnumStandard {
return Enum.valueOf(enumType, value);
}
@Override
public Object convertFromMillis(long systemTimeMillis) {
throw new TextException("Not Supported");
}
@Override
public boolean isDateTimeCapable() {
return false;
}
@Override
public Object jsonRead(JsonParser parser) throws IOException {
if (parser.getCodec() != null) {
@@ -39,16 +39,6 @@ class ScalarTypeEnumWithMapping extends ScalarTypeEnumStandard.EnumBase implemen
return enumType == null;
}
@Override
public long asVersion(Object value) {
throw new RuntimeException("not supported");
}
@Override
public boolean isBinaryType() {
return false;
}
/**
* Return the IN values for DB constraint construction.
*/
@@ -126,16 +126,6 @@ final class ScalarTypeFile extends ScalarTypeBase<File> {
throw new TextException("Not supported");
}
@Override
public File convertFromMillis(long systemTimeMillis) {
throw new TextException("Not supported");
}
@Override
public boolean isDateTimeCapable() {
return false;
}
@Override
public File readData(DataInput dataInput) throws IOException {
// skip reading large file
@@ -80,16 +80,6 @@ abstract class ScalarTypeJsonCollection<T> extends ScalarTypeBase<T> implements
return docPropertyType;
}
@Override
public boolean isDateTimeCapable() {
return false;
}
@Override
public T convertFromMillis(long dateTime) {
return null;
}
@Override
public T readData(DataInput dataInput) throws IOException {
if (!dataInput.readBoolean()) {
@@ -215,16 +215,6 @@ abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
}
}
@Override
public final Map convertFromMillis(long dateTime) {
throw new RuntimeException("Should never be called");
}
@Override
public final boolean isDateTimeCapable() {
return false;
}
@Override
public final Map readData(DataInput dataInput) throws IOException {
if (!dataInput.readBoolean()) {
@@ -167,16 +167,6 @@ abstract class ScalarTypeJsonNode extends ScalarTypeBase<JsonNode> {
}
}
@Override
public JsonNode convertFromMillis(long dateTime) {
throw new RuntimeException("Should never be called");
}
@Override
public boolean isDateTimeCapable() {
return false;
}
@Override
public JsonNode readData(DataInput dataInput) throws IOException {
if (!dataInput.readBoolean()) {
@@ -55,16 +55,6 @@ final class ScalarTypeJsonObjectMapper {
NoMutationDetection(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
super(Object.class, jsonManager, field, dbType, docType);
}
@Override
public boolean isMutable() {
return false;
}
@Override
public boolean isDirty(Object value) {
return false;
}
}
/**
@@ -79,6 +69,11 @@ final class ScalarTypeJsonObjectMapper {
this.jsonb = "jsonb".equals(pgType);
}
@Override
public boolean isMutable() {
return true;
}
@Override
public boolean isJsonMapper() {
return true;
@@ -144,11 +139,6 @@ final class ScalarTypeJsonObjectMapper {
this.objectWriter = helper.objectWriter();
}
@Override
public boolean isMutable() {
return true;
}
@Override
public T read(DataReader reader) throws SQLException {
String json = reader.getString();
@@ -212,16 +202,6 @@ final class ScalarTypeJsonObjectMapper {
return docType;
}
@Override
public final boolean isDateTimeCapable() {
return false;
}
@Override
public final T convertFromMillis(long dateTime) {
throw new IllegalStateException("Not supported");
}
@Override
public final T jsonRead(JsonParser parser) throws IOException {
return objectReader.readValue(parser, deserType);
@@ -0,0 +1,41 @@
package io.ebeaninternal.server.type;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.ExtraDbTypes;
import io.ebean.core.type.DataBinder;
import io.ebean.core.type.ScalarType;
import java.sql.SQLException;
final class ScalarTypeJsonString {
static final Postgres JSONB = new Postgres(ExtraDbTypes.JSONB, PostgresHelper.JSONB_TYPE);
static final Postgres JSON = new Postgres(ExtraDbTypes.JSON, PostgresHelper.JSON_TYPE);
static ScalarType<?> typeFor(boolean postgres, int dbType) {
if (postgres) {
switch (dbType) {
case DbPlatformType.JSONB:
return JSONB;
case DbPlatformType.JSON:
return JSON;
}
}
return ScalarTypeString.INSTANCE;
}
private static class Postgres extends ScalarTypeStringBase {
final String postgresType;
Postgres(int jdbcType, String postgresType) {
super(true, jdbcType);
this.postgresType = postgresType;
}
@Override
public void bind(DataBinder binder, String rawJson) throws SQLException {
binder.setObject(PostgresHelper.asObject(postgresType, rawJson));
}
}
}
@@ -78,16 +78,6 @@ final class ScalarTypeMonthDay extends ScalarTypeBase<MonthDay> {
return MonthDay.parse(value);
}
@Override
public boolean isDateTimeCapable() {
return false;
}
@Override
public MonthDay convertFromMillis(long dateTime) {
throw new RuntimeException("Not supported on this type");
}
@Override
public MonthDay readData(DataInput dataInput) throws IOException {
if (!dataInput.readBoolean()) {
@@ -22,25 +22,6 @@ class ScalarTypeNotFound implements ScalarType<Void> {
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() {
@@ -102,21 +83,6 @@ class ScalarTypeNotFound implements ScalarType<Void> {
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();
@@ -82,16 +82,6 @@ final class ScalarTypePostgresHstore extends ScalarTypeBase<Map> {
}
}
@Override
public Map convertFromMillis(long dateTime) {
throw new RuntimeException("Should never be called");
}
@Override
public boolean isDateTimeCapable() {
return false;
}
@Override
public Map readData(DataInput dataInput) throws IOException {
if (!dataInput.readBoolean()) {
@@ -57,16 +57,6 @@ final class ScalarTypeShort extends ScalarTypeBase<Short> {
return Short.valueOf(value);
}
@Override
public Short convertFromMillis(long systemTimeMillis) {
throw new TextException("Not Supported");
}
@Override
public boolean isDateTimeCapable() {
return false;
}
@Override
public Short readData(DataInput dataInput) throws IOException {
if (!dataInput.readBoolean()) {
@@ -25,16 +25,6 @@ abstract class ScalarTypeUUIDBase extends ScalarTypeBase<UUID> implements Scalar
return DbPlatformType.UUID;
}
@Override
public boolean isMutable() {
return false;
}
@Override
public boolean isDirty(Object value) {
return true;
}
@Override
public String format(Object value) {
return String.valueOf(value);
@@ -50,16 +40,6 @@ abstract class ScalarTypeUUIDBase extends ScalarTypeBase<UUID> implements Scalar
return UUID.fromString(value);
}
@Override
public UUID convertFromMillis(long dateTime) {
throw new RuntimeException("Should never be called");
}
@Override
public boolean isDateTimeCapable() {
return false;
}
@Override
public UUID toBeanType(Object value) {
return BasicTypeConverter.toUUID(value, false);
@@ -44,8 +44,7 @@ public class ScalarTypeWrapper<B, S> implements ScalarType<B> {
@Override
public long asVersion(B value) {
S unwrapValue = converter.unwrapValue(value);
return scalarType.asVersion(unwrapValue);
return scalarType.asVersion(converter.unwrapValue(value));
}
@Override
@@ -81,16 +81,6 @@ final class ScalarTypeYear extends ScalarTypeBase<Year> {
return Year.parse(value);
}
@Override
public boolean isDateTimeCapable() {
return false;
}
@Override
public Year convertFromMillis(long systemTimeMillis) {
throw new TextException("Not Supported");
}
@Override
public Year jsonRead(JsonParser parser) throws IOException {
return Year.of(parser.getIntValue());
@@ -17,11 +17,11 @@ class BasicProfileLocationTest {
assertThat(loc.fullLocation()).endsWith("invoke0(Native Method:12)");
assertThat(loc.location()).isEqualTo("sun.reflect.NativeMethodAccessorImpl.invoke0");
assertThat(loc.label()).isEqualTo("NativeMethodAccessorImpl.invoke0");
} else if (javaVersion.startsWith("18") || javaVersion.startsWith("19")){
} else if (javaVersion.startsWith("18") || javaVersion.startsWith("19") || javaVersion.startsWith("20")){
assertThat(loc.fullLocation()).endsWith("jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)");
assertThat(loc.location()).isEqualTo("java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke");
assertThat(loc.label()).isEqualTo("DirectMethodHandleAccessor.invoke");
} else {
} else if (javaVersion.startsWith("11") || javaVersion.startsWith("17")) {
assertThat(loc.fullLocation()).endsWith("jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method:12)");
assertThat(loc.location()).isEqualTo("java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0");
assertThat(loc.label()).isEqualTo("NativeMethodAccessorImpl.invoke0");
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.type;
import io.ebean.text.TextException;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
@@ -11,13 +10,12 @@ import java.time.Duration;
import static org.junit.jupiter.api.Assertions.*;
public class ScalarTypeDurationTest {
class ScalarTypeDurationTest {
ScalarTypeDuration type = new ScalarTypeDuration();
@Test
public void testReadData() throws Exception {
void testReadData() throws Exception {
Duration duration = Duration.ofSeconds(1234);
ByteArrayOutputStream os = new ByteArrayOutputStream();
@@ -39,8 +37,7 @@ public class ScalarTypeDurationTest {
}
@Test
public void testToJdbcType() throws Exception {
void testToJdbcType() throws Exception {
Duration duration = Duration.ofSeconds(1234);
long seconds = duration.getSeconds();
@@ -52,8 +49,7 @@ public class ScalarTypeDurationTest {
}
@Test
public void testToBeanType() throws Exception {
void testToBeanType() throws Exception {
Duration duration = Duration.ofSeconds(1234);
long seconds = duration.getSeconds();
@@ -67,37 +63,34 @@ public class ScalarTypeDurationTest {
}
@Test
public void testFormatValue() throws Exception {
void testFormatValue() {
Duration duration = Duration.ofSeconds(1234);
String formatValue = type.formatValue(duration);
assertEquals("PT20M34S", formatValue);
}
@Test
public void testParse() {
void testParse() {
Duration duration = type.parse("PT20M34S");
assertEquals(Duration.ofSeconds(1234), duration);
}
@Test
public void testIsDateTimeCapable() {
void testIsDateTimeCapable() {
assertFalse(type.isDateTimeCapable());
}
@Test
public void testConvertFromMillis() {
assertThrows(TextException.class, () -> type.convertFromMillis(1000));
void testConvertFromMillis() {
assertThrows(UnsupportedOperationException.class, () -> type.convertFromMillis(1000));
}
@Test
public void testJsonRead() throws Exception {
void testJsonRead() throws Exception {
Duration duration = Duration.ofSeconds(1234);
JsonTester<Duration> jsonTester = new JsonTester<>(type);
jsonTester.test(duration);
}
}
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.type;
import io.ebean.text.TextException;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
@@ -12,13 +11,12 @@ import java.time.Duration;
import static org.junit.jupiter.api.Assertions.*;
public class ScalarTypeDurationWithNanosTest {
class ScalarTypeDurationWithNanosTest {
ScalarTypeDurationWithNanos type = new ScalarTypeDurationWithNanos();
@Test
public void testReadData() throws Exception {
void testReadData() throws Exception {
Duration duration = Duration.ofSeconds(323, 1500000);
ByteArrayOutputStream os = new ByteArrayOutputStream();
@@ -40,8 +38,7 @@ public class ScalarTypeDurationWithNanosTest {
}
@Test
public void testToJdbcType() throws Exception {
void testToJdbcType() throws Exception {
Duration duration = Duration.ofSeconds(323, 1500000);
BigDecimal bigDecimal = DecimalUtils.toDecimal(duration);
@@ -53,8 +50,7 @@ public class ScalarTypeDurationWithNanosTest {
}
@Test
public void testToBeanType() throws Exception {
void testToBeanType() throws Exception {
Duration duration = Duration.ofSeconds(323, 1500000);
BigDecimal bigDecimal = DecimalUtils.toDecimal(duration);
@@ -66,32 +62,31 @@ public class ScalarTypeDurationWithNanosTest {
}
@Test
public void testFormatValue() throws Exception {
void testFormatValue() {
Duration duration = Duration.ofSeconds(323, 1500000);
String formatValue = type.formatValue(duration);
assertEquals("PT5M23.0015S", formatValue);
}
@Test
public void testParse() {
void testParse() {
Duration duration = Duration.ofSeconds(323, 1500000);
Duration val1 = type.parse("PT5M23.0015S");
assertEquals(duration, val1);
}
@Test
public void testIsDateTimeCapable() {
void testIsDateTimeCapable() {
assertFalse(type.isDateTimeCapable());
}
@Test
public void testConvertFromMillis() {
assertThrows(TextException.class, () -> type.convertFromMillis(1000));
void testConvertFromMillis() {
assertThrows(UnsupportedOperationException.class, () -> type.convertFromMillis(1000));
}
@Test
public void testJsonRead() throws Exception {
void testJsonRead() throws Exception {
Duration duration = Duration.ofSeconds(323, 1500000);
JsonTester<Duration> jsonTester = new JsonTester<>(type);
@@ -11,13 +11,12 @@ import java.time.Year;
import static org.junit.jupiter.api.Assertions.*;
public class ScalarTypeYearTest {
class ScalarTypeYearTest {
ScalarTypeYear type = new ScalarTypeYear();
@Test
public void testReadData() throws Exception {
void testReadData() throws Exception {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(os);
@@ -37,8 +36,7 @@ public class ScalarTypeYearTest {
}
@Test
public void testToJdbcType() throws Exception {
void testToJdbcType() throws Exception {
Integer year = 2013;
Object val1 = type.toJdbcType(Year.of(2013));
Object val2 = type.toJdbcType(2013);
@@ -50,8 +48,7 @@ public class ScalarTypeYearTest {
}
@Test
public void testToBeanType() throws Exception {
void testToBeanType() throws Exception {
Year year = Year.of(2013);
Year val1 = type.toBeanType(year);
Year val2 = type.toBeanType(2013);
@@ -63,29 +60,29 @@ public class ScalarTypeYearTest {
}
@Test
public void testFormatValue() throws Exception {
void testFormatValue() {
String formatted = type.formatValue(Year.of(2013));
assertEquals("2013", formatted);
}
@Test
public void testParse() {
void testParse() {
Year year = type.parse("2013");
assertEquals(Year.of(2013), year);
}
@Test
public void testIsDateTimeCapable() {
void testIsDateTimeCapable() {
assertFalse(type.isDateTimeCapable());
}
@Test
public void testConvertFromMillis() {
assertThrows(TextException.class, () -> type.convertFromMillis(1000));
void testConvertFromMillis() {
assertThrows(UnsupportedOperationException.class, () -> type.convertFromMillis(1000));
}
@Test
public void testJson() throws Exception {
void testJson() throws Exception {
JsonTester<Year> jsonTester = new JsonTester<>(type);
jsonTester.test(Year.of(2013));
}
@@ -11,19 +11,12 @@ import io.ebean.util.IOUtils;
import io.ebean.util.JdbcClose;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.dbmigration.model.CurrentModel;
import io.ebeaninternal.dbmigration.model.MTable;
import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
import io.ebeaninternal.server.deploy.PartitionMeta;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.LineNumberReader;
import java.io.Reader;
import java.io.Writer;
import java.io.*;
import java.sql.Connection;
import java.sql.SQLException;
@@ -201,7 +194,6 @@ public class DdlPlugin implements Plugin {
createAllContent = readFile(getCreateFileName());
}
runScript(connection, false, createAllContent, getCreateFileName());
if (extraDdl && jaxbPresent) {
if (currentModel().isTablePartitioning()) {
String extraPartitioning = ExtraDdlXmlReader.buildPartitioning(platform);
@@ -209,43 +201,10 @@ public class DdlPlugin implements Plugin {
runScript(connection, false, extraPartitioning, "builtin-partitioning-ddl");
}
}
String extraApply = ExtraDdlXmlReader.buildExtra(platform, false);
if (extraApply != null) {
runScript(connection, false, extraApply, "extra-ddl");
}
if (currentModel().isTablePartitioning()) {
checkInitialTablePartitions(connection);
}
}
}
/**
* Check if table partitions exist and if not create some. The expectation is that
* extra-ddl.xml should have some partition initialisation but this helps people get going.
*/
private void checkInitialTablePartitions(Connection connection) {
DatabasePlatform databasePlatform = server.databasePlatform();
try {
StringBuilder sb = new StringBuilder();
for (MTable table : currentModel.getPartitionedTables()) {
String tableName = table.getName();
if (!databasePlatform.tablePartitionsExist(connection, tableName)) {
log.info("No table partitions for table {}", tableName);
PartitionMeta meta = table.getPartitionMeta();
String initPart = databasePlatform.tablePartitionInit(tableName, meta.getMode());
sb.append(initPart).append("\n");
}
}
String initialPartitionSql = sb.toString();
if (!initialPartitionSql.isEmpty()) {
runScript(connection, false, initialPartitionSql, "initial table partitions");
}
} catch (SQLException e) {
log.error("Error checking initial table partitions", e);
}
}
@@ -214,6 +214,8 @@ public class BaseTableDdl implements TableDdl {
String partitionMode = createTable.getPartitionMode();
if (partitionMode != null) {
platformDdl.addTablePartition(apply, partitionMode, createTable.getPartitionColumn());
apply.endOfStatement().newLine();
platformDdl.addDefaultTablePartition(apply, createTable.getName());
}
apply.endOfStatement();
@@ -13,6 +13,22 @@ public class NuoDbDdl extends PlatformDdl {
this.dropConstraintIfExists = "drop constraint";
}
@Override
public String createSequence(String sequenceName, DdlIdentity identity) {
StringBuilder sb = new StringBuilder("create sequence ");
sb.append(quote(sequenceName));
int start = identity.getStart();
if (start > 0) {
sb.append(" ").append(sequenceStartWith).append(" ").append(start);
}
int cache = identity.getCache();
if (cache > 0) {
sb.append(" quantum size ").append(cache);
}
sb.append(";");
return sb.toString();
}
@Override
public void addTableComment(DdlBuffer apply, String tableName, String tableComment) {
// do nothing
@@ -733,7 +733,11 @@ public class PlatformDdl {
}
public void addTablePartition(DdlBuffer apply, String partitionMode, String partitionColumn) {
// only supported by postgres initially
// only supported by postgres and yugabyte
}
public void addDefaultTablePartition(DdlBuffer apply, String tableName) {
// only supported by postgres and yugabyte
}
/**
@@ -45,6 +45,11 @@ public class PostgresDdl extends PlatformDdl {
apply.append(" partition by range (").append(partitionColumn).append(")");
}
@Override
public void addDefaultTablePartition(DdlBuffer apply, String tableName) {
apply.append("create table ").append(tableName).append("_default partition of ").append(tableName).append(" default");
}
@Override
public String dropIndex(String indexName, String tableName, boolean concurrent) {
return (concurrent ? dropIndexConcurrentlyIfExists : dropIndexIfExists) + maxConstraintName(indexName);
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<extra-ddl xmlns="http://ebean-orm.github.io/xml/ns/extraddl">
<ddl-script name="partition help" init="true" platforms="postgres">
<ddl-script name="partition help" init="true" platforms="postgres,yugabyte">
-- partitioning helper functions (UTC based)
------------------------------------------------------------------------------------
@@ -7,11 +7,10 @@ import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class PostgresPlatformTest {
class PostgresPlatformTest {
@Test
public void testTypeConversion() {
void testTypeConversion() {
PostgresPlatform platform = new PostgresPlatform();
PlatformDdl ddl = PlatformDdlBuilder.create(platform);
@@ -86,26 +86,6 @@ abstract class ScalarTypePgisBase<T extends Geometry> implements ScalarType<T> {
}
@Override
public boolean isBinaryType() {
return false;
}
@Override
public boolean isMutable() {
return false;
}
@Override
public boolean isDirty(Object value) {
return false;
}
@Override
public int getLength() {
return 0;
}
@Override
public void loadIgnore(DataReader reader) {
reader.incrementPos(1);
@@ -141,22 +121,6 @@ abstract class ScalarTypePgisBase<T extends Geometry> implements ScalarType<T> {
return null;
}
@Override
public boolean isDateTimeCapable() {
return false;
}
@Override
public long asVersion(T value) {
return 0;
}
@Override
public T convertFromMillis(long dateTime) {
return null;
}
@Override
public T jsonRead(JsonParser parser) {
return null;
@@ -77,26 +77,6 @@ abstract class ScalarTypeGeoLatteBase<T extends Geometry> implements ScalarType<
}
@Override
public boolean isBinaryType() {
return false;
}
@Override
public boolean isMutable() {
return false;
}
@Override
public boolean isDirty(Object value) {
return false;
}
@Override
public int getLength() {
return 0;
}
@Override
public void loadIgnore(DataReader reader) {
reader.incrementPos(1);
@@ -132,21 +112,6 @@ abstract class ScalarTypeGeoLatteBase<T extends Geometry> implements ScalarType<
return null;
}
@Override
public boolean isDateTimeCapable() {
return false;
}
@Override
public long asVersion(T value) {
return 0;
}
@Override
public T convertFromMillis(long dateTime) {
return null;
}
@Override
public T jsonRead(JsonParser parser) {
return null;
+1 -1
View File
@@ -228,7 +228,7 @@
<dependency>
<groupId>com.nuodb.jdbc</groupId>
<artifactId>nuodb-jdbc</artifactId>
<version>22.0.0</version>
<version>23.0.0</version>
<scope>test</scope>
</dependency>
@@ -17,7 +17,7 @@ class Config {
/**
* Common optional docker parameters that we just transfer to docker properties.
*/
private static final String[] DOCKER_TEST_PARAMS = {"fastStartMode", "inMemory", "initSqlFile", "seedSqlFile", "adminUser", "adminPassword", "extraDb", "extraDb.dbName", "extraDb.username", "extraDb.password", "extraDb.initSqlFile", "extraDb.seedSqlFile"};
private static final String[] DOCKER_TEST_PARAMS = {"fastStartMode", "inMemory", "initSqlFile", "seedSqlFile", "adminUser", "adminPassword", "extraDb", "extraDb.dbName", "extraDb.username", "extraDb.password", "extraDb.extensions", "extraDb.initSqlFile", "extraDb.seedSqlFile"};
private static final String[] DOCKER_PLATFORM_PARAMS = {"containerName", "image", "internalPort", "startMode", "shutdownMode", "maxReadyAttempts", "tmpfs", "collation", "characterSet"};
private static final String DDL_MODE_OPTIONS = "dropCreate, create, none, migration, createOnly or migrationDropCreate";
@@ -224,14 +224,22 @@ class Config {
properties.setProperty(dsKey, val);
}
void setUrl(String urlPattern) {
String val = getKey("url", urlPattern);
private void setUrl(String key, String urlPattern) {
String val = getKey(key, urlPattern);
val = val.replace("${host}", host());
val = val.replace("${port}", String.valueOf(port));
val = val.replace("${databaseName}", databaseName);
this.url = val;
}
void setUrl(String urlPattern) {
setUrl("url", urlPattern);
}
void setExtraUrl(String urlPattern) {
setUrl("extraDb.url", urlPattern);
}
String host() {
String explicitDockerHost = getKey("dockerHost", null);
return getKey("host", dockerHost.dockerHost(explicitDockerHost));
@@ -343,9 +351,17 @@ class Config {
void setExtensions(String defaultValue) {
// ebean.test.postgres.extensions=hstore,pgcrypto
String val = getKey("extensions", defaultValue);
setExtensionsInternal("extensions", defaultValue);
}
void setExtraExtensions(String defaultValue) {
setExtensionsInternal("extraDb.extensions", defaultValue);
}
void setExtensionsInternal(String key, String defaultValue) {
String val = getKey(key, defaultValue);
if (val != null) {
dockerProperties.setProperty(dockerKey("extensions"), trimExtensions(val));
dockerProperties.setProperty(dockerKey(key), trimExtensions(val));
}
}
@@ -6,25 +6,21 @@ class OracleSetup implements PlatformSetup {
@Override
public Properties setup(Config config) {
config.ddlMode("dropCreate");
config.setDefaultPort(1521);
config.setUsernameDefault();
config.setPasswordDefault();
config.setDatabaseName("XE");
config.setUrl("jdbc:oracle:thin:@localhost:${port}:${databaseName}");
config.setDriver("oracle.jdbc.driver.OracleDriver");
config.datasourceDefaults();
return dockerProperties(config);
}
private Properties dockerProperties(Config dbConfig) {
if (!dbConfig.isUseDocker()) {
return new Properties();
}
dbConfig.setDockerVersion("latest");
dbConfig.setDockerVersion("21.3.0-slim");
return dbConfig.getDockerProperties();
}
@@ -12,8 +12,7 @@ class PostgisSetup implements PlatformSetup {
@Override
public Properties setup(Config config) {
int defaultPort = config.isUseDocker() ? 7432 : 5432;
config.setDockerPlatform("postgres");
config.setDockerPlatform("postgis");
config.ddlMode("dropCreate");
config.setDefaultPort(defaultPort);
config.setUsernameDefault();
@@ -35,14 +34,18 @@ class PostgisSetup implements PlatformSetup {
}
config.setExtensions("hstore,pgcrypto,postgis");
config.setDockerContainerName("ut_postgis");
config.setDockerImage("postgis/postgis");
config.setDockerVersion("14");
config.setDockerVersion("14-3.2");
return config.getDockerProperties();
}
@Override
public void setupExtraDbDataSource(Config config) {
// not supported yet
int defaultPort = config.isUseDocker() ? 7432 : 5432;
config.setDefaultPort(defaultPort);
config.setExtraUsernameDefault();
config.setExtraDbPasswordDefault();
config.setExtraUrl("jdbc:postgresql_lwgis://${host}:${port}/${databaseName}");
config.extraDatasourceDefaults();
}
@Override
@@ -35,6 +35,43 @@ class ConfigTest {
assertThat(config.trimExtensions(" a , , b ")).isEqualTo("a,b");
}
@Test
void extensions_whenNoSetValues() {
DatabaseConfig databaseConfig = new DatabaseConfig();
databaseConfig.loadFromProperties(new Properties());
Config config = new Config("db", "postgis", "db", databaseConfig);
config.setUsernameDefault();
config.setPasswordDefault();
config.setDefaultPort(42);
config.setExtensions("a,b");
config.setExtraExtensions("c,d");
Properties dockerProperties = config.getDockerProperties();
assertThat(dockerProperties.getProperty("postgis.extensions")).isEqualTo("a,b");
assertThat(dockerProperties.getProperty("postgis.extraDb.extensions")).isEqualTo("c,d");
}
@Test
void extensions_whenSetValues() {
DatabaseConfig databaseConfig = new DatabaseConfig();
Properties properties = new Properties();
properties.setProperty("ebean.test.extensions", "x,y");
properties.setProperty("ebean.test.extraDb.extensions", "z");
databaseConfig.loadFromProperties(properties);
Config config = new Config("db", "postgis", "db", databaseConfig);
config.setExtensions("a,b");
config.setExtraExtensions("c,d");
Properties dockerProperties = config.getDockerProperties();
assertThat(dockerProperties.getProperty("postgis.extensions")).isEqualTo("x,y");
assertThat(dockerProperties.getProperty("postgis.extraDb.extensions")).isEqualTo("z");
}
@Test
void extraDbProperties_basic() {
Properties p = new Properties();
@@ -4,6 +4,7 @@ package io.ebean.xtest.event;
import io.ebean.Database;
import io.ebean.DatabaseFactory;
import io.ebean.Transaction;
import io.ebean.ValuePair;
import io.ebean.config.DatabaseConfig;
import io.ebean.event.BeanDeleteIdRequest;
import io.ebean.event.BeanPersistAdapter;
@@ -13,9 +14,7 @@ import org.tests.model.basic.EBasicVer;
import org.tests.model.basic.UTDetail;
import org.tests.model.basic.UTMaster;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.*;
import static org.assertj.core.api.Assertions.assertThat;
@@ -26,8 +25,30 @@ public class BeanPersistControllerTest {
private final PersistAdapter stopPersistingAdapter = new PersistAdapter(false);
@Test
public void issue_1341() {
public void issued1() {
Database db = getDatabase(continuePersistingAdapter);
UTMaster bean0 = new UTMaster("m0");
bean0.setJournal(new UTMaster.Journal());
db.save(bean0);
UTMaster change0 = db.find(UTMaster.class, bean0.getId());
change0.setName("change0");
db.save(change0);
UTMaster change1 = db.find(UTMaster.class, bean0.getId());
change1.setName("change1");
db.save(change1);
UTMaster again = db.find(UTMaster.class, bean0.getId());
UTMaster.Journal journal = again.getJournal();
assertThat(journal.getEntries()).hasSize(2);
db.shutdown();
}
@Test
public void issue_1341() {
Database db = getDatabase(continuePersistingAdapter);
UTMaster bean0 = new UTMaster("one0");
@@ -118,7 +139,6 @@ public class BeanPersistControllerTest {
}
private Database getDatabase(PersistAdapter persistAdapter) {
DatabaseConfig config = new DatabaseConfig();
config.setName("h2ebasicver");
config.loadFromProperties();
@@ -133,7 +153,6 @@ public class BeanPersistControllerTest {
config.getClasses().add(UTDetail.class);
config.add(persistAdapter);
return DatabaseFactory.create(config);
}
@@ -177,6 +196,16 @@ public class BeanPersistControllerTest {
// invoke lazy loading ... which invoke the flush of the jdbc batch
detail.setQty(42);
}
if (bean instanceof UTMaster) {
UTMaster master = (UTMaster)bean;
UTMaster.Journal journal = master.getJournal();
if (journal == null) {
journal = new UTMaster.Journal();
master.setJournal(journal);
}
// modify a "mutable scalar type" in preUpdate, should be included in update
journal.addEntry();
}
return continueDefaultPersisting;
}
@@ -178,16 +178,16 @@ class DefaultPersistenceContextTest {
assertThat(pc.size(Customer.class)).isEqualTo(100);
assertThat(pc.size(Contact.class)).isEqualTo(1010);
addCustomers(pc, 200, 100);
addContacts(pc, 2000, 1010);
assertThat(pc.size(Customer.class)).isEqualTo(200);
assertThat(pc.size(Contact.class)).isEqualTo(2020);
addCustomers(pc, 200, 103);
assertThat(pc.size(Customer.class)).isEqualTo(203);
addContacts(pc, 2000, 1013);
assertThat(pc.size(Contact.class)).isEqualTo(2023);
pc.endIterate();
System.gc();
Thread.sleep(50); // give the GC some time
Thread.sleep(100); // give the GC some time
// back to pre beginIterate() now
assertThat(pc.size(Customer.class)).isEqualTo(100);
assertThat(pc.size(Contact.class)).isEqualTo(1010);
}
@@ -5,12 +5,12 @@ import io.ebean.test.containers.PostgresContainer;
public class StartPostgres {
public static void main(String[] args) {
PostgresContainer.builder("13")
.port(5432)
PostgresContainer.builder("14")
.dbName("unit")
.user("unit")
.password("unit")
.containerName("pg13x")
//.port(6432)
//.user("unit")
//.password("test")
//.containerName("ut_postgres")
.extensions("hstore,pgcrypto")
.build()
.startWithDropCreate();
@@ -7,45 +7,15 @@ import org.tests.model.basic.ESimple;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class TestSimpleIdInsert extends BaseTestCase {
class TestSimpleIdInsert extends BaseTestCase {
@Test
public void test() {
void test() {
ESimple e = new ESimple();
e.setName("name");
DB.save(e);
assertNotNull(e.getId());
}
// // This test fails with jdbc drivers that don't
// // support batch insert with getGeneratedKeys
// public void testJdbcBatch() {
//
// GlobalProperties.put("datasource.default", "hsqldb");
// GlobalProperties.put("ebean.classes", ESimple.class.getName());
//
// Transaction transaction = DB.beginTransaction();
// try {
// transaction.setBatchMode(true);
// transaction.setLogLevel(LogLevel.SQL);
// ESimple e = new ESimple();
// e.setName("name");
// DB.save(e);
//
// ESimple e2 = new ESimple();
// e2.setName("name2");
// DB.save(e2);
// transaction.commit();
//
// Assert.assertNotNull(e.getId());
// Assert.assertNotNull(e2.getId());
//
// } finally {
// DB.endTransaction();
// }
// }
}
@@ -0,0 +1,47 @@
package org.tests.json;
import io.ebean.DB;
import io.ebean.test.LoggedSql;
import org.junit.jupiter.api.Test;
import org.tests.model.json.EBasicJsonBString;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
class TestDbJsonBStringType {
@Test
void test() {
var bean = new EBasicJsonBString("hi").content("{\"mykey\": 42}"); // JsonB format with space
DB.save(bean);
var found = DB.find(EBasicJsonBString.class, bean.id());
// Note that Postgres JsonB will format the result
assertThat(found.content()).isEqualTo("{\"mykey\": 42}");
LoggedSql.start();
// change title only, expect content not in update
found.title("changeTitleOnly");
DB.save(found);
List<String> sql = LoggedSql.collect();
// update does NOT contain our json content
assertThat(sql.get(0)).contains("update ebasic_json_bstring set title=?, version=? where id=? and version=?");
// change title and content
found.title("changeAgain");
found.content("{\"mykey\": 92}");
DB.save(found);
sql = LoggedSql.collect();
assertThat(sql.get(0)).contains("update ebasic_json_bstring set title=?, content=?, version=? where id=? and version=?");
// change content only
found.content("{\"mykey\": 95}");
DB.save(found);
sql = LoggedSql.stop();
assertThat(sql.get(0)).contains("update ebasic_json_bstring set content=?, version=? where id=? and version=?");
}
}
@@ -0,0 +1,47 @@
package org.tests.json;
import io.ebean.DB;
import io.ebean.test.LoggedSql;
import org.junit.jupiter.api.Test;
import org.tests.model.json.EBasicJsonString;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
class TestDbJsonStringType {
@Test
void test() {
var bean = new EBasicJsonString("hi").content("{\"mykey\": 52}"); // JsonB format with space
DB.save(bean);
var found = DB.find(EBasicJsonString.class, bean.id());
// Note that Postgres JsonB will format the result
assertThat(found.content()).isEqualTo("{\"mykey\": 52}");
LoggedSql.start();
// change title only, expect content not in update
found.title("changeTitleOnly");
DB.save(found);
List<String> sql = LoggedSql.collect();
// update does NOT contain our json content
assertThat(sql.get(0)).contains("update ebasic_json_string set title=?, version=? where id=? and version=?");
// change title and content
found.title("changeAgain");
found.content("{\"mykey\": 92}");
DB.save(found);
sql = LoggedSql.collect();
assertThat(sql.get(0)).contains("update ebasic_json_string set title=?, content=?, version=? where id=? and version=?");
// change content only
found.content("{\"mykey\": 95}");
DB.save(found);
sql = LoggedSql.stop();
assertThat(sql.get(0)).contains("update ebasic_json_string set content=?, version=? where id=? and version=?");
}
}
@@ -1,9 +1,11 @@
package org.tests.model.basic;
import io.ebean.Model;
import io.ebean.annotation.DbJsonB;
import javax.persistence.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@@ -20,12 +22,31 @@ public class UTMaster extends Model {
LocalDate eventDate;
@DbJsonB
Journal journal;
@Version
Integer version;
@OneToMany(cascade = CascadeType.ALL)
List<UTDetail> details;
/**
* Mutating content persisted as JSON.
*/
public static class Journal {
private List<String> entries = new ArrayList<>();
public List<String> getEntries() {
return entries;
}
public void setEntries(List<String> entries) {
this.entries = entries;
}
public void addEntry() {
entries.add(LocalDateTime.now().toString());
}
}
public UTMaster() {
}
@@ -74,6 +95,14 @@ public class UTMaster extends Model {
this.version = version;
}
public Journal getJournal() {
return journal;
}
public void setJournal(Journal journal) {
this.journal = journal;
}
public List<UTDetail> getDetails() {
return details;
}
@@ -0,0 +1,62 @@
package org.tests.model.json;
import io.ebean.annotation.DbJsonB;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Version;
@Entity
public class EBasicJsonBString {
@Id
long id;
String title;
@DbJsonB
String content;
@Version
long version;
public EBasicJsonBString(String title) {
this.title = title;
}
public long id() {
return id;
}
public EBasicJsonBString id(long id) {
this.id = id;
return this;
}
public String title() {
return title;
}
public EBasicJsonBString title(String title) {
this.title = title;
return this;
}
public String content() {
return content;
}
public EBasicJsonBString content(String content) {
this.content = content;
return this;
}
public long version() {
return version;
}
public EBasicJsonBString version(long version) {
this.version = version;
return this;
}
}
@@ -0,0 +1,62 @@
package org.tests.model.json;
import io.ebean.annotation.DbJson;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Version;
@Entity
public class EBasicJsonString {
@Id
long id;
String title;
@DbJson
String content;
@Version
long version;
public EBasicJsonString(String title) {
this.title = title;
}
public long id() {
return id;
}
public EBasicJsonString id(long id) {
this.id = id;
return this;
}
public String title() {
return title;
}
public EBasicJsonString title(String title) {
this.title = title;
return this;
}
public String content() {
return content;
}
public EBasicJsonString content(String content) {
this.content = content;
return this;
}
public long version() {
return version;
}
public EBasicJsonString version(long version) {
this.version = version;
return this;
}
}
+1 -1
View File
@@ -1,3 +1,3 @@
ebean.test.platform=nuodb
ebean.test.dbName=testdb
ebean.test.nuodb.version=latest
ebean.test.nuodb.version=4.3.2
@@ -147,40 +147,4 @@ public class PostgresPlatform extends DatabasePlatform {
}
return FOR_UPDATE;
}
@Override
public boolean tablePartitionsExist(Connection connection, String table) throws SQLException {
try (PreparedStatement statement = connection.prepareStatement("select count(*) from pg_inherits i WHERE i.inhparent = ?::regclass")) {
statement.setString(1, table);
try (ResultSet resultSet = statement.executeQuery()) {
return resultSet.next() && resultSet.getInt(1) > 0;
}
}
}
/**
* Return SQL using built in partition helper functions to create some initial partitions.
* <p>
* Only use this if extra-ddl doesn't have some initial partitions defined (which it should).
*/
@Override
public String tablePartitionInit(String tableName, PartitionMode mode) {
// default partition required pg11 but this is only used for testing but bumped test docker container to pg14 by default
String[] schemaTable = SplitName.split(tableName);
String baseTable;
String plusSchema;
if (schemaTable[0] == null) {
plusSchema = "";
baseTable = tableName;
} else {
// table in an explicit schema
plusSchema = ",'" + schemaTable[0] + "'";
baseTable = schemaTable[1];
}
return
"create table " + tableName + "_default" + " partition of " + tableName + " default;\n" +
"select partition('" + mode.name().toLowerCase() + "','" + baseTable + "',1" + plusSchema + ");";
}
}
@@ -1,6 +1,5 @@
package io.ebean.platform.postgres;
import io.ebean.annotation.PartitionMode;
import io.ebean.annotation.Platform;
import io.ebean.config.PlatformConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
@@ -16,7 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
class PostgresPlatformTest {
@Test
void testUuidType() {
void testUuidType() {
PostgresPlatform platform = new PostgresPlatform();
platform.configure(new PlatformConfig());
@@ -26,20 +25,6 @@ class PostgresPlatformTest {
assertThat(columnDefn).isEqualTo("uuid");
}
@Test
void tablePartitionInit() {
String sql = new PostgresPlatform().tablePartitionInit("foo", PartitionMode.WEEK);
assertThat(sql).isEqualTo("create table foo_default partition of foo default;\n" +
"select partition('week','foo',1);");
}
@Test
void tablePartitionInit_withSchema() {
String sql = new PostgresPlatform().tablePartitionInit("bar.foo", PartitionMode.WEEK);
assertThat(sql).isEqualTo("create table bar.foo_default partition of bar.foo default;\n" +
"select partition('week','foo',1,'bar');");
}
@Test
void default_forUpdate_expect_noKeyUsed() {
PostgresPlatform platform = new PostgresPlatform();
+3 -3
View File
@@ -44,10 +44,10 @@
<ebean-ddl-runner.version>2.0</ebean-ddl-runner.version>
<ebean-migration-auto.version>1.2</ebean-migration-auto.version>
<ebean-migration.version>13.6.0</ebean-migration.version>
<ebean-test-containers.version>6.0</ebean-test-containers.version>
<ebean-test-containers.version>6.1</ebean-test-containers.version>
<ebean-datasource.version>8.0</ebean-datasource.version>
<ebean-agent.version>13.6.3</ebean-agent.version>
<ebean-maven-plugin.version>13.6.3</ebean-maven-plugin.version>
<ebean-agent.version>13.6.4</ebean-agent.version>
<ebean-maven-plugin.version>13.6.4</ebean-maven-plugin.version>
<surefire.useModulePath>false</surefire.useModulePath>
</properties>
+1 -1
View File
@@ -52,7 +52,7 @@
<path>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>13.6.4-SNAPSHOT</version>
<version>13.6.5-SNAPSHOT</version>
</path>
</annotationProcessorPaths>
</configuration>