Compare commits

..
12 changed files with 279 additions and 41 deletions
+3 -3
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.21.1</version>
<version>11.22.1</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.21.1</tag>
<tag>ebean-11.22.1</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>
@@ -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;
@@ -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();
}
}
@@ -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");
}
}
@@ -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.