From 359a55d7a57d3a82f79728403b2dd625a1a005cc Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Tue, 29 Aug 2023 18:28:09 +1200 Subject: [PATCH] #3173 - BeanSet lazy loading / Too many DataSource connections used - The error reported was that a DataSource connection pool maxed out - The symptom was that there was an additional lazy loading queries invoked via the hashCode/equals implementation of ManyToMany Set. - The fix is for BeanSet init() to lazy load with onlyIds = false, that avoids the extra lazy loading query from being executed --- .../main/java/io/ebean/common/BeanSet.java | 2 +- .../test/java/org/tests/sets/M2MDepart.java | 38 ++++++++++++ .../src/test/java/org/tests/sets/M2MEmp.java | 61 +++++++++++++++++++ .../test/java/org/tests/sets/TestM2MSet.java | 31 ++++++++++ 4 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 ebean-test/src/test/java/org/tests/sets/M2MDepart.java create mode 100644 ebean-test/src/test/java/org/tests/sets/M2MEmp.java create mode 100644 ebean-test/src/test/java/org/tests/sets/TestM2MSet.java 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 81d72284b..2f860bb0d 100644 --- a/ebean-api/src/main/java/io/ebean/common/BeanSet.java +++ b/ebean-api/src/main/java/io/ebean/common/BeanSet.java @@ -136,7 +136,7 @@ public final class BeanSet extends AbstractBeanCollection implements Set(); } else { - lazyLoadCollection(true); + lazyLoadCollection(false); } } } finally { diff --git a/ebean-test/src/test/java/org/tests/sets/M2MDepart.java b/ebean-test/src/test/java/org/tests/sets/M2MDepart.java new file mode 100644 index 000000000..bf945f751 --- /dev/null +++ b/ebean-test/src/test/java/org/tests/sets/M2MDepart.java @@ -0,0 +1,38 @@ +package org.tests.sets; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToMany; +import java.util.*; + +@Entity +public class M2MDepart { + + @Id + private UUID id; + + private final String name; + + @ManyToMany + private final Set employees = new LinkedHashSet<>(); + + public M2MDepart(String name) { + this.name = name; + } + + public void addEmployee(M2MEmp employee) { + this.employees.add(employee); + } + + public UUID getId() { + return id; + } + + public String getName() { + return name; + } + + public Set employees() { + return employees; + } +} diff --git a/ebean-test/src/test/java/org/tests/sets/M2MEmp.java b/ebean-test/src/test/java/org/tests/sets/M2MEmp.java new file mode 100644 index 000000000..fa70d8818 --- /dev/null +++ b/ebean-test/src/test/java/org/tests/sets/M2MEmp.java @@ -0,0 +1,61 @@ +package org.tests.sets; + +import javax.persistence.*; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +@Entity +public class M2MEmp { + + @Id + private UUID id; + + private final String code; + + private String name; + + @ManyToMany + @JoinTable(name="m2m_dept_emp", + joinColumns= + @JoinColumn(name="employee_id", referencedColumnName="id"), + inverseJoinColumns= + @JoinColumn(name="department_id", referencedColumnName="id") + ) + private final Set departments = new HashSet<>(); + + public M2MEmp(String name, String code) { + this.name = name; + this.code = code; + } + + public UUID getId() { + return id; + } + + public String getName() { + return name; + } + + public String getCode() { + return code; + } + + public Set getDepartments() { + return departments; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + M2MEmp employee = (M2MEmp) o; + return Objects.equals(code, employee.code); + } + + @Override + public int hashCode() { + return Objects.hash(code); + } +} diff --git a/ebean-test/src/test/java/org/tests/sets/TestM2MSet.java b/ebean-test/src/test/java/org/tests/sets/TestM2MSet.java new file mode 100644 index 000000000..6c71ebf75 --- /dev/null +++ b/ebean-test/src/test/java/org/tests/sets/TestM2MSet.java @@ -0,0 +1,31 @@ +package org.tests.sets; + +import io.ebean.DB; +import io.ebean.test.LoggedSql; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class TestM2MSet { + + @Test + void lazyLoadM2M_when_setWithHashCode_expect_selectProperties() { + final M2MDepart department = new M2MDepart("Test"); + final M2MEmp employee = new M2MEmp("Test", "Code"); + DB.save(employee); + department.addEmployee(employee); + DB.save(department); + + LoggedSql.start(); + DB.find(M2MDepart.class, department.getId()) + .employees() + .forEach(e -> assertThat(e.getName()).isNotNull()); + + List sql = LoggedSql.stop(); + assertThat(sql).hasSize(2); + assertThat(sql.get(0)).contains("select t0.id, t0.name from m2_mdepart t0 where t0.id = ?"); + assertThat(sql.get(1)).contains("select int_.m2_mdepart_id, t0.id, t0.code, t0.name from m2_memp t0 left join m2_mdepart_memp int_ on int_.m2_memp_id = t0.id where"); + } +}