Merge pull request #3594 from ebean-orm/feature/improved-readOnly-immutable-DisabledLazyLoad

Modify disableLazyLoad to throw LazyInitialisationException instead o…
This commit is contained in:
Rob Bygrave
2025-04-29 23:32:46 +12:00
committed by GitHub
11 changed files with 64 additions and 48 deletions
@@ -381,11 +381,6 @@ public interface EntityBeanIntercept extends Serializable {
*/
void loadBean(int loadProperty);
/**
* Invoke the lazy loading. This method is synchronised externally.
*/
void loadBeanInternal(int loadProperty, BeanLoader loader);
/**
* Called when a BeanCollection is initialised automatically.
*/
@@ -393,11 +393,6 @@ public final class InterceptReadOnly extends InterceptBase {
}
@Override
public void loadBeanInternal(int loadProperty, BeanLoader loader) {
}
@Override
public void initialisedMany(int propertyIndex) {
loaded[propertyIndex] = true;
@@ -2,6 +2,7 @@ package io.ebean.bean;
import io.ebean.DB;
import io.ebean.Database;
import io.ebean.LazyInitialisationException;
import io.ebean.ValuePair;
import jakarta.persistence.EntityNotFoundException;
@@ -59,7 +60,7 @@ public final class InterceptReadWrite extends InterceptBase {
private EntityBean embeddedOwner;
private int embeddedOwnerIndex;
/**
* One of NEW, REF, UPD.
* One of NEW, REFERENCE, LOADED.
*/
private int state;
private boolean forceUpdate;
@@ -645,6 +646,9 @@ public final class InterceptReadWrite extends InterceptBase {
public void loadBean(int loadProperty) {
lock.lock();
try {
if (disableLazyLoad) {
throw new LazyInitialisationException("Property not loaded: " + property(loadProperty));
}
if (beanLoader == null) {
final Database database = DB.byName(ebeanServerName);
if (database == null) {
@@ -668,8 +672,7 @@ public final class InterceptReadWrite extends InterceptBase {
}
}
@Override
public void loadBeanInternal(int loadProperty, BeanLoader loader) {
private void loadBeanInternal(int loadProperty, BeanLoader loader) {
if ((flags[loadProperty] & FLAG_LOADED_PROP) != 0) {
// race condition where multiple threads calling preGetter concurrently
return;
@@ -771,7 +774,7 @@ public final class InterceptReadWrite extends InterceptBase {
@Override
public void preGetter(int propertyIndex) {
preGetterCallback(propertyIndex);
if (state == STATE_NEW || disableLazyLoad) {
if (state == STATE_NEW) {
return;
}
if (!isLoadedProperty(propertyIndex)) {
@@ -97,7 +97,7 @@ public final class DLoadContext implements LoadContext {
this.profilingListener = query.profilingListener();
this.planLabel = query.planLabel();
this.profileLocation = query.profileLocation();
this.secondaryProperties = query.isUnmodifiable() ? new HashSet<>() : null;
this.secondaryProperties = query.isUnmodifiable() || query.isDisableLazyLoading() ? new HashSet<>() : null;
ObjectGraphNode parentNode = query.parentNode();
if (parentNode != null) {
@@ -32,6 +32,7 @@ class SqlTreeLoadBean implements SqlTreeLoad {
private final boolean readIdNormal;
private final boolean disableLazyLoad;
private final boolean unmodifiable;
private final boolean loadListReferences;
private final InheritInfo inheritInfo;
final String prefix;
private final Map<String, String> pathMap;
@@ -55,6 +56,7 @@ class SqlTreeLoadBean implements SqlTreeLoad {
this.readIdNormal = readId && !temporalVersions;
this.disableLazyLoad = node.disableLazyLoad;
this.unmodifiable = node.unmodifiable;
this.loadListReferences = !unmodifiable && !disableLazyLoad;
this.partialObject = node.partialObject;
this.properties = node.properties;
this.pathMap = node.pathMap;
@@ -301,7 +303,7 @@ class SqlTreeLoadBean implements SqlTreeLoad {
boolean forceNewReference = queryMode == Mode.REFRESH_BEAN;
for (STreePropertyAssocMany many : localDesc.propsMany()) {
if (many != loadingChildProperty) {
if (!unmodifiable || ctx.includeSecondary(many.asMany())) {
if (loadListReferences || ctx.includeSecondary(many.asMany())) {
// create a proxy for the many (deferred fetching)
BeanCollection<?> ref = many.createReference(localBean, forceNewReference);
if (ref != null) {
@@ -1,10 +1,7 @@
package io.ebean.xtest.config;
import io.ebean.Database;
import io.ebean.DatabaseFactory;
import io.ebean.Transaction;
import io.ebean.*;
import io.ebean.annotation.Platform;
import io.ebean.DatabaseBuilder;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.dbplatform.DbIdentity;
import io.ebean.config.dbplatform.IdType;
@@ -15,6 +12,7 @@ import org.tests.model.basic.EBasicVer;
import org.tests.model.draftable.BasicDraftableBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class PlatformNoGeneratedKeysTest {
@@ -39,7 +37,7 @@ public class PlatformNoGeneratedKeysTest {
.findOne();
assertThat(found.getName()).isEqualTo("basic");
assertThat(found.getDescription()).isNull();
assertThrows(LazyInitialisationException.class, found::getDescription);
}
@Test
@@ -2,7 +2,7 @@ package org.tests.batchload;
import io.ebean.BeanState;
import io.ebean.DB;
import io.ebean.UnmodifiableEntityException;
import io.ebean.LazyInitialisationException;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.xtest.BaseTestCase;
@@ -83,7 +83,7 @@ class TestBeanState extends BaseTestCase {
BeanState beanState = DB.beanState(customer);
beanState.setDisableLazyLoad(true);
assertNull(customer.getName());
assertThrows(LazyInitialisationException.class, customer::getName);
}
@Test
@@ -1,18 +1,18 @@
package org.tests.batchload;
import io.ebean.LazyInitialisationException;
import io.ebean.xtest.BaseTestCase;
import io.ebean.DB;
import io.ebean.test.LoggedSql;
import org.junit.jupiter.api.Test;
import org.tests.model.basic.Order;
import org.tests.model.basic.OrderDetail;
import org.tests.model.basic.ResetBasicData;
import java.sql.Date;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.*;
public class TestQueryDisableLazyLoad extends BaseTestCase {
@@ -32,8 +32,7 @@ public class TestQueryDisableLazyLoad extends BaseTestCase {
Order order = l0.get(0);
List<OrderDetail> details = order.getDetails();
assertEquals(details.size(), 0);
assertThrows(LazyInitialisationException.class, order::getDetails);
List<String> loggedSql = LoggedSql.stop();
assertThat(loggedSql).hasSize(1);
@@ -59,7 +58,7 @@ public class TestQueryDisableLazyLoad extends BaseTestCase {
Order order = l0.get(0);
// normally invokes lazy loading
assertNull(order.getCustomer().getStatus());
assertThrows(LazyInitialisationException.class, () -> order.getCustomer().getStatus());
List<String> loggedSql = LoggedSql.stop();
assertThat(loggedSql).hasSize(1);
@@ -83,7 +82,29 @@ public class TestQueryDisableLazyLoad extends BaseTestCase {
Order order = l0.get(0);
// normally invokes lazy loading
assertNull(order.getCustomer().getStatus());
assertThrows(LazyInitialisationException.class, () -> order.getCustomer().getStatus());
List<String> loggedSql = LoggedSql.stop();
assertThat(loggedSql).hasSize(1);
}
@Test
public void onSetter_expect_LazyInitialisationException() {
ResetBasicData.reset();
LoggedSql.start();
List<Order> l0 = DB.find(Order.class)
.setDisableLazyLoading(true)
.select("status, orderDate")
.orderBy().asc("id")
.findList();
assertThat(l0).isNotEmpty();
Order order = l0.get(0);
// normally invokes lazy loading
assertThrows(LazyInitialisationException.class, () -> order.setShipDate(new Date(System.currentTimeMillis())));
List<String> loggedSql = LoggedSql.stop();
assertThat(loggedSql).hasSize(1);
@@ -1,5 +1,6 @@
package org.tests.batchload;
import io.ebean.LazyInitialisationException;
import io.ebean.xtest.BaseTestCase;
import io.ebean.DB;
import io.ebean.test.LoggedSql;
@@ -16,6 +17,7 @@ import java.util.Arrays;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class TestQueryJoinToAssocOne extends BaseTestCase {
@@ -91,15 +93,15 @@ public class TestQueryJoinToAssocOne extends BaseTestCase {
Order order = l0.get(0);
// normally invokes lazy loading
order.getOrderDate();
assertThrows(LazyInitialisationException.class, order::getOrderDate);
List<OrderDetail> details = order.getDetails();
OrderDetail orderDetail = details.get(0);
// normally invokes lazy loading
orderDetail.getShipQty();
assertThrows(LazyInitialisationException.class, orderDetail::getShipQty);
// normally invokes lazy loading
order.getShipments().size();
assertThrows(LazyInitialisationException.class, order::getShipments);
List<String> loggedSql = LoggedSql.stop();
assertThat(loggedSql).hasSize(2);
@@ -129,13 +131,11 @@ public class TestQueryJoinToAssocOne extends BaseTestCase {
Order order = l0.get(0);
// try to invoke lazy loading on the bean
assertThat(order.getCustomer()).isNull();
assertThat(order.getCretime()).isNull();
assertThrows(LazyInitialisationException.class, order::getCustomer);
assertThrows(LazyInitialisationException.class, order::getCretime);
// try to invoke lazy loading on the OneToMany ...
List<OrderDetail> details = order.getDetails();
assertThat(details).isEmpty();
assertThat(details.size()).isEqualTo(0);
assertThrows(LazyInitialisationException.class, order::getDetails);
List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
@@ -163,7 +163,7 @@ public class TestQueryJoinToAssocOne extends BaseTestCase {
.setId(tenant.getId())
.findOne();
assertThat(found.getRoles().size()).isEqualTo(0);
assertThrows(LazyInitialisationException.class, found::getRoles);
List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
@@ -189,7 +189,7 @@ public class TestQueryJoinToAssocOne extends BaseTestCase {
.findOne();
// normally invokes lazy loading
assertThat(found.getRoles().size()).isEqualTo(0);
assertThrows(LazyInitialisationException.class, found::getRoles);
// only 1 query ... no lazy loading query
List<String> sql = LoggedSql.stop();
@@ -217,12 +217,12 @@ public class TestQueryJoinToAssocOne extends BaseTestCase {
Order order = l0.get(0);
// normally invokes lazy loading
order.getOrderDate();
assertThrows(LazyInitialisationException.class, order::getOrderDate);
List<OrderDetail> details = order.getDetails();
OrderDetail orderDetail = details.get(0);
// normally invokes lazy loading
orderDetail.getShipQty();
assertThrows(LazyInitialisationException.class, orderDetail::getShipQty);
List<String> loggedSql = LoggedSql.stop();
assertThat(loggedSql).hasSize(1);
@@ -1,5 +1,6 @@
package org.tests.model.aggregation;
import io.ebean.LazyInitialisationException;
import io.ebean.xtest.BaseTestCase;
import io.ebean.DB;
import io.ebean.test.LoggedSql;
@@ -8,6 +9,7 @@ import org.junit.jupiter.api.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class TestAggregationMany extends BaseTestCase {
@@ -26,7 +28,7 @@ public class TestAggregationMany extends BaseTestCase {
for (DMachine machine : machines) {
assertThat(machine.getAuxUseAggs()).isNotEmpty();
assertThat(machine.getMachineStats()).isEmpty();
assertThrows(LazyInitialisationException.class, machine::getMachineStats);
}
List<String> sql = LoggedSql.stop();
@@ -57,7 +59,7 @@ public class TestAggregationMany extends BaseTestCase {
for (DMachine machine : machines) {
assertThat(machine.getAuxUseAggs()).isNotEmpty();
assertThat(machine.getMachineStats()).isEmpty();
assertThrows(LazyInitialisationException.class, machine::getMachineStats);
}
List<String> sql = LoggedSql.stop();
@@ -83,7 +85,7 @@ public class TestAggregationMany extends BaseTestCase {
for (DMachine machine : machines) {
assertThat(machine.getAuxUseAggs()).isNotEmpty();
System.out.println(machine);
assertThat(machine.getMachineStats()).isEmpty();
assertThrows(LazyInitialisationException.class, machine::getMachineStats);
}
List<String> sql = LoggedSql.stop();
@@ -1,5 +1,6 @@
package org.tests.text.json;
import io.ebean.LazyInitialisationException;
import io.ebean.text.json.JsonReadOptions;
import io.ebean.xtest.BaseTestCase;
import io.ebean.BeanState;
@@ -19,8 +20,7 @@ import java.io.IOException;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.*;
public class TestTextJsonReferenceBean extends BaseTestCase {
@@ -33,7 +33,7 @@ public class TestTextJsonReferenceBean extends BaseTestCase {
assertThat(DB.beanState(productRefBean).isReference()).isTrue();
// does not lazy load by default
assertThat(productRefBean.getName()).isNull();
assertThrows(LazyInitialisationException.class, productRefBean::getName);
}
@Test