diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/InputStreamInfo.java b/ebean-core-type/src/main/java/io/ebean/core/type/InputStreamInfo.java index 67394a62b..137ab4da6 100644 --- a/ebean-core-type/src/main/java/io/ebean/core/type/InputStreamInfo.java +++ b/ebean-core-type/src/main/java/io/ebean/core/type/InputStreamInfo.java @@ -7,9 +7,9 @@ import java.io.InputStream; * * @author Roland Praml, FOCONIS AG */ -public class InputStreamInfo { - private final InputStream stream; +public final class InputStreamInfo { + private final InputStream stream; private final long length; public InputStreamInfo(InputStream stream, long length) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/bind/DataBind.java b/ebean-core/src/main/java/io/ebeaninternal/server/bind/DataBind.java index bb3e79e97..3b65b788f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/bind/DataBind.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/bind/DataBind.java @@ -132,37 +132,37 @@ public class DataBind implements DataBinder { @Override public final void setInt(int value) throws SQLException { pstmt.setInt(++pos, value); - lastObject = value; + lastObject = null; } @Override public final void setLong(long value) throws SQLException { pstmt.setLong(++pos, value); - lastObject = value; + lastObject = null; } @Override public final void setShort(short value) throws SQLException { pstmt.setShort(++pos, value); - lastObject = value; + lastObject = null; } @Override public final void setFloat(float value) throws SQLException { pstmt.setFloat(++pos, value); - lastObject = value; + lastObject = null; } @Override public final void setDouble(double value) throws SQLException { pstmt.setDouble(++pos, value); - lastObject = value; + lastObject = null; } @Override public final void setBigDecimal(BigDecimal value) throws SQLException { pstmt.setBigDecimal(++pos, value); - lastObject = value; + lastObject = null; } @Override @@ -173,7 +173,7 @@ public class DataBind implements DataBinder { } else { pstmt.setDate(++pos, value); } - lastObject = value; + lastObject = null; } @Override @@ -184,7 +184,7 @@ public class DataBind implements DataBinder { } else { pstmt.setTimestamp(++pos, value); } - lastObject = value; + lastObject = null; } @Override @@ -195,13 +195,13 @@ public class DataBind implements DataBinder { } else { pstmt.setTime(++pos, value); } - lastObject = value; + lastObject = null; } @Override public void setBoolean(boolean value) throws SQLException { pstmt.setBoolean(++pos, value); - lastObject = value; + lastObject = null; } @Override @@ -213,13 +213,13 @@ public class DataBind implements DataBinder { @Override public void setByte(byte value) throws SQLException { pstmt.setByte(++pos, value); - lastObject = value; + lastObject = null; } @Override public void setChar(char value) throws SQLException { pstmt.setString(++pos, String.valueOf(value)); - lastObject = value; + lastObject = null; } @Override @@ -252,7 +252,7 @@ public class DataBind implements DataBinder { @Override public void setArray(String arrayType, Object[] elements) throws SQLException { pstmt.setArray(++pos, connection.createArrayOf(arrayType, elements)); - lastObject = elements; + lastObject = null; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java index 7e73a5a94..aa1289f15 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -105,6 +105,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy private final String asOfViewSuffix; private final boolean jacksonCorePresent; private final int queryPlanTTLSeconds; + private final BindMaxLength bindMaxLength; private int entityBeanCount; private List> immutableDescriptorList; /** @@ -159,6 +160,19 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy this.changeLogListener = config.changeLogListener(bootupClasses.getChangeLogListener()); this.changeLogRegister = config.changeLogRegister(bootupClasses.getChangeLogRegister()); this.jacksonCorePresent = config.isJacksonCorePresent(); + this.bindMaxLength = initMaxLength(); + } + + BindMaxLength initMaxLength() { + LengthCheck lengthCheck = this.config.getLengthCheck(); + switch (lengthCheck) { + case OFF: + return null; + case UTF8: + return BindMaxLength.ofUtf8(); + default: + return BindMaxLength.ofStandard(); + } } @Override @@ -1502,6 +1516,10 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy return list; } + public BindMaxLength bindMaxLength() { + return bindMaxLength; + } + /** * Comparator to sort the BeanDescriptors by name. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index c77e8b67f..7c3d82950 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -171,6 +171,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { */ private final String dbComment; private final DbEncryptFunction dbEncryptFunction; + private final BindMaxLength bindMaxLength; private int deployOrder; final boolean jsonSerialize; final boolean jsonDeserialize; @@ -263,6 +264,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { } this.jsonSerialize = deploy.isJsonSerialize(); this.jsonDeserialize = deploy.isJsonDeserialize(); + this.bindMaxLength = deploy.bindMaxLength(); } private String tableAliasIntern(BeanDescriptor descriptor, String s, boolean dbEncrypted, String dbColumn) { @@ -350,6 +352,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn); this.jsonSerialize = source.jsonSerialize; this.jsonDeserialize = source.jsonDeserialize; + this.bindMaxLength = source.bindMaxLength; } /** @@ -559,20 +562,16 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { @SuppressWarnings("unchecked") public void bind(DataBind b, Object value) throws SQLException { scalarType.bind(b, value); - - if (needsLengthCheck()) { - LengthCheck lengthCheck = descriptor().config().getLengthCheck(); - if (lengthCheck != LengthCheck.OFF) { - Object obj = b.popLastObject(); - long l = getLength(obj, lengthCheck == LengthCheck.UTF8); - if (l > dbLength) { - b.closeInputStreams(); - String s = String.valueOf(value); // take original bind value here. - if (s.length() > 100) { - s = s.substring(0, 97) + "..."; - } - throw new DataIntegrityException("Cannot bind value '" + s + "' (effective length=" + l + ") to column '" + dbColumn + "' (length=" + dbLength + ")"); + if (bindMaxLength != null) { + Object obj = b.popLastObject(); + long length = bindMaxLength.length(dbLength, obj); + if (length > dbLength) { + b.closeInputStreams(); + String s = String.valueOf(value); // take original bind value here. + if (s.length() > 50) { + s = s.substring(0, 47) + "..."; } + throw new DataIntegrityException("Cannot bind value '" + s + "' (effective length=" + length + ") to column '" + dbColumn + "' (length=" + dbLength + ")"); } } } @@ -586,43 +585,6 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { return scalarType.readData(dataInput); } - /** - * Returns, if this property needs a length check. - */ - boolean needsLengthCheck() { - if (dbLength == 0) { - return false; - } - switch (dbType) { - case Types.VARCHAR: - case Types.BLOB: - case ExtraDbTypes.JSON: - return true; - default: - return false; - } - } - - /** - * Returns the length of obj. Note: for UTF8 strings -1 will be retuned, if the string length is lower than 1/4th of db length - */ - private long getLength(Object obj, boolean utf8) { - if (obj instanceof String) { - String s = (String) obj; - if (utf8) { - return s.length() * 4 <= dbLength ? -1 : s.getBytes(StandardCharsets.UTF_8).length; - } else { - return s.length(); - } - } else if (obj instanceof byte[]) { - return ((byte[]) obj).length; - } else if (obj instanceof InputStreamInfo) { - return ((InputStreamInfo) obj).length(); - } else { - return -1; - } - } - @Override public BeanProperty beanProperty() { return this; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BindMaxLength.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BindMaxLength.java new file mode 100644 index 000000000..d5fcc3e5e --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BindMaxLength.java @@ -0,0 +1,68 @@ +package io.ebeaninternal.server.deploy; + +import io.ebean.core.type.InputStreamInfo; + +import java.nio.charset.StandardCharsets; + +public interface BindMaxLength { + + /** + * Return a UTF8 based implementation. + */ + static BindMaxLength ofUtf8() { + return new UTF8(); + } + + /** + * Return a standard implementation. + */ + static BindMaxLength ofStandard() { + return new Standard(); + } + + /** + * Return the length of the object. + */ + long length(int dbLength, Object obj); + + final class UTF8 implements BindMaxLength { + + @Override + public long length(int dbLength, Object obj) { + if (obj instanceof String) { + String s = (String) obj; + int stringLength = s.length(); + if (stringLength > dbLength) { + return stringLength; + } else if (stringLength * 4 <= dbLength) { + return -1; + } else { + return s.getBytes(StandardCharsets.UTF_8).length; + } + + } else if (obj instanceof byte[]) { + return ((byte[]) obj).length; + } else if (obj instanceof InputStreamInfo) { + return ((InputStreamInfo) obj).length(); + } else { + return -1; + } + } + } + + final class Standard implements BindMaxLength { + + @Override + public long length(int dbLength, Object obj) { + if (obj instanceof String) { + return ((String) obj).length(); + } else if (obj instanceof byte[]) { + return ((byte[]) obj).length; + } else if (obj instanceof InputStreamInfo) { + return ((InputStreamInfo) obj).length(); + } else { + return -1; + } + } + } +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java index 97df0a16d..22c77e77c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java @@ -139,6 +139,10 @@ public class DeployBeanDescriptor { this.beanType = beanType; } + public BindMaxLength bindMaxLength() { + return manager.bindMaxLength(); + } + /** * Set the IdClass to use. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java index 4f927043c..23fc61797 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java @@ -1,14 +1,17 @@ package io.ebeaninternal.server.deploy.meta; +import io.avaje.lang.Nullable; import io.ebean.annotation.*; import io.ebean.config.ScalarTypeConverter; import io.ebean.config.dbplatform.DbDefaultValue; import io.ebean.config.dbplatform.DbEncrypt; import io.ebean.config.dbplatform.DbEncryptFunction; +import io.ebean.config.dbplatform.ExtraDbTypes; import io.ebean.core.type.ScalarType; import io.ebean.util.AnnotationUtil; import io.ebeaninternal.server.core.InternString; import io.ebeaninternal.server.deploy.BeanProperty; +import io.ebeaninternal.server.deploy.BindMaxLength; import io.ebeaninternal.server.deploy.DbMigrationInfo; import io.ebeaninternal.server.deploy.DeployDocPropertyOptions; import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; @@ -1133,4 +1136,20 @@ public class DeployBeanProperty { boolean isJsonType() { return mutationDetection != null; } + + @Nullable + public BindMaxLength bindMaxLength() { + if (dbLength == 0) { + return null; + } + switch (dbType) { + case Types.VARCHAR: + case Types.BLOB: + case ExtraDbTypes.JSON: + return desc.bindMaxLength(); + default: + return null; + } + + } }