diff --git a/src/main/java/io/ebean/BeanState.java b/src/main/java/io/ebean/BeanState.java index ddb76d358..cbe4e9849 100644 --- a/src/main/java/io/ebean/BeanState.java +++ b/src/main/java/io/ebean/BeanState.java @@ -3,6 +3,8 @@ package io.ebean; import java.util.Map; import java.util.Set; +import javax.annotation.Nullable; + /** * Provides access to the internal state of an entity bean. */ @@ -111,4 +113,10 @@ public interface BeanState { * Reset the bean putting it into NEW state such that a save() results in an insert. */ void resetForInsert(); + + /** + * Returns a map with load erros. + */ + @Nullable + Map getLoadErrors(); } diff --git a/src/main/java/io/ebean/bean/EntityBeanIntercept.java b/src/main/java/io/ebean/bean/EntityBeanIntercept.java index 87b4f3023..22b667608 100644 --- a/src/main/java/io/ebean/bean/EntityBeanIntercept.java +++ b/src/main/java/io/ebean/bean/EntityBeanIntercept.java @@ -98,6 +98,8 @@ public final class EntityBeanIntercept implements Serializable { private Object[] origValues; + private Exception[] loadErrors; + private int lazyLoadProperty = -1; private Object ownerId; @@ -1115,4 +1117,36 @@ public final class EntityBeanIntercept implements Serializable { public void setSortOrder(int sortOrder) { this.sortOrder = sortOrder; } + + /** + * Set the load error that happened on this property. + */ + public void setLoadError(int propertyIndex, Exception t) { + if (loadErrors == null) { + loadErrors = new Exception[owner._ebean_getPropertyNames().length]; + } + loadErrors[propertyIndex] = t; + flags[propertyIndex] |= FLAG_LOADED_PROP; + } + + /** + * Returns the loadErrors. + */ + public Map getLoadErrors() { + if (loadErrors == null) { + return null; + } + Map ret = null; + int len = getPropertyLength(); + for (int i = 0; i < len; i++) { + Exception loadError = loadErrors[i]; + if (loadError != null) { + if (ret == null) { + ret = new LinkedHashMap<>(); + } + ret.put(getProperty(i), loadError); + } + } + return ret; + } } diff --git a/src/main/java/io/ebean/config/ServerConfig.java b/src/main/java/io/ebean/config/ServerConfig.java index 1abf7bb56..3d4048fa3 100644 --- a/src/main/java/io/ebean/config/ServerConfig.java +++ b/src/main/java/io/ebean/config/ServerConfig.java @@ -30,9 +30,15 @@ import io.ebean.event.readaudit.ReadAuditLogger; import io.ebean.event.readaudit.ReadAuditPrepare; import io.ebean.meta.MetaInfoManager; import io.ebean.migration.MigrationRunner; +import io.ebean.plugin.LoadErrorHandler; import io.ebean.util.StringHelper; +import javax.persistence.PersistenceException; import javax.sql.DataSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.time.Clock; import java.util.ArrayList; import java.util.Collections; @@ -518,6 +524,10 @@ public class ServerConfig { */ private boolean idGeneratorAutomatic = true; + private LoadErrorHandler loadErrorHandler = (bean, prop, fullName, e) -> { + throw new PersistenceException("Error loading on " + fullName, e); + }; + /** * Construct a Server Configuration for programmatically creating an EbeanServer. */ @@ -2985,6 +2995,13 @@ public class ServerConfig { String mappingsProp = p.get("mappingLocations", null); mappingLocations = getSearchList(mappingsProp, mappingLocations); + + if (!p.getBoolean("failOnLoadError", true)) { + Logger logger = LoggerFactory.getLogger("io.ebean.SQL"); + loadErrorHandler = (bean, prop, fullName, e) -> { + logger.error("Error loading on {}", fullName, e); + }; + } } private NamingConvention createNamingConvention(PropertiesWrapper properties, NamingConvention namingConvention) { @@ -3244,6 +3261,20 @@ public class ServerConfig { this.idGeneratorAutomatic = idGeneratorAutomatic; } + /** + * Returns the load error handler. + */ + public LoadErrorHandler getLoadErrorHandler() { + return loadErrorHandler; + } + + /** + * Sets the loadErrorHandler. + */ + public void setLoadErrorHandler(LoadErrorHandler loadErrorHandler) { + this.loadErrorHandler = loadErrorHandler; + } + /** * Return true if query plan capture is enabled. */ diff --git a/src/main/java/io/ebean/plugin/LoadErrorHandler.java b/src/main/java/io/ebean/plugin/LoadErrorHandler.java new file mode 100644 index 000000000..278a70f96 --- /dev/null +++ b/src/main/java/io/ebean/plugin/LoadErrorHandler.java @@ -0,0 +1,14 @@ +package io.ebean.plugin; + +import io.ebean.bean.EntityBean; + +/** + * Errorhandler to handle load errors and may be recover correct value. + * + * @author Roland Praml, FOCONIS AG + * + */ +@FunctionalInterface +public interface LoadErrorHandler { + void handleLoadError(EntityBean bean, Property prop, String fullName, Exception e); +} diff --git a/src/main/java/io/ebean/text/TextException.java b/src/main/java/io/ebean/text/TextException.java index e5036e112..f7dd7cfa9 100644 --- a/src/main/java/io/ebean/text/TextException.java +++ b/src/main/java/io/ebean/text/TextException.java @@ -6,6 +6,7 @@ package io.ebean.text; public class TextException extends RuntimeException { private static final long serialVersionUID = 1601310159486033148L; + private String text; /** * Construct with an error message. @@ -27,4 +28,41 @@ public class TextException extends RuntimeException { public TextException(Exception e) { super(e); } + + /** + * Constructor for a detailed exception. + * + * @param message + * the message. The placeholder {} will be replaced by + * text + * @param text + * the fault text. + * @param cause + * the case + */ + public TextException(String message, String text, Exception cause) { + super(message.replace("{}", String.valueOf(text)), cause); + this.text = text; + } + + /** + * Constructor for a detailed exception. + * + * @param message + * the message. The placeholder {} will be replaced by + * text + * @param text + * the fault text. + */ + public TextException(String message, String text) { + super(message.replace("{}", String.valueOf(text))); + this.text = text; + } + + /** + * Return the text, that caused the error. (e.g. the JSON). May be null. + */ + public String getText() { + return text; + } } diff --git a/src/main/java/io/ebeaninternal/api/SpiQuery.java b/src/main/java/io/ebeaninternal/api/SpiQuery.java index 881cae5e3..efdefd718 100644 --- a/src/main/java/io/ebeaninternal/api/SpiQuery.java +++ b/src/main/java/io/ebeaninternal/api/SpiQuery.java @@ -8,6 +8,7 @@ import io.ebean.PersistenceContextScope; import io.ebean.ProfileLocation; import io.ebean.Query; import io.ebean.bean.CallStack; +import io.ebean.bean.EntityBean; import io.ebean.bean.ObjectGraphNode; import io.ebean.bean.PersistenceContext; import io.ebean.event.readaudit.ReadEvent; @@ -15,6 +16,7 @@ import io.ebean.plugin.BeanType; import io.ebeaninternal.server.autotune.ProfilingListener; import io.ebeaninternal.server.core.SpiOrmQueryRequest; import io.ebeaninternal.server.deploy.BeanDescriptor; +import io.ebeaninternal.server.deploy.BeanProperty; import io.ebeaninternal.server.deploy.BeanPropertyAssocMany; import io.ebeaninternal.server.deploy.TableJoin; import io.ebeaninternal.server.query.CancelableQuery; @@ -844,4 +846,9 @@ public interface SpiQuery extends Query, TxnProfileEventCodes { * Returns the count distinct order setting. */ CountDistinctOrder getCountDistinctOrder(); + + /** + * Handles load errors. + */ + void handleLoadError(EntityBean bean, BeanProperty prop, String fullName, Exception e); } diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultBeanState.java b/src/main/java/io/ebeaninternal/server/core/DefaultBeanState.java index 3b489948e..22f2b7ae8 100644 --- a/src/main/java/io/ebeaninternal/server/core/DefaultBeanState.java +++ b/src/main/java/io/ebeaninternal/server/core/DefaultBeanState.java @@ -88,4 +88,10 @@ public class DefaultBeanState implements BeanState { public void resetForInsert() { intercept.setNew(); } + + @Override + public Map getLoadErrors() { + return intercept.getLoadErrors(); + } + } diff --git a/src/main/java/io/ebeaninternal/server/deploy/DbReadContext.java b/src/main/java/io/ebeaninternal/server/deploy/DbReadContext.java index c282eca47..09e257a93 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/DbReadContext.java +++ b/src/main/java/io/ebeaninternal/server/deploy/DbReadContext.java @@ -90,4 +90,10 @@ public interface DbReadContext { * Return true if this request disables lazy loading. */ boolean isDisableLazyLoading(); + + /** + * Handles a load error on given property. + */ + void handleLoadError(EntityBean bean, BeanProperty prop, String fullName, Exception e); + } diff --git a/src/main/java/io/ebeaninternal/server/deploy/DynamicPropertyAggregationFormula.java b/src/main/java/io/ebeaninternal/server/deploy/DynamicPropertyAggregationFormula.java index 8b1d95561..efc45759a 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/DynamicPropertyAggregationFormula.java +++ b/src/main/java/io/ebeaninternal/server/deploy/DynamicPropertyAggregationFormula.java @@ -49,15 +49,16 @@ class DynamicPropertyAggregationFormula extends DynamicPropertyBase { @Override public void load(SqlBeanLoad sqlBeanLoad) { - + Object value; try { - Object value = scalarType.read(sqlBeanLoad.ctx().getDataReader()); - if (asTarget != null) { - sqlBeanLoad.load(asTarget, value); - } + value = scalarType.read(sqlBeanLoad.ctx().getDataReader()); } catch (Exception e) { - throw new PersistenceException("Error loading on " + fullName, e); + sqlBeanLoad.ctx().handleLoadError(null, asTarget, fullName, e); + return; + } + if (asTarget != null) { + sqlBeanLoad.load(asTarget, value); } } diff --git a/src/main/java/io/ebeaninternal/server/query/CQuery.java b/src/main/java/io/ebeaninternal/server/query/CQuery.java index 3b04a99a8..d142e4e1d 100644 --- a/src/main/java/io/ebeaninternal/server/query/CQuery.java +++ b/src/main/java/io/ebeaninternal/server/query/CQuery.java @@ -21,6 +21,7 @@ import io.ebeaninternal.server.core.SpiOrmQueryRequest; import io.ebeaninternal.server.deploy.BeanCollectionHelp; import io.ebeaninternal.server.deploy.BeanCollectionHelpFactory; import io.ebeaninternal.server.deploy.BeanDescriptor; +import io.ebeaninternal.server.deploy.BeanProperty; import io.ebeaninternal.server.deploy.BeanPropertyAssocMany; import io.ebeaninternal.server.deploy.DbReadContext; import io.ebeaninternal.server.type.DataBind; @@ -815,6 +816,11 @@ public class CQuery implements DbReadContext, CancelableQuery, SpiProfileTran return pstmt; } + @Override + public void handleLoadError(EntityBean bean, BeanProperty prop, String fullName, Exception e) { + query.handleLoadError(bean, prop, fullName, e); + } + public Set getDependentTables() { return queryPlan.getDependentTables(); } diff --git a/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java b/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java index b5855576d..c6557a5e0 100644 --- a/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java +++ b/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java @@ -6,8 +6,6 @@ import io.ebeaninternal.api.SpiQuery.Mode; import io.ebeaninternal.server.deploy.BeanProperty; import io.ebeaninternal.server.deploy.DbReadContext; -import javax.persistence.PersistenceException; - /** * Controls the loading of property data into a bean. *

@@ -88,8 +86,9 @@ public class SqlBeanLoad { return dbVal; } catch (Exception e) { - String msg = "Error loading on " + prop.getFullBeanName(); - throw new PersistenceException(msg, e); + bean._ebean_getIntercept().setLoadError(prop.getPropertyIndex(), e); + ctx.handleLoadError(bean, prop, prop.getFullBeanName(), e); + return prop.getValue(bean); } } diff --git a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 4b4b906dd..1cfbd9bff 100644 --- a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -25,6 +25,7 @@ import io.ebean.Transaction; import io.ebean.UpdateQuery; import io.ebean.Version; import io.ebean.bean.CallStack; +import io.ebean.bean.EntityBean; import io.ebean.bean.ObjectGraphNode; import io.ebean.bean.ObjectGraphOrigin; import io.ebean.bean.PersistenceContext; @@ -46,6 +47,7 @@ import io.ebeaninternal.api.SpiQuerySecondary; import io.ebeaninternal.server.autotune.ProfilingListener; import io.ebeaninternal.server.core.SpiOrmQueryRequest; import io.ebeaninternal.server.deploy.BeanDescriptor; +import io.ebeaninternal.server.deploy.BeanProperty; import io.ebeaninternal.server.deploy.BeanPropertyAssocMany; import io.ebeaninternal.server.deploy.TableJoin; import io.ebeaninternal.server.expression.DefaultExpressionList; @@ -1921,6 +1923,11 @@ public class DefaultOrmQuery implements SpiQuery { return profileLocation; } + @Override + public void handleLoadError(EntityBean bean, BeanProperty prop, String fullName, Exception e) { + server.getServerConfig().getLoadErrorHandler().handleLoadError(bean, prop, fullName, e); + } + @Override public Query orderById(boolean orderById) { this.orderById = orderById; diff --git a/src/main/java/io/ebeaninternal/server/text/csv/TCsvReader.java b/src/main/java/io/ebeaninternal/server/text/csv/TCsvReader.java index e294e7e49..01a3b6065 100644 --- a/src/main/java/io/ebeaninternal/server/text/csv/TCsvReader.java +++ b/src/main/java/io/ebeaninternal/server/text/csv/TCsvReader.java @@ -351,7 +351,7 @@ public class TCsvReader implements CsvReader { return path.parseDateTime(dt.getTime()); } catch (ParseException e) { - throw new TextException("Error parsing [" + value + "] using format[" + format + "]", e); + throw new TextException("Error parsing [{}] using format[" + format + "]", value, e); } } diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayList.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayList.java index f9e07fd60..b7511cab3 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayList.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayList.java @@ -3,6 +3,7 @@ package io.ebeaninternal.server.type; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; +import io.ebean.text.TextException; import io.ebean.text.json.EJson; import io.ebeaninternal.json.ModifyAwareList; import io.ebeanservice.docstore.api.mapping.DocPropertyType; @@ -145,7 +146,7 @@ public class ScalarTypeArrayList extends ScalarTypeJsonCollection implemen try { return EJson.parseList(value, false); } catch (IOException e) { - throw new PersistenceException("Failed to parse JSON content as List: [" + value + "]", e); + throw new TextException("Failed to parse JSON [{}] as List", value, e); } } diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeArraySet.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeArraySet.java index 78c3ca3db..ea3c0bc84 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeArraySet.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeArraySet.java @@ -3,6 +3,7 @@ package io.ebeaninternal.server.type; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; +import io.ebean.text.TextException; import io.ebean.text.json.EJson; import io.ebeaninternal.json.ModifyAwareSet; import io.ebeanservice.docstore.api.mapping.DocPropertyType; @@ -144,7 +145,7 @@ public class ScalarTypeArraySet extends ScalarTypeJsonCollection> impl try { return EJson.parseSet(value, false); } catch (IOException e) { - throw new PersistenceException("Failed to parse JSON content as List: [" + value + "]", e); + throw new TextException("Failed to parse JSON [{}] as Set", value, e); } } diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeInetAddress.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeInetAddress.java index c710b7d75..1de5e1db7 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeInetAddress.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeInetAddress.java @@ -42,7 +42,7 @@ public class ScalarTypeInetAddress extends ScalarTypeBaseVarchar { try { return ConvertInetAddresses.forUriString(value); } catch (IllegalArgumentException e) { - throw new TextException("Error with InetAddresses [" + value + "] ", e); + throw new TextException("Error with InetAddresses [{}]", value, e); } } } diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonList.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonList.java index 92f12f1cf..a4df1f732 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonList.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonList.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.type; import io.ebean.config.dbplatform.DbPlatformType; +import io.ebean.text.TextException; import io.ebean.text.json.EJson; import io.ebeanservice.docstore.api.mapping.DocPropertyType; import com.fasterxml.jackson.core.JsonGenerator; @@ -75,7 +76,7 @@ public class ScalarTypeJsonList { // parse JSON into modifyAware list return EJson.parseList(json, true); } catch (IOException e) { - throw new SQLException("Failed to parse JSON content as List: [" + json + "]", e); + throw new TextException("Failed to parse JSON [{}] as List", json, e); } } @@ -109,7 +110,7 @@ public class ScalarTypeJsonList { try { return EJson.parseList(value, false); } catch (IOException e) { - throw new PersistenceException("Failed to parse JSON content as List: [" + value + "]", e); + throw new TextException("Failed to parse JSON [{}] as List", value, e); } } diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonMap.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonMap.java index 84146d218..cd84774f8 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonMap.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonMap.java @@ -175,7 +175,7 @@ public abstract class ScalarTypeJsonMap extends ScalarTypeBase { // return a modify aware map return EJson.parseObject(value, true); } catch (IOException e) { - throw new TextException(e); + throw new TextException("Failed to parse JSON [{}] as Object", value, e); } } diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonNode.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonNode.java index 4c5c42dfe..922e46dc9 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonNode.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonNode.java @@ -158,7 +158,7 @@ public abstract class ScalarTypeJsonNode extends ScalarTypeBase { try { return objectMapper.readValue(value, JsonNode.class); } catch (IOException e) { - throw new TextException(e); + throw new TextException("Failed to parse JSON [{}] as JsonNode", value, e); } } diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java index 3e6b06952..265e336cc 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java @@ -12,6 +12,7 @@ import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.SerializationConfig; import com.fasterxml.jackson.databind.introspect.AnnotatedField; import io.ebean.config.dbplatform.DbPlatformType; +import io.ebean.text.TextException; import io.ebeaninternal.json.ModifyAwareList; import io.ebeaninternal.json.ModifyAwareMap; import io.ebeaninternal.json.ModifyAwareOwner; @@ -208,7 +209,7 @@ public class ScalarTypeJsonObjectMapper { try { return objectReader.readValue(json, deserType); } catch (IOException e) { - throw new SQLException("Unable to convert JSON", e); + throw new TextException("Failed to parse JSON [{}] as " + deserType, json, e); } } @@ -258,7 +259,7 @@ public class ScalarTypeJsonObjectMapper { try { return objectReader.readValue(value, deserType); } catch (IOException e) { - throw new PersistenceException("Unable to convert JSON", e); + throw new TextException("Failed to parse JSON [{}] as " + deserType, value, e); } } diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonSet.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonSet.java index afe521fe6..129ca28bc 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonSet.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonSet.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.type; import io.ebean.config.dbplatform.DbPlatformType; +import io.ebean.text.TextException; import io.ebean.text.json.EJson; import io.ebeanservice.docstore.api.mapping.DocPropertyType; import com.fasterxml.jackson.core.JsonGenerator; @@ -78,7 +79,7 @@ public class ScalarTypeJsonSet { // parse JSON into modifyAware list return EJson.parseSet(json, true); } catch (IOException e) { - throw new SQLException("Failed to parse JSON content as List: [" + json + "]", e); + throw new TextException("Failed to parse JSON [{}] as Set", json, e); } } diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypePostgresHstore.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypePostgresHstore.java index 0f5984bdf..13d6e4c4f 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypePostgresHstore.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypePostgresHstore.java @@ -78,7 +78,7 @@ public class ScalarTypePostgresHstore extends ScalarTypeBase { try { return EJson.parseObject(value); } catch (IOException e) { - throw new TextException(e); + throw new TextException("Failed to parse JSON [{}] as Object", value, e); } } diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeURI.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeURI.java index 504f95454..3e3a67146 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeURI.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeURI.java @@ -38,7 +38,7 @@ public class ScalarTypeURI extends ScalarTypeBaseVarchar { try { return new URI(value); } catch (URISyntaxException e) { - throw new TextException("Error with URI [" + value + "] ", e); + throw new TextException("Error with URI [{}]", value, e); } } } diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeURL.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeURL.java index b9ae39870..577c20b19 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeURL.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeURL.java @@ -38,7 +38,7 @@ public class ScalarTypeURL extends ScalarTypeBaseVarchar { try { return new URL(value); } catch (MalformedURLException e) { - throw new TextException(e); + throw new TextException("Error with URL [{}]", value, e); } } diff --git a/src/test/java/org/tests/json/TestDbJson_List.java b/src/test/java/org/tests/json/TestDbJson_List.java index 04dc55f6f..00c7b1820 100644 --- a/src/test/java/org/tests/json/TestDbJson_List.java +++ b/src/test/java/org/tests/json/TestDbJson_List.java @@ -2,6 +2,10 @@ package org.tests.json; import io.ebean.BaseTestCase; import io.ebean.Ebean; +import io.ebean.annotation.ForPlatform; +import io.ebean.annotation.Platform; +import io.ebean.text.TextException; + import org.tests.model.json.EBasicJsonList; import org.tests.model.json.PlainBean; import org.ebeantest.LoggedSqlCollector; @@ -10,6 +14,7 @@ import org.junit.Test; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; @@ -165,4 +170,33 @@ public class TestDbJson_List extends BaseTestCase { String asJson = Ebean.json().toJson(found); assertNotNull(asJson); } + + @ForPlatform(Platform.H2) + @Test + public void find_corrupt_json() { + + + EBasicJsonList bean = new EBasicJsonList(); + + PlainBean plainBean = new PlainBean(); + plainBean.setName("Blubb"); + bean.getBeanMap().put("bla", plainBean ); + + Ebean.save(bean); + + Ebean.update(EBasicJsonList.class) + .set("beanMap", "blabla") + .where().eq("id", bean.getId()) + .update(); + + + bean = Ebean.find(EBasicJsonList.class, bean.getId()); + Map errors = server().getBeanState(bean).getLoadErrors(); + + assertThat(errors).containsKey("beanMap").hasSize(1); + assertThat(errors.values().iterator().next()) + .isInstanceOf(TextException.class) + .hasMessageContaining("blabla"); + //assertThat(bean.getBeanMap().get("bla").getName()).isEqualTo("Blubb"); + } } diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index cd6b5c07c..6efadcc56 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -26,6 +26,7 @@ ebean.collectQueryPlans=true ebean.autoReadOnlyDataSource=true +ebean.failOnLoadError=false #ebean.persistBatch=NONE #ebean.h2.idType=SEQUENCE