From fdb573bfe2a652943e8aea1f264d9420f9eaa298 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Sat, 26 Jul 2014 00:09:26 +1200 Subject: [PATCH] Fix for #177 - EbeanServer.refresh() not loading/refreshing @Lob property unless it is annotated with @Basic(fetch=FetchType.EAGER) --- .../server/core/DefaultBeanLoader.java | 7 ++ .../basic/lob/TestBasicClobFetchEager.java | 82 +++++++++++++++++++ .../tests/basic/lob/TestBasicClobNoVer.java | 50 ++++++++++- .../model/basic/EBasicClobFetchEager.java | 73 +++++++++++++++++ .../tests/model/basic/EBasicClobNoVer.java | 3 + 5 files changed, 212 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/avaje/tests/basic/lob/TestBasicClobFetchEager.java create mode 100644 src/test/java/com/avaje/tests/model/basic/EBasicClobFetchEager.java diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java index 863b03896..7252611a6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java @@ -415,6 +415,13 @@ public class DefaultBeanLoader { if (ebi.isReadOnly()) { query.setReadOnly(true); } + + if (SpiQuery.Mode.REFRESH_BEAN.equals(mode)) { + // explicitly state to load all properties on REFRESH. + // Lobs default to fetch lazy so this forces lobs to be + // included in a 'refresh' query + query.select("*"); + } Object dbBean = query.findUnique(); if (dbBean == null) { diff --git a/src/test/java/com/avaje/tests/basic/lob/TestBasicClobFetchEager.java b/src/test/java/com/avaje/tests/basic/lob/TestBasicClobFetchEager.java new file mode 100644 index 000000000..44f3b0715 --- /dev/null +++ b/src/test/java/com/avaje/tests/basic/lob/TestBasicClobFetchEager.java @@ -0,0 +1,82 @@ +package com.avaje.tests.basic.lob; + +import java.util.List; + +import org.avaje.ebeantest.LoggedSqlCollector; +import org.junit.Assert; +import org.junit.Test; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.Query; +import com.avaje.tests.model.basic.EBasicClobFetchEager; + +public class TestBasicClobFetchEager extends BaseTestCase { + + @Test + public void test() { + + EBasicClobFetchEager entity = new EBasicClobFetchEager(); + entity.setName("test"); + entity.setDescription("initialClobValue"); + EbeanServer server = Ebean.getServer(null); + server.save(entity); + + + String expectedSql = "select t0.id c0, t0.name c1, t0.title c2, t0.description c3, t0.last_update c4 from ebasic_clob_fetch_eager t0 where t0.id = ?"; + + // Clob included in fetch as FetchType.EAGER set by annotation + Query defaultQuery = Ebean.find(EBasicClobFetchEager.class).setId(entity.getId()); + defaultQuery.findUnique(); + String sql = defaultQuery.getGeneratedSql(); + + Assert.assertTrue("Clob is eager fetched", sql.contains(expectedSql)); + + + LoggedSqlCollector.start(); + + // Same as previous query - clob included by default based on annotation + Ebean.find(EBasicClobFetchEager.class, entity.getId()); + + // Assert query same as previous ... + List loggedSql = LoggedSqlCollector.stop(); + Assert.assertEquals(1, loggedSql.size()); + Assert.assertTrue(loggedSql.get(0).contains(expectedSql)); + + + + // Explicitly select * including Clob + Query explicitQuery = Ebean.find(EBasicClobFetchEager.class).setId(entity.getId()).select("*"); + + explicitQuery.findUnique(); + sql = explicitQuery.getGeneratedSql(); + + Assert.assertTrue("Explicitly include Clob", sql.contains(expectedSql)); + + // Update description to test refresh + + EBasicClobFetchEager updateBean = new EBasicClobFetchEager(); + updateBean.setId(entity.getId()); + updateBean.setDescription("modified"); + Ebean.update(updateBean); + + + // Test refresh function + + Assert.assertEquals("initialClobValue", entity.getDescription()); + + LoggedSqlCollector.start(); + + // Refresh query includes all properties + server.refresh(entity); + + // Assert all properties fetched in refresh + loggedSql = LoggedSqlCollector.stop(); + Assert.assertEquals(1, loggedSql.size()); + Assert.assertTrue("Refresh includes all", loggedSql.get(0).contains(expectedSql)); + Assert.assertEquals("modified", entity.getDescription()); + + } + +} diff --git a/src/test/java/com/avaje/tests/basic/lob/TestBasicClobNoVer.java b/src/test/java/com/avaje/tests/basic/lob/TestBasicClobNoVer.java index 226a9cc7d..8666bed32 100644 --- a/src/test/java/com/avaje/tests/basic/lob/TestBasicClobNoVer.java +++ b/src/test/java/com/avaje/tests/basic/lob/TestBasicClobNoVer.java @@ -1,10 +1,15 @@ package com.avaje.tests.basic.lob; +import java.util.List; + +import org.avaje.ebeantest.LoggedSqlCollector; +import org.junit.Assert; import org.junit.Test; import com.avaje.ebean.BaseTestCase; import com.avaje.ebean.Ebean; import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.Query; import com.avaje.tests.model.basic.EBasicClobNoVer; public class TestBasicClobNoVer extends BaseTestCase { @@ -14,14 +19,53 @@ public class TestBasicClobNoVer extends BaseTestCase { EBasicClobNoVer entity = new EBasicClobNoVer(); entity.setName("test"); - entity.setDescription("This is a test"); + entity.setDescription("initialClobValue"); EbeanServer server = Ebean.getServer(null); server.save(entity); - Ebean.find(EBasicClobNoVer.class).select("*").findList(); + + String sqlNoClob = "select t0.id c0, t0.name c1 from ebasic_clob_no_ver t0 where t0.id = ?"; + String sqlWithClob = "select t0.id c0, t0.name c1, t0.description c2 from ebasic_clob_no_ver t0 where t0.id = ?"; + + + // Clob by default is Fetch Lazy + Query defaultQuery = Ebean.find(EBasicClobNoVer.class).setId(entity.getId()); + defaultQuery.findUnique(); + String sql = defaultQuery.getGeneratedSql(); + Assert.assertTrue("Clob is fetch lazy by default", sql.contains(sqlNoClob)); + + + // Explicitly select * including Clob + Query explicitQuery = Ebean.find(EBasicClobNoVer.class).setId(entity.getId()).select("*"); + + explicitQuery.findUnique(); + sql = explicitQuery.getGeneratedSql(); + + Assert.assertTrue("Explicitly include Clob", sql.contains(sqlWithClob)); + + // Update description to test refresh + + EBasicClobNoVer updateBean = new EBasicClobNoVer(); + updateBean.setId(entity.getId()); + updateBean.setDescription("modified"); + Ebean.update(updateBean); + + + // Test refresh function + + Assert.assertEquals("initialClobValue", entity.getDescription()); + + LoggedSqlCollector.start(); + + // Refresh query includes all properties server.refresh(entity); - System.out.println("description=" + entity.getDescription()); + + // Assert all properties fetched in refresh + List loggedSql = LoggedSqlCollector.stop(); + Assert.assertEquals(1, loggedSql.size()); + Assert.assertTrue("Refresh includes all", loggedSql.get(0).contains(sqlWithClob)); + Assert.assertEquals("modified", entity.getDescription()); } diff --git a/src/test/java/com/avaje/tests/model/basic/EBasicClobFetchEager.java b/src/test/java/com/avaje/tests/model/basic/EBasicClobFetchEager.java new file mode 100644 index 000000000..89ec8889b --- /dev/null +++ b/src/test/java/com/avaje/tests/model/basic/EBasicClobFetchEager.java @@ -0,0 +1,73 @@ +package com.avaje.tests.model.basic; + +import java.sql.Timestamp; + +import javax.persistence.Basic; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.Lob; +import javax.persistence.Version; + +@Entity +public class EBasicClobFetchEager { + + @Id + private Long id; + + private String name; + + private String title; + + /** + * Lob properties default to FetchType.LAZY and need to be explicitly included in a fetch + * via query.select("*") or by defaulting them to FetchType.EAGER as this case. + */ + @Lob + @Basic(fetch = FetchType.EAGER) + private String description; + + @Version + private Timestamp lastUpdate; + + public void setId(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Timestamp getLastUpdate() { + return lastUpdate; + } + + public void setLastUpdate(Timestamp lastUpdate) { + this.lastUpdate = lastUpdate; + } + +} diff --git a/src/test/java/com/avaje/tests/model/basic/EBasicClobNoVer.java b/src/test/java/com/avaje/tests/model/basic/EBasicClobNoVer.java index 86eb73cef..dd704f048 100644 --- a/src/test/java/com/avaje/tests/model/basic/EBasicClobNoVer.java +++ b/src/test/java/com/avaje/tests/model/basic/EBasicClobNoVer.java @@ -12,6 +12,9 @@ public class EBasicClobNoVer { private String name; + /** + * Note that lobs default to FetchType.LAZY - see EBasicClobFetchEager. + */ @Lob private String description;