diff --git a/ebean-api/src/main/java/io/ebean/bean/BeanCollection.java b/ebean-api/src/main/java/io/ebean/bean/BeanCollection.java
index 6271d4051..96a37bb4a 100644
--- a/ebean-api/src/main/java/io/ebean/bean/BeanCollection.java
+++ b/ebean-api/src/main/java/io/ebean/bean/BeanCollection.java
@@ -18,7 +18,7 @@ import java.util.Set;
* java.util.Collection. The reason being that java.util.Map is not a
* Collection. I realise this makes this name confusing so I apologise for that.
*/
-public interface BeanCollection extends Serializable {
+public interface BeanCollection extends Serializable, ToStringAware {
enum ModifyListenMode {
/**
diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBean.java b/ebean-api/src/main/java/io/ebean/bean/EntityBean.java
index ee54991ad..e12621829 100644
--- a/ebean-api/src/main/java/io/ebean/bean/EntityBean.java
+++ b/ebean-api/src/main/java/io/ebean/bean/EntityBean.java
@@ -11,7 +11,7 @@ import java.io.Serializable;
* general application consumption.
*
*/
-public interface EntityBean extends Serializable {
+public interface EntityBean extends Serializable, ToStringAware {
/**
* Return all the property names in defined order.
@@ -109,4 +109,8 @@ public interface EntityBean extends Serializable {
throw new NotEnhancedException();
}
+ @Override
+ default void toString(ToStringBuilder builder) {
+ throw new NotEnhancedException();
+ }
}
diff --git a/ebean-api/src/main/java/io/ebean/bean/ToStringAware.java b/ebean-api/src/main/java/io/ebean/bean/ToStringAware.java
new file mode 100644
index 000000000..bb86d9629
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/bean/ToStringAware.java
@@ -0,0 +1,12 @@
+package io.ebean.bean;
+
+/**
+ * A type that can participate in building toString content with ToStringBuilder.
+ */
+public interface ToStringAware {
+
+ /**
+ * Append to the ToStringBuilder.
+ */
+ void toString(ToStringBuilder builder);
+}
diff --git a/ebean-api/src/main/java/io/ebean/bean/ToStringBuilder.java b/ebean-api/src/main/java/io/ebean/bean/ToStringBuilder.java
new file mode 100644
index 000000000..30894b9f6
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/bean/ToStringBuilder.java
@@ -0,0 +1,153 @@
+package io.ebean.bean;
+
+import java.util.Collection;
+import java.util.IdentityHashMap;
+
+/**
+ * Helps build toString content taking into account recursion.
+ *
+ * That is, it detects and handles the case where there are relationships that recurse
+ * and would otherwise become an infinite loop (e.g. bidirectional parent child).
+ */
+public final class ToStringBuilder {
+
+ /**
+ * The max number of objects that we allow before stopping content being appended.
+ */
+ private static final int MAX = 100;
+
+ /**
+ * Max length of content in string form added for any given value.
+ */
+ private static final int TRIM_LENGTH = 500;
+
+ /**
+ * The max total content after which we stop content being appended.
+ */
+ private static final int MAX_TOTAL_CONTENT = 2000;
+
+ private final IdentityHashMap