mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-21 05:21:01 +00:00
Support unmodifiable Embedded and EmbeddedId
This commit is contained in:
@@ -7,6 +7,8 @@ import java.sql.SQLException;
|
||||
|
||||
public interface DataReader {
|
||||
|
||||
boolean unmodifiable();
|
||||
|
||||
void close() throws SQLException;
|
||||
|
||||
boolean next() throws SQLException;
|
||||
|
||||
@@ -67,7 +67,7 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
@Override
|
||||
protected void setResultSet(ResultSet resultSet, Object queryPlanKey) throws SQLException {
|
||||
this.resultSet = resultSet;
|
||||
this.dataReader = new RsetDataReader(server.dataTimeZone(), resultSet);
|
||||
this.dataReader = new RsetDataReader(false, server.dataTimeZone(), resultSet);
|
||||
obtainPlan(queryPlanKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import java.sql.SQLException;
|
||||
abstract class AssocOneHelp {
|
||||
|
||||
final BeanPropertyAssocOne<?> property;
|
||||
private final BeanDescriptor<?> target;
|
||||
protected final BeanDescriptor<?> target;
|
||||
private final String path;
|
||||
|
||||
AssocOneHelp(BeanPropertyAssocOne<?> property) {
|
||||
|
||||
@@ -32,7 +32,7 @@ final class AssocOneHelpEmbedded extends AssocOneHelp {
|
||||
|
||||
@Override
|
||||
Object read(DataReader reader) throws SQLException {
|
||||
EntityBean embeddedBean = property.targetDescriptor.createEntityBean();
|
||||
EntityBean embeddedBean = property.targetDescriptor.createEntityBean2(reader.unmodifiable());
|
||||
boolean notNull = false;
|
||||
for (BeanProperty property : property.embeddedProps) {
|
||||
Object value = property.readSet(reader, embeddedBean);
|
||||
@@ -62,7 +62,7 @@ final class AssocOneHelpEmbedded extends AssocOneHelp {
|
||||
|
||||
@Override
|
||||
Object read(DbReadContext ctx) throws SQLException {
|
||||
EntityBean embeddedBean = property.targetDescriptor.createEntityBean();
|
||||
EntityBean embeddedBean = property.targetDescriptor.createEntityBean2(ctx.unmodifiable());
|
||||
boolean notNull = false;
|
||||
for (BeanProperty property : property.embeddedProps) {
|
||||
Object value = property.readSet(ctx, embeddedBean);
|
||||
|
||||
@@ -3,7 +3,6 @@ package io.ebeaninternal.server.deploy.id;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.SpiExpressionBind;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
@@ -324,7 +323,7 @@ public final class IdBinderEmbedded implements IdBinder {
|
||||
|
||||
@Override
|
||||
public Object read(DbReadContext ctx) throws SQLException {
|
||||
final EntityBean embId = idDesc.createEntityBean();
|
||||
final EntityBean embId = idDesc.createEntityBean2(ctx.unmodifiable());
|
||||
boolean nullValue = true;
|
||||
for (BeanProperty prop : props) {
|
||||
final Object value = prop.read(ctx);
|
||||
|
||||
@@ -401,7 +401,7 @@ public final class Binder {
|
||||
return new DataBind(dataTimeZone, stmt, connection);
|
||||
}
|
||||
|
||||
public DataReader createDataReader(ResultSet resultSet) {
|
||||
return new RsetDataReader(dataTimeZone, resultSet);
|
||||
public DataReader createDataReader(boolean unmodifiable, ResultSet resultSet) {
|
||||
return new RsetDataReader(unmodifiable, dataTimeZone, resultSet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,7 +309,7 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
|
||||
if (resultSet == null) {
|
||||
return false;
|
||||
}
|
||||
dataReader = queryPlan.createDataReader(resultSet);
|
||||
dataReader = queryPlan.createDataReader(query.isUnmodifiable(), resultSet);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -137,7 +137,7 @@ final class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent, Ca
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
dataReader = new RsetDataReader(request.dataTimeZone(), pstmt.executeQuery());
|
||||
dataReader = new RsetDataReader(query.isUnmodifiable(), request.dataTimeZone(), pstmt.executeQuery());
|
||||
query.checkCancelled();
|
||||
}
|
||||
|
||||
|
||||
@@ -205,8 +205,8 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
return new DQueryPlanOutput(beanType(), name, hash, sql, profileLocation, bind, planString);
|
||||
}
|
||||
|
||||
public DataReader createDataReader(ResultSet rset) {
|
||||
return new RsetDataReader(dataTimeZone, rset);
|
||||
public DataReader createDataReader(boolean unmodifiable, ResultSet rset) {
|
||||
return new RsetDataReader(unmodifiable, dataTimeZone, rset);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,8 +22,8 @@ final class CQueryPlanRawSql extends CQueryPlan {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataReader createDataReader(ResultSet rset) {
|
||||
return new RsetDataReaderIndexed(dataTimeZone, rset, rsetIndexPositions);
|
||||
public DataReader createDataReader(boolean unmodifiable, ResultSet rset) {
|
||||
return new RsetDataReaderIndexed(unmodifiable, dataTimeZone, rset, rsetIndexPositions);
|
||||
}
|
||||
|
||||
private int[] createIndexPositions(OrmQueryRequest<?> request, SqlTree sqlTree) {
|
||||
|
||||
+3
-3
@@ -147,7 +147,7 @@ public final class DefaultRelationalQueryEngine implements RelationalQueryEngine
|
||||
request.setDefaultFetchBuffer(defaultFetchSizeFindList);
|
||||
}
|
||||
request.executeSql(binder, SpiQuery.Type.ATTRIBUTE);
|
||||
final DataReader dataReader = binder.createDataReader(request.resultSet());
|
||||
final DataReader dataReader = binder.createDataReader(false, request.resultSet());
|
||||
T value = null;
|
||||
if (dataReader.next()) {
|
||||
value = scalarType.read(dataReader);
|
||||
@@ -172,7 +172,7 @@ public final class DefaultRelationalQueryEngine implements RelationalQueryEngine
|
||||
request.setDefaultFetchBuffer(defaultFetchSizeFindList);
|
||||
}
|
||||
request.executeSql(binder, SpiQuery.Type.ATTRIBUTE);
|
||||
final DataReader dataReader = binder.createDataReader(request.resultSet());
|
||||
final DataReader dataReader = binder.createDataReader(false, request.resultSet());
|
||||
List<T> rows = new ArrayList<>();
|
||||
while (dataReader.next()) {
|
||||
rows.add(scalarType.read(dataReader));
|
||||
@@ -197,7 +197,7 @@ public final class DefaultRelationalQueryEngine implements RelationalQueryEngine
|
||||
request.setDefaultFetchBuffer(defaultFetchSizeFindEach);
|
||||
}
|
||||
request.executeSql(binder, SpiQuery.Type.ATTRIBUTE);
|
||||
final DataReader dataReader = binder.createDataReader(request.resultSet());
|
||||
final DataReader dataReader = binder.createDataReader(false, request.resultSet());
|
||||
while (dataReader.next()) {
|
||||
consumer.accept(scalarType.read(dataReader));
|
||||
}
|
||||
|
||||
@@ -23,16 +23,23 @@ public class RsetDataReader implements DataReader {
|
||||
static final int clobBufferSize = 512;
|
||||
static final int stringInitialSize = 512;
|
||||
|
||||
private final boolean unmodifiable;
|
||||
private final DataTimeZone dataTimeZone;
|
||||
private final ResultSet rset;
|
||||
protected int pos;
|
||||
private String json;
|
||||
|
||||
public RsetDataReader(DataTimeZone dataTimeZone, ResultSet rset) {
|
||||
public RsetDataReader(boolean unmodifiable, DataTimeZone dataTimeZone, ResultSet rset) {
|
||||
this.unmodifiable = unmodifiable;
|
||||
this.dataTimeZone = dataTimeZone;
|
||||
this.rset = rset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unmodifiable() {
|
||||
return unmodifiable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void pushJson(String json) {
|
||||
this.json = json;
|
||||
|
||||
@@ -11,8 +11,8 @@ public final class RsetDataReaderIndexed extends RsetDataReader {
|
||||
|
||||
private final int[] rsetIndexPositions;
|
||||
|
||||
public RsetDataReaderIndexed(DataTimeZone dataTimeZone, ResultSet rset, int[] rsetIndexPositions) {
|
||||
super(dataTimeZone, rset);
|
||||
public RsetDataReaderIndexed(boolean unmodifiable, DataTimeZone dataTimeZone, ResultSet rset, int[] rsetIndexPositions) {
|
||||
super(unmodifiable, dataTimeZone, rset);
|
||||
this.rsetIndexPositions = rsetIndexPositions;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.tests.compositekeys;
|
||||
|
||||
import io.ebean.UnmodifiableEntityException;
|
||||
import io.ebean.xtest.BaseTestCase;
|
||||
import io.ebean.DB;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -9,6 +10,7 @@ import org.tests.model.basic.CKeyParentId;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
@@ -51,6 +53,14 @@ public class TestCKeyDelete extends BaseTestCase {
|
||||
List<CKeyParentId> ids = DB.find(CKeyParent.class).where().eq("id.oneKey", 101).findIds();
|
||||
assertThat(ids).hasSize(1);
|
||||
|
||||
List<CKeyParentId> idsUnmodifiable = DB.find(CKeyParent.class)
|
||||
.setUnmodifiable(true)
|
||||
.where().eq("id.oneKey", 101)
|
||||
.findIds();
|
||||
assertThat(idsUnmodifiable).hasSize(1);
|
||||
assertThatThrownBy(() -> idsUnmodifiable.get(0).setOneKey(7))
|
||||
.isInstanceOf(UnmodifiableEntityException.class);
|
||||
|
||||
CKeyParentId foundId = ids.get(0);
|
||||
assertThat(foundId.getOneKey()).isEqualTo(101);
|
||||
assertThat(foundId.getTwoKey()).isEqualTo("deleteMe2");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.tests.model.composite;
|
||||
|
||||
import io.ebean.UnmodifiableEntityException;
|
||||
import io.ebean.xtest.BaseTestCase;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.test.LoggedSql;
|
||||
@@ -8,11 +9,12 @@ import org.junit.jupiter.api.Test;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
public class TestCompositeKeyUserClient extends BaseTestCase {
|
||||
class TestCompositeKeyUserClient extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
void test() {
|
||||
|
||||
CkeUser user0 = new CkeUser();
|
||||
user0.setUserPK(new CkeUserKey(20, "sally"));
|
||||
@@ -29,6 +31,19 @@ public class TestCompositeKeyUserClient extends BaseTestCase {
|
||||
client.setClientPK(new CkeClientKey(20, "susan"));
|
||||
client.setUser(user1);
|
||||
|
||||
List<CkeUser> unmodifiable = DB.find(CkeUser.class)
|
||||
.setUnmodifiable(true)
|
||||
.where().eq("userPK.codCompany", 20)
|
||||
.findList();
|
||||
|
||||
assertThat(unmodifiable).hasSize(2);
|
||||
for (CkeUser ckeUser : unmodifiable) {
|
||||
CkeUserKey userPK = ckeUser.getUserPK();
|
||||
assertThatThrownBy(() -> userPK.setCodCompany(7))
|
||||
.describedAs("EmbeddedId is unmodifiable")
|
||||
.isInstanceOf(UnmodifiableEntityException.class);
|
||||
}
|
||||
|
||||
LoggedSql.start();
|
||||
|
||||
DB.save(client);
|
||||
|
||||
@@ -138,6 +138,23 @@ class TestQueryOrderById extends BaseTestCase {
|
||||
assertThat(read.getName()).isEqualTo(customer.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void unmodifiableWithReference() {
|
||||
ResetBasicData.reset();
|
||||
|
||||
List<Contact> result = DB.find(Contact.class)
|
||||
.setUnmodifiable(true)
|
||||
.findList();
|
||||
|
||||
assertThat(result).isNotEmpty();
|
||||
for (Contact contact : result) {
|
||||
Customer customer = contact.getCustomer();
|
||||
assertThat(customer.getId()).isNotNull();
|
||||
assertThatThrownBy(() -> customer.setId(42))
|
||||
.isInstanceOf(UnsupportedOperationException.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void immutableResult() {
|
||||
ResetBasicData.reset();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.tests.query.embedded;
|
||||
|
||||
import io.ebean.CacheMode;
|
||||
import io.ebean.xtest.BaseTestCase;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.Database;
|
||||
@@ -14,13 +15,14 @@ import org.tests.model.embedded.EInvoice.State;
|
||||
import java.util.Date;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
public class TestMultipleEmbeddedLoading extends BaseTestCase {
|
||||
class TestMultipleEmbeddedLoading extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void testSimpleCase() {
|
||||
void testSimpleCase() {
|
||||
|
||||
// prepare test
|
||||
EAddress ship = new EAddress();
|
||||
@@ -51,6 +53,19 @@ public class TestMultipleEmbeddedLoading extends BaseTestCase {
|
||||
assertEquals("2 Apple St", invoice.getBillAddress().getStreet());
|
||||
assertEquals("2 Apple St", invoice2.getBillAddress().getStreet());
|
||||
|
||||
EInvoice readOnlyInvoice = DB.find(EInvoice.class)
|
||||
.setId(invoice.getId())
|
||||
.setUnmodifiable(true)
|
||||
.setBeanCacheMode(CacheMode.OFF)
|
||||
.findOne();
|
||||
|
||||
assertThatThrownBy(() -> readOnlyInvoice.getBillAddress().setCity("junk"))
|
||||
.describedAs("embedded bean is unmodifiable")
|
||||
.isInstanceOf(UnsupportedOperationException.class);
|
||||
assertThatThrownBy(() -> readOnlyInvoice.getShipAddress().setCity("junk"))
|
||||
.describedAs("embedded bean is unmodifiable")
|
||||
.isInstanceOf(UnsupportedOperationException.class);
|
||||
|
||||
// act: only update one of the embedded fields
|
||||
invoice2.getBillAddress().setStreet("3 Pineapple St");
|
||||
// bean should be dirty
|
||||
|
||||
Reference in New Issue
Block a user