From ef1143bb702822654d40b4e05312e4ada477c7a0 Mon Sep 17 00:00:00 2001
From: Roland Praml
Date: Tue, 2 Mar 2021 15:26:29 +0100
Subject: [PATCH 01/72] FIX: orderBy does not work when used on formula
property or in conjunction with "exists" query
---
.../server/query/CQueryBuilder.java | 12 +++++--
.../server/querydefn/DefaultOrmQuery.java | 9 +++++
.../java/org/tests/basic/TestFetchId.java | 36 +++++++++++++++++++
3 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java
index ab008a5cf..1162e914d 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java
@@ -616,11 +616,19 @@ class CQueryBuilder {
if (request.isInlineCountDistinct()) {
sb.append(")");
}
- if (distinct && dbOrderBy != null && !query.isSingleAttribute()) {
+ if (distinct && dbOrderBy != null) {
// add the orderBy columns to the select clause (due to distinct)
final OrderBy> orderBy = query.getOrderBy();
if (orderBy != null && orderBy.supportsSelect()) {
- sb.append(", ").append(DbOrderByTrim.trim(dbOrderBy));
+ String trimmed = DbOrderByTrim.trim(dbOrderBy);
+ if (query.isSingleAttribute() && trimmed.equals(select.getSelectSql())) {
+ // NOP, already in SQL
+ // TODO: what to do if we select("id").orderBy("prop,id")?
+ // Can we live with a query like "select t0.id, t0.prop, t0.id from"
+ // or should we elliminate the second "t0.id" from select
+ } else {
+ sb.append(", ").append(trimmed);
+ }
}
}
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java
index 6f72febb9..e6eafc0b8 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java
@@ -53,6 +53,7 @@ import io.ebeaninternal.server.deploy.BeanNaturalKey;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.TableJoin;
+import io.ebeaninternal.server.el.ElPropertyDeploy;
import io.ebeaninternal.server.expression.DefaultExpressionList;
import io.ebeaninternal.server.expression.IdInExpression;
import io.ebeaninternal.server.expression.SimpleExpression;
@@ -537,6 +538,14 @@ public class DefaultOrmQuery implements SpiQuery {
if (havingExpressions != null) {
havingExpressions.containsMany(beanDescriptor, manyWhereJoins);
}
+ if (orderBy != null) {
+ for (Property orderProperty : orderBy.getProperties()) {
+ ElPropertyDeploy elProp = beanDescriptor.getElPropertyDeploy(orderProperty.getProperty());
+ if (elProp != null && elProp.containsFormulaWithJoin()) {
+ manyWhereJoins.addFormulaWithJoin(orderProperty.getProperty());
+ }
+ }
+ }
}
/**
diff --git a/ebean-core/src/test/java/org/tests/basic/TestFetchId.java b/ebean-core/src/test/java/org/tests/basic/TestFetchId.java
index cf207995c..c830208d5 100644
--- a/ebean-core/src/test/java/org/tests/basic/TestFetchId.java
+++ b/ebean-core/src/test/java/org/tests/basic/TestFetchId.java
@@ -1,10 +1,14 @@
package org.tests.basic;
import io.ebean.BaseTestCase;
+import io.ebean.DB;
import io.ebean.Ebean;
import io.ebean.FutureIds;
import io.ebean.Query;
+import io.ebeantest.LoggedSql;
+
import org.tests.model.basic.Order;
+import org.tests.model.basic.OrderDetail;
import org.tests.model.basic.ResetBasicData;
import org.junit.Test;
@@ -36,4 +40,36 @@ public class TestFetchId extends BaseTestCase {
List
*/
public void setDefaultSelectClause(BeanDescriptor> desc) {
-
if (desc.hasDefaultSelectClause() && !hasSelectClause()) {
baseProps = new OrmQueryProperties(null, desc.getDefaultSelectClause());
}
-
for (OrmQueryProperties joinProps : fetchPaths.values()) {
if (!joinProps.hasSelectClause()) {
BeanDescriptor> assocDesc = desc.getBeanDescriptor(joinProps.getPath());
@@ -506,7 +481,6 @@ public class OrmQueryDetail implements Serializable {
props = new OrmQueryProperties(path);
fetch(props);
return props;
-
} else {
return props;
}
@@ -516,9 +490,7 @@ public class OrmQueryDetail implements Serializable {
* Return true if the fetch path is included.
*/
public boolean includesPath(String path) {
-
OrmQueryProperties chunk = fetchPaths.get(path);
-
// may not have fetch properties if just +cache etc
return chunk != null && !chunk.isCache();
}
@@ -537,6 +509,15 @@ public class OrmQueryDetail implements Serializable {
return fetchPaths.entrySet();
}
+ /**
+ * Prepare filterMany expressions that are being included into the main query.
+ */
+ public void prepareExpressions(BeanQueryRequest> request) {
+ for (OrmQueryProperties value : fetchPaths.values()) {
+ value.prepareExpressions(request);
+ }
+ }
+
private static class FetchEntry implements Comparable {
private final int index;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryProperties.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryProperties.java
index 466bb3eb9..0a8b51465 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryProperties.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryProperties.java
@@ -4,6 +4,7 @@ import io.ebean.ExpressionFactory;
import io.ebean.FetchConfig;
import io.ebean.OrderBy;
import io.ebean.Query;
+import io.ebean.event.BeanQueryRequest;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionFactory;
@@ -178,8 +179,6 @@ public class OrmQueryProperties implements Serializable {
SpiExpressionFactory queryEf = (SpiExpressionFactory) rootQuery.getExpressionFactory();
ExpressionFactory filterEf = queryEf.createExpressionFactory();// exprPath);
filterMany = new FilterExpressionList(exprPath, filterEf, rootQuery);
- // by default we need to make this a 'query join' now
- markForQueryJoin = true;
}
return filterMany;
}
@@ -194,6 +193,24 @@ public class OrmQueryProperties implements Serializable {
return filterMany.trimPath(trimPath);
}
+ /**
+ * Adjust filterMany expressions for inclusion in main query.
+ */
+ public void filterManyInline() {
+ if (filterMany != null){
+ filterMany.prefixProperty(path);
+ }
+ }
+
+ /**
+ * Prepare filterMany expressions for query plan key.
+ */
+ public void prepareExpressions(BeanQueryRequest> request) {
+ if (filterMany != null) {
+ filterMany.prepareExpression(request);
+ }
+ }
+
/**
* Return the filterMany expression list (can be null).
*/
@@ -206,7 +223,6 @@ public class OrmQueryProperties implements Serializable {
*/
public void setFilterMany(SpiExpressionList> filterMany) {
this.filterMany = filterMany;
- this.markForQueryJoin = true;
}
/**
diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/core/DefaultServer_createOrmQueryRequestTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/core/DefaultServer_createOrmQueryRequestTest.java
index 12c731115..1a5484f16 100644
--- a/ebean-core/src/test/java/io/ebeaninternal/server/core/DefaultServer_createOrmQueryRequestTest.java
+++ b/ebean-core/src/test/java/io/ebeaninternal/server/core/DefaultServer_createOrmQueryRequestTest.java
@@ -288,7 +288,7 @@ public class DefaultServer_createOrmQueryRequestTest extends BaseTestCase {
}
@Test
- public void test_removeJoinToMany_when_filterMany() {
+ public void test_filterMany_included() {
Query query = Ebean.find(Order.class)
.fetch("details")
@@ -301,6 +301,40 @@ public class DefaultServer_createOrmQueryRequestTest extends BaseTestCase {
OrmQueryRequest queryRequest = queryRequest(query);
OrmQueryDetail detail = queryRequest.getQuery().getDetail();
+ assertThat(detail.getFetchPaths()).containsExactly("details", "details.product", "customer");
+ }
+
+ @Test
+ public void test_filterMany_excludedByOrdering() {
+
+ Query query = Ebean.find(Order.class)
+ .fetch("customer")
+ .fetch("customer.contacts")
+ .fetch("details")
+ .fetch("details.product")
+ .filterMany("details").eq("orderQuantity", 10)
+ .query();
+
+ OrmQueryRequest queryRequest = queryRequest(query);
+ OrmQueryDetail detail = queryRequest.getQuery().getDetail();
+
+ assertThat(detail.getFetchPaths()).containsExactly("customer", "customer.contacts");
+ }
+
+ @Test
+ public void test_filterMany_excludedExplicitly() {
+
+ Query query = Ebean.find(Order.class)
+ .fetchQuery("details")
+ .fetch("details.product")
+ .fetch("customer")
+ .fetch("customer.contacts")
+ .filterMany("details").eq("orderQuantity", 10)
+ .query();
+
+ OrmQueryRequest queryRequest = queryRequest(query);
+ OrmQueryDetail detail = queryRequest.getQuery().getDetail();
+
assertThat(detail.getFetchPaths()).containsExactly("customer", "customer.contacts");
}
diff --git a/ebean-core/src/test/java/org/tests/query/TestQueryFilterMany.java b/ebean-core/src/test/java/org/tests/query/TestQueryFilterMany.java
index 976dac580..be58fb8c4 100644
--- a/ebean-core/src/test/java/org/tests/query/TestQueryFilterMany.java
+++ b/ebean-core/src/test/java/org/tests/query/TestQueryFilterMany.java
@@ -172,7 +172,6 @@ public class TestQueryFilterMany extends BaseTestCase {
LoggedSqlCollector.start();
Query query = Ebean.find(Customer.class)
- .fetch("orders")
.filterMany("orders").raw("1=0")
.where().isNotEmpty("orders")
.query();
@@ -183,12 +182,10 @@ public class TestQueryFilterMany extends BaseTestCase {
}
List sqlList = LoggedSqlCollector.stop();
- assertEquals(2, sqlList.size());
- assertThat(sqlList.get(0)).contains("where exists (select 1 from o_order x where x.kcustomer_id = t0.id)");
- assertThat(sqlList.get(1)).contains("and 1=0");
+ assertEquals(1, sqlList.size());
+ assertThat(sqlList.get(0)).contains("from o_customer t0 left join o_order t1 on t1.kcustomer_id = t0.id and t1.order_date is not null left join o_customer t2 on t2.id = t1.kcustomer_id where exists (select 1 from o_order x where x.kcustomer_id = t0.id) and 1=0 order by t0.id");
}
-
@Test
public void test_filterMany_in_findCount() {
@@ -212,7 +209,6 @@ public class TestQueryFilterMany extends BaseTestCase {
public void test_filterMany_copy_findList() {
ResetBasicData.reset();
-
LoggedSqlCollector.start();
Query query = Ebean.find(Customer.class)
@@ -222,17 +218,35 @@ public class TestQueryFilterMany extends BaseTestCase {
query.copy().findList();
+ List sqlList = LoggedSqlCollector.stop();
+ assertEquals(1, sqlList.size());
+ assertThat(sqlList.get(0)).contains("from o_customer t0 left join o_order t1 on t1.kcustomer_id = t0.id and t1.order_date is not null left join o_customer t2 on t2.id = t1.kcustomer_id where t1.status in (?) order by t0.id");
+ }
+
+ @Test
+ public void test_filterMany_fetchQuery() {
+
+ ResetBasicData.reset();
+ LoggedSqlCollector.start();
+
+ Query query = Ebean.find(Customer.class)
+ .fetchQuery("orders") // explicitly fetch orders separately
+ .filterMany("orders").in("status", Order.Status.NEW)
+ .order().asc("id");
+
+ query.findList();
+
List sqlList = LoggedSqlCollector.stop();
assertEquals(2, sqlList.size());
assertThat(sqlList.get(0)).contains("from o_customer t0");
- assertThat(sqlList.get(1)).contains("from o_order t0 join o_customer t1");
+ assertThat(sqlList.get(1)).contains("from o_order t0 join o_customer t1 on t1.id = t0.kcustomer_id where t0.order_date is not null and (t0.kcustomer_id) in ");
+ assertThat(sqlList.get(1)).contains(" and t0.status in ");
}
@Test
public void testDisjunction() {
ResetBasicData.reset();
-
LoggedSqlCollector.start();
Ebean.find(Customer.class)
@@ -243,8 +257,8 @@ public class TestQueryFilterMany extends BaseTestCase {
.findList();
List sql = LoggedSqlCollector.stop();
- assertEquals(2, sql.size());
- assertSql(sql.get(1)).contains("and (t0.status = ? or t0.order_date = ?");
+ assertEquals(1, sql.size());
+ assertSql(sql.get(0)).contains(" from o_customer t0 left join o_order t1 on t1.kcustomer_id = t0.id and t1.order_date is not null left join o_customer t2 on t2.id = t1.kcustomer_id where (t1.status = ? or t1.order_date = ?) order by t0.id");
}
@Test
@@ -259,12 +273,10 @@ public class TestQueryFilterMany extends BaseTestCase {
List sql = LoggedSqlCollector.stop();
- assertThat(sql).hasSize(3);
- assertSql(sql.get(0)).contains(" from o_customer t0; --bind()");
- platformAssertIn(sql.get(1), " from contact t0 where (t0.customer_id)");
- assertSql(sql.get(1)).contains(" and t0.first_name is not null");
- platformAssertIn(sql.get(2), " from contact_note t0 where (t0.contact_id)");
- assertSql(sql.get(2)).contains(" and lower(t0.title) like");
+ assertThat(sql).hasSize(2);
+ assertSql(sql.get(0)).contains(" from o_customer t0 left join contact t1 on t1.customer_id = t0.id where t1.first_name is not null order by t0.id; --bind()");
+ platformAssertIn(sql.get(1), " from contact_note t0 where (t0.contact_id)");
+ assertSql(sql.get(1)).contains(" and lower(t0.title) like");
}
@Test
@@ -280,9 +292,7 @@ public class TestQueryFilterMany extends BaseTestCase {
List sql = LoggedSqlCollector.stop();
- assertThat(sql).hasSize(2);
- assertSql(sql.get(0)).contains(" from o_customer t0");
- assertSql(sql.get(1)).contains("from contact t0 where ");
- assertSql(sql.get(1)).contains("and (t0.first_name is not null and lower(t0.email) like ?");
+ assertThat(sql).hasSize(1);
+ assertSql(sql.get(0)).contains(" from o_customer t0 left join contact t1 on t1.customer_id = t0.id where (t1.first_name is not null and lower(t1.email) like ? escape'|' ) order by t0.id; --bind(rob%)");
}
}
From d6fdb4ee34129f4b98b35686b822c3c4154d77b6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 26 Apr 2021 20:37:55 +0000
Subject: [PATCH 03/72] Bump commons-io from 2.5 to 2.7 in /ebean-core
Bumps commons-io from 2.5 to 2.7.
Signed-off-by: dependabot[bot]
---
ebean-core/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml
index dbde9f167..13732d513 100644
--- a/ebean-core/pom.xml
+++ b/ebean-core/pom.xml
@@ -282,7 +282,7 @@
commons-iocommons-io
- 2.5
+ 2.7test
From ad52711fe172e70d5ab2f649f163399f2dc4ce62 Mon Sep 17 00:00:00 2001
From: Brian Payne
Date: Tue, 11 May 2021 08:52:08 -0700
Subject: [PATCH 04/72] Add test demonstrating soft-delete bug with optional
relationship
---
.../org/tests/model/softdelete/ESoftDelX.java | 54 +++++++++++++++++++
.../org/tests/model/softdelete/ESoftDelY.java | 53 ++++++++++++++++++
.../org/tests/model/softdelete/ESoftDelZ.java | 43 +++++++++++++++
.../TestSoftDeleteOptionalRelationship.java | 31 +++++++++++
4 files changed, 181 insertions(+)
create mode 100644 ebean-core/src/test/java/org/tests/model/softdelete/ESoftDelX.java
create mode 100644 ebean-core/src/test/java/org/tests/model/softdelete/ESoftDelY.java
create mode 100644 ebean-core/src/test/java/org/tests/model/softdelete/ESoftDelZ.java
diff --git a/ebean-core/src/test/java/org/tests/model/softdelete/ESoftDelX.java b/ebean-core/src/test/java/org/tests/model/softdelete/ESoftDelX.java
new file mode 100644
index 000000000..fbc3f9964
--- /dev/null
+++ b/ebean-core/src/test/java/org/tests/model/softdelete/ESoftDelX.java
@@ -0,0 +1,54 @@
+package org.tests.model.softdelete;
+
+import io.ebean.annotation.SoftDelete;
+
+import javax.persistence.*;
+import java.util.UUID;
+
+@Entity
+public class ESoftDelX {
+
+ @Id
+ private UUID id;
+
+ @OneToOne
+ private ESoftDelY y;
+
+ @ManyToOne
+ private ESoftDelZ organization;
+
+ @SoftDelete
+ boolean deleted;
+
+ public UUID getId() {
+ return id;
+ }
+
+ public void setId(UUID id) {
+ this.id = id;
+ }
+
+ public ESoftDelY getY() {
+ return y;
+ }
+
+ public void setY(ESoftDelY y) {
+ this.y = y;
+ }
+
+ public ESoftDelZ getOrganization() {
+ return organization;
+ }
+
+ public void setOrganization(ESoftDelZ organization) {
+ this.organization = organization;
+ }
+
+ public boolean isDeleted() {
+ return deleted;
+ }
+
+ public void setDeleted(boolean deleted) {
+ this.deleted = deleted;
+ }
+}
diff --git a/ebean-core/src/test/java/org/tests/model/softdelete/ESoftDelY.java b/ebean-core/src/test/java/org/tests/model/softdelete/ESoftDelY.java
new file mode 100644
index 000000000..a65cc75c1
--- /dev/null
+++ b/ebean-core/src/test/java/org/tests/model/softdelete/ESoftDelY.java
@@ -0,0 +1,53 @@
+package org.tests.model.softdelete;
+
+import io.ebean.annotation.SoftDelete;
+
+import javax.persistence.*;
+
+@Entity
+public class ESoftDelY {
+
+ @Id
+ private Long id;
+
+ @ManyToOne
+ private ESoftDelZ organization;
+
+ @OneToOne(mappedBy = "y")
+ private ESoftDelX x;
+
+ @SoftDelete
+ boolean deleted;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public ESoftDelZ getOrganization() {
+ return organization;
+ }
+
+ public void setOrganization(ESoftDelZ organization) {
+ this.organization = organization;
+ }
+
+ public ESoftDelX getX() {
+ return x;
+ }
+
+ public void setX(ESoftDelX x) {
+ this.x = x;
+ }
+
+ public boolean isDeleted() {
+ return deleted;
+ }
+
+ public void setDeleted(boolean deleted) {
+ this.deleted = deleted;
+ }
+}
diff --git a/ebean-core/src/test/java/org/tests/model/softdelete/ESoftDelZ.java b/ebean-core/src/test/java/org/tests/model/softdelete/ESoftDelZ.java
new file mode 100644
index 000000000..5ce11c175
--- /dev/null
+++ b/ebean-core/src/test/java/org/tests/model/softdelete/ESoftDelZ.java
@@ -0,0 +1,43 @@
+package org.tests.model.softdelete;
+
+import io.ebean.annotation.SoftDelete;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import java.util.UUID;
+
+@Entity
+public class ESoftDelZ {
+
+ @Id
+ private Long id;
+
+ @SoftDelete
+ private boolean deleted;
+
+ private UUID uuid;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public boolean isDeleted() {
+ return deleted;
+ }
+
+ public void setDeleted(boolean deleted) {
+ this.deleted = deleted;
+ }
+
+ public UUID getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(UUID uuid) {
+ this.uuid = uuid;
+ }
+}
diff --git a/ebean-core/src/test/java/org/tests/softdelete/TestSoftDeleteOptionalRelationship.java b/ebean-core/src/test/java/org/tests/softdelete/TestSoftDeleteOptionalRelationship.java
index fc925f62c..2488060d4 100644
--- a/ebean-core/src/test/java/org/tests/softdelete/TestSoftDeleteOptionalRelationship.java
+++ b/ebean-core/src/test/java/org/tests/softdelete/TestSoftDeleteOptionalRelationship.java
@@ -1,9 +1,15 @@
package org.tests.softdelete;
import io.ebean.BaseTestCase;
+import io.ebean.DB;
import io.ebean.Ebean;
import org.tests.model.softdelete.ESoftDelMid;
+import io.ebean.Finder;
import org.junit.Test;
+import org.tests.model.softdelete.ESoftDelY;
+import org.tests.model.softdelete.ESoftDelZ;
+
+import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
@@ -24,5 +30,30 @@ public class TestSoftDeleteOptionalRelationship extends BaseTestCase {
assertThat(bean.getTop()).isNull();
}
+ @Test
+ public void testFindNullWhenMultiple() {
+ UUID uuid = UUID.randomUUID();
+ {
+ ESoftDelZ z = new ESoftDelZ();
+ z.setUuid(uuid);
+ DB.save(z);
+
+ ESoftDelY y = new ESoftDelY();
+ y.setOrganization(z);
+ y.setX(null);
+ DB.save(y);
+ }
+
+ Finder finder = new Finder<>(ESoftDelY.class);
+ ESoftDelY bean = finder
+ .query()
+ .where()
+ .eq("organization.uuid", uuid)
+ .isNull("x")
+ .findOne();
+
+ assertThat(bean).isNotNull();
+ }
+
}
From 66f7f69ec70bc301d8effb18f01185b817064fdd Mon Sep 17 00:00:00 2001
From: Brian Payne
Date: Wed, 12 May 2021 09:24:33 -0700
Subject: [PATCH 05/72] Fixed bug with soft-delete on one-to-one exported
---
.../server/query/SqlTreeNodeExtraJoin.java | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java
index 074457a30..7238224d7 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java
@@ -5,6 +5,7 @@ import io.ebean.bean.EntityBean;
import io.ebean.core.type.ScalarType;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.SpiQuery;
+import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.DbReadContext;
import io.ebeaninternal.server.deploy.DbSqlContext;
import io.ebeaninternal.server.deploy.TableJoin;
@@ -130,11 +131,19 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode {
}
}
+ boolean oneToOneExported = false;
+ if (assocBeanProperty instanceof BeanPropertyAssocOne) {
+ BeanPropertyAssocOne> oneToOneProp = (BeanPropertyAssocOne>) assocBeanProperty;
+ if (oneToOneProp.isOneToOneExported()) {
+ oneToOneExported = true;
+ }
+ }
+
if (pathContainsMany) {
// "promote" to left join as the path contains a many
joinType = SqlJoinType.OUTER;
}
- if (!manyToMany) {
+ if (!manyToMany && !oneToOneExported) {
if (assocBeanProperty.isFormula()) {
// add joins for formula beans
assocBeanProperty.appendFrom(ctx, joinType);
From eb00690b3034b970d4e7a1778d61eb0c305c82aa Mon Sep 17 00:00:00 2001
From: rbygrave
Date: Thu, 20 May 2021 14:09:22 +1200
Subject: [PATCH 06/72] #2231 - Change OrderBy to not be final to support
mocking via Mockito
---
ebean-api/src/main/java/io/ebean/OrderBy.java | 15 ++++-----------
1 file changed, 4 insertions(+), 11 deletions(-)
diff --git a/ebean-api/src/main/java/io/ebean/OrderBy.java b/ebean-api/src/main/java/io/ebean/OrderBy.java
index 820a76314..8b39fc19e 100644
--- a/ebean-api/src/main/java/io/ebean/OrderBy.java
+++ b/ebean-api/src/main/java/io/ebean/OrderBy.java
@@ -16,7 +16,7 @@ import java.util.Objects;
* on the Query object.
*
*/
-public final class OrderBy implements Serializable {
+public class OrderBy implements Serializable {
private static final long serialVersionUID = 9157089257745730539L;
@@ -69,7 +69,6 @@ public final class OrderBy implements Serializable {
* Add a property with ascending order to this OrderBy.
*/
public Query asc(String propertyName) {
-
list.add(new Property(propertyName, true));
return query;
}
@@ -98,7 +97,6 @@ public final class OrderBy implements Serializable {
return query;
}
-
/**
* Return true if the property is known to be contained in the order by clause.
*/
@@ -207,7 +205,6 @@ public final class OrderBy implements Serializable {
if (!(obj instanceof OrderBy>)) {
return false;
}
-
OrderBy> e = (OrderBy>) obj;
return e.list.equals(list);
}
@@ -249,7 +246,7 @@ public final class OrderBy implements Serializable {
/**
* A property and its ascending descending order.
*/
- public static final class Property implements Serializable {
+ public static class Property implements Serializable {
private static final long serialVersionUID = 1546009780322478077L;
@@ -415,13 +412,10 @@ public final class OrderBy implements Serializable {
}
private void parse(String orderByClause) {
-
if (orderByClause == null) {
return;
}
-
- String[] chunks = orderByClause.split(",");
- for (String chunk : chunks) {
+ for (String chunk : orderByClause.split(",")) {
Property p = parseProperty(chunk);
if (p != null) {
list.add(p);
@@ -467,8 +461,7 @@ public final class OrderBy implements Serializable {
if (s.startsWith("desc")) {
return false;
}
- String m = "Expecting [" + s + "] to be asc or desc?";
- throw new RuntimeException(m);
+ throw new RuntimeException("Expecting [" + s + "] to be asc or desc?");
}
private boolean isEmptyString(String s) {
From b17593ba3cbf4a2c70967fbf0a93568607d10961 Mon Sep 17 00:00:00 2001
From: rbygrave
Date: Thu, 20 May 2021 21:44:07 +1200
Subject: [PATCH 07/72] No effective change - tidy BeanList, BeanMap, BeanSet
internals
---
ebean-api/src/main/java/io/ebean/common/BeanList.java | 3 +--
ebean-api/src/main/java/io/ebean/common/BeanMap.java | 1 -
ebean-api/src/main/java/io/ebean/common/BeanSet.java | 1 -
3 files changed, 1 insertion(+), 4 deletions(-)
diff --git a/ebean-api/src/main/java/io/ebean/common/BeanList.java b/ebean-api/src/main/java/io/ebean/common/BeanList.java
index 0aa774a81..ba0adca99 100644
--- a/ebean-api/src/main/java/io/ebean/common/BeanList.java
+++ b/ebean-api/src/main/java/io/ebean/common/BeanList.java
@@ -198,10 +198,9 @@ public final class BeanList extends AbstractBeanCollection implements List
}
if (list == null) {
sb.append("deferred ");
-
} else {
sb.append("size[").append(list.size()).append("] ");
- sb.append("list").append(list).append("");
+ sb.append("list").append(list);
}
return sb.toString();
}
diff --git a/ebean-api/src/main/java/io/ebean/common/BeanMap.java b/ebean-api/src/main/java/io/ebean/common/BeanMap.java
index 09b37d38a..2b9ef350d 100644
--- a/ebean-api/src/main/java/io/ebean/common/BeanMap.java
+++ b/ebean-api/src/main/java/io/ebean/common/BeanMap.java
@@ -194,7 +194,6 @@ public final class BeanMap extends AbstractBeanCollection implements Ma
}
if (map == null) {
sb.append("deferred ");
-
} else {
sb.append("size[").append(map.size()).append("]");
sb.append(" map").append(map);
diff --git a/ebean-api/src/main/java/io/ebean/common/BeanSet.java b/ebean-api/src/main/java/io/ebean/common/BeanSet.java
index 880ece106..0c6f0c508 100644
--- a/ebean-api/src/main/java/io/ebean/common/BeanSet.java
+++ b/ebean-api/src/main/java/io/ebean/common/BeanSet.java
@@ -176,7 +176,6 @@ public final class BeanSet extends AbstractBeanCollection implements Set
Date: Fri, 21 May 2021 08:33:31 +1200
Subject: [PATCH 08/72] #2233 Failing test
---
.../org/tests/lazyloadconf/AppConfig.java | 36 +++++++
.../tests/lazyloadconf/AppConfigControl.java | 42 ++++++++
.../java/org/tests/lazyloadconf/MyTest.java | 97 +++++++++++++++++++
.../org/tests/lazyloadconf/Relationship.java | 35 +++++++
4 files changed, 210 insertions(+)
create mode 100644 ebean-core/src/test/java/org/tests/lazyloadconf/AppConfig.java
create mode 100644 ebean-core/src/test/java/org/tests/lazyloadconf/AppConfigControl.java
create mode 100644 ebean-core/src/test/java/org/tests/lazyloadconf/MyTest.java
create mode 100644 ebean-core/src/test/java/org/tests/lazyloadconf/Relationship.java
diff --git a/ebean-core/src/test/java/org/tests/lazyloadconf/AppConfig.java b/ebean-core/src/test/java/org/tests/lazyloadconf/AppConfig.java
new file mode 100644
index 000000000..999ef1aa0
--- /dev/null
+++ b/ebean-core/src/test/java/org/tests/lazyloadconf/AppConfig.java
@@ -0,0 +1,36 @@
+package org.tests.lazyloadconf;
+
+import io.ebean.annotation.Cache;
+
+import javax.persistence.*;
+import java.util.List;
+
+@Entity
+@Cache
+@Table(name = "app_config")
+public class AppConfig {
+
+ @Id
+ @Column(name = "id")
+ private Integer id;
+
+ @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "appConfig")
+ //@JoinColumn(name = "id", referencedColumnName = "id")
+ private List items;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public List getItems() {
+ return items;
+ }
+
+ public void setItems(List items) {
+ this.items = items;
+ }
+}
diff --git a/ebean-core/src/test/java/org/tests/lazyloadconf/AppConfigControl.java b/ebean-core/src/test/java/org/tests/lazyloadconf/AppConfigControl.java
new file mode 100644
index 000000000..8da38cd59
--- /dev/null
+++ b/ebean-core/src/test/java/org/tests/lazyloadconf/AppConfigControl.java
@@ -0,0 +1,42 @@
+package org.tests.lazyloadconf;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "app_config_control")
+public class AppConfigControl {
+
+ @Id
+ private Integer id;
+
+ private String name;
+
+ @ManyToOne
+ @JoinColumn(name = "config_id", referencedColumnName = "id")
+ private AppConfig appConfig;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public AppConfig getAppConfig() {
+ return appConfig;
+ }
+
+ public void setAppConfig(AppConfig appConfig) {
+ this.appConfig = appConfig;
+ }
+}
+
diff --git a/ebean-core/src/test/java/org/tests/lazyloadconf/MyTest.java b/ebean-core/src/test/java/org/tests/lazyloadconf/MyTest.java
new file mode 100644
index 000000000..34ec36ea7
--- /dev/null
+++ b/ebean-core/src/test/java/org/tests/lazyloadconf/MyTest.java
@@ -0,0 +1,97 @@
+package org.tests.lazyloadconf;
+
+import io.ebean.Ebean;
+import io.ebean.Query;
+import org.junit.Test;
+
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class MyTest {
+
+ @Test
+ public void testRe() {
+
+ AppConfig globalAppConfig = new AppConfig();
+ globalAppConfig.setId(1);
+ globalAppConfig.setItems(new ArrayList<>());
+ AppConfigControl global = new AppConfigControl();
+ global.setId(1);
+ global.setName("global");
+ global.setAppConfig(globalAppConfig);
+ globalAppConfig.getItems().add(global);
+ Ebean.save(globalAppConfig);
+
+ AppConfig userAppConfig = new AppConfig();
+ userAppConfig.setId(2);
+ userAppConfig.setItems(new ArrayList<>());
+ AppConfigControl user = new AppConfigControl();
+ user.setId(2);
+ user.setName("user");
+ user.setAppConfig(userAppConfig);
+ userAppConfig.getItems().add(user);
+ Ebean.save(userAppConfig);
+
+ AppConfig otherAppConfig = new AppConfig();
+ otherAppConfig.setId(3);
+ otherAppConfig.setItems(new ArrayList<>());
+ Ebean.save(otherAppConfig);
+
+ Relationship globalRe = new Relationship();
+ globalRe.setId(1);
+ globalRe.setAppConfig(globalAppConfig);
+ Ebean.save(globalRe);
+
+ Relationship userRe = new Relationship();
+ userRe.setId(2);
+ userRe.setAppConfig(userAppConfig);
+ Ebean.save(userRe);
+
+ Relationship otherRe = new Relationship();
+ otherRe.setId(3);
+ otherRe.setAppConfig(otherAppConfig);
+ Ebean.save(otherRe);
+
+
+
+// Start business processing
+ Query relationshipQuery = Ebean.find(Relationship.class);
+ List relationshipList = relationshipQuery.where().idIn(1, 2, 3).findList();
+
+ assertThat(relationshipList.size()).isEqualTo(3);
+
+ Map map = relationshipList.stream()
+ .map(Relationship::getAppConfig)
+ .map((ac) -> new AbstractMap.SimpleImmutableEntry<>(ac.getId(), ac))
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+
+// final List items = DB.find(AppConfig.class)
+// .fetch("items")
+// .findList();
+
+ AppConfig g = map.get(1);
+ AppConfig u = map.get(2);
+ AppConfig o = map.get(3);
+ if(!u.getItems().isEmpty()){
+ g.setItems(u.getItems());
+ }
+
+ assertThat(g.getItems().size()).isEqualTo(1);
+
+// If this line of code is commented out, this test case will be successfully passed
+ assertThat(o.getItems().size()).isEqualTo(0);
+
+// org.junit.ComparisonFailure:
+// Expected :1
+// Actual :2
+ assertThat(g.getItems().size()).isEqualTo(1);
+
+ assertThat(g.getItems().get(0).getName()).isEqualTo("user");
+ }
+
+}
diff --git a/ebean-core/src/test/java/org/tests/lazyloadconf/Relationship.java b/ebean-core/src/test/java/org/tests/lazyloadconf/Relationship.java
new file mode 100644
index 000000000..f59e7677a
--- /dev/null
+++ b/ebean-core/src/test/java/org/tests/lazyloadconf/Relationship.java
@@ -0,0 +1,35 @@
+package org.tests.lazyloadconf;
+
+import io.ebean.annotation.Cache;
+
+import javax.persistence.*;
+
+@Entity
+@Cache(enableBeanCache = true)
+@Table(name="app_config_re")
+public class Relationship {
+
+ @Id
+ private Integer id;
+
+ @ManyToOne
+ @JoinColumn(name="config_id",referencedColumnName="id")
+ private AppConfig appConfig;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public AppConfig getAppConfig() {
+ return appConfig;
+ }
+
+ public void setAppConfig(AppConfig appConfig) {
+ this.appConfig = appConfig;
+ }
+}
+
From 77c6f1cb9f97773abd41b13cc4eef950aef608b3 Mon Sep 17 00:00:00 2001
From: rbygrave
Date: Fri, 21 May 2021 08:44:04 +1200
Subject: [PATCH 09/72] #2233 Tidy failing test Ebean -> DB and rename test
---
...ava => BeanCollectionLazyLoadingTest.java} | 37 ++++++++-----------
1 file changed, 16 insertions(+), 21 deletions(-)
rename ebean-core/src/test/java/org/tests/lazyloadconf/{MyTest.java => BeanCollectionLazyLoadingTest.java} (75%)
diff --git a/ebean-core/src/test/java/org/tests/lazyloadconf/MyTest.java b/ebean-core/src/test/java/org/tests/lazyloadconf/BeanCollectionLazyLoadingTest.java
similarity index 75%
rename from ebean-core/src/test/java/org/tests/lazyloadconf/MyTest.java
rename to ebean-core/src/test/java/org/tests/lazyloadconf/BeanCollectionLazyLoadingTest.java
index 34ec36ea7..b0ef7592b 100644
--- a/ebean-core/src/test/java/org/tests/lazyloadconf/MyTest.java
+++ b/ebean-core/src/test/java/org/tests/lazyloadconf/BeanCollectionLazyLoadingTest.java
@@ -1,6 +1,6 @@
package org.tests.lazyloadconf;
-import io.ebean.Ebean;
+import io.ebean.DB;
import io.ebean.Query;
import org.junit.Test;
@@ -12,10 +12,10 @@ import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
-public class MyTest {
+public class BeanCollectionLazyLoadingTest {
@Test
- public void testRe() {
+ public void test() {
AppConfig globalAppConfig = new AppConfig();
globalAppConfig.setId(1);
@@ -25,7 +25,7 @@ public class MyTest {
global.setName("global");
global.setAppConfig(globalAppConfig);
globalAppConfig.getItems().add(global);
- Ebean.save(globalAppConfig);
+ DB.save(globalAppConfig);
AppConfig userAppConfig = new AppConfig();
userAppConfig.setId(2);
@@ -35,32 +35,31 @@ public class MyTest {
user.setName("user");
user.setAppConfig(userAppConfig);
userAppConfig.getItems().add(user);
- Ebean.save(userAppConfig);
+ DB.save(userAppConfig);
AppConfig otherAppConfig = new AppConfig();
otherAppConfig.setId(3);
otherAppConfig.setItems(new ArrayList<>());
- Ebean.save(otherAppConfig);
+ DB.save(otherAppConfig);
Relationship globalRe = new Relationship();
globalRe.setId(1);
globalRe.setAppConfig(globalAppConfig);
- Ebean.save(globalRe);
+ DB.save(globalRe);
Relationship userRe = new Relationship();
userRe.setId(2);
userRe.setAppConfig(userAppConfig);
- Ebean.save(userRe);
+ DB.save(userRe);
Relationship otherRe = new Relationship();
otherRe.setId(3);
otherRe.setAppConfig(otherAppConfig);
- Ebean.save(otherRe);
+ DB.save(otherRe);
-
-// Start business processing
- Query relationshipQuery = Ebean.find(Relationship.class);
+ // Start business processing
+ Query relationshipQuery = DB.find(Relationship.class);
List relationshipList = relationshipQuery.where().idIn(1, 2, 3).findList();
assertThat(relationshipList.size()).isEqualTo(3);
@@ -70,25 +69,21 @@ public class MyTest {
.map((ac) -> new AbstractMap.SimpleImmutableEntry<>(ac.getId(), ac))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
-// final List items = DB.find(AppConfig.class)
-// .fetch("items")
-// .findList();
-
AppConfig g = map.get(1);
AppConfig u = map.get(2);
AppConfig o = map.get(3);
- if(!u.getItems().isEmpty()){
+ if (!u.getItems().isEmpty()) {
+ // a source of the problem came from invoking lazy loading here
+ // with the setItems call which is unnecessary due to being a ToMany
g.setItems(u.getItems());
}
assertThat(g.getItems().size()).isEqualTo(1);
-// If this line of code is commented out, this test case will be successfully passed
+ // If this line of code is commented out, this test case will be successfully passed
assertThat(o.getItems().size()).isEqualTo(0);
-// org.junit.ComparisonFailure:
-// Expected :1
-// Actual :2
+ // org.junit.ComparisonFailure: Expected :1 Actual :2
assertThat(g.getItems().size()).isEqualTo(1);
assertThat(g.getItems().get(0).getName()).isEqualTo("user");
From 79740accfa688d14988ee6ffe3f21276a8126e33 Mon Sep 17 00:00:00 2001
From: rbygrave
Date: Fri, 21 May 2021 08:48:42 +1200
Subject: [PATCH 10/72] #2233 Fix with setter on ToMany now not invoking lazy
loading
- ebean-agent changed to not invoke preGetter on ToMany (just like Id)
- EntityBeanIntercept preSetterMany() changed to ensure loading flag set
---
.../src/main/java/io/ebean/bean/EntityBeanIntercept.java | 8 +++++++-
ebean-bom/pom.xml | 4 ++--
ebean-core/pom.xml | 2 +-
ebean-ddl-generator/pom.xml | 2 +-
kotlin-querybean-generator/pom.xml | 2 +-
5 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java
index 12f3fda8f..0c2333026 100644
--- a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java
+++ b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java
@@ -85,6 +85,8 @@ public final class EntityBeanIntercept implements Serializable {
*/
private static final byte FLAG_CHANGED_PROP = 2;
+ private static final byte FLAG_CHANGEDLOADED_PROP = 3;
+
/**
* Flags indicating if a property is a dirty embedded bean. Used to distinguish
* between an embedded bean being completely overwritten and one of its
@@ -569,6 +571,10 @@ public final class EntityBeanIntercept implements Serializable {
flags[propertyIndex] |= FLAG_CHANGED_PROP;
}
+ private void setChangeLoaded(int propertyIndex) {
+ flags[propertyIndex] |= FLAG_CHANGEDLOADED_PROP;
+ }
+
/**
* Set that an embedded bean has had one of its properties changed.
*/
@@ -937,7 +943,7 @@ public final class EntityBeanIntercept implements Serializable {
if (readOnly) {
throw new IllegalStateException("This bean is readOnly");
}
- setChangedProperty(propertyIndex);
+ setChangeLoaded(propertyIndex);
}
}
diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml
index aaa2b5961..edc2708cf 100644
--- a/ebean-bom/pom.xml
+++ b/ebean-bom/pom.xml
@@ -18,8 +18,8 @@
12.4.04.17.0
- 12.8.2
- 12.8.2
+ 12.8.4
+ 12.8.4
diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml
index 13732d513..559d763ac 100644
--- a/ebean-core/pom.xml
+++ b/ebean-core/pom.xml
@@ -302,7 +302,7 @@
io.ebeanebean-maven-plugin
- 12.8.2
+ 12.8.4test
diff --git a/ebean-ddl-generator/pom.xml b/ebean-ddl-generator/pom.xml
index f94924830..0c5089130 100644
--- a/ebean-ddl-generator/pom.xml
+++ b/ebean-ddl-generator/pom.xml
@@ -76,7 +76,7 @@
io.ebeanebean-maven-plugin
- 12.8.2
+ 12.8.4test
diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml
index a556671bb..99433a038 100644
--- a/kotlin-querybean-generator/pom.xml
+++ b/kotlin-querybean-generator/pom.xml
@@ -146,7 +146,7 @@
io.ebeanebean-maven-plugin
- 12.8.2
+ 12.8.4test
From 676bac390ea018b895903bfaee026e57808cf9ee Mon Sep 17 00:00:00 2001
From: rbygrave
Date: Fri, 21 May 2021 09:01:45 +1200
Subject: [PATCH 11/72] #2233 Followup change that unsets load buffer from
BeanCollection
- Clears the buffer after it has been used to load BeanCollection's
- Sets the BeanCollection to use the server rather than the Load Buffer just in case there are subsequent calls for loading (which there should not be)
---
.../io/ebeaninternal/api/LoadManyRequest.java | 15 +++------------
.../server/loadcontext/DLoadManyContext.java | 9 ++++-----
2 files changed, 7 insertions(+), 17 deletions(-)
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/LoadManyRequest.java b/ebean-core/src/main/java/io/ebeaninternal/api/LoadManyRequest.java
index d7dc2f56a..aeb9ff7dd 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/LoadManyRequest.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/LoadManyRequest.java
@@ -71,7 +71,6 @@ public class LoadManyRequest extends LoadRequest {
* This for use when lazy loading is invoked on methods such as clear() and removeAll() where it
* generally makes sense to only fetch the Id values as the other property information is not
* used.
- *
*/
private boolean isOnlyIds() {
return onlyIds;
@@ -91,18 +90,16 @@ public class LoadManyRequest extends LoadRequest {
return loadContext.getBatchSize();
}
- private List getParentIdList() {
-
+ private List parentIdList(SpiEbeanServer server) {
List idList = new ArrayList<>();
-
BeanPropertyAssocMany> many = getMany();
for (BeanCollection> bc : batch) {
idList.add(many.getParentId(bc.getOwnerBean()));
+ bc.setLoader(server); // don't use the load buffer again
}
if (many.getTargetDescriptor().isPadInExpression()) {
BindPadding.padIds(idList);
}
-
return idList;
}
@@ -111,9 +108,7 @@ public class LoadManyRequest extends LoadRequest {
}
public SpiQuery> createQuery(SpiEbeanServer server) {
-
BeanPropertyAssocMany> many = getMany();
-
SpiQuery> query = many.newQuery(server);
String orderBy = many.getLazyFetchOrderBy();
if (orderBy != null) {
@@ -128,7 +123,7 @@ public class LoadManyRequest extends LoadRequest {
}
query.setLazyLoadForParents(many);
- many.addWhereParentIdIn(query, getParentIdList(), loadContext.isUseDocStore());
+ many.addWhereParentIdIn(query, parentIdList(server), loadContext.isUseDocStore());
query.setPersistenceContext(loadContext.getPersistenceContext());
String mode = isLazy() ? "+lazy" : "+query";
@@ -146,7 +141,6 @@ public class LoadManyRequest extends LoadRequest {
// override to just select the Id values
query.select(many.getTargetIdProperty());
}
-
return query;
}
@@ -154,10 +148,8 @@ public class LoadManyRequest extends LoadRequest {
* After the query execution check for empty collections and load L2 cache if desired.
*/
public void postLoad() {
-
BeanDescriptor> desc = loadContext.getBeanDescriptor();
BeanPropertyAssocMany> many = getMany();
-
// check for BeanCollection's that where never processed
// in the +query or +lazy load due to no rows (predicates)
for (BeanCollection> bc : batch) {
@@ -172,6 +164,5 @@ public class LoadManyRequest extends LoadRequest {
desc.cacheManyPropPut(many, bc, parentId);
}
}
-
}
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadManyContext.java b/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadManyContext.java
index 5035f0802..5d2b781c7 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadManyContext.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadManyContext.java
@@ -201,7 +201,6 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
@Override
public void loadMany(BeanCollection> bc, boolean onlyIds) {
-
lock.lock();
try {
boolean useCache = !onlyIds && context.hitCache && context.property.isUseCache();
@@ -215,6 +214,7 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
// find it using instance equality - avoiding equals() and potential deadlock issue
if (list.get(i) == bc) {
list.remove(i);
+ bc.setLoader(context.parent.getEbeanServer());
return;
}
}
@@ -222,10 +222,9 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
}
}
- // Should reduce the list by checking each beanCollection in the L2 first before executing the query
-
- LoadManyRequest req = new LoadManyRequest(this, onlyIds, useCache);
- context.parent.getEbeanServer().loadMany(req);
+ context.parent.getEbeanServer().loadMany(new LoadManyRequest(this, onlyIds, useCache));
+ // clear the buffer as all entries have been loaded
+ list.clear();
} finally {
lock.unlock();
}
From 643b0499c44c9ec9b2215fdb83349d262ead8208 Mon Sep 17 00:00:00 2001
From: rbygrave
Date: Fri, 21 May 2021 16:09:15 +1200
Subject: [PATCH 12/72] #2233 Add ebean-version.mf so ebean-agent can sniff the
internal version
This allows the ebean-agent to support the older behaviour. Devs using the latest IntelliJ ebean plugin will get the old behaviour unless they bump ebean-core to 12.9 or later.
---
ebean-core/src/main/resources/META-INF/ebean-version.mf | 1 +
1 file changed, 1 insertion(+)
create mode 100644 ebean-core/src/main/resources/META-INF/ebean-version.mf
diff --git a/ebean-core/src/main/resources/META-INF/ebean-version.mf b/ebean-core/src/main/resources/META-INF/ebean-version.mf
new file mode 100644
index 000000000..6bcacf1d6
--- /dev/null
+++ b/ebean-core/src/main/resources/META-INF/ebean-version.mf
@@ -0,0 +1 @@
+ebean-version: 129
From 0da86a82ae74dd2f90c045fb1c842cf1d18ecae4 Mon Sep 17 00:00:00 2001
From: rbygrave
Date: Fri, 21 May 2021 17:14:49 +1200
Subject: [PATCH 13/72] #2233 Bump to ebean-agent 12.9.0
---
ebean-bom/pom.xml | 4 ++--
ebean-core/pom.xml | 2 +-
ebean-ddl-generator/pom.xml | 2 +-
kotlin-querybean-generator/pom.xml | 2 +-
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml
index edc2708cf..6109effa9 100644
--- a/ebean-bom/pom.xml
+++ b/ebean-bom/pom.xml
@@ -18,8 +18,8 @@
12.4.04.17.0
- 12.8.4
- 12.8.4
+ 12.9.0
+ 12.9.0
diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml
index 559d763ac..624e91c91 100644
--- a/ebean-core/pom.xml
+++ b/ebean-core/pom.xml
@@ -302,7 +302,7 @@
io.ebeanebean-maven-plugin
- 12.8.4
+ 12.9.0test
diff --git a/ebean-ddl-generator/pom.xml b/ebean-ddl-generator/pom.xml
index 0c5089130..d063c222c 100644
--- a/ebean-ddl-generator/pom.xml
+++ b/ebean-ddl-generator/pom.xml
@@ -76,7 +76,7 @@
io.ebeanebean-maven-plugin
- 12.8.4
+ 12.9.0test
diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml
index 99433a038..e7967307e 100644
--- a/kotlin-querybean-generator/pom.xml
+++ b/kotlin-querybean-generator/pom.xml
@@ -146,7 +146,7 @@
io.ebeanebean-maven-plugin
- 12.8.4
+ 12.9.0test
From 2a6108087ca01e5ba8ae192c2912cfe1c43ead83 Mon Sep 17 00:00:00 2001
From: rbygrave
Date: Fri, 21 May 2021 17:18:30 +1200
Subject: [PATCH 14/72] Bump version to 12.9.0-SNAPSHOT
---
ebean-api/pom.xml | 2 +-
ebean-autotune/pom.xml | 4 ++--
ebean-bom/pom.xml | 30 +++++++++++++++---------------
ebean-core-type/pom.xml | 4 ++--
ebean-core/pom.xml | 8 ++++----
ebean-ddl-generator/pom.xml | 6 +++---
ebean-externalmapping-api/pom.xml | 2 +-
ebean-externalmapping-xml/pom.xml | 8 ++++----
ebean-postgis/pom.xml | 6 +++---
ebean-querybean/pom.xml | 10 +++++-----
ebean-redis/pom.xml | 12 ++++++------
ebean-test/pom.xml | 6 +++---
ebean/pom.xml | 8 ++++----
kotlin-querybean-generator/pom.xml | 8 ++++----
pom.xml | 2 +-
querybean-generator/pom.xml | 2 +-
16 files changed, 59 insertions(+), 59 deletions(-)
diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml
index 380438cc2..10e919049 100644
--- a/ebean-api/pom.xml
+++ b/ebean-api/pom.xml
@@ -4,7 +4,7 @@
ebean-parentio.ebean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTebean api
diff --git a/ebean-autotune/pom.xml b/ebean-autotune/pom.xml
index 590680c8a..1c7d48b4e 100644
--- a/ebean-autotune/pom.xml
+++ b/ebean-autotune/pom.xml
@@ -4,7 +4,7 @@
ebean-parentio.ebean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOT
@@ -26,7 +26,7 @@
io.ebeanebean-core
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTprovided
diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml
index 6109effa9..dc1478d34 100644
--- a/ebean-bom/pom.xml
+++ b/ebean-bom/pom.xml
@@ -4,7 +4,7 @@
ebean-parentio.ebean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTebean bom
@@ -81,88 +81,88 @@
io.ebeanebean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTio.ebeanebean-api
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTio.ebeanebean-core
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTio.ebeanebean-core-type
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTio.ebeanebean-ddl-generator
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTio.ebeanebean-externalmapping-api
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTio.ebeanebean-externalmapping-xml
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTio.ebeanebean-autotune
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTio.ebeanebean-querybean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTio.ebeanquerybean-generator
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTprovidedio.ebeankotlin-querybean-generator
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTprovidedio.ebeanebean-test
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTtestio.ebeanebean-postgis
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTio.ebeanebean-redis
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOT
diff --git a/ebean-core-type/pom.xml b/ebean-core-type/pom.xml
index 04497471c..e6589d839 100644
--- a/ebean-core-type/pom.xml
+++ b/ebean-core-type/pom.xml
@@ -4,7 +4,7 @@
ebean-parentio.ebean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTebean-core-type
@@ -16,7 +16,7 @@
io.ebeanebean-api
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOT
diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml
index 624e91c91..16e400f91 100644
--- a/ebean-core/pom.xml
+++ b/ebean-core/pom.xml
@@ -3,7 +3,7 @@
ebean-parentio.ebean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTebean-core
@@ -87,19 +87,19 @@
io.ebeanebean-api
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTio.ebeanebean-core-type
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTio.ebeanebean-externalmapping-api
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOT
diff --git a/ebean-ddl-generator/pom.xml b/ebean-ddl-generator/pom.xml
index d063c222c..6b16e9b2a 100644
--- a/ebean-ddl-generator/pom.xml
+++ b/ebean-ddl-generator/pom.xml
@@ -4,7 +4,7 @@
ebean-parentio.ebean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTebean ddl generation
@@ -28,14 +28,14 @@
io.ebeanebean-core-type
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTprovidedio.ebeanebean-core
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTprovided
diff --git a/ebean-externalmapping-api/pom.xml b/ebean-externalmapping-api/pom.xml
index 079eebec9..57372b097 100644
--- a/ebean-externalmapping-api/pom.xml
+++ b/ebean-externalmapping-api/pom.xml
@@ -4,7 +4,7 @@
ebean-parentio.ebean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTebean external mapping api
diff --git a/ebean-externalmapping-xml/pom.xml b/ebean-externalmapping-xml/pom.xml
index bcf9f10ff..0fb961ac4 100644
--- a/ebean-externalmapping-xml/pom.xml
+++ b/ebean-externalmapping-xml/pom.xml
@@ -4,7 +4,7 @@
ebean-parentio.ebean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOT
@@ -33,7 +33,7 @@
io.ebeanebean-externalmapping-api
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOT
@@ -59,14 +59,14 @@
io.ebeanebean-core
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTtestio.ebeanebean-ddl-generator
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTtest
diff --git a/ebean-postgis/pom.xml b/ebean-postgis/pom.xml
index 4e1b6063b..e000db688 100644
--- a/ebean-postgis/pom.xml
+++ b/ebean-postgis/pom.xml
@@ -4,7 +4,7 @@
ebean-parentio.ebean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTebean postgis
@@ -23,7 +23,7 @@
io.ebeanebean-core
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTprovided
@@ -74,7 +74,7 @@
io.ebeanebean-test
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTtest
diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml
index ff6dd8754..57b01e349 100644
--- a/ebean-querybean/pom.xml
+++ b/ebean-querybean/pom.xml
@@ -4,7 +4,7 @@
ebean-parentio.ebean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTebean querybean
@@ -17,7 +17,7 @@
io.ebeanebean-core
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTprovided
@@ -57,21 +57,21 @@
io.ebeanebean-ddl-generator
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTtestio.ebeanquerybean-generator
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTtestio.ebeanebean-test
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTtest
diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml
index 573fbcf3b..c500e7508 100644
--- a/ebean-redis/pom.xml
+++ b/ebean-redis/pom.xml
@@ -4,7 +4,7 @@
ebean-parentio.ebean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTebean-redis
@@ -22,35 +22,35 @@
io.ebeanebean-api
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTprovidedio.ebeanebean-core
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTprovidedio.ebeanebean-querybean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTtestio.ebeanquerybean-generator
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTtestio.ebeanebean-test
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTtest
diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml
index c3ed1f57f..94cba52d6 100644
--- a/ebean-test/pom.xml
+++ b/ebean-test/pom.xml
@@ -4,7 +4,7 @@
ebean-parentio.ebean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTebean test
@@ -29,14 +29,14 @@
io.ebeanebean-core
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTprovidedio.ebeanebean-ddl-generator
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOT
diff --git a/ebean/pom.xml b/ebean/pom.xml
index ba312953f..d284c0bda 100644
--- a/ebean/pom.xml
+++ b/ebean/pom.xml
@@ -4,7 +4,7 @@
ebean-parentio.ebean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTebean composite
@@ -22,20 +22,20 @@
io.ebeanebean-api
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTio.ebeanebean-core
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTio.ebeanebean-querybean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOT
diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml
index e7967307e..a475fdcc4 100644
--- a/kotlin-querybean-generator/pom.xml
+++ b/kotlin-querybean-generator/pom.xml
@@ -4,7 +4,7 @@
ebean-parentio.ebean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTkotlin querybean generator
@@ -29,7 +29,7 @@
io.ebeanebean-querybean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTtest
@@ -43,7 +43,7 @@
io.ebeanebean-core
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTtest
@@ -64,7 +64,7 @@
io.ebeanebean-ddl-generator
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTtest
diff --git a/pom.xml b/pom.xml
index fb82113fa..14f2e3f08 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
io.ebeanebean-parent
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTpomebean parent
diff --git a/querybean-generator/pom.xml b/querybean-generator/pom.xml
index 1ea632096..d5d769ac4 100644
--- a/querybean-generator/pom.xml
+++ b/querybean-generator/pom.xml
@@ -4,7 +4,7 @@
ebean-parentio.ebean
- 12.8.4-SNAPSHOT
+ 12.9.0-SNAPSHOTquerybean generator
From 3c4d600187c2443016e56635d0b8afb7a6a1db39 Mon Sep 17 00:00:00 2001
From: rbygrave
Date: Sun, 23 May 2021 17:17:34 +1200
Subject: [PATCH 15/72] #2223 #2224 Support BeanMap modification via entrySet()
and keySet()
---
.../main/java/io/ebean/common/BeanMap.java | 20 +-
.../io/ebean/common/ModifyCollection.java | 2 +-
.../java/io/ebean/common/ModifyEntrySet.java | 131 +++++++++
.../java/io/ebean/common/ModifyKeySet.java | 126 +++++++++
.../main/java/io/ebean/common/ModifySet.java | 24 --
.../java/io/ebean/common/BeanMapTest.java | 265 +++++++++++++++++-
.../model/map/BeanMapOrphanRemovalTest.java | 50 ++++
.../test/java/org/tests/model/map/MpUser.java | 12 +-
8 files changed, 572 insertions(+), 58 deletions(-)
create mode 100644 ebean-api/src/main/java/io/ebean/common/ModifyEntrySet.java
create mode 100644 ebean-api/src/main/java/io/ebean/common/ModifyKeySet.java
delete mode 100644 ebean-api/src/main/java/io/ebean/common/ModifySet.java
create mode 100644 ebean-core/src/test/java/org/tests/model/map/BeanMapOrphanRemovalTest.java
diff --git a/ebean-api/src/main/java/io/ebean/common/BeanMap.java b/ebean-api/src/main/java/io/ebean/common/BeanMap.java
index 2b9ef350d..643febc14 100644
--- a/ebean-api/src/main/java/io/ebean/common/BeanMap.java
+++ b/ebean-api/src/main/java/io/ebean/common/BeanMap.java
@@ -175,10 +175,6 @@ public final class BeanMap extends AbstractBeanCollection implements Ma
/**
* Returns the map entrySet.
- *
- * This is because the key values may need to be set against the details (so
- * they don't need to be set twice).
- *
*/
@Override
public Collection> getActualEntries() {
@@ -242,17 +238,12 @@ public final class BeanMap extends AbstractBeanCollection implements Ma
}
@Override
- @SuppressWarnings({"unchecked"})
public Set> entrySet() {
init();
if (isReadOnly()) {
return Collections.unmodifiableSet(map.entrySet());
}
- if (modifyListening) {
- Set> s = map.entrySet();
- return new ModifySet(this, s);
- }
- return map.entrySet();
+ return modifyListening ? new ModifyEntrySet<>(this, map.entrySet()) : map.entrySet();
}
@Override
@@ -273,8 +264,7 @@ public final class BeanMap extends AbstractBeanCollection implements Ma
if (isReadOnly()) {
return Collections.unmodifiableSet(map.keySet());
}
- // we don't really care about modifications to the ketSet?
- return map.keySet();
+ return modifyListening ? new ModifyKeySet<>(this, map.keySet()) : map.keySet();
}
@Override
@@ -345,11 +335,7 @@ public final class BeanMap extends AbstractBeanCollection implements Ma
if (isReadOnly()) {
return Collections.unmodifiableCollection(map.values());
}
- if (modifyListening) {
- Collection c = map.values();
- return new ModifyCollection<>(this, c);
- }
- return map.values();
+ return modifyListening ? new ModifyCollection<>(this, map.values()) : map.values();
}
@Override
diff --git a/ebean-api/src/main/java/io/ebean/common/ModifyCollection.java b/ebean-api/src/main/java/io/ebean/common/ModifyCollection.java
index 05b203a2f..48f703ba8 100644
--- a/ebean-api/src/main/java/io/ebean/common/ModifyCollection.java
+++ b/ebean-api/src/main/java/io/ebean/common/ModifyCollection.java
@@ -25,7 +25,7 @@ class ModifyCollection implements Collection {
* The owner is notified of the additions and removals.
*
*/
- public ModifyCollection(BeanCollection owner, Collection c) {
+ ModifyCollection(BeanCollection owner, Collection c) {
this.owner = owner;
this.c = c;
}
diff --git a/ebean-api/src/main/java/io/ebean/common/ModifyEntrySet.java b/ebean-api/src/main/java/io/ebean/common/ModifyEntrySet.java
new file mode 100644
index 000000000..174863a74
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/common/ModifyEntrySet.java
@@ -0,0 +1,131 @@
+package io.ebean.common;
+
+import java.util.*;
+
+/**
+ * Handles the Entry Set for BeanMap.
+ */
+class ModifyEntrySet implements Set> {
+
+ private final BeanMap owner;
+ private final Set> entrySet;
+
+ ModifyEntrySet(BeanMap owner, Set> entrySet) {
+ this.owner = owner;
+ this.entrySet = entrySet;
+ }
+
+ @Override
+ public int size() {
+ return entrySet.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return entrySet.isEmpty();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return entrySet.contains(o);
+ }
+
+ @Override
+ public Object[] toArray() {
+ return entrySet.toArray();
+ }
+
+ @Override
+ public T[] toArray(T[] a) {
+ return entrySet.toArray(a);
+ }
+
+ @Override
+ public boolean containsAll(Collection> entries) {
+ return entrySet.containsAll(entries);
+ }
+
+ @Override
+ public void clear() {
+ owner.clear();
+ }
+
+ @Override
+ public boolean add(Map.Entry entry) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean addAll(Collection extends Map.Entry> c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public boolean remove(Object o) {
+ if (o instanceof Map.Entry) {
+ Map.Entry entry = (Map.Entry) o;
+ final E val = owner.get(entry.getKey());
+ if (Objects.equals(val, entry.getValue())) {
+ owner.remove(entry.getKey());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean retainAll(Collection> entries) {
+ boolean modified = false;
+ final Iterator> it = iterator();
+ while (it.hasNext()) {
+ if (!entries.contains(it.next())) {
+ it.remove();
+ modified = true;
+ }
+ }
+ return modified;
+ }
+
+ @Override
+ public boolean removeAll(Collection> entries) {
+ boolean modified = false;
+ for (Object entry : entries) {
+ modified |= remove(entry);
+ }
+ return modified;
+ }
+
+ @Override
+ public Iterator> iterator() {
+ return new EntrySetIterator(new ArrayList<>(entrySet).iterator());
+ }
+
+ class EntrySetIterator implements Iterator> {
+
+ private final Iterator> iterator;
+ private Map.Entry entry;
+
+ EntrySetIterator(Iterator> iterator) {
+ this.iterator = iterator;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public Map.Entry next() {
+ entry = iterator.next();
+ return entry;
+ }
+
+ @Override
+ public void remove() {
+ owner.remove(entry.getKey());
+ iterator.remove();
+ }
+ }
+
+}
diff --git a/ebean-api/src/main/java/io/ebean/common/ModifyKeySet.java b/ebean-api/src/main/java/io/ebean/common/ModifyKeySet.java
new file mode 100644
index 000000000..e9f829b9b
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/common/ModifyKeySet.java
@@ -0,0 +1,126 @@
+package io.ebean.common;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Handle the Key Set for BeanMap.
+ */
+class ModifyKeySet implements Set {
+
+ private final Set keySet;
+ private final BeanMap owner;
+
+ ModifyKeySet(BeanMap owner, Set keySet) {
+ this.owner = owner;
+ this.keySet = keySet;
+ }
+
+ @Override
+ public int size() {
+ return keySet.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return keySet.isEmpty();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return keySet.contains(o);
+ }
+
+ @Override
+ public Object[] toArray() {
+ return keySet.toArray();
+ }
+
+ @Override
+ public T[] toArray(T[] a) {
+ return keySet.toArray(a);
+ }
+
+ @Override
+ public boolean add(E key) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean addAll(Collection extends E> keys) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ return owner.remove(o) != null;
+ }
+
+ @Override
+ public boolean containsAll(Collection> keys) {
+ return keySet.containsAll(keys);
+ }
+
+ @Override
+ public void clear() {
+ owner.clear();
+ }
+
+ @Override
+ public Iterator iterator() {
+ return new KeySetIterator<>(new ArrayList<>(keySet).iterator());
+ }
+
+ @Override
+ public boolean retainAll(Collection> keys) {
+ return keysMatch(keys, false);
+ }
+
+ @Override
+ public boolean removeAll(Collection> keys) {
+ return keysMatch(keys, true);
+ }
+
+ private boolean keysMatch(Collection> keys, boolean containsMatch) {
+ boolean changed = false;
+ final Iterator iterator = iterator();
+ while (iterator.hasNext()) {
+ final E key = iterator.next();
+ if (keys.contains(key) == containsMatch) {
+ iterator.remove();
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
+
+ class KeySetIterator implements Iterator {
+
+ private final Iterator iterator;
+ private K key;
+
+ KeySetIterator(Iterator iterator) {
+ this.iterator = iterator;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public K next() {
+ key = iterator.next();
+ return key;
+ }
+
+ @Override
+ public void remove() {
+ owner.remove(key);
+ iterator.remove();
+ }
+ }
+}
diff --git a/ebean-api/src/main/java/io/ebean/common/ModifySet.java b/ebean-api/src/main/java/io/ebean/common/ModifySet.java
deleted file mode 100644
index 1a0a2e1bf..000000000
--- a/ebean-api/src/main/java/io/ebean/common/ModifySet.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package io.ebean.common;
-
-import io.ebean.bean.BeanCollection;
-
-import java.util.Set;
-
-/**
- * Wraps a Set for the purposes of notifying removals and additions to the
- * BeanCollection owner.
- *
- * This is required for persisting ManyToMany objects. Additions and removals
- * become inserts and deletes to the intersection table.
- *
- */
-class ModifySet extends ModifyCollection implements Set {
-
- /**
- * Create with an Owner that is notified of any additions or deletions.
- */
- public ModifySet(BeanCollection owner, Set s) {
- super(owner, s);
- }
-
-}
diff --git a/ebean-core/src/test/java/io/ebean/common/BeanMapTest.java b/ebean-core/src/test/java/io/ebean/common/BeanMapTest.java
index 08a57e05a..003539a72 100644
--- a/ebean-core/src/test/java/io/ebean/common/BeanMapTest.java
+++ b/ebean-core/src/test/java/io/ebean/common/BeanMapTest.java
@@ -2,18 +2,21 @@ package io.ebean.common;
import io.ebean.bean.BeanCollection;
import org.junit.Test;
+import org.tests.model.basic.EBasic;
-import java.util.LinkedHashMap;
-import java.util.Map;
+import java.util.*;
+import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
public class BeanMapTest {
- Object object1 = new Object();
- Object object2 = new Object();
- Object object3 = new Object();
+ 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 Map all() {
Map all = new LinkedHashMap<>();
@@ -174,9 +177,7 @@ public class BeanMapTest {
@Test
public void testClear_given_someBeansInAdditions() throws Exception {
- BeanMap map = new BeanMap<>();
- map.put("1", object1);
- map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
+ BeanMap map = newModifyListeningMap();
map.put("2", object2);
map.put("3", object3);
@@ -188,4 +189,252 @@ public class BeanMapTest {
assertThat(map.getModifyAdditions()).isEmpty();
}
+ @Test(expected = UnsupportedOperationException.class)
+ public void keySet_add_whenModifyListening() {
+ BeanMap map = newModifyListeningMap();
+ map.keySet().add("3");
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void keySet_add() {
+ BeanMap map = new BeanMap<>();
+ map.keySet().add("3");
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void keySet_addAll_whenModifyListening() {
+ BeanMap map = newModifyListeningMap();
+ map.keySet().addAll(asList("3", "4"));
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void keySet_addAll() {
+ BeanMap map = new BeanMap<>();
+ map.keySet().addAll(asList("3", "4"));
+ }
+
+ @Test
+ public void keySet_remove() {
+
+ BeanMap map = new BeanMap<>();
+ map.put("1", object1);
+ map.put("2", object2);
+ map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
+
+ final Set keySet = map.keySet();
+ keySet.remove("1");
+
+ assertThat(keySet.contains("1")).isFalse();
+ assertThat(map).doesNotContainKeys("1");
+ assertThat(map.get("1")).isNull();
+
+ assertThat(map.getModifyRemovals()).containsExactly(object1);
+ }
+
+ @Test
+ public void keySet_clear() {
+
+ BeanMap map = new BeanMap<>();
+ map.put("1", object1);
+ map.put("2", object2);
+ map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
+
+ final Set keySet = map.keySet();
+ keySet.clear();
+
+ assertThat(map).isEmpty();
+ assertThat(keySet).isEmpty();
+
+ assertThat(map.getModifyRemovals()).containsExactly(object1, object2);
+ }
+
+ @Test
+ public void keySet_iterator_remove() {
+
+ BeanMap map = new BeanMap<>();
+ map.put("1", object1);
+ map.put("2", object2);
+ map.put("3", object3);
+ map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
+
+ final Set keySet = map.keySet();
+ final Iterator iterator = keySet.iterator();
+ while (iterator.hasNext()) {
+ final String key = iterator.next();
+ if (key.equals("2")) {
+ iterator.remove();
+ }
+ }
+
+ assertThat(map).hasSize(2);
+ assertThat(keySet).hasSize(2);
+ assertThat(keySet).containsExactly("1", "3");
+ assertThat(map).containsKeys("1", "3");
+
+ assertThat(map.getModifyRemovals()).containsExactly(object2);
+ }
+
+ @Test
+ public void keySet_removeAll() {
+
+ BeanMap map = new BeanMap<>();
+ map.put("1", object1);
+ map.put("2", object2);
+ map.put("3", object3);
+ map.put("4", object4);
+ map.put("5", object5);
+ map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
+
+ final Set keySet = map.keySet();
+ final boolean changed = keySet.removeAll(asList("2", "3", "5"));
+
+ assertThat(changed).isTrue();
+ assertThat(map).hasSize(2);
+ assertThat(keySet).hasSize(2);
+ assertThat(keySet).containsExactly("1", "4");
+ assertThat(map).containsKeys("1", "4");
+
+ assertThat(map.getModifyRemovals()).containsExactly(object2, object3, object5);
+ }
+
+
+ @Test
+ public void keySet_retainAll() {
+
+ BeanMap map = new BeanMap<>();
+ map.put("1", object1);
+ map.put("2", object2);
+ map.put("3", object3);
+ map.put("4", object4);
+ map.put("5", object5);
+ map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
+
+ final Set keySet = map.keySet();
+ final boolean changed = keySet.retainAll(asList("2", "3", "5"));
+
+ assertThat(changed).isTrue();
+ assertThat(map).hasSize(3);
+ assertThat(keySet).hasSize(3);
+ assertThat(keySet).containsExactly("2", "3", "5");
+ assertThat(map).containsKeys("2", "3", "5");
+
+ assertThat(map.getModifyRemovals()).containsExactly(object1, object4);
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void values_add() {
+ BeanMap map = new BeanMap<>();
+ map.values().add(object3);
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void values_addAll() {
+ BeanMap map = new BeanMap<>();
+ map.values().addAll(asList(object3, object5));
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void entrySet_add() {
+ newModifyListeningMap()
+ .entrySet()
+ .add(new AbstractMap.SimpleEntry<>("3", object3));
+ }
+
+ @Test
+ public void entrySet_clear() {
+ final BeanMap map = newModifyListeningMap();
+ final Set> entries = map.entrySet();
+ entries.clear();
+
+ assertThat(entries).isEmpty();
+ assertThat(map).isEmpty();
+ assertThat(map.getModifyRemovals()).containsExactly(object1);
+ }
+
+ @Test
+ public void entrySet_remove() {
+ final BeanMap map = newModifyListeningMap5();
+ final Set> entries = map.entrySet();
+
+ assertThat(map).hasSize(5);
+
+ final boolean existed1 = entries.remove(new AbstractMap.SimpleEntry<>("1", object1));
+ assertThat(existed1).isTrue();
+
+ final boolean existed22 = entries.remove(new AbstractMap.SimpleEntry<>("22", object1));
+ assertThat(existed22).isFalse();
+
+ assertThat(map).hasSize(4);
+ assertThat(map.getModifyRemovals()).containsExactly(object1);
+ }
+
+ @Test
+ public void entrySet_remove_whenNotEqualValue() {
+ final BeanMap map = newModifyListeningMap5();
+ final Set> entries = map.entrySet();
+
+ assertThat(map).hasSize(5);
+
+ final boolean modified = entries.remove(new AbstractMap.SimpleEntry<>("1", object2));
+ assertThat(modified).isFalse();
+
+ assertThat(map).hasSize(5);
+ assertThat(map.getModifyRemovals()).isNull();
+ }
+
+ @Test
+ public void entrySet_iterator_remove() {
+ final BeanMap map = newModifyListeningMap5();
+ final Set> entries = map.entrySet();
+ final Iterator> iterator = entries.iterator();
+ while (iterator.hasNext()) {
+ final Map.Entry entry = iterator.next();
+ if (entry.getKey().equals("2") || entry.getKey().equals("5")) {
+ iterator.remove();
+ }
+ }
+ assertThat(map).hasSize(3);
+ assertThat(entries).hasSize(3);
+ assertThat(map.getModifyRemovals()).containsExactly(object2, object5);
+ }
+
+ @Test
+ public void entrySet_removeAll() {
+ 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);
+ assertThat(entries).hasSize(3);
+ assertThat(map.getModifyRemovals()).containsExactly(object1, object4);
+ }
+
+ @Test
+ public void entrySet_retainAll() {
+ 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);
+ assertThat(entries).hasSize(2);
+ assertThat(map.getModifyRemovals()).containsExactly(object2, object3, object5);
+ }
+
+ 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<>();
+ map.put("1", object1);
+ map.put("2", object2);
+ map.put("3", object3);
+ map.put("4", object4);
+ map.put("5", object5);
+ map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
+ return map;
+ }
}
diff --git a/ebean-core/src/test/java/org/tests/model/map/BeanMapOrphanRemovalTest.java b/ebean-core/src/test/java/org/tests/model/map/BeanMapOrphanRemovalTest.java
new file mode 100644
index 000000000..48288e7a3
--- /dev/null
+++ b/ebean-core/src/test/java/org/tests/model/map/BeanMapOrphanRemovalTest.java
@@ -0,0 +1,50 @@
+package org.tests.model.map;
+
+import io.ebean.DB;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+
+public class BeanMapOrphanRemovalTest {
+
+ @Test
+ public void keySet_retainAll() {
+
+ MpUser user = new MpUser();
+ user.setName("u1");
+ addRoles(user, "r1", "r2", "r3", "r4");
+ DB.save(user);
+
+ final MpUser user1 = DB.find(MpUser.class, user.getId());
+ final Map roles = user1.getRoles();
+ assertThat(roles).hasSize(4);
+
+
+ final Set keySet = roles.keySet();
+ keySet.retainAll(Arrays.asList("r2", "r3"));
+
+ DB.save(user1);
+
+ final MpUser user2 = DB.find(MpUser.class, user.getId());
+ final Map roles2 = user2.getRoles();
+ assertThat(roles2).hasSize(2);
+ }
+
+ private void addRoles(MpUser user, String... roles){
+ for (String code : roles) {
+ MpRole role = newRole(code);
+ user.getRoles().put(role.getCode(), role);
+ }
+ }
+
+ private MpRole newRole(String code) {
+ MpRole role = new MpRole();
+ role.setCode(code);
+ return role;
+ }
+}
diff --git a/ebean-core/src/test/java/org/tests/model/map/MpUser.java b/ebean-core/src/test/java/org/tests/model/map/MpUser.java
index 100e1e6a7..94edddb19 100644
--- a/ebean-core/src/test/java/org/tests/model/map/MpUser.java
+++ b/ebean-core/src/test/java/org/tests/model/map/MpUser.java
@@ -1,11 +1,7 @@
package org.tests.model.map;
-import javax.persistence.CascadeType;
-import javax.persistence.Entity;
-import javax.persistence.Id;
-import javax.persistence.MapKey;
-import javax.persistence.OneToMany;
-import java.util.HashMap;
+import javax.persistence.*;
+import java.util.LinkedHashMap;
import java.util.Map;
@Entity
@@ -16,9 +12,9 @@ public class MpUser {
private String name;
- @OneToMany(cascade = CascadeType.ALL)
+ @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@MapKey(name = "code")
- public Map roles = new HashMap<>();
+ private Map roles = new LinkedHashMap<>();
public Long getId() {
return id;
From 2158925d3ca77b9610c3ae03c244594d74ed6639 Mon Sep 17 00:00:00 2001
From: rbygrave
Date: Sun, 23 May 2021 21:28:11 +1200
Subject: [PATCH 16/72] #2202 - kotlin-maven-plugin issue with JDK 16
InaccessibleObjectException: Unable to make protected void java.util.ResourceBundle.setParent(java.util.ResourceBundle) accessible: module java.base does not "opens java.util" to unnamed module @62732be7
---
kotlin-querybean-generator/pom.xml | 75 +++++++++++++++---------------
1 file changed, 37 insertions(+), 38 deletions(-)
diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml
index a475fdcc4..917d4275a 100644
--- a/kotlin-querybean-generator/pom.xml
+++ b/kotlin-querybean-generator/pom.xml
@@ -12,7 +12,7 @@
kotlin-querybean-generator
- 1.4.31
+ 1.5.0
@@ -81,43 +81,42 @@
src/test/kotlin
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ ${kotlin.version}
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
+ test-kapt
+
+ test-kapt
+
+
+
+ src/test/kotlin
+
+
+
+ io.ebean
+ kotlin-querybean-generator
+ 12.8.2
+
+
+
+
+
+
+ 1.8
+
+
+ org.apache.maven.pluginsmaven-compiler-plugin
From 01bce5cfa622af053545ce15f885d1a25a2e0337 Mon Sep 17 00:00:00 2001
From: rbygrave
Date: Sun, 23 May 2021 22:20:40 +1200
Subject: [PATCH 17/72] #2226 - Automatically determining join columns and
ignoring PrimaryKeyJoinColumn.name Id warning when I want to use Id as a
foreign key
Improve the message and dropping to INFO level.
---
.../server/deploy/parse/AnnotationAssocOnes.java | 11 ++---------
1 file changed, 2 insertions(+), 9 deletions(-)
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java
index d313ce925..b5929d6a0 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java
@@ -54,7 +54,6 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
}
private void readAssocOne(DeployBeanPropertyAssocOne> prop) {
-
ManyToOne manyToOne = get(prop, ManyToOne.class);
if (manyToOne != null) {
readManyToOne(manyToOne, prop);
@@ -183,7 +182,6 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
}
private void readManyToOne(ManyToOne propAnn, DeployBeanPropertyAssocOne> beanProp) {
-
setCascadeTypes(propAnn.cascade(), beanProp.getCascadeInfo());
setTargetType(propAnn.targetEntity(), beanProp);
setBeanTable(beanProp);
@@ -194,7 +192,6 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
}
private void readOneToOne(OneToOne propAnn, DeployBeanPropertyAssocOne> prop) {
-
prop.setOneToOne();
prop.setDbInsertable(true);
prop.setDbUpdateable(true);
@@ -223,21 +220,19 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
}
private void readPrimaryKeyJoin(PrimaryKeyJoinColumn primaryKeyJoin, DeployBeanPropertyAssocOne> prop) {
-
if (!prop.isOneToOne()) {
throw new IllegalStateException("Expecting property " + prop.getFullBeanName() + " with PrimaryKeyJoinColumn to be a OneToOne?");
}
prop.setPrimaryKeyJoin(true);
if (!primaryKeyJoin.name().isEmpty()) {
- log.warn("Automatically determining join columns and ignoring PrimaryKeyJoinColumn.name {} on {}", primaryKeyJoin.name(), prop.getFullBeanName());
+ log.info("Automatically determining join columns for @PrimaryKeyJoinColumn - ignoring PrimaryKeyJoinColumn.name attribute [{}] on {}", primaryKeyJoin.name(), prop.getFullBeanName());
}
if (!primaryKeyJoin.referencedColumnName().isEmpty()) {
- log.warn("Automatically determining join columns and Ignoring PrimaryKeyJoinColumn.referencedColumnName {} on {}", primaryKeyJoin.referencedColumnName(), prop.getFullBeanName());
+ log.info("Automatically determining join columns for @PrimaryKeyJoinColumn - Ignoring PrimaryKeyJoinColumn.referencedColumnName attribute [{}] on {}", primaryKeyJoin.referencedColumnName(), prop.getFullBeanName());
}
BeanTable baseBeanTable = factory.getBeanTable(info.getDescriptor().getBeanType());
-
String localPrimaryKey = baseBeanTable.getIdColumn();
String foreignColumn = getBeanTable(prop).getIdColumn();
@@ -245,7 +240,6 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
}
private void readEmbedded(DeployBeanPropertyAssocOne> prop, Embedded embedded) {
-
if (descriptor.isDocStoreOnly() && prop.getDocStoreDoc() == null) {
prop.setDocStoreEmbedded("");
}
@@ -257,7 +251,6 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
} catch (NoSuchMethodError e) {
// using standard JPA API without prefix option, maybe in EE container
}
-
readEmbeddedAttributeOverrides(prop);
}
From 4c317f11ae097acd5414bf7414b2e592b1ee3d68 Mon Sep 17 00:00:00 2001
From: rbygrave
Date: Sun, 23 May 2021 22:21:05 +1200
Subject: [PATCH 18/72] Add additional test for PrimaryKeyJoinColumn
---
.../tests/model/onetoone/calcd/CalcDData.java | 46 +++++++++++++++++++
.../model/onetoone/calcd/CalcDInput.java | 42 +++++++++++++++++
.../TestOneToOnePrimaryKeyJoinMapping.java | 26 +++++++++++
3 files changed, 114 insertions(+)
create mode 100644 ebean-core/src/test/java/org/tests/model/onetoone/calcd/CalcDData.java
create mode 100644 ebean-core/src/test/java/org/tests/model/onetoone/calcd/CalcDInput.java
create mode 100644 ebean-core/src/test/java/org/tests/model/onetoone/calcd/TestOneToOnePrimaryKeyJoinMapping.java
diff --git a/ebean-core/src/test/java/org/tests/model/onetoone/calcd/CalcDData.java b/ebean-core/src/test/java/org/tests/model/onetoone/calcd/CalcDData.java
new file mode 100644
index 000000000..eef657be5
--- /dev/null
+++ b/ebean-core/src/test/java/org/tests/model/onetoone/calcd/CalcDData.java
@@ -0,0 +1,46 @@
+package org.tests.model.onetoone.calcd;
+
+import io.ebean.Model;
+import io.ebean.annotation.ConstraintMode;
+import io.ebean.annotation.DbForeignKey;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "calcd_data")
+public class CalcDData extends Model {
+
+ @Id
+ private Integer id;
+
+ @OneToOne(optional = false, cascade = CascadeType.ALL)
+ @DbForeignKey(onDelete = ConstraintMode.CASCADE)
+ @PrimaryKeyJoinColumn//(name = "Id", referencedColumnName = "Id")
+ private CalcDInput input;
+
+ private final String name;
+
+ public CalcDData(String name) {
+ this.name = name;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public CalcDInput getInput() {
+ return input;
+ }
+
+ public void setInput(CalcDInput input) {
+ this.input = input;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/ebean-core/src/test/java/org/tests/model/onetoone/calcd/CalcDInput.java b/ebean-core/src/test/java/org/tests/model/onetoone/calcd/CalcDInput.java
new file mode 100644
index 000000000..00704eb27
--- /dev/null
+++ b/ebean-core/src/test/java/org/tests/model/onetoone/calcd/CalcDInput.java
@@ -0,0 +1,42 @@
+package org.tests.model.onetoone.calcd;
+
+import io.ebean.Model;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "calcd_input")
+public class CalcDInput extends Model {
+
+ @Id
+ private Integer id;
+
+ @OneToOne(optional = false, fetch = FetchType.EAGER, orphanRemoval = true, mappedBy = "input")
+ private CalcDData data;
+
+ private final String name;
+
+ public CalcDInput(String name) {
+ this.name = name;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public CalcDData getData() {
+ return data;
+ }
+
+ public void setData(CalcDData data) {
+ this.data = data;
+ }
+}
diff --git a/ebean-core/src/test/java/org/tests/model/onetoone/calcd/TestOneToOnePrimaryKeyJoinMapping.java b/ebean-core/src/test/java/org/tests/model/onetoone/calcd/TestOneToOnePrimaryKeyJoinMapping.java
new file mode 100644
index 000000000..9e1aa68f0
--- /dev/null
+++ b/ebean-core/src/test/java/org/tests/model/onetoone/calcd/TestOneToOnePrimaryKeyJoinMapping.java
@@ -0,0 +1,26 @@
+package org.tests.model.onetoone.calcd;
+
+import io.ebean.DB;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class TestOneToOnePrimaryKeyJoinMapping {
+
+ @Test
+ public void test() {
+
+ CalcDInput inputs = new CalcDInput("input0");
+
+ CalcDData data = new CalcDData("data0");
+ inputs.setData(data);
+ inputs.save();
+
+ CalcDInput found = DB.find(CalcDInput.class, inputs.getId());
+
+ assertThat(found).isNotNull();
+ assertThat(found.getName()).isEqualTo("input0");
+ assertThat(found.getData().getName()).isEqualTo("data0");
+
+ }
+}
From 622a90c7d5dd27b2cb477df635611314e100688d Mon Sep 17 00:00:00 2001
From: rbygrave
Date: Sun, 23 May 2021 23:00:34 +1200
Subject: [PATCH 19/72] No effective change - tidy whitespace in
TransactionManager
---
.../transaction/TransactionManager.java | 31 -------------------
1 file changed, 31 deletions(-)
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/TransactionManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/TransactionManager.java
index b71868e5d..0728eebac 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/TransactionManager.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/TransactionManager.java
@@ -53,7 +53,6 @@ import java.util.concurrent.atomic.AtomicLong;
* Manages transactions.
*
* Keeps the Cache and Cluster in sync when transactions are committed.
- *
*/
public class TransactionManager implements SpiTransactionManager {
@@ -198,8 +197,6 @@ public class TransactionManager implements SpiTransactionManager {
this.txnMain = metricFactory.createTimedMetric("txn.main");
this.txnReadOnly = metricFactory.createTimedMetric("txn.readonly");
this.txnNamed = metricFactory.createTimedMetricMap("txn.named.");
- // Add gauges for db pool size
-
scopeManager.register(this);
}
@@ -304,20 +301,16 @@ public class TransactionManager implements SpiTransactionManager {
* There is a potential optimisation available when read committed is the default
* isolation level. If it is, then Connections used only for queries do not require
* commit or rollback but instead can just be put back into the pool via close().
- *
*
* If the Isolation level is higher (say SERIALIZABLE) then Connections used
* just for queries do need to be committed or rollback after the query.
- *
*/
OnQueryOnly initOnQueryOnly(OnQueryOnly dbPlatformOnQueryOnly) {
-
// first check for a system property 'override'
String systemPropertyValue = System.getProperty("ebean.transaction.onqueryonly");
if (systemPropertyValue != null) {
return OnQueryOnly.valueOf(systemPropertyValue.trim().toUpperCase());
}
-
// default to rollback if not defined on the platform
return dbPlatformOnQueryOnly == null ? OnQueryOnly.COMMIT : dbPlatformOnQueryOnly;
}
@@ -359,9 +352,7 @@ public class TransactionManager implements SpiTransactionManager {
* Wrap an externally supplied Connection with a known transaction id.
*/
private SpiTransaction wrapExternalConnection(String id, Connection c) {
-
ExternalJdbcTransaction t = new ExternalJdbcTransaction(id, true, c, this);
-
// set the default batch mode
t.setBatchMode(persistBatch);
t.setBatchOnCascade(persistBatchOnCascade);
@@ -409,7 +400,6 @@ public class TransactionManager implements SpiTransactionManager {
*/
@Override
public void notifyOfRollback(SpiTransaction transaction, Throwable cause) {
-
try {
if (txnLogger.isDebug()) {
String msg = transaction.getLogPrefix() + "Rollback";
@@ -418,7 +408,6 @@ public class TransactionManager implements SpiTransactionManager {
}
txnLogger.debug(msg);
}
-
} catch (Exception ex) {
logger.error("Error while notifying TransactionEventListener of rollback event", ex);
}
@@ -445,7 +434,6 @@ public class TransactionManager implements SpiTransactionManager {
}
private void formatThrowable(Throwable e, StringBuilder sb) {
-
sb.append(e.toString());
StackTraceElement[] stackTrace = e.getStackTrace();
if (stackTrace.length > 0) {
@@ -468,11 +456,9 @@ public class TransactionManager implements SpiTransactionManager {
if (txnLogger.isDebug()) {
txnLogger.debug(transaction.getLogPrefix() + "Commit");
}
-
PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, transaction);
postCommit.notifyLocalCache();
backgroundExecutor.execute(postCommit.backgroundNotify());
-
} catch (Exception ex) {
logger.error("NotifyOfCommit failed. L2 Cache potentially not notified.", ex);
}
@@ -488,7 +474,6 @@ public class TransactionManager implements SpiTransactionManager {
}
private void externalModificationEvent(TransactionEventTable tableEvents) {
-
TransactionEvent event = new TransactionEvent();
event.add(tableEvents);
@@ -501,25 +486,21 @@ public class TransactionManager implements SpiTransactionManager {
* Notify local BeanPersistListeners etc of events from another server in the cluster.
*/
public void remoteTransactionEvent(RemoteTransactionEvent remoteEvent) {
-
if (clusterLogger.isDebugEnabled()) {
clusterLogger.debug("processing {}", remoteEvent);
}
-
CacheChangeSet changeSet = new CacheChangeSet();
RemoteTableMod tableMod = remoteEvent.getRemoteTableMod();
if (tableMod != null) {
changeSet.addInvalidate(tableMod.getTables());
}
-
List tableIUDList = remoteEvent.getTableIUDList();
if (tableIUDList != null) {
for (TableIUD tableIUD : tableIUDList) {
beanDescriptorManager.cacheNotify(tableIUD, changeSet);
}
}
-
// note DeleteById is written as BeanPersistIds and getBeanPersistList()
// processes both Bean IUD and DeleteById
List beanPersistList = remoteEvent.getBeanPersistList();
@@ -528,7 +509,6 @@ public class TransactionManager implements SpiTransactionManager {
persistIds.notifyCache(changeSet);
}
}
-
changeSet.apply();
}
@@ -543,10 +523,8 @@ public class TransactionManager implements SpiTransactionManager {
* Prepare and then send/log the changeSet.
*/
void sendChangeLog(final ChangeSet changeSet) {
-
// can set userId, userIpAddress & userContext if desired
if (changeLogPrepare.prepare(changeSet)) {
-
if (changeLogAsync) {
// call the log method in background
backgroundExecutor.execute(() -> changeLogListener.log(changeSet));
@@ -653,9 +631,7 @@ public class TransactionManager implements SpiTransactionManager {
* Begin a scoped transaction.
*/
public ScopedTransaction beginScopedTransaction(TxScope txScope) {
-
txScope = initTxScope(txScope);
-
ScopedTransaction txnContainer = getActiveScoped();
boolean setToScope;
@@ -678,7 +654,6 @@ public class TransactionManager implements SpiTransactionManager {
if (nestedSavepoint && (type == TxType.REQUIRED || type == TxType.REQUIRES_NEW)) {
createTransaction = true;
transaction = createSavepoint(transaction, this);
-
} else {
createTransaction = isCreateNewTransaction(transaction, type);
if (createTransaction) {
@@ -748,32 +723,26 @@ public class TransactionManager implements SpiTransactionManager {
* Determine whether to create a new transaction or not.
*
* This will also potentially throw exceptions for MANDATORY and NEVER types.
- *
* For new profiling entries it is useful to compare the profiling against the current
* query detail that is specified in the code (as the query might already be manually optimised).
- *
*/
private ProfileOrigin createProfileOrigin(ObjectGraphNode origin, SpiQuery> query) {
ProfileOrigin profileOrigin = new ProfileOrigin(origin.getOriginQueryPoint(), queryTuningAddVersion, profilingBase, profilingRate);
// set the current query detail (fetch group) so that we can compare against profiling for new entries
- profileOrigin.setOriginalQuery(query.getDetail().toString());
+ profileOrigin.setOriginalQuery(query.getDetail().asString());
return profileOrigin;
}
@@ -77,7 +75,6 @@ public class ProfileManager implements ProfilingListener {
*/
@Override
public void collectQueryInfo(ObjectGraphNode node, long beans, long micros) {
-
if (node != null) {
ObjectGraphOrigin origin = node.getOriginQueryPoint();
if (origin != null) {
@@ -92,11 +89,9 @@ public class ProfileManager implements ProfilingListener {
*
* This is sent to use from a EntityBeanIntercept when the finalise method
* is called on the bean.
- *