findSingleAttributeOrEmpty() {
+ return query.findSingleAttributeOrEmpty();
+ }
+
/**
* Execute the query processing the beans one at a time.
*
diff --git a/ebean-querybean/src/test/java/org/example/domain/Contact.java b/ebean-querybean/src/test/java/org/example/domain/Contact.java
index 7d2554f57..505cb556a 100644
--- a/ebean-querybean/src/test/java/org/example/domain/Contact.java
+++ b/ebean-querybean/src/test/java/org/example/domain/Contact.java
@@ -1,8 +1,9 @@
package org.example.domain;
import io.ebean.annotation.DbArray;
-
import jakarta.persistence.*;
+import org.example.domain.finder.ContactFinder;
+
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.time.ZonedDateTime;
@@ -17,6 +18,11 @@ import java.util.Map;
@Table(name = "be_contact")
public class Contact extends BaseModel {
+ /**
+ * Convenience Finder for 'active record' style.
+ */
+ public static final ContactFinder find = new ContactFinder();
+
@DbArray
List<@Size(max=20) String> phoneNumbers = new ArrayList<>();
diff --git a/ebean-querybean/src/test/java/org/example/domain/PFile.java b/ebean-querybean/src/test/java/org/example/domain/PFile.java
new file mode 100644
index 000000000..63b5ef1bf
--- /dev/null
+++ b/ebean-querybean/src/test/java/org/example/domain/PFile.java
@@ -0,0 +1,12 @@
+package org.example.domain;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+
+@Entity
+public class PFile {
+
+ @Id
+ long id;
+ String name;
+}
diff --git a/ebean-querybean/src/test/java/org/example/domain/PLong.java b/ebean-querybean/src/test/java/org/example/domain/PLong.java
new file mode 100644
index 000000000..7df62ba00
--- /dev/null
+++ b/ebean-querybean/src/test/java/org/example/domain/PLong.java
@@ -0,0 +1,12 @@
+package org.example.domain;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+
+@Entity
+public class PLong {
+
+ @Id
+ long id;
+ String name;
+}
diff --git a/ebean-querybean/src/test/java/org/example/domain/Pinstant.java b/ebean-querybean/src/test/java/org/example/domain/Pinstant.java
new file mode 100644
index 000000000..0f4b210fa
--- /dev/null
+++ b/ebean-querybean/src/test/java/org/example/domain/Pinstant.java
@@ -0,0 +1,12 @@
+package org.example.domain;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+
+@Entity
+public class Pinstant {
+
+ @Id
+ long id;
+ String name;
+}
diff --git a/ebean-querybean/src/test/java/org/example/domain/finder/ContactFinder.java b/ebean-querybean/src/test/java/org/example/domain/finder/ContactFinder.java
new file mode 100644
index 000000000..42f0daccb
--- /dev/null
+++ b/ebean-querybean/src/test/java/org/example/domain/finder/ContactFinder.java
@@ -0,0 +1,18 @@
+package org.example.domain.finder;
+
+import io.ebean.Finder;
+import org.example.domain.Contact;
+import org.example.domain.query.QContact;
+
+/**
+ */
+public class ContactFinder extends Finder {
+
+ public ContactFinder() {
+ super(Contact.class);
+ }
+
+ public QContact typed() {
+ return new QContact();
+ }
+}
diff --git a/ebean-querybean/src/test/java/org/querytest/QCustomerAndOrTest.java b/ebean-querybean/src/test/java/org/querytest/QCustomerAndOrTest.java
index b930c1509..7862e0dcd 100644
--- a/ebean-querybean/src/test/java/org/querytest/QCustomerAndOrTest.java
+++ b/ebean-querybean/src/test/java/org/querytest/QCustomerAndOrTest.java
@@ -1,6 +1,8 @@
package org.querytest;
+import org.example.domain.Contact;
import org.example.domain.Customer;
+import org.example.domain.query.QCustomer;
import org.junit.jupiter.api.Test;
import java.time.LocalDate;
@@ -9,6 +11,8 @@ import java.time.ZoneId;
import java.util.Date;
import java.util.List;
+import static org.assertj.core.api.Assertions.assertThat;
+
public class QCustomerAndOrTest {
@Test
@@ -55,6 +59,30 @@ public class QCustomerAndOrTest {
}
+ @Test
+ public void testOrWithExists() {
+ QCustomer query = Customer.find.typed()
+ .alias("_cust")
+ .or()
+ .name.eq("Superman")
+ .exists(Contact.find.typed()
+ .alias("contact")
+ .firstName.eq("Superman")
+ .raw("contact.customer_id = _cust.id")
+ .query()
+ )
+ .endOr()
+ .select(QCustomer.alias().id);
+
+ query.findList();
+
+ assertThat(query.getGeneratedSql()).isEqualTo(
+ "select _cust.id from be_customer _cust where (" +
+ "_cust.name = ? or exists (select 1 from be_contact contact where " +
+ "contact.first_name = ? and contact.customer_id = _cust.id))"
+ );
+ }
+
private Date fiveDaysAgo() {
LocalDateTime fiveDaysAgo = LocalDate.now().atStartOfDay().minusDays(5);
return new Date(fiveDaysAgo.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
diff --git a/ebean-querybean/src/test/java/org/querytest/QCustomerTest.java b/ebean-querybean/src/test/java/org/querytest/QCustomerTest.java
index 72a82654b..de80d71b5 100644
--- a/ebean-querybean/src/test/java/org/querytest/QCustomerTest.java
+++ b/ebean-querybean/src/test/java/org/querytest/QCustomerTest.java
@@ -63,6 +63,25 @@ public class QCustomerTest {
}
+ @Test
+ public void copy() {
+ var origin = new QCustomer()
+ .setDistinct(true)
+ .status.equalTo(Customer.Status.BAD);
+
+ var copy1 = origin.copy().name.isNotNull();
+ var q1 = copy1.query();
+ copy1.findList();
+
+ assertThat(q1.getGeneratedSql()).contains("from be_customer t0 where t0.status = ? and t0.name is not null");
+
+ var copy2 = origin.copy().version.ge(1L);
+ var q2 = copy2.query();
+ copy2.findList();
+
+ assertThat(q2.getGeneratedSql()).contains("from be_customer t0 where t0.status = ? and t0.version >= ?");
+ }
+
@Test
public void findSingleAttribute() {
@@ -813,6 +832,30 @@ public class QCustomerTest {
new QContact()
.firstName.inRangeWith(lastName, "B")
.findList();
+
+ new QContact()
+ .firstName.between("A", "B")
+ .findList();
+ }
+
+ @Test
+ void betweenProperties() {
+ var query = new QContact()
+ .firstName.betweenProperties(lastName, "B");
+
+ query.findList();
+ assertThat(query.getGeneratedSql()).contains(" where ? between t0.first_name and t0.last_name");
+ }
+
+ @Test
+ void betweenProperties_notFirstPredicate() {
+ var query = new QContact()
+ .lastName.isNotNull()
+ .firstName.betweenProperties(lastName, "B")
+ .email.isNotNull();
+
+ query.findList();
+ assertThat(query.getGeneratedSql()).contains(" where t0.last_name is not null and ? between t0.first_name and t0.last_name and t0.email is not null");
}
@Test
@@ -1026,6 +1069,32 @@ public class QCustomerTest {
assertThat(maxDate).isNotNull();
}
+ @Test
+ public void findSingleAttributeOrEmpty() {
+
+ Customer cust = new Customer();
+ cust.setName("MaybeIExist yeah");
+ cust.setStatus(Customer.Status.GOOD);
+ cust.setRegistered(new Date());
+ cust.save();
+
+ Optional customerName = new QCustomer()
+ .select(name)
+ .status.eq(Customer.Status.GOOD)
+ .name.startsWith("MaybeIExist")
+ .findSingleAttributeOrEmpty();
+
+ assertThat(customerName).isPresent();
+
+ Optional customerName2 = new QCustomer()
+ .select(name)
+ .status.eq(Customer.Status.GOOD)
+ .name.eq("NahIDoNotExist")
+ .findSingleAttributeOrEmpty();
+
+ assertThat(customerName2).isEmpty();
+ }
+
@Test
public void testFetchByScalarValue() {
diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml
index 7b54f4117..350068c70 100644
--- a/ebean-redis/pom.xml
+++ b/ebean-redis/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 13.25.0-jakarta
+ 14.0.0
ebean-redis
@@ -22,28 +22,28 @@
io.ebean
ebean-api
- 13.25.0-jakarta
+ 14.0.0
provided
io.ebean
ebean-core
- 13.25.0-jakarta
+ 14.0.0
provided
io.ebean
ebean-querybean
- 13.25.0-jakarta
+ 14.0.0
test
io.ebean
ebean-test
- 13.25.0-jakarta
+ 14.0.0
test
@@ -57,7 +57,7 @@
io.ebean
querybean-generator
- 13.25.0-jakarta
+ 14.0.0
provided
diff --git a/ebean-spring-txn/pom.xml b/ebean-spring-txn/pom.xml
index 03694ce61..6d2d9a2c8 100644
--- a/ebean-spring-txn/pom.xml
+++ b/ebean-spring-txn/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 13.25.0-jakarta
+ 14.0.0
ebean-spring-txn
@@ -28,7 +28,7 @@
io.ebean
ebean-core
- 13.25.0-jakarta
+ 14.0.0
provided
@@ -77,7 +77,7 @@
io.ebean
ebean-test
- 13.25.0-jakarta
+ 14.0.0
test
diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml
index e7d584120..6cf4553db 100644
--- a/ebean-test/pom.xml
+++ b/ebean-test/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 13.25.0-jakarta
+ 14.0.0
ebean test
@@ -12,8 +12,8 @@
ebean-test
- 1.14.9
- 3.24.2
+ 1.14.12
+ 3.25.3
@@ -33,20 +33,20 @@
io.ebean
ebean-platform-h2
- 13.25.0-jakarta
+ 14.0.0
io.ebean
ebean-core
- 13.25.0-jakarta
+ 14.0.0
provided
io.ebean
ebean-ddl-generator
- 13.25.0-jakarta
+ 14.0.0
@@ -149,14 +149,14 @@
io.ebean
ebean-jackson-mapper
- 13.25.0-jakarta
+ 14.0.0
test
io.ebean
ebean-platform-all
- 13.25.0-jakarta
+ 14.0.0
test
@@ -293,7 +293,7 @@
io.avaje
junit
- 1.3
+ 1.4
provided
diff --git a/ebean-test/src/test/java/io/ebean/xtest/base/TransactionalTestCase.java b/ebean-test/src/test/java/io/ebean/xtest/base/TransactionalTestCase.java
index 225888156..9e18e4b9d 100644
--- a/ebean-test/src/test/java/io/ebean/xtest/base/TransactionalTestCase.java
+++ b/ebean-test/src/test/java/io/ebean/xtest/base/TransactionalTestCase.java
@@ -1,5 +1,6 @@
package io.ebean.xtest.base;
+import io.ebean.Transaction;
import io.ebean.xtest.BaseTestCase;
import io.ebean.DB;
import org.junit.jupiter.api.AfterEach;
@@ -18,15 +19,17 @@ import org.tests.model.basic.ResetBasicData;
*/
public abstract class TransactionalTestCase extends BaseTestCase {
+ private Transaction transaction;
+
@BeforeEach
public void startTransaction() {
ResetBasicData.reset();
- DB.beginTransaction();
+ transaction = DB.beginTransaction();
}
@AfterEach
public void endTransaction() {
- DB.rollbackTransaction();
- DB.endTransaction();
+ transaction.rollback();
+ transaction.end();
}
}
diff --git a/ebean-test/src/test/java/io/ebean/xtest/common/BeanListTest.java b/ebean-test/src/test/java/io/ebean/xtest/common/BeanListTest.java
index 69aea90ff..3285b60a8 100644
--- a/ebean-test/src/test/java/io/ebean/xtest/common/BeanListTest.java
+++ b/ebean-test/src/test/java/io/ebean/xtest/common/BeanListTest.java
@@ -3,6 +3,7 @@ package io.ebean.xtest.common;
import io.ebean.bean.BeanCollection;
import io.ebean.common.BeanList;
import org.junit.jupiter.api.Test;
+import org.tests.model.basic.Product;
import java.util.ArrayList;
import java.util.Comparator;
@@ -13,22 +14,22 @@ import static org.assertj.core.api.Assertions.assertThat;
public class BeanListTest {
- private final Object object1 = new Object();
- private final Object object2 = new Object();
- private final Object object3 = new Object();
+ private final Product object1 = new Product(1);
+ private final Product object2 = new Product(2);
+ private final Product object3 = new Product(3);
private List all() {
List all = new ArrayList<>();
- all.add(object1);
- all.add(object2);
- all.add(object3);
+ all.add(new Product(1));
+ all.add(new Product(2));
+ all.add(new Product(3));
return all;
}
private List some() {
List some = new ArrayList<>();
- some.add(object2);
- some.add(object3);
+ some.add(new Product(2));
+ some.add(new Product(3));
return some;
}
@@ -163,14 +164,15 @@ public class BeanListTest {
@Test
public void testRemove_given_beansInAdditions() {
+ List all = all();
BeanList list = new BeanList<>();
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
- list.addAll(all());
+ list.addAll(all);
assertThat(list.modifyAdditions()).containsOnly(object1, object2, object3);
// act
- list.remove(object2);
- list.remove(object3);
+ list.remove(all.get(1));
+ list.remove(all.get(2));
assertThat(list.modifyAdditions()).containsOnly(object1);
assertThat(list.modifyRemovals()).isEmpty();
@@ -179,13 +181,14 @@ public class BeanListTest {
@Test
public void testRemoveAll_given_beansInAdditions() {
+ List all = all();
BeanList list = new BeanList<>();
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
- list.addAll(all());
+ list.addAll(all);
assertThat(list.modifyAdditions()).containsOnly(object1, object2, object3);
// act
- list.removeAll(some());
+ list.removeAll(List.of(all.get(1), all.get(2)));
assertThat(list.modifyAdditions()).containsOnly(object1);
assertThat(list.modifyRemovals()).isEmpty();
diff --git a/ebean-test/src/test/java/io/ebean/xtest/common/BeanMapTest.java b/ebean-test/src/test/java/io/ebean/xtest/common/BeanMapTest.java
index 4b6a48c76..a72a17b2a 100644
--- a/ebean-test/src/test/java/io/ebean/xtest/common/BeanMapTest.java
+++ b/ebean-test/src/test/java/io/ebean/xtest/common/BeanMapTest.java
@@ -3,7 +3,7 @@ package io.ebean.xtest.common;
import io.ebean.bean.BeanCollection;
import io.ebean.common.BeanMap;
import org.junit.jupiter.api.Test;
-import org.tests.model.basic.EBasic;
+import org.tests.model.basic.Product;
import java.util.*;
@@ -14,24 +14,24 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
public class BeanMapTest {
- private final EBasic object1 = new EBasic("o1");
- private final EBasic object2 = new EBasic("o2");
- private final EBasic object3 = new EBasic("o3");
- private final EBasic object4 = new EBasic("o4");
- private final EBasic object5 = new EBasic("o5");
+ private final Product object1 = new Product(1);
+ private final Product object2 = new Product(2);
+ private final Product object3 = new Product(3);
+ private final Product object4 = new Product(4);
+ private final Product object5 = new Product(5);
private Map all() {
Map all = new LinkedHashMap<>();
- all.put("1", object1);
- all.put("2", object2);
- all.put("3", object3);
+ all.put("1", new Product(1));
+ all.put("2", new Product(2));
+ all.put("3", new Product(3));
return all;
}
private Map some() {
Map all = new LinkedHashMap<>();
- all.put("2", object2);
- all.put("3", object3);
+ all.put("2", new Product(2));
+ all.put("3", new Product(3));
return all;
}
@@ -73,8 +73,8 @@ public class BeanMapTest {
@Test
public void testAdd_given_someAlreadyIn() {
-
- BeanMap map = new BeanMap<>(some());
+ Map some = new LinkedHashMap<>(Map.of("2", object2, "3", object3));
+ BeanMap map = new BeanMap<>(some);
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
// act
@@ -90,11 +90,13 @@ public class BeanMapTest {
@Test
public void testAddSome_given_someAlreadyIn() {
- BeanMap map = new BeanMap<>(some());
+ Map some = new LinkedHashMap<>(Map.of("2", object2, "3", object3));
+ BeanMap map = new BeanMap<>(some);
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
// act
- map.putAll(all());
+ Map all = Map.of("1", object1, "2", object2, "3", object3);
+ map.putAll(all);
assertThat(map.modifyAdditions()).containsOnly(object1);
assertThat(map.modifyRemovals()).isEmpty();
@@ -179,7 +181,7 @@ public class BeanMapTest {
@Test
public void testClear_given_someBeansInAdditions() {
- BeanMap map = newModifyListeningMap();
+ BeanMap map = newModifyListeningMap();
map.put("2", object2);
map.put("3", object3);
@@ -193,7 +195,7 @@ public class BeanMapTest {
@Test
public void keySet_add_whenModifyListening() {
- BeanMap map = newModifyListeningMap();
+ BeanMap map = newModifyListeningMap();
assertThrows(UnsupportedOperationException.class, () -> map.keySet().add("3"));
}
@@ -205,7 +207,7 @@ public class BeanMapTest {
@Test
public void keySet_addAll_whenModifyListening() {
- BeanMap map = newModifyListeningMap();
+ BeanMap map = newModifyListeningMap();
assertThrows(UnsupportedOperationException.class, () -> map.keySet().addAll(asList("3", "4")));
}
@@ -317,13 +319,13 @@ public class BeanMapTest {
@Test
public void values_add() {
- BeanMap map = new BeanMap<>();
+ BeanMap map = new BeanMap<>();
assertThrows(UnsupportedOperationException.class, () -> map.values().add(object3));
}
@Test
public void values_addAll() {
- BeanMap map = new BeanMap<>();
+ BeanMap map = new BeanMap<>();
assertThrows(UnsupportedOperationException.class, () -> map.values().addAll(asList(object3, object5)));
}
@@ -337,8 +339,8 @@ public class BeanMapTest {
@Test
public void entrySet_clear() {
- final BeanMap map = newModifyListeningMap();
- final Set> entries = map.entrySet();
+ final BeanMap map = newModifyListeningMap();
+ final Set> entries = map.entrySet();
entries.clear();
assertThat(entries).isEmpty();
@@ -348,8 +350,8 @@ public class BeanMapTest {
@Test
public void entrySet_remove() {
- final BeanMap map = newModifyListeningMap5();
- final Set> entries = map.entrySet();
+ final BeanMap map = newModifyListeningMap5();
+ final Set> entries = map.entrySet();
assertThat(map).hasSize(5);
@@ -365,8 +367,8 @@ public class BeanMapTest {
@Test
public void entrySet_remove_whenNotEqualValue() {
- final BeanMap map = newModifyListeningMap5();
- final Set> entries = map.entrySet();
+ final BeanMap map = newModifyListeningMap5();
+ final Set> entries = map.entrySet();
assertThat(map).hasSize(5);
@@ -379,11 +381,11 @@ public class BeanMapTest {
@Test
public void entrySet_iterator_remove() {
- final BeanMap map = newModifyListeningMap5();
- final Set> entries = map.entrySet();
- final Iterator> iterator = entries.iterator();
+ final BeanMap map = newModifyListeningMap5();
+ final Set> entries = map.entrySet();
+ final Iterator> iterator = entries.iterator();
while (iterator.hasNext()) {
- final Map.Entry entry = iterator.next();
+ final Map.Entry entry = iterator.next();
if (entry.getKey().equals("2") || entry.getKey().equals("5")) {
iterator.remove();
}
@@ -395,8 +397,8 @@ public class BeanMapTest {
@Test
public void entrySet_removeAll() {
- final BeanMap map = newModifyListeningMap5();
- final Set> entries = map.entrySet();
+ final BeanMap map = newModifyListeningMap5();
+ final Set> entries = map.entrySet();
entries.removeAll(asList(new AbstractMap.SimpleEntry<>("1", object1), new AbstractMap.SimpleEntry<>("3", object4), new AbstractMap.SimpleEntry<>("4", object4)));
assertThat(map).hasSize(3);
@@ -406,8 +408,8 @@ public class BeanMapTest {
@Test
public void entrySet_retainAll() {
- final BeanMap map = newModifyListeningMap5();
- final Set> entries = map.entrySet();
+ final BeanMap map = newModifyListeningMap5();
+ final Set> entries = map.entrySet();
entries.retainAll(asList(new AbstractMap.SimpleEntry<>("1", object1), new AbstractMap.SimpleEntry<>("3", object4), new AbstractMap.SimpleEntry<>("4", object4)));
assertThat(map).hasSize(2);
@@ -415,15 +417,15 @@ public class BeanMapTest {
assertThat(map.modifyRemovals()).containsOnly(object2, object3, object5);
}
- private BeanMap newModifyListeningMap() {
- BeanMap map = new BeanMap<>();
+ private BeanMap newModifyListeningMap() {
+ BeanMap map = new BeanMap<>();
map.put("1", object1);
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
return map;
}
- private BeanMap newModifyListeningMap5() {
- BeanMap map = new BeanMap<>();
+ private BeanMap newModifyListeningMap5() {
+ BeanMap map = new BeanMap<>();
map.put("1", object1);
map.put("2", object2);
map.put("3", object3);
diff --git a/ebean-test/src/test/java/io/ebean/xtest/common/BeanSetTest.java b/ebean-test/src/test/java/io/ebean/xtest/common/BeanSetTest.java
index 5053f71dc..2152af71b 100644
--- a/ebean-test/src/test/java/io/ebean/xtest/common/BeanSetTest.java
+++ b/ebean-test/src/test/java/io/ebean/xtest/common/BeanSetTest.java
@@ -3,6 +3,7 @@ package io.ebean.xtest.common;
import io.ebean.bean.BeanCollection;
import io.ebean.common.BeanSet;
import org.junit.jupiter.api.Test;
+import org.tests.model.basic.Product;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -12,22 +13,22 @@ import static org.assertj.core.api.Assertions.assertThat;
public class BeanSetTest {
- Object object1 = new Object();
- Object object2 = new Object();
- Object object3 = new Object();
+ Product object1 = new Product(1);
+ Product object2 = new Product(2);
+ Product object3 = new Product(3);
private Set all() {
Set all = new LinkedHashSet<>();
- all.add(object1);
- all.add(object2);
- all.add(object3);
+ all.add(new Product(1));
+ all.add(new Product(2));
+ all.add(new Product(3));
return all;
}
private Set some() {
Set some = new LinkedHashSet<>();
- some.add(object2);
- some.add(object3);
+ some.add(new Product(2));
+ some.add(new Product(3));
return some;
}
@@ -99,7 +100,9 @@ public class BeanSetTest {
BeanSet set = new BeanSet<>();
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
- set.addAll(all());
+
+ Set all = Set.of(object1, object2, object3);
+ set.addAll(all);
assertThat(set.modifyAdditions()).containsOnly(object1, object2, object3);
// act
@@ -115,11 +118,13 @@ public class BeanSetTest {
BeanSet set = new BeanSet<>();
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
- set.addAll(all());
+
+ Set all = Set.of(object1, object2, object3);
+ set.addAll(all);
assertThat(set.modifyAdditions()).containsOnly(object1, object2, object3);
// act
- set.removeAll(some());
+ set.removeAll(Set.of(object2, object3));
assertThat(set.modifyAdditions()).containsOnly(object1);
assertThat(set.modifyRemovals()).isEmpty();
diff --git a/ebean-test/src/test/java/io/ebean/xtest/config/ContainerConfigTest.java b/ebean-test/src/test/java/io/ebean/xtest/config/ContainerConfigTest.java
index 2ae4f24e2..6fba07abe 100644
--- a/ebean-test/src/test/java/io/ebean/xtest/config/ContainerConfigTest.java
+++ b/ebean-test/src/test/java/io/ebean/xtest/config/ContainerConfigTest.java
@@ -5,28 +5,19 @@ import org.junit.jupiter.api.Test;
import java.util.Properties;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
-public class ContainerConfigTest {
+class ContainerConfigTest {
@Test
- public void loadFromProperties() {
-
- Properties p = new Properties();
- p.setProperty("ebean.cluster.active", "true");
- p.setProperty("ebean.cluster.serviceName", "a");
- p.setProperty("ebean.cluster.namespace", "b");
- p.setProperty("ebean.cluster.podName", "c");
- p.setProperty("ebean.cluster.port", "42");
-
-
+ void loadFromProperties() {
ContainerConfig containerConfig = new ContainerConfig();
- containerConfig.loadFromProperties(p);
- assertEquals(true, containerConfig.isActive());
- assertEquals("a", containerConfig.getServiceName());
- assertEquals("b", containerConfig.getNamespace());
- assertEquals("c", containerConfig.getPodName());
- assertEquals(42, containerConfig.getPort());
+ assertThat(containerConfig.isActive()).isFalse();
+ assertThat(containerConfig.getServiceName()).isNull();
+ assertThat(containerConfig.getNamespace()).isNull();
+ assertThat(containerConfig.getPodName()).isNull();
+ assertThat(containerConfig.getPort()).isEqualTo(0);
}
}
diff --git a/ebean-test/src/test/java/io/ebean/xtest/internal/api/TDSpiEbeanServer.java b/ebean-test/src/test/java/io/ebean/xtest/internal/api/TDSpiEbeanServer.java
index 18af5d4c4..289360021 100644
--- a/ebean-test/src/test/java/io/ebean/xtest/internal/api/TDSpiEbeanServer.java
+++ b/ebean-test/src/test/java/io/ebean/xtest/internal/api/TDSpiEbeanServer.java
@@ -500,14 +500,6 @@ public class TDSpiEbeanServer extends TDSpiServer implements SpiEbeanServer {
public void flush() {
}
- @Override
- public void commitTransaction() {
- }
-
- @Override
- public void rollbackTransaction() {
- }
-
@Override
public void endTransaction() {
}
diff --git a/ebean-test/src/test/java/io/ebean/xtest/internal/api/TDSpiServer.java b/ebean-test/src/test/java/io/ebean/xtest/internal/api/TDSpiServer.java
index 5efc5458e..1a8bd2c9c 100644
--- a/ebean-test/src/test/java/io/ebean/xtest/internal/api/TDSpiServer.java
+++ b/ebean-test/src/test/java/io/ebean/xtest/internal/api/TDSpiServer.java
@@ -207,21 +207,6 @@ public class TDSpiServer implements SpiServer {
}
- @Override
- public void commitTransaction() {
-
- }
-
- @Override
- public void rollbackTransaction() {
-
- }
-
- @Override
- public void endTransaction() {
-
- }
-
@Override
public void refresh(Object bean) {
@@ -438,21 +423,41 @@ public class TDSpiServer implements SpiServer {
}
+ @Override
+ public void insert(Object bean, InsertOptions insertOptions) {
+
+ }
+
@Override
public void insert(Object bean, Transaction transaction) {
}
+ @Override
+ public void insert(Object bean, InsertOptions insertOptions, Transaction transaction) {
+
+ }
+
@Override
public void insertAll(Collection> beans) {
}
+ @Override
+ public void insertAll(Collection> beans, InsertOptions options) {
+
+ }
+
@Override
public void insertAll(Collection> beans, Transaction transaction) {
}
+ @Override
+ public void insertAll(Collection> beans, InsertOptions options, Transaction transaction) {
+
+ }
+
@Override
public int execute(SqlUpdate updSql, Transaction transaction) {
return 0;
diff --git a/ebean-test/src/test/java/io/ebean/xtest/internal/server/grammer/EqlParserTest.java b/ebean-test/src/test/java/io/ebean/xtest/internal/server/grammer/EqlParserTest.java
new file mode 100644
index 000000000..e69de29bb
diff --git a/ebean-test/src/test/java/io/ebean/xtest/internal/server/transaction/DefaultTransactionThreadLocalTest.java b/ebean-test/src/test/java/io/ebean/xtest/internal/server/transaction/DefaultTransactionThreadLocalTest.java
index 8c9455bcb..96a2353d8 100644
--- a/ebean-test/src/test/java/io/ebean/xtest/internal/server/transaction/DefaultTransactionThreadLocalTest.java
+++ b/ebean-test/src/test/java/io/ebean/xtest/internal/server/transaction/DefaultTransactionThreadLocalTest.java
@@ -79,9 +79,7 @@ public class DefaultTransactionThreadLocalTest extends BaseTestCase {
@ForPlatform({Platform.H2})
@Test
public void end_withoutActiveTransaction_isFine() {
-
assertNull(DB.currentTransaction());
- DB.endTransaction();
}
@ForPlatform({Platform.H2})
diff --git a/ebean-test/src/test/java/io/ebean/xtest/plugin/SpiServerTest.java b/ebean-test/src/test/java/io/ebean/xtest/plugin/SpiServerTest.java
index e5669c2dc..7fdc4b859 100644
--- a/ebean-test/src/test/java/io/ebean/xtest/plugin/SpiServerTest.java
+++ b/ebean-test/src/test/java/io/ebean/xtest/plugin/SpiServerTest.java
@@ -26,7 +26,7 @@ public class SpiServerTest extends BaseTestCase {
BeanType beanType = pluginApi.beanType(Customer.class);
assertEquals("o_customer", beanType.baseTable());
assertNotNull(pluginApi.databasePlatform());
- assertNull(beanType.findController());
+ assertNotNull(beanType.findController());
assertNotNull(beanType.persistController());
assertNull(beanType.persistListener());
assertNull(beanType.queryAdapter());
diff --git a/ebean-test/src/test/java/org/tests/basic/TestDynamicUpdate.java b/ebean-test/src/test/java/org/tests/basic/TestDynamicUpdate.java
index 1174f7a34..8675c2387 100644
--- a/ebean-test/src/test/java/org/tests/basic/TestDynamicUpdate.java
+++ b/ebean-test/src/test/java/org/tests/basic/TestDynamicUpdate.java
@@ -1,5 +1,6 @@
package org.tests.basic;
+import io.ebean.Transaction;
import io.ebean.xtest.BaseTestCase;
import io.ebean.BeanState;
import io.ebean.DB;
@@ -36,13 +37,11 @@ public class TestDynamicUpdate extends BaseTestCase {
server.save(b2);
- server.beginTransaction();
- try {
+ try (Transaction txn = server.beginTransaction()) {
EMain b3 = server.find(EMain.class, b.getId());
assertEquals("ABC", b3.getEmbeddable().getDescription());
- } finally {
- server.endTransaction();
}
+
EMain b4 = server.find(EMain.class, b.getId());
b4.setName("bbb");
b4.getEmbeddable().setDescription("123");
diff --git a/ebean-test/src/test/java/org/tests/basic/TestInheritRef.java b/ebean-test/src/test/java/org/tests/basic/TestInheritRef.java
new file mode 100644
index 000000000..e69de29bb
diff --git a/ebean-test/src/test/java/org/tests/basic/TestPersistenceContext.java b/ebean-test/src/test/java/org/tests/basic/TestPersistenceContext.java
index a41255450..3bfa60b61 100644
--- a/ebean-test/src/test/java/org/tests/basic/TestPersistenceContext.java
+++ b/ebean-test/src/test/java/org/tests/basic/TestPersistenceContext.java
@@ -47,10 +47,9 @@ public class TestPersistenceContext extends BaseTestCase {
// implicit transaction with its own persistence context
Order oBefore = DB.find(Order.class, 1);
// start a persistence context
- DB.beginTransaction();
Order order;
- try {
+ try (Transaction txn = DB.beginTransaction()) {
order = DB.find(Order.class, 1);
// not the same instance ...as a different persistence context
assertNotSame(order, oBefore);
@@ -63,9 +62,6 @@ public class TestPersistenceContext extends BaseTestCase {
// all the same instance
assertSame(order, o2);
assertSame(order, o3);
-
- } finally {
- DB.endTransaction();
}
// implicit transaction with its own persistence context
@@ -74,8 +70,7 @@ public class TestPersistenceContext extends BaseTestCase {
assertNotSame(oAfter, order);
// start a persistence context
- DB.beginTransaction();
- try {
+ try (Transaction txn = DB.beginTransaction()) {
Order testOrder = ResetBasicData.createOrderCustAndOrder("testPC");
Integer id = testOrder.getCustomer().getId();
Integer orderId = testOrder.getId();
@@ -94,9 +89,6 @@ public class TestPersistenceContext extends BaseTestCase {
assertEquals(customer.getId(), customer2.getId());
assertSame(customer, customer2);
-
- } finally {
- DB.endTransaction();
}
}
diff --git a/ebean-test/src/test/java/org/tests/basic/TestQueryForUpdate.java b/ebean-test/src/test/java/org/tests/basic/TestQueryForUpdate.java
index 5b331a59b..75b0c3e6c 100644
--- a/ebean-test/src/test/java/org/tests/basic/TestQueryForUpdate.java
+++ b/ebean-test/src/test/java/org/tests/basic/TestQueryForUpdate.java
@@ -27,14 +27,14 @@ public class TestQueryForUpdate extends BaseTestCase {
try (final Transaction transaction = DB.beginTransaction()) {
query = DB.find(Customer.class)
.forUpdate()
- .order().desc("id");
+ .orderBy().desc("id");
query.findList();
}
if (isSqlServer()) {
assertThat(sqlOf(query)).contains("with (updlock)");
- } else if (!isDb2()){
+ } else {
assertThat(sqlOf(query)).contains("for update");
}
}
@@ -48,21 +48,21 @@ public class TestQueryForUpdate extends BaseTestCase {
query = DB.find(Customer.class)
.forUpdate()
.setMaxRows(3)
- .order().desc("id");
+ .orderBy().desc("id");
query.findList();
}
if (isSqlServer()) {
assertThat(sqlOf(query)).contains("with (updlock)");
- } else if (!isOracle() && !isDb2()) {
+ } else if (!isOracle()) {
// Oracle does not support FOR UPDATE with FETCH
assertThat(sqlOf(query)).contains("for update");
}
}
@Test
- @ForPlatform({Platform.H2, Platform.ORACLE, Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL, Platform.MARIADB})
+ @ForPlatform({Platform.H2, Platform.ORACLE, Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL, Platform.MARIADB, Platform.DB2})
public void testForUpdate_when_alreadyInPCAsReference() {
ResetBasicData.reset();
Order o0 = DB.find(Order.class).orderBy("id").setMaxRows(1).findOne();
@@ -96,7 +96,7 @@ public class TestQueryForUpdate extends BaseTestCase {
}
@Test
- @ForPlatform({Platform.H2, Platform.ORACLE, Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL, Platform.MARIADB})
+ @ForPlatform({Platform.H2, Platform.ORACLE, Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL, Platform.MARIADB, Platform.DB2})
public void testForUpdate_when_alreadyInPCAsReference_usingLock() {
ResetBasicData.reset();
Order o0 = DB.find(Order.class).orderBy("id").setMaxRows(1).findOne();
@@ -131,7 +131,7 @@ public class TestQueryForUpdate extends BaseTestCase {
}
@Test
- @ForPlatform({Platform.H2, Platform.ORACLE, Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL, Platform.MARIADB})
+ @ForPlatform({Platform.H2, Platform.ORACLE, Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL, Platform.MARIADB, Platform.DB2})
public void testForUpdate_when_alreadyInPC() {
EBasic basic = new EBasic("initialValue");
@@ -193,7 +193,7 @@ public class TestQueryForUpdate extends BaseTestCase {
Query query = DB.find(Customer.class)
.forUpdateNoWait()
- .order().desc("id");
+ .orderBy().desc("id");
query.findList();
if (isOracle()) {
@@ -219,7 +219,7 @@ public class TestQueryForUpdate extends BaseTestCase {
Query query = DB.find(Customer.class)
.forUpdateNoWait()
.setMaxRows(1)
- .order().desc("id");
+ .orderBy().desc("id");
List list = query.findList();
Customer first = list.get(0);
diff --git a/ebean-test/src/test/java/org/tests/basic/TestQueryUsingConnection.java b/ebean-test/src/test/java/org/tests/basic/TestQueryUsingConnection.java
index f1b30b586..039c6e617 100644
--- a/ebean-test/src/test/java/org/tests/basic/TestQueryUsingConnection.java
+++ b/ebean-test/src/test/java/org/tests/basic/TestQueryUsingConnection.java
@@ -73,9 +73,26 @@ public class TestQueryUsingConnection extends BaseTestCase {
assertThat(count).isEqualTo(otherCount + 1);
assertThat(otherCount).isEqualTo(masterCount);
}
-
}
+ @IgnorePlatform({Platform.SQLSERVER, Platform.COCKROACH})
+ @Test
+ public void dtoQueryUsingConnection() {
+ ResetBasicData.reset();
+
+ try (Transaction transaction = DB.createTransaction()) {
+ final CountryDto dto = DB.findDto(CountryDto.class, "select code, name from o_country where code=?")
+ .usingConnection(transaction.connection())
+ .setParameter("NZ")
+ .findOne();
+
+ assertThat(dto).isNotNull();
+ assertEquals("NZ", dto.code);
+ transaction.rollback();
+ }
+ }
+
+
public static class CountryDto {
final String code;
final String name;
diff --git a/ebean-test/src/test/java/org/tests/basic/TestUpdate.java b/ebean-test/src/test/java/org/tests/basic/TestUpdate.java
index d7973ca59..5f6e9aa43 100644
--- a/ebean-test/src/test/java/org/tests/basic/TestUpdate.java
+++ b/ebean-test/src/test/java/org/tests/basic/TestUpdate.java
@@ -1,8 +1,8 @@
package org.tests.basic;
-import io.ebean.xtest.BaseTestCase;
import io.ebean.DB;
import io.ebean.Update;
+import io.ebean.xtest.BaseTestCase;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -10,6 +10,7 @@ import org.tests.model.basic.Customer;
import org.tests.model.basic.ResetBasicData;
import java.util.Arrays;
+import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@@ -37,13 +38,17 @@ public class TestUpdate extends BaseTestCase {
@Test
public void testNormal() {
+ String sillyContent = "a123456789b123456789c123456789d123456789e123456789";
for (int i = 1; i <= 3; i++) {
Update update = DB.createUpdate(Customer.class,
"update customer set smallnote = :smallnote where name in (:name)");
- update.setParameter("name", Arrays.asList("testUpdate" + i)).setParameter("smallnote", "Note #" + i).execute();
+ update
+ .setParameter("name", List.of("testUpdate" + i))
+ .setParameter("smallnote", "Note #" + i + sillyContent)
+ .execute();
}
Customer cust = DB.find(Customer.class).where().eq("name", "testUpdate3").findOne();
- assertThat(cust.getSmallnote()).isEqualTo("Note #3");
+ assertThat(cust.getSmallnote()).isEqualTo("Note #3" + sillyContent);
}
@Test
diff --git a/ebean-test/src/test/java/org/tests/cache/TestCacheInterceptSaveWhenLazyLoaded.java b/ebean-test/src/test/java/org/tests/cache/TestCacheInterceptSaveWhenLazyLoaded.java
index b69276295..941aed9cb 100644
--- a/ebean-test/src/test/java/org/tests/cache/TestCacheInterceptSaveWhenLazyLoaded.java
+++ b/ebean-test/src/test/java/org/tests/cache/TestCacheInterceptSaveWhenLazyLoaded.java
@@ -1,5 +1,6 @@
package org.tests.cache;
+import io.ebean.Transaction;
import io.ebean.xtest.BaseTestCase;
import io.ebean.DB;
import org.junit.jupiter.api.Test;
@@ -26,8 +27,7 @@ public class TestCacheInterceptSaveWhenLazyLoaded extends BaseTestCase {
order.setCustomer(customer);
DB.save(order);
- DB.beginTransaction();
- try {
+ try (Transaction txn = DB.beginTransaction()) {
Order foundOrder = DB.find(Order.class)
.where().eq("id", order.getId())
@@ -47,9 +47,6 @@ public class TestCacheInterceptSaveWhenLazyLoaded extends BaseTestCase {
assertSame(foundOrder, order1);
assertTrue(DB.beanState(foundOrder).isDirty());
-
- } finally {
- DB.endTransaction();
}
// cleanup
diff --git a/ebean-test/src/test/java/org/tests/genkey/TestGeneratedKeys.java b/ebean-test/src/test/java/org/tests/genkey/TestGeneratedKeys.java
index a663738c8..f8209e897 100644
--- a/ebean-test/src/test/java/org/tests/genkey/TestGeneratedKeys.java
+++ b/ebean-test/src/test/java/org/tests/genkey/TestGeneratedKeys.java
@@ -1,6 +1,7 @@
package org.tests.genkey;
import io.ebean.DB;
+import io.ebean.Transaction;
import org.junit.jupiter.api.Test;
import org.tests.model.basic.TOne;
@@ -19,12 +20,10 @@ public class TestGeneratedKeys {
c1.setName("Two");
c1.setDescription("Test Gen Key Two");
- DB.beginTransaction();
- try {
+ try (Transaction txn = DB.beginTransaction()) {
DB.save(c);
DB.save(c1);
- } finally {
- DB.commitTransaction();
+ txn.commit();
}
Integer id = c.getId();
assertNotNull(id);
diff --git a/ebean-test/src/test/java/org/tests/insert/TestInsertOnConflict.java b/ebean-test/src/test/java/org/tests/insert/TestInsertOnConflict.java
new file mode 100644
index 000000000..02b69639e
--- /dev/null
+++ b/ebean-test/src/test/java/org/tests/insert/TestInsertOnConflict.java
@@ -0,0 +1,411 @@
+package org.tests.insert;
+
+import io.ebean.*;
+import io.ebean.annotation.Platform;
+import io.ebean.test.LoggedSql;
+import io.ebean.xtest.BaseTestCase;
+import io.ebean.xtest.ForPlatform;
+import org.junit.jupiter.api.Test;
+import org.tests.update.EPersonOnline;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+import static io.ebean.InsertOptions.ON_CONFLICT_NOTHING;
+import static io.ebean.InsertOptions.ON_CONFLICT_UPDATE;
+import static org.assertj.core.api.Assertions.assertThat;
+
+class TestInsertOnConflict extends BaseTestCase {
+
+ InsertOptions onConflictDoUpdateAndGetGeneratedKeys = InsertOptions.builder()
+ .onConflictUpdate()
+ .getGeneratedKeys(true)
+ .build();
+
+ @ForPlatform({Platform.POSTGRES, Platform.YUGABYTE})
+ @Test
+ void insertOnConflictUpdateExplicitTransaction() {
+ Database db = DB.getDefault();
+ db.truncate(EPersonOnline.class);
+ LoggedSql.start();
+
+ var bean = newBean("a@b.com");
+
+ try (Transaction txn = DB.createTransaction()) {
+ db.insert(bean, ON_CONFLICT_UPDATE, txn);
+ txn.commit();
+ }
+ assertThat(bean.getId()).isNotNull();
+
+ var bean2 = newBean("a@b.com");
+ bean2.setOnlineStatus(false);
+ try (Transaction txn = DB.createTransaction()) {
+ db.insert(bean2, ON_CONFLICT_UPDATE, txn);
+ txn.commit();
+ }
+ assertThat(bean2.getId()).isEqualTo(bean.getId());
+
+ var sql = LoggedSql.stop();
+ assertThat(sql).hasSize(2);
+ assertThat(sql.get(0)).contains("insert into e_person_online (email, online_status, when_updated) values (?,?,?) on conflict (email) do update set online_status=excluded.online_status, when_updated=excluded.when_updated");
+ assertThat(sql.get(1)).contains("insert into e_person_online (email, online_status, when_updated) values (?,?,?) on conflict (email) do update set online_status=excluded.online_status, when_updated=excluded.when_updated");
+
+ List list = db.find(EPersonOnline.class).findList();
+ assertThat(list).hasSize(1);
+ assertThat(list.get(0).getWhenUpdated()).isEqualTo(bean2.getWhenUpdated());
+ }
+
+ @ForPlatform({Platform.POSTGRES, Platform.YUGABYTE})
+ @Test
+ void insertOnConflictUpdate_when_noIdValue() {
+ Database db = DB.getDefault();
+ db.truncate(EPersonOnline.class);
+ LoggedSql.start();
+
+ var bean = newBean("a@b.com");
+ db.insert(bean, onConflictDoUpdateAndGetGeneratedKeys);
+ assertThat(bean.getId()).isNotNull();
+
+ var bean2 = newBean("a@b.com");
+ bean2.setOnlineStatus(false);
+ db.insert(bean2, onConflictDoUpdateAndGetGeneratedKeys);
+ assertThat(bean2.getId()).isEqualTo(bean.getId());
+
+ var sql = LoggedSql.stop();
+ assertThat(sql).hasSize(2);
+ assertThat(sql.get(0)).contains("insert into e_person_online (email, online_status, when_updated) values (?,?,?) on conflict (email) do update set online_status=excluded.online_status, when_updated=excluded.when_updated");
+ assertThat(sql.get(1)).contains("insert into e_person_online (email, online_status, when_updated) values (?,?,?) on conflict (email) do update set online_status=excluded.online_status, when_updated=excluded.when_updated");
+
+ List list = db.find(EPersonOnline.class).findList();
+
+ assertThat(list).hasSize(1);
+ assertThat(list.get(0).getWhenUpdated()).isEqualTo(bean2.getWhenUpdated());
+ }
+
+ @ForPlatform({Platform.POSTGRES, Platform.YUGABYTE})
+ @Test
+ void insertOnConflictUpdate_when_idValueSupplied() {
+ Database db = DB.getDefault();
+ db.truncate(EPersonOnline.class);
+ LoggedSql.start();
+
+ var bean = newBean("a@b.com");
+ bean.setId(40_042L);
+ db.insert(bean, ON_CONFLICT_UPDATE);
+ assertThat(bean.getId()).isNotNull();
+
+ var bean2 = newBean("a@b.com");
+ bean2.setId(40_043L); // not expected but can be different
+ bean2.setOnlineStatus(false);
+
+ db.insert(bean2, onConflictDoUpdateAndGetGeneratedKeys);
+
+ var sql = LoggedSql.stop();
+ assertThat(sql).hasSize(2);
+ assertThat(sql.get(0)).contains("insert into e_person_online (id, email, online_status, when_updated) values (?,?,?,?) on conflict (email) do update set online_status=excluded.online_status, when_updated=excluded.when_updated");
+ assertThat(sql.get(1)).contains("insert into e_person_online (id, email, online_status, when_updated) values (?,?,?,?) on conflict (email) do update set online_status=excluded.online_status, when_updated=excluded.when_updated");
+
+ List list = db.find(EPersonOnline.class).findList();
+
+ assertThat(list).hasSize(1);
+ assertThat(list.get(0).getWhenUpdated()).isEqualTo(bean2.getWhenUpdated());
+ }
+
+ @ForPlatform({Platform.POSTGRES, Platform.YUGABYTE})
+ @Test
+ void insertAll_onConflictUpdate_when_noIdValue() {
+ Database db = DB.getDefault();
+ db.truncate(EPersonOnline.class);
+ LoggedSql.start();
+
+ var bean = newBean("a1@b.com");
+ var bean2 = newBean("a2@b.com");
+ var bean3 = newBean("a3@b.com");
+ db.insertAll(List.of(bean, bean2, bean3), ON_CONFLICT_UPDATE);
+
+ var sql = LoggedSql.stop();
+ assertThat(sql).hasSize(5);
+ assertThat(sql.get(0)).contains("insert into e_person_online (email, online_status, when_updated) values (?,?,?) on conflict (email) do update set online_status=excluded.online_status, when_updated=excluded.when_updated");
+ assertThat(sql.get(1)).contains(" -- bind");
+ assertThat(sql.get(2)).contains(" -- bind");
+ assertThat(sql.get(3)).contains(" -- bind");
+ assertThat(sql.get(4)).contains(" -- executeBatch()");
+
+ var bean4 = newBean("a1@b.com");
+ var bean5 = newBean("a5@b.com");
+ db.insertAll(List.of(bean4, bean5), ON_CONFLICT_UPDATE);
+
+ List list = db.find(EPersonOnline.class).orderBy("id").findList();
+ assertThat(list).hasSize(4);
+ }
+
+ @ForPlatform({Platform.POSTGRES, Platform.YUGABYTE})
+ @Test
+ void insertAll_onConflictUpdate_explicitTransaction() {
+ Database db = DB.getDefault();
+ db.truncate(EPersonOnline.class);
+ LoggedSql.start();
+
+ try (Transaction txn = DB.createTransaction()) {
+ txn.setBatchSize(3);
+ var bean = newBean("a1@b.com");
+ var bean2 = newBean("a2@b.com");
+ var bean3 = newBean("a3@b.com");
+ var bean4 = newBean("a4@b.com");
+ db.insertAll(List.of(bean, bean2, bean3, bean4), ON_CONFLICT_UPDATE, txn);
+ txn.commit();
+ }
+
+ var sql = LoggedSql.stop();
+ assertThat(sql).hasSize(8);
+ assertThat(sql.get(0)).contains("insert into e_person_online (email, online_status, when_updated) values (?,?,?) on conflict (email) do update set online_status=excluded.online_status, when_updated=excluded.when_updated");
+ assertThat(sql.get(1)).contains(" -- bind");
+ assertThat(sql.get(2)).contains(" -- bind");
+ assertThat(sql.get(3)).contains(" -- bind");
+ assertThat(sql.get(4)).contains(" -- executeBatch()");
+ assertThat(sql.get(5)).contains("insert into e_person_online (email, online_status, when_updated) values (?,?,?) on conflict (email) do update set online_status=excluded.online_status, when_updated=excluded.when_updated");
+ assertThat(sql.get(6)).contains(" -- bind");
+ assertThat(sql.get(7)).contains(" -- executeBatch()");
+
+ List list = db.find(EPersonOnline.class).orderBy("id").findList();
+ assertThat(list).hasSize(4);
+ }
+
+ @ForPlatform({Platform.POSTGRES, Platform.YUGABYTE})
+ @Test
+ void explicitConstraint() {
+ Database db = DB.getDefault();
+ db.truncate(EPersonOnline.class);
+ LoggedSql.start();
+
+ var bean = newBean("a1@b.com");
+ var bean2 = newBean("a2@b.com");
+ var bean3 = newBean("a3@b.com");
+
+ var ON_CONFLICT_ = InsertOptions.builder()
+ .onConflictUpdate()
+ .constraint("uq_e_person_online_email")
+ .build();
+
+ db.insertAll(List.of(bean, bean2, bean3), ON_CONFLICT_);
+
+ var sql = LoggedSql.stop();
+ assertThat(sql).hasSize(5);
+ assertThat(sql.get(0)).contains("insert into e_person_online (email, online_status, when_updated) values (?,?,?) on conflict on constraint uq_e_person_online_email do update set online_status=excluded.online_status, when_updated=excluded.when_updated");
+ assertThat(sql.get(1)).contains(" -- bind");
+ assertThat(sql.get(2)).contains(" -- bind");
+ assertThat(sql.get(3)).contains(" -- bind");
+ assertThat(sql.get(4)).contains(" -- executeBatch()");
+
+ List list = db.find(EPersonOnline.class).orderBy("id").findList();
+ assertThat(list).hasSize(3);
+ }
+
+ @ForPlatform({Platform.POSTGRES, Platform.YUGABYTE})
+ @Test
+ void explicitUniqueColumns() {
+ Database db = DB.getDefault();
+ db.truncate(EPersonOnline.class);
+ LoggedSql.start();
+
+ var bean = newBean("a1@b.com");
+ var bean2 = newBean("a2@b.com");
+ var bean3 = newBean("a3@b.com");
+
+ var ON_CONFLICT_ = InsertOptions.builder()
+ .onConflictUpdate()
+ .uniqueColumns(" email ")
+ .build();
+
+ db.insertAll(List.of(bean, bean2, bean3), ON_CONFLICT_);
+
+ var sql = LoggedSql.stop();
+ assertThat(sql).hasSize(5);
+ assertThat(sql.get(0)).contains("insert into e_person_online (email, online_status, when_updated) values (?,?,?) on conflict ( email ) do update set online_status=excluded.online_status, when_updated=excluded.when_updated");
+ assertThat(sql.get(1)).contains(" -- bind");
+ assertThat(sql.get(2)).contains(" -- bind");
+ assertThat(sql.get(3)).contains(" -- bind");
+ assertThat(sql.get(4)).contains(" -- executeBatch()");
+
+ List list = db.find(EPersonOnline.class).orderBy("id").findList();
+ assertThat(list).hasSize(3);
+ }
+
+ @ForPlatform({Platform.POSTGRES, Platform.YUGABYTE})
+ @Test
+ void explicitUpdateSet() {
+ Database db = DB.getDefault();
+ db.truncate(EPersonOnline.class);
+ LoggedSql.start();
+
+ var bean = newBean("a1@b.com");
+ var bean2 = newBean("a2@b.com");
+
+ var ON_CONFLICT_ = InsertOptions.builder()
+ .onConflictUpdate()
+ .updateSet("when_updated=excluded.when_updated, online_status=true")
+ .build();
+
+ db.insertAll(List.of(bean, bean2), ON_CONFLICT_);
+
+ var sql = LoggedSql.stop();
+ assertThat(sql).hasSize(4);
+ assertThat(sql.get(0)).contains("insert into e_person_online (email, online_status, when_updated) values (?,?,?) on conflict (email) do update set when_updated=excluded.when_updated, online_status=true");
+ assertThat(sql.get(1)).contains(" -- bind");
+ assertThat(sql.get(2)).contains(" -- bind");
+ assertThat(sql.get(3)).contains(" -- executeBatch()");
+
+ List list = db.find(EPersonOnline.class).orderBy("id").findList();
+ assertThat(list).hasSize(2);
+ }
+
+ @ForPlatform({Platform.POSTGRES, Platform.YUGABYTE})
+ @Test
+ void insertOnConflictNothing_when_noIdValue() {
+ Database db = DB.getDefault();
+ db.truncate(EPersonOnline.class);
+ LoggedSql.start();
+
+ var bean = newBean("a@b.com");
+ db.insert(bean, ON_CONFLICT_NOTHING);
+ assertThat(bean.getId()).isNotNull();
+
+ var bean2 = newBean("a@b.com");
+ bean2.setOnlineStatus(false);
+ db.insert(bean2, ON_CONFLICT_NOTHING);
+ assertThat(bean2.getId()).isNull();
+
+ var sql = LoggedSql.stop();
+ assertThat(sql).hasSize(2);
+ assertThat(sql.get(0)).contains("insert into e_person_online (email, online_status, when_updated) values (?,?,?) on conflict (email) do nothing");
+ assertThat(sql.get(1)).contains("insert into e_person_online (email, online_status, when_updated) values (?,?,?) on conflict (email) do nothing");
+
+ List list = db.find(EPersonOnline.class).findList();
+
+ assertThat(list).hasSize(1);
+ assertThat(list.get(0).getWhenUpdated()).isEqualTo(bean.getWhenUpdated());
+ }
+
+ @ForPlatform({Platform.POSTGRES, Platform.YUGABYTE})
+ @Test
+ void updateQueryReturning() throws SQLException {
+ Database db = DB.getDefault();
+ db.truncate(EPersonOnline.class);
+
+ var bean1 = newBean("a1@bee.com");
+ var bean2 = newBean("a2@cee.com");
+ var bean3 = newBean("a3@bee.com");
+ db.saveAll(bean1, bean2, bean3);
+
+ String sql = "update e_person_online set email = concat('x',email) where email like ? returning id, email, online_status";
+
+ try (Transaction txn = db.createTransaction()) {
+ Connection connection = txn.connection();
+ try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
+ pstmt.setString(1, "%bee.com");
+ try (ResultSet resultSet = pstmt.executeQuery()) {
+ while (resultSet.next()) {
+ long id = resultSet.getLong(1);
+ String email = resultSet.getString(2);
+ boolean status = resultSet.getBoolean(3);
+ // do something with the id, email and status
+ assertThat(id).isGreaterThan(0);
+ assertThat(email).startsWith("x");
+ assertThat(status).isTrue();
+ }
+ }
+ }
+ txn.commit();
+ }
+
+ try (Transaction txn = db.beginTransaction()) {
+ List sqlRowList = DB.sqlQuery(sql)
+ .setParameter("%bee.com")
+ .findList();
+
+ assertThat(sqlRowList).hasSize(2);
+ assertThat(sqlRowList.get(0).getString("email")).startsWith("xx");
+
+ txn.addModification("e_person_online", false, true, false);
+ txn.commit();
+ }
+
+ try (Transaction txn = db.beginTransaction()) {
+ List dtoList = db.findDto(ReturnDto.class, sql)
+ .setParameter("%bee.com")
+ .findList();
+
+ assertThat(dtoList).hasSize(2);
+ assertThat(dtoList.get(0).email).startsWith("xxx");
+
+ txn.addModification("e_person_online", false, true, false);
+ txn.commit();
+ }
+
+ try (Transaction txn = db.beginTransaction()) {
+ List dtoList2 = db.findDto(ReturnDto2.class, sql)
+ .setParameter("%bee.com")
+ .findList();
+
+ assertThat(dtoList2).hasSize(2);
+ assertThat(dtoList2.get(0).email).startsWith("xxxx");
+ assertThat(dtoList2.get(0).id).isGreaterThan(0);
+ txn.addModification("e_person_online", false, true, false);
+ txn.commit();
+ }
+ }
+
+ public static class ReturnDto {
+
+ private final long id;
+ private final String email;
+ private final boolean onlineStatus;
+
+ public ReturnDto(long id, String email, boolean onlineStatus) {
+ this.id = id;
+ this.email = email;
+ this.onlineStatus = onlineStatus;
+ }
+ }
+
+ public static class ReturnDto2 {
+
+ private long id;
+ private String email;
+ private boolean onlineStatus;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public boolean isOnlineStatus() {
+ return onlineStatus;
+ }
+
+ public void setOnlineStatus(boolean onlineStatus) {
+ this.onlineStatus = onlineStatus;
+ }
+ }
+
+ private static EPersonOnline newBean(String email) {
+ EPersonOnline bean = new EPersonOnline();
+ bean.setEmail(email);
+ bean.setOnlineStatus(true);
+ return bean;
+ }
+}
diff --git a/ebean-test/src/test/java/org/tests/iud/TestPersistCascade.java b/ebean-test/src/test/java/org/tests/iud/TestPersistCascade.java
index 39136c284..d8d4424a9 100644
--- a/ebean-test/src/test/java/org/tests/iud/TestPersistCascade.java
+++ b/ebean-test/src/test/java/org/tests/iud/TestPersistCascade.java
@@ -35,11 +35,9 @@ public class TestPersistCascade extends BaseTestCase {
assertThat(sql.get(23)).contains("insert into pcf_city");
assertSqlBind(sql, 24, 26);
assertThat(sql.get(28)).contains("insert into pcf_event");
- assertSqlBind(sql, 29, 48);
- assertThat(sql.get(50)).contains("insert into pcf_event");
- assertSqlBind(sql, 51, 70);
- assertThat(sql.get(72)).contains("insert into pcf_event");
- assertSqlBind(sql, 73, 87);
+ assertSqlBind(sql, 29, 128);
+ assertThat(sql.get(130)).contains("insert into pcf_event");
+ assertSqlBind(sql, 131, 150);
}
country.deletePermanent();
diff --git a/ebean-test/src/test/java/org/tests/level/test/ManyToManyTest.java b/ebean-test/src/test/java/org/tests/level/test/ManyToManyTest.java
index 5973dcf7d..34a71b899 100644
--- a/ebean-test/src/test/java/org/tests/level/test/ManyToManyTest.java
+++ b/ebean-test/src/test/java/org/tests/level/test/ManyToManyTest.java
@@ -1,5 +1,6 @@
package org.tests.level.test;
+import io.ebean.Transaction;
import io.ebean.xtest.BaseTestCase;
import io.ebean.DB;
import org.junit.jupiter.api.Test;
@@ -19,8 +20,7 @@ public class ManyToManyTest extends BaseTestCase {
@Test
public void test() {
- DB.beginTransaction();
- try {
+ try (Transaction txn = DB.beginTransaction()) {
Level4 i = new Level4("i");
Level4 ii = new Level4("ii");
Level4 iii = new Level4("iii");
@@ -92,9 +92,6 @@ public class ManyToManyTest extends BaseTestCase {
.findList();
validateObjectGraph(i, ii, iii, one, two, x1, x2, x3, x4, x5, things);
-
- } finally {
- DB.endTransaction();
}
}
diff --git a/ebean-test/src/test/java/org/tests/model/array/EArrayBean.java b/ebean-test/src/test/java/org/tests/model/array/EArrayBean.java
index 14cea519e..b24ebaa79 100644
--- a/ebean-test/src/test/java/org/tests/model/array/EArrayBean.java
+++ b/ebean-test/src/test/java/org/tests/model/array/EArrayBean.java
@@ -9,6 +9,8 @@ import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Version;
import java.math.BigDecimal;
+import java.time.Instant;
+import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -58,6 +60,12 @@ public class EArrayBean {
@DbArray
Set status2;
+ @DbArray
+ List times;
+
+ @DbArray
+ List dates;
+
@Version
Long version;
@@ -165,6 +173,24 @@ public class EArrayBean {
this.status2 = status2;
}
+ public List getTimes() {
+ return times;
+ }
+
+ public EArrayBean setTimes(List times) {
+ this.times = times;
+ return this;
+ }
+
+ public List getDates() {
+ return dates;
+ }
+
+ public EArrayBean setDates(List dates) {
+ this.dates = dates;
+ return this;
+ }
+
public Long getVersion() {
return version;
}
diff --git a/ebean-test/src/test/java/org/tests/model/array/TestDbArray_basic.java b/ebean-test/src/test/java/org/tests/model/array/TestDbArray_basic.java
index 89cd68095..dcbbde859 100644
--- a/ebean-test/src/test/java/org/tests/model/array/TestDbArray_basic.java
+++ b/ebean-test/src/test/java/org/tests/model/array/TestDbArray_basic.java
@@ -10,6 +10,8 @@ import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.sql.SQLException;
+import java.time.Instant;
+import java.time.LocalDate;
import java.util.*;
import static org.assertj.core.api.Assertions.assertThat;
@@ -58,6 +60,8 @@ class TestDbArray_basic extends BaseTestCase {
bean.setStatus2(new LinkedHashSet<>());
bean.getStatus2().add(EArrayBean.Status.TWO);
bean.getStatus2().add(EArrayBean.Status.ONE);
+ bean.setTimes(List.of(Instant.now(), Instant.now().minusSeconds(60)));
+ bean.setDates(List.of(LocalDate.now(), LocalDate.now().minusDays(1)));
DB.save(bean);
@@ -66,6 +70,8 @@ class TestDbArray_basic extends BaseTestCase {
assertThat(found.getPhoneNumbers()).containsExactly("4321", "9823");
assertThat(found.getDoubs()).hasSize(2);
assertThat(found.getFloats()).hasSize(2);
+ assertThat(found.getTimes()).hasSize(2);
+ assertThat(found.getDates()).hasSize(2);
if (isPostgresCompatible()) {
Query query = DB.find(EArrayBean.class)
diff --git a/ebean-test/src/test/java/org/tests/model/basic/CustomerFindController.java b/ebean-test/src/test/java/org/tests/model/basic/CustomerFindController.java
new file mode 100644
index 000000000..996c80d9f
--- /dev/null
+++ b/ebean-test/src/test/java/org/tests/model/basic/CustomerFindController.java
@@ -0,0 +1,35 @@
+package org.tests.model.basic;
+
+import io.ebean.bean.BeanCollection;
+import io.ebean.event.BeanFindController;
+import io.ebean.event.BeanQueryRequest;
+
+/**
+ * @author Noemi Praml, FOCONIS AG
+ */
+public class CustomerFindController implements BeanFindController {
+ @Override
+ public boolean isRegisterFor(Class> cls) {
+ return Customer.class.isAssignableFrom(cls);
+ }
+
+ @Override
+ public boolean isInterceptFind(BeanQueryRequest> request) {
+ return false;
+ }
+
+ @Override
+ public T find(BeanQueryRequest request) {
+ return null;
+ }
+
+ @Override
+ public boolean isInterceptFindMany(BeanQueryRequest> request) {
+ return false;
+ }
+
+ @Override
+ public BeanCollection findMany(BeanQueryRequest request) {
+ return null;
+ }
+}
diff --git a/ebean-test/src/test/java/org/tests/model/basic/Product.java b/ebean-test/src/test/java/org/tests/model/basic/Product.java
index df57dadd0..4f5557660 100644
--- a/ebean-test/src/test/java/org/tests/model/basic/Product.java
+++ b/ebean-test/src/test/java/org/tests/model/basic/Product.java
@@ -39,6 +39,13 @@ public class Product implements Serializable {
@Version
Timestamp updtime;
+ public Product(Integer id) {
+ this.id = id;
+ }
+
+ public Product() {
+ }
+
/**
* Return id.
*/
diff --git a/ebean-test/src/test/java/org/tests/model/basic/xtra/TestDeleteUnloadedChildren.java b/ebean-test/src/test/java/org/tests/model/basic/xtra/TestDeleteUnloadedChildren.java
index 09c7de91e..467f2121e 100644
--- a/ebean-test/src/test/java/org/tests/model/basic/xtra/TestDeleteUnloadedChildren.java
+++ b/ebean-test/src/test/java/org/tests/model/basic/xtra/TestDeleteUnloadedChildren.java
@@ -54,7 +54,6 @@ public class TestDeleteUnloadedChildren extends BaseTestCase {
@Test
public void testCascadeDelete2() {
-
init();
try (Transaction txn = DB.beginTransaction()) {
@@ -68,18 +67,14 @@ public class TestDeleteUnloadedChildren extends BaseTestCase {
@Test
public void testCascadeDelete3() {
-
init();
- DB.beginTransaction();
- try {
+ try (Transaction txn = DB.beginTransaction()) {
EdExtendedParent extendedParent = DB.find(EdExtendedParent.class).where()
.eq("name", "My second computer").findOne();
extendedParent.getChildren().size();
DB.delete(extendedParent);
- DB.commitTransaction();
- } finally {
- DB.endTransaction();
+ txn.commit();
}
}
diff --git a/ebean-test/src/test/java/org/tests/model/orphanremoval/OmBeanListChild.java b/ebean-test/src/test/java/org/tests/model/orphanremoval/OmBeanListChild.java
index c4b07d3cd..7ed80c302 100644
--- a/ebean-test/src/test/java/org/tests/model/orphanremoval/OmBeanListChild.java
+++ b/ebean-test/src/test/java/org/tests/model/orphanremoval/OmBeanListChild.java
@@ -28,6 +28,11 @@ public class OmBeanListChild extends Model {
public Long getId() {
return id;
}
+
+ public OmBeanListChild setId(Long id) {
+ this.id = id;
+ return this;
+ }
}
diff --git a/ebean-test/src/test/java/org/tests/model/orphanremoval/OmBeanListParent.java b/ebean-test/src/test/java/org/tests/model/orphanremoval/OmBeanListParent.java
index 1aacb79d6..026199f70 100644
--- a/ebean-test/src/test/java/org/tests/model/orphanremoval/OmBeanListParent.java
+++ b/ebean-test/src/test/java/org/tests/model/orphanremoval/OmBeanListParent.java
@@ -2,10 +2,14 @@ package org.tests.model.orphanremoval;
import io.ebean.Model;
+import io.ebean.annotation.WhenCreated;
+import io.ebean.annotation.WhenModified;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Version;
+
+import java.time.Instant;
import java.util.List;
import static jakarta.persistence.CascadeType.ALL;
@@ -19,6 +23,13 @@ public class OmBeanListParent extends Model {
@Version
private long version;
+ private String name;
+
+ @WhenCreated
+ private Instant whenCreated;
+ @WhenModified
+ private Instant whenModified;
+
@OneToMany(cascade = ALL, mappedBy = "parent", orphanRemoval = true)
private List children;
@@ -35,6 +46,42 @@ public class OmBeanListParent extends Model {
this.children.clear();
this.children.addAll(children);
}
+
+ public void setChildren2(List children) {
+ this.children = children;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public long getVersion() {
+ return version;
+ }
+
+ public void setVersion(long version) {
+ this.version = version;
+ }
+
+ public Instant getWhenModified() {
+ return whenModified;
+ }
+
+ public void setWhenModified(Instant whenModified) {
+ this.whenModified = whenModified;
+ }
+
+ public Instant getWhenCreated() {
+ return whenCreated;
+ }
+
+ public void setWhenCreated(Instant whenCreated) {
+ this.whenCreated = whenCreated;
+ }
}
diff --git a/ebean-test/src/test/java/org/tests/model/orphanremoval/TestOrphanRemovalOverwrite.java b/ebean-test/src/test/java/org/tests/model/orphanremoval/TestOrphanRemovalOverwrite.java
index 446fff4b2..d03575e8c 100644
--- a/ebean-test/src/test/java/org/tests/model/orphanremoval/TestOrphanRemovalOverwrite.java
+++ b/ebean-test/src/test/java/org/tests/model/orphanremoval/TestOrphanRemovalOverwrite.java
@@ -1,17 +1,20 @@
package org.tests.model.orphanremoval;
+import io.ebean.DB;
+import io.ebean.test.LoggedSql;
import org.junit.jupiter.api.Test;
import java.util.List;
import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
-public class TestOrphanRemovalOverwrite {
+class TestOrphanRemovalOverwrite {
@Test
- public void testOverwritingMapping() {
+ void testOverwritingMapping() {
OmBeanListParent parent = new OmBeanListParent();
parent.save();
@@ -29,8 +32,45 @@ public class TestOrphanRemovalOverwrite {
// Refreshing here generates new objects for the associated children that are referred to by the parent.
parent.refresh();
- assertNotNull(childList.get(0).getId());
- assertEquals(childList.get(0).getId(), parent.getChildren().get(0).getId());
+ OmBeanListChild child = childList.get(0);
+ assertNotNull(child.getId());
+
+ OmBeanListChild refreshedChild = parent.getChildren().get(0);
+ assertEquals(child.getId(), refreshedChild.getId());
+ assertEquals(child, refreshedChild);
assertEquals(childList, parent.getChildren());
}
+
+ @Test
+ void clearAddAll() {
+ OmBeanListChild c1 = new OmBeanListChild("c1");
+ OmBeanListChild c2 = new OmBeanListChild("c2");
+
+ OmBeanListParent parent = new OmBeanListParent();
+ parent.getChildren().add(c1);
+ parent.getChildren().add(c2);
+ parent.save();
+
+
+ OmBeanListChild c3 = new OmBeanListChild("c3");
+ c3.setId(c1.getId());
+
+ OmBeanListParent p1 = DB.find(OmBeanListParent.class, parent.getId());
+ List children = p1.getChildren();
+ children.clear();
+ children.add(c3);
+
+ LoggedSql.start();
+ DB.save(p1);
+
+ List sql = LoggedSql.stop();
+ assertThat(sql).hasSize(7);
+ assertThat(sql.get(0)).contains("delete from om_bean_list_child where id=?");
+ assertThat(sql.get(1)).contains(" -- bind");
+ assertThat(sql.get(2)).contains(" -- bind");
+ assertThat(sql.get(3)).contains(" -- executeBatch()");
+ assertThat(sql.get(4)).contains(" insert into om_bean_list_child");
+ assertThat(sql.get(5)).contains(" -- bind");
+ assertThat(sql.get(6)).contains(" -- executeBatch()");
+ }
}
diff --git a/ebean-test/src/test/java/org/tests/o2m/OneToManyListMarkAsDirtyTest.java b/ebean-test/src/test/java/org/tests/o2m/OneToManyListMarkAsDirtyTest.java
new file mode 100644
index 000000000..ae358b85d
--- /dev/null
+++ b/ebean-test/src/test/java/org/tests/o2m/OneToManyListMarkAsDirtyTest.java
@@ -0,0 +1,114 @@
+package org.tests.o2m;
+
+import io.ebean.DB;
+import io.ebean.xtest.BaseTestCase;
+import org.junit.jupiter.api.Test;
+import org.tests.model.orphanremoval.OmBeanListChild;
+import org.tests.model.orphanremoval.OmBeanListParent;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.LockSupport;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class OneToManyListMarkAsDirtyTest extends BaseTestCase {
+ @Test
+ void usingNewListWithNonDirtyParent_expect_orphanDeleted_works() {
+ // setup
+ var parent = new OmBeanListParent();
+ var a_b = new OmBeanListChild("b");
+ parent.getChildren().add(a_b);
+ DB.save(parent);
+ LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1));
+
+ // act
+ var secondParent = DB.find(OmBeanListParent.class, parent.getId());
+ secondParent.setChildren2(new ArrayList<>()); //