Compare commits

..
Author SHA1 Message Date
rob bygrave 4d4886d808 [maven-release-plugin] prepare release ebean-11.22.2 2018-08-21 21:15:07 +12:00
rob bygrave d11bb2c4e5 No effective change - add a new test for OneToMany orphanRemoval=true 2018-08-21 21:08:17 +12:00
rob bygrave 663b1bb4aa #1474 - IllegalStateException: Table [some_table] does not exists in model? ... when applying drop table migration and table is no longer in the model 2018-08-21 20:45:31 +12:00
SnōwballandRob Bygrave f3d26e32ec Possible fix error with multiple uses of enum (#1473)
When an enum is exists in the type cache and isn't String, @DbArray
fails. This includes a test that should fail everywhere (order of
reading fields isn't guaranteed afaik and the column needs to be read
before the @DbArray for the test to fail).
2018-08-21 20:26:08 +12:00
rob bygrave 5f83584315 [maven-release-plugin] prepare for next development iteration 2018-08-16 00:05:31 +12:00
rob bygrave d28e3ee456 [maven-release-plugin] prepare release ebean-11.22.1 2018-08-16 00:05:16 +12:00
rob bygrave e1b7161b22 Bump version to 11.22.1-SNAPSHOT 2018-08-16 00:03:15 +12:00
rob bygrave f63a3cb926 Bump ebean-migration to 11.9.1 2018-08-16 00:02:10 +12:00
rob bygrave 29c5a17202 #1472 - Cascade insert with managed id ... results in update rather than insert statement 2018-08-15 23:12:06 +12:00
rob bygrave 340b100a1a No effective change - Add test for OneToOne orphanRemoval=true and String Ids (non generated ids) 2018-08-14 22:09:01 +12:00
rob bygrave 80975acfbf #1471 - Change stateless update on bean with no @Version property that updates 0 rows to throw EntityNotFoundException 2018-08-14 14:23:28 +12:00
rob bygrave 95dab97b1b No effective change - adjust test adding @Draft property to @Draftable Document 2018-08-14 14:16:08 +12:00
rob bygrave ce10dd79f9 [maven-release-plugin] prepare for next development iteration 2018-08-13 22:06:41 +12:00
rob bygrave 3f12739e71 [maven-release-plugin] prepare release ebean-11.21.1 2018-08-13 22:06:26 +12:00
rob bygrave cd1ae20015 Bump to 11.21.1-SNAPSHOT 2018-08-13 22:00:15 +12:00
rob bygrave 6d1e3a08e1 #1470 - Change OnQueryOnly to default to OnQueryOnly.COMMIT 2018-08-13 21:59:33 +12:00
rob bygrave 94e6fe90e5 No effective change - white space format 2018-08-13 21:54:19 +12:00
SnōwballandRob Bygrave 9bbe99657a Let enum arrays be saved as integers or strings (#1467)
* Let enums be saved as integers or strings as needed

* Add tests for int and varchar @DbArray enums
2018-08-13 21:26:14 +12:00
rob bygrave 0faf9f54ea [maven-release-plugin] prepare for next development iteration 2018-08-10 15:54:42 +12:00
29 changed files with 600 additions and 82 deletions
+3 -3
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.20.2</version>
<version>11.22.2</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -22,7 +22,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-11.20.2</tag>
<tag>ebean-11.22.2</tag>
</scm>
<profiles>
@@ -135,7 +135,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration</artifactId>
<version>11.8.2</version>
<version>11.9.1</version>
</dependency>
<dependency>
@@ -52,7 +52,7 @@ public class DatabasePlatform {
/**
* The behaviour used when ending a read only transaction at read committed isolation level.
*/
protected OnQueryOnly onQueryOnly = OnQueryOnly.ROLLBACK;
protected OnQueryOnly onQueryOnly = OnQueryOnly.COMMIT;
/**
* The open quote used by quoted identifiers.
@@ -184,13 +184,12 @@ public class ModelContainer {
/**
* Unset the withHistory flag on the associated base table.
*/
private void applyChange(DropHistoryTable change) {
protected void applyChange(DropHistoryTable change) {
MTable table = tables.get(change.getBaseTable());
if (table == null) {
throw new IllegalStateException("Table [" + change.getBaseTable() + "] does not exist in model?");
if (table != null) {
table.setWithHistory(false);
}
table.setWithHistory(false);
}
private void applyChange(AddUniqueConstraint change) {
@@ -241,19 +240,14 @@ public class ModelContainer {
if (tables.containsKey(tableName)) {
throw new IllegalStateException("Table [" + tableName + "] already exists in model?");
}
MTable table = new MTable(createTable);
tables.put(tableName, table);
tables.put(tableName, new MTable(createTable));
}
/**
* Apply a DropTable change to the model.
*/
protected void applyChange(DropTable dropTable) {
String tableName = dropTable.getName();
if (!tables.containsKey(tableName)) {
throw new IllegalStateException("Table [" + tableName + "] does not exists in model?");
}
tables.remove(tableName);
tables.remove(dropTable.getName());
}
/**
@@ -264,22 +258,16 @@ public class ModelContainer {
if (indexes.containsKey(indexName)) {
throw new IllegalStateException("Index [" + indexName + "] already exists in model?");
}
MIndex index = new MIndex(createIndex);
indexes.put(createIndex.getIndexName(), index);
indexes.put(createIndex.getIndexName(), new MIndex(createIndex));
}
/**
* Apply a DropTable change to the model.
*/
protected void applyChange(DropIndex dropIndex) {
String name = dropIndex.getIndexName();
if (!indexes.containsKey(name)) {
throw new IllegalStateException("Index [" + name + "] does not exist in model?");
}
indexes.remove(name);
indexes.remove(dropIndex.getIndexName());
}
/**
* Apply a AddColumn change to the model.
*/
@@ -31,6 +31,7 @@ import io.ebeanservice.docstore.api.DocStoreUpdate;
import io.ebeanservice.docstore.api.DocStoreUpdateContext;
import io.ebeanservice.docstore.api.DocStoreUpdates;
import javax.persistence.EntityNotFoundException;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import java.io.IOException;
@@ -854,12 +855,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
*/
@Override
public final void checkRowCount(int rowCount) {
if (ConcurrencyMode.VERSION == concurrencyMode && rowCount != 1) {
// fix for oracle.
// see: https://stackoverflow.com/questions/19022175/executebatch-method-return-array-of-value-2-in-java
if (rowCount != Statement.SUCCESS_NO_INFO) {
String m = Message.msg("persist.conc2", String.valueOf(rowCount));
throw new OptimisticLockException(m, null, bean);
if (rowCount != 1 && rowCount != Statement.SUCCESS_NO_INFO) {
if (ConcurrencyMode.VERSION == concurrencyMode) {
throw new OptimisticLockException(Message.msg("persist.conc2", String.valueOf(rowCount)), null, bean);
} else if (rowCount == 0 && type == Type.UPDATE) {
throw new EntityNotFoundException("No rows updated");
}
}
switch (type) {
@@ -1307,14 +1307,22 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* Set the request flags indicating this is an insert.
*/
public void flagInsert() {
flags = Flags.setInsert(flags);
if (intercept.isNew()) {
flags = Flags.setInsertNormal(flags);
} else {
flags = Flags.setInsert(flags);
}
}
/**
* Unset the request insert flag indicating this is an update.
*/
public void flagUpdate() {
flags = Flags.unsetInsert(flags);
if (intercept.isLoaded()) {
flags = Flags.setUpdateNormal(flags);
} else {
flags = Flags.setUpdate(flags);
}
}
/**
@@ -456,8 +456,7 @@ public final class DefaultPersister implements Persister {
void saveRecurse(EntityBean bean, Transaction t, Object parentBean, int flags) {
// determine insert or update taking into account stateless updates
PersistRequestBean<?> request = createRequestRecurse(bean, t, parentBean, flags);
saveRecurse(request);
saveRecurse(createRequestRecurse(bean, t, parentBean, flags));
}
private void saveRecurse(PersistRequestBean<?> request) {
@@ -1229,8 +1228,8 @@ public final class DefaultPersister implements Persister {
BeanDescriptor<T> desc = mgr.getBeanDescriptor();
EntityBean entityBean = (EntityBean) bean;
PersistRequest.Type type;
if (Flags.isPublishOrMerge(flags)) {
// insert if it is a new bean (as publish created it)
if (Flags.isPublishMergeOrNormal(flags)) {
// just use bean state to determine insert or update
type = entityBean._ebean_getIntercept().isUpdate() ? Type.UPDATE : Type.INSERT;
} else {
// determine Insert or Update based on bean state and insert flag
@@ -5,7 +5,7 @@ package io.ebeaninternal.server.persist;
* <p>
* Allows passing of flag state when recursively persisting.
*/
public class Flags {
public final class Flags {
/**
* Indicates the bean is being inserted.
@@ -27,6 +27,11 @@ public class Flags {
*/
public static final int MERGE = 0x00000008;
/**
* Indicates Normal insert or update (not forced).
*/
public static final int NORMAL = 0x00000010;
/**
* No flags set.
*/
@@ -34,7 +39,9 @@ public class Flags {
public static final int PUBLISH_RECURSE = PUBLISH + RECURSE;
private static final int PUBLISH_MERGE = PUBLISH + MERGE;
private static final int PUBLISH_MERGE_NORMAL = PUBLISH + MERGE + NORMAL;
private static final int INSERT_NORMAL = INSERT + NORMAL;
/**
* Return true if the bean is being inserted.
@@ -65,10 +72,10 @@ public class Flags {
}
/**
* Return true if part of a Merge or Publish.
* Return true if part of a Merge or Publish or Normal (bean state matches persist).
*/
public static boolean isPublishOrMerge(long state) {
return (state & PUBLISH_MERGE) != 0;
public static boolean isPublishMergeOrNormal(int state) {
return (state & PUBLISH_MERGE_NORMAL) != 0;
}
/**
@@ -82,54 +89,70 @@ public class Flags {
* Set Insert flag.
*/
public static int setInsert(int state) {
return set(state, INSERT, true);
return set(state, INSERT);
}
/**
* Insert flag and normal in that bean is in NEW state (for insert).
*/
public static int setInsertNormal(int state) {
return set(state, INSERT_NORMAL);
}
/**
* Parent was not inserted.
*/
public static int unsetInsert(int state) {
return set(state, INSERT, false);
public static int setUpdate(int state) {
return unset(state, INSERT);
}
/**
* Not Insert and normal in that bean is in LOADED state (for update).
*/
public static int setUpdateNormal(int state) {
state &= ~INSERT;
state |= NORMAL;
return state;
}
/**
* Set Recurse flag.
*/
public static int setRecurse(int state) {
return set(state, RECURSE, true);
return set(state, RECURSE);
}
public static int unsetRecuse(int state) {
return set(state, RECURSE, false);
return unset(state, RECURSE);
}
/**
* Set Publish flag.
*/
public static int setPublish(int state) {
return set(state, PUBLISH, true);
return set(state, PUBLISH);
}
public static int unsetPublish(int state) {
return set(state, PUBLISH, false);
return unset(state, PUBLISH);
}
/**
* Set Merge flag.
*/
public static int setMerge(int state) {
return set(state, MERGE, true);
return set(state, MERGE);
}
public static int unsetMerge(int state) {
return set(state, MERGE, false);
return unset(state, MERGE);
}
private static int set(int state, int flag, boolean setFlag) {
if (setFlag) {
return (state |= flag);
} else {
return state &= ~flag;
}
private static int set(int state, int flag) {
return (state |= flag);
}
private static int unset(int state, int flag) {
return state &= ~flag;
}
}
@@ -7,7 +7,6 @@ import io.ebeaninternal.server.lib.util.Str;
import io.ebeaninternal.server.persist.BatchedPstmt;
import io.ebeaninternal.server.persist.BatchedPstmtHolder;
import io.ebeaninternal.server.persist.dmlbind.BindableRequest;
import io.ebeaninternal.server.transaction.TransactionManager;
import io.ebeaninternal.server.type.DataBind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -301,7 +301,7 @@ public class TransactionManager implements SpiTransactionManager {
}
// default to rollback if not defined on the platform
return dbPlatformOnQueryOnly == null ? OnQueryOnly.ROLLBACK : dbPlatformOnQueryOnly;
return dbPlatformOnQueryOnly == null ? OnQueryOnly.COMMIT : dbPlatformOnQueryOnly;
}
public String getServerName() {
@@ -112,14 +112,14 @@ interface ArrayElementConverter<T> {
@Override
public Object toElement(Object rawValue) {
return scalarType.parse(rawValue.toString());
return scalarType.toBeanType(rawValue);
}
@Override
public Object[] toDbArray(Object[] objects) {
Object[] dbArray = new Object[objects.length];
for (int i = 0; i < objects.length; i++) {
dbArray[i] = scalarType.format(objects[i]);
dbArray[i] = scalarType.toJdbcType(objects[i]);
}
return dbArray;
}
@@ -351,7 +351,7 @@ public final class DefaultTypeManager implements TypeManager {
if (type.equals(List.class)) {
if (arrayTypeListFactory != null) {
if (isEnumType(valueType)) {
return arrayTypeListFactory.typeForEnum(createEnumScalarType(asEnumClass(valueType), EnumType.STRING));
return arrayTypeListFactory.typeForEnum(createEnumScalarType(asEnumClass(valueType), null));
}
return arrayTypeListFactory.typeFor(valueType);
}
@@ -360,7 +360,7 @@ public final class DefaultTypeManager implements TypeManager {
} else if (type.equals(Set.class)) {
if (arrayTypeSetFactory != null) {
if (isEnumType(valueType)) {
return arrayTypeSetFactory.typeForEnum(createEnumScalarType(asEnumClass(valueType), EnumType.STRING));
return arrayTypeSetFactory.typeForEnum(createEnumScalarType(asEnumClass(valueType), null));
}
return arrayTypeSetFactory.typeFor(valueType);
}
@@ -60,7 +60,19 @@ public class ScalarTypeArrayList extends ScalarTypeJsonCollection<List> implemen
@Override
public ScalarTypeArrayList typeForEnum(ScalarType<?> scalarType) {
return new ScalarTypeArrayList("varchar", DocPropertyType.TEXT, new ArrayElementConverter.EnumConverter(scalarType));
final String arrayType;
switch (scalarType.getJdbcType()) {
case Types.INTEGER:
arrayType = "integer";
break;
case Types.VARCHAR:
arrayType = "varchar";
break;
default:
throw new IllegalArgumentException("JdbcType [" + scalarType.getJdbcType() + "] not supported for @DbArray mapping on set.");
}
return new ScalarTypeArrayList(arrayType, scalarType.getDocType(), new ArrayElementConverter.EnumConverter(scalarType));
}
}
@@ -54,13 +54,24 @@ public class ScalarTypeArraySet<T> extends ScalarTypeJsonCollection<Set<T>> impl
if (valueType.equals(String.class)) {
return STRING;
}
throw new IllegalArgumentException("Type [" + valueType + "] not supported for @DbArray mapping on set");
throw new IllegalArgumentException("Type [" + valueType + "] not supported for @DbArray mapping on list");
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
@SuppressWarnings({"unchecked", "rawtypes"})
public ScalarTypeArraySet typeForEnum(ScalarType<?> scalarType) {
return new ScalarTypeArraySet("varchar", DocPropertyType.TEXT, new ArrayElementConverter.EnumConverter(scalarType));
final String arrayType;
switch (scalarType.getJdbcType()) {
case Types.INTEGER:
arrayType = "integer";
break;
case Types.VARCHAR:
arrayType = "varchar";
break;
default:
throw new IllegalArgumentException("JdbcType [" + scalarType.getJdbcType() + "] not supported for @DbArray mapping on set.");
}
return new ScalarTypeArraySet(arrayType, scalarType.getDocType(), new ArrayElementConverter.EnumConverter(scalarType));
}
}
@@ -68,9 +79,9 @@ public class ScalarTypeArraySet<T> extends ScalarTypeJsonCollection<Set<T>> impl
private final ArrayElementConverter<T> converter;
@SuppressWarnings({ "unchecked", "rawtypes" })
@SuppressWarnings({"unchecked", "rawtypes"})
public ScalarTypeArraySet(String arrayType, DocPropertyType docPropertyType, ArrayElementConverter<T> converter) {
super((Class)Set.class, Types.ARRAY, docPropertyType);
super((Class) Set.class, Types.ARRAY, docPropertyType);
this.arrayType = arrayType;
this.converter = converter;
}
@@ -7,6 +7,7 @@ import java.sql.SQLException;
import java.sql.Types;
import java.util.Set;
import java.util.UUID;
/**
* H2 database support for DB ARRAY.
*/
@@ -48,13 +49,13 @@ class ScalarTypeArraySetH2<T> extends ScalarTypeArraySet<T> {
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
@SuppressWarnings({"unchecked", "rawtypes"})
public ScalarTypeArraySetH2 typeForEnum(ScalarType<?> scalarType) {
return new ScalarTypeArraySetH2("varchar", DocPropertyType.TEXT, new ArrayElementConverter.EnumConverter(scalarType));
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@SuppressWarnings({"unchecked", "rawtypes"})
private ScalarTypeArraySetH2(String arrayType, DocPropertyType docPropertyType, ArrayElementConverter converter) {
super(arrayType, docPropertyType, converter);
}
@@ -1,6 +1,9 @@
package io.ebeaninternal.dbmigration.model;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.migration.DropIndex;
import io.ebeaninternal.dbmigration.migration.DropTable;
import io.ebeaninternal.dbmigration.migration.Migration;
import io.ebeaninternal.dbmigration.migrationreader.MigrationXmlReader;
import org.junit.Test;
@@ -79,6 +82,31 @@ public class ModelContainerTest {
assertThat(container.getPendingDrops()).isEmpty();
}
@Test
public void apply_dropTable_when_notInModel_then_ok() {
ModelContainer container = new ModelContainer();
container.apply(mig("5.0__dropTable.model.xml"), ver("5.0"));
assertThat(container.getTables()).isEmpty();
}
@Test
public void apply_drop_when_notInModel_then_ok() {
ModelContainer container = new ModelContainer();
DropTable dropTable = new DropTable();
dropTable.setName("DoesNotExist");
container.applyChange(dropTable);
DropIndex dropIndex = new DropIndex();
dropIndex.setIndexName("DoesNotExist");
container.applyChange(dropIndex);
DropHistoryTable dropHistoryTable = new DropHistoryTable();
dropHistoryTable.setBaseTable("DoesNotExist");
container.applyChange(dropHistoryTable);
}
private ModelContainer container_2_1() {
ModelContainer container = new ModelContainer();
container.apply(mig("2.0.model.xml"), ver("2.0"));
@@ -26,19 +26,60 @@ public class FlagsTest {
assertThat(Flags.isSet(state, Flags.PUBLISH)).isFalse();
assertThat(Flags.isSet(state, Flags.MERGE)).isTrue();
assertThat(Flags.isSet(state, Flags.INSERT)).isTrue();
}
@Test
public void insert() {
int state = 0;
state = Flags.setInsert(state);
assertThat(Flags.isSet(state, Flags.INSERT)).isTrue();
assertThat(Flags.isSet(state, Flags.NORMAL)).isFalse();
}
@Test
public void insertNormal() {
int state = 0;
state = Flags.setInsertNormal(state);
assertThat(Flags.isSet(state, Flags.INSERT)).isTrue();
assertThat(Flags.isSet(state, Flags.NORMAL)).isTrue();
}
@Test
public void update() {
int state = 0;
state = Flags.setUpdate(state);
assertThat(Flags.isSet(state, Flags.INSERT)).isFalse();
assertThat(Flags.isSet(state, Flags.NORMAL)).isFalse();
}
@Test
public void updateNormal() {
int state = 0;
state = Flags.setUpdateNormal(state);
assertThat(Flags.isSet(state, Flags.INSERT)).isFalse();
assertThat(Flags.isSet(state, Flags.NORMAL)).isTrue();
}
@Test
public void isPublishOrMerge() {
assertThat(Flags.isPublishOrMerge(0)).isFalse();
assertThat(Flags.isPublishOrMerge(Flags.INSERT)).isFalse();
assertThat(Flags.isPublishMergeOrNormal(0)).isFalse();
assertThat(Flags.isPublishMergeOrNormal(Flags.INSERT)).isFalse();
assertThat(Flags.isPublishMergeOrNormal(Flags.RECURSE)).isFalse();
assertThat(Flags.isPublishOrMerge(Flags.PUBLISH)).isTrue();
assertThat(Flags.isPublishOrMerge(Flags.MERGE)).isTrue();
assertThat(Flags.isPublishMergeOrNormal(Flags.PUBLISH)).isTrue();
assertThat(Flags.isPublishMergeOrNormal(Flags.MERGE)).isTrue();
assertThat(Flags.isPublishMergeOrNormal(Flags.NORMAL)).isTrue();
int mergePublish = Flags.setMerge(Flags.setPublish(0));
assertThat(Flags.isPublishOrMerge(mergePublish)).isTrue();
assertThat(Flags.isPublishMergeOrNormal(mergePublish)).isTrue();
}
}
@@ -18,6 +18,8 @@ public class EArrayBean {
ONE, TWO, THREE
}
IntEnum foo;
@Id
Long id;
@@ -38,12 +40,26 @@ public class EArrayBean {
@DbArray
List<Status> statuses;
@DbArray
List<VarcharEnum> vcEnums = new ArrayList<>();
@DbArray
List<IntEnum> intEnums = new ArrayList<>();
@DbArray
Set<Status> status2;
@Version
Long version;
public IntEnum getFoo() {
return foo;
}
public void setFoo(final IntEnum foo) {
this.foo = foo;
}
public Long getId() {
return id;
}
@@ -100,6 +116,21 @@ public class EArrayBean {
this.statuses = statuses;
}
public List<VarcharEnum> getVcEnums() {
return vcEnums;
}
public void setVcEnums(final List<VarcharEnum> vcEnums) {
this.vcEnums = vcEnums;
}
public List<IntEnum> getIntEnums() {
return intEnums;
}
public void setIntEnums(final List<IntEnum> intEnums) {
this.intEnums = intEnums;
}
public Set<Status> getStatus2() {
return status2;
@@ -0,0 +1,13 @@
package org.tests.model.array;
import io.ebean.annotation.DbEnumType;
import io.ebean.annotation.DbEnumValue;
public enum IntEnum {
ZERO, ONE, TWO;
@DbEnumValue(storage = DbEnumType.INTEGER)
public int dbValue() {
return 100 + ordinal();
}
}
@@ -1,19 +1,22 @@
package org.tests.model.array;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.Query;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.Query;
import io.ebean.SqlRow;
public class TestDbArray_basic extends BaseTestCase {
@@ -22,7 +25,7 @@ public class TestDbArray_basic extends BaseTestCase {
private EArrayBean found;
@Test
public void insert() {
public void insert() throws SQLException {
bean.setName("some stuff");
@@ -44,6 +47,10 @@ public class TestDbArray_basic extends BaseTestCase {
bean.setStatuses(new ArrayList<>());
bean.getStatuses().add(EArrayBean.Status.ONE);
bean.getStatuses().add(EArrayBean.Status.THREE);
bean.getVcEnums().add(VarcharEnum.ONE);
bean.getVcEnums().add(VarcharEnum.TWO);
bean.getIntEnums().add(IntEnum.ZERO);
bean.getIntEnums().add(IntEnum.TWO);
bean.setStatus2(new LinkedHashSet<>());
bean.getStatus2().add(EArrayBean.Status.TWO);
@@ -64,16 +71,23 @@ public class TestDbArray_basic extends BaseTestCase {
.arrayIsNotEmpty("phoneNumbers")
.arrayContains("statuses", EArrayBean.Status.ONE)
.arrayContains("status2", EArrayBean.Status.TWO)
.arrayContains("vcEnums", VarcharEnum.TWO)
.arrayContains("intEnums", IntEnum.ZERO)
.query();
List<EArrayBean> list = query.findList();
List<EArrayBean.Status> statuses = list.get(0).getStatuses();
Set<EArrayBean.Status> status2 = list.get(0).getStatus2();
List<IntEnum> intEnums = list.get(0).getIntEnums();
List<VarcharEnum> varcharEnums = list.get(0).getVcEnums();
assertThat(statuses).contains(EArrayBean.Status.ONE, EArrayBean.Status.THREE);
assertThat(status2).contains(EArrayBean.Status.ONE, EArrayBean.Status.TWO);
assertThat(intEnums).containsExactly(IntEnum.ZERO, IntEnum.TWO);
assertThat(varcharEnums).containsExactly(VarcharEnum.ONE, VarcharEnum.TWO);
assertThat(query.getGeneratedSql()).contains(" t0.other_ids @> array[?,?]::bigint[] ");
assertThat(query.getGeneratedSql()).contains(" t0.uids @> array[?] ");
assertThat(query.getGeneratedSql()).contains(" t0.phone_numbers @> array[?] ");
@@ -87,6 +101,15 @@ public class TestDbArray_basic extends BaseTestCase {
.query();
query.findList();
final SqlRow row = Ebean.createSqlQuery("select * from earray_bean").findOne();
final String[] vcs = (String[]) ((java.sql.Array) row.get("vc_enums")).getArray();
assertThat(vcs).containsExactly("xXxONE", "xXxTWO");
final Integer[] ints = (Integer[]) ((java.sql.Array) row.get("int_enums")).getArray();
assertThat(ints).containsExactly(100, 102);
assertThat(query.getGeneratedSql()).contains(" coalesce(cardinality(t0.other_ids),0) = 0");
assertThat(query.getGeneratedSql()).contains(" not (t0.uids @> array[?])");
}
@@ -0,0 +1,13 @@
package org.tests.model.array;
import io.ebean.annotation.DbEnumType;
import io.ebean.annotation.DbEnumValue;
public enum VarcharEnum {
ZERO, ONE, TWO;
@DbEnumValue(storage = DbEnumType.VARCHAR)
public String dbValue() {
return "xXx" + name();
}
}
@@ -1,6 +1,7 @@
package org.tests.model.draftable;
import io.ebean.Finder;
import io.ebean.annotation.Draft;
import io.ebean.annotation.DraftOnly;
import io.ebean.annotation.Draftable;
@@ -25,6 +26,9 @@ public class Document extends BaseDomain {
String body;
@Draft
boolean draft;
@DraftOnly
Timestamp whenPublish;
@@ -54,6 +58,14 @@ public class Document extends BaseDomain {
this.body = body;
}
public boolean isDraft() {
return draft;
}
public void setDraft(boolean draft) {
this.draft = draft;
}
public Organisation getOrganisation() {
return organisation;
}
@@ -0,0 +1,19 @@
package org.tests.model.onetoone;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class OtoAone {
@Id
private String id;
private String description;
public OtoAone(String id, String description){
this.id = id;
this.description = description;
}
}
@@ -0,0 +1,47 @@
package org.tests.model.onetoone;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
@Entity
public class OtoAtwo {
@Id
private String id;
private String description;
@OneToOne(orphanRemoval=true, cascade = CascadeType.ALL)
private OtoAone aone;
public OtoAtwo(String id, String description){
this.id = id;
this.description = description;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public OtoAone getAone() {
return aone;
}
public void setAone(OtoAone aone) {
this.aone = aone;
}
}
@@ -0,0 +1,66 @@
package org.tests.model.onetoone;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class TestOneToOneOrphanStringId extends BaseTestCase {
@Test
public void test_updateInsert() {
OtoAtwo b = new OtoAtwo("b1", "b test");
Ebean.save(b);
OtoAone a = new OtoAone("a1", "a test");
b.setAone(a);
LoggedSqlCollector.start();
Ebean.save(b);
List<String> update = LoggedSqlCollector.current();
assertThat(update).hasSize(2);
assertThat(update.get(0)).contains("insert into oto_aone");
assertThat(update.get(1)).contains("update oto_atwo set aone_id=? where id=?");
Ebean.delete(b);
List<String> deletes = LoggedSqlCollector.stop();
assertThat(deletes).hasSize(2);
assertThat(deletes.get(0)).contains("delete from oto_atwo");
assertThat(deletes.get(1)).contains("delete from oto_aone");
}
@Test
public void test_cascade() {
OtoAone a = new OtoAone("a2", "a test");
OtoAtwo b = new OtoAtwo("b2", "b test");
b.setAone(a);
LoggedSqlCollector.start();
Ebean.save(b);
List<String> inserts = LoggedSqlCollector.current();
assertThat(inserts).hasSize(2);
assertThat(inserts.get(0)).contains("insert into oto_aone");
assertThat(inserts.get(1)).contains("insert into oto_atwo");
Ebean.delete(b);
List<String> deletes = LoggedSqlCollector.stop();
assertThat(deletes).hasSize(2);
assertThat(deletes.get(0)).contains("delete from oto_atwo");
assertThat(deletes.get(1)).contains("delete from oto_aone");
}
}
@@ -0,0 +1,58 @@
package org.tests.model.orphanremoval;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Version;
@Entity
public class OrpDetail {
@Id
String id;
String detail;
@ManyToOne
OrpMaster master;
@Version
long version;
public OrpDetail(String id, String detail) {
this.id = id;
this.detail = detail;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public OrpMaster getMaster() {
return master;
}
public void setMaster(OrpMaster master) {
this.master = master;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
@@ -0,0 +1,60 @@
package org.tests.model.orphanremoval;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Version;
import java.util.List;
@Entity
public class OrpMaster {
@Id
String id;
String name;
@OneToMany(orphanRemoval = true, cascade = CascadeType.ALL)
List<OrpDetail> details;
@Version
long version;
public OrpMaster(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<OrpDetail> getDetails() {
return details;
}
public void setDetails(List<OrpDetail> details) {
this.details = details;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
@@ -0,0 +1,36 @@
package org.tests.model.orphanremoval;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class TestOrphanRemoveO2M extends BaseTestCase {
@Test
public void clear_expect_deletes() {
OrpMaster master = new OrpMaster("m","master");
master.getDetails().add(new OrpDetail("d1", "d1"));
master.getDetails().add(new OrpDetail("d2", "d2"));
Ebean.save(master);
master.getDetails().clear();
LoggedSqlCollector.start();
Ebean.save(master);
List<String> sql = LoggedSqlCollector.stop();
assertThat(Ebean.find(OrpDetail.class, "d1")).isNull();
assertThat(Ebean.find(OrpDetail.class, "d2")).isNull();
assertThat(sql).hasSize(2);
assertThat(sql.get(0)).contains("delete from orp_detail where id=?");
}
}
@@ -34,6 +34,7 @@ public class TestTransactionTryResources extends BaseTestCase {
Document document = Document.find.asDraft(doc.getId());
assertThat(document).isNotNull();
assertThat(document.isDraft()).isTrue();
// cleanup
Ebean.delete(document);
@@ -11,6 +11,7 @@ import org.tests.model.basic.EBasic.Status;
import org.junit.Assert;
import org.junit.Test;
import javax.persistence.EntityNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
@@ -70,6 +71,28 @@ public class TestStatelessUpdate extends TransactionalTestCase {
}
@Test(expected = EntityNotFoundException.class)
public void update_NoRowsUpdated_expect_EntityNotFoundException() {
EBasic basic = new EBasic();
basic.setId(999999999);
basic.setName("something");
basic.setStatus(Status.ACTIVE);
Ebean.update(basic);
}
@Test
public void delete_NoRowsDeleted_expect_false() {
EBasic basic = new EBasic();
basic.setId(999999999);
basic.setName("something");
basic.setStatus(Status.ACTIVE);
assertThat(Ebean.delete(basic)).isFalse();
}
/**
* I am expecting that Ebean detects there aren't any changes and don't execute any query.
* Currently a {@link javax.persistence.PersistenceException} with message 'Invalid value "null" for parameter "SQL"' is thrown.
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply">
<dropTable name="some_table"/>
</changeSet>
</migration>