Merge remote-tracking branch 'ebean/master' into pr/bugfix/connection_closed_on_clob

This commit is contained in:
Jonas Pöhler (JPo)
2021-04-26 14:54:32 +02:00
128 changed files with 2558 additions and 454 deletions
@@ -1,6 +1,5 @@
package io.ebean;
import io.ebean.FetchConfig;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -10,7 +9,7 @@ public class FetchConfigTest {
@Test
public void testLazy() {
FetchConfig config = new FetchConfig().lazy();
assertThat(config.getBatchSize()).isEqualTo(10);
assertThat(config.getBatchSize()).isEqualTo(0);
}
@Test
@@ -72,10 +72,16 @@ public class ServerConfigTest {
props.setProperty("enabledL2Regions", "r0,users,orgs");
props.setProperty("caseSensitiveCollation", "false");
props.setProperty("loadModuleInfo", "true");
props.setProperty("collectQueryPlanThresholdMicros", "10000");
props.setProperty("forUpdateNoKey", "true");
props.setProperty("defaultServer", "false");
props.setProperty("queryPlan.enable", "true");
props.setProperty("queryPlan.thresholdMicros", "10000");
props.setProperty("queryPlan.capture", "true");
props.setProperty("queryPlan.capturePeriodSecs", "42");
props.setProperty("queryPlan.captureMaxTimeMillis", "560");
props.setProperty("queryPlan.captureMaxCount", "7");
serverConfig.loadFromProperties(props);
assertFalse(serverConfig.isDefaultServer());
@@ -97,7 +103,6 @@ public class ServerConfigTest {
assertEquals(PlatformConfig.DbUuid.BINARY, serverConfig.getPlatformConfig().getDbUuid());
assertEquals(JsonConfig.DateTime.MILLIS, serverConfig.getJsonDateTime());
assertEquals(JsonConfig.Date.MILLIS, serverConfig.getJsonDate());
assertEquals(10000, serverConfig.getCollectQueryPlanThresholdMicros());
assertEquals("r0,users,orgs", serverConfig.getEnabledL2Regions());
@@ -106,6 +111,13 @@ public class ServerConfigTest {
assertEquals(4, serverConfig.getBackgroundExecutorSchedulePoolSize());
assertEquals(98, serverConfig.getBackgroundExecutorShutdownSecs());
assertTrue(serverConfig.isQueryPlanEnable());
assertEquals(10000, serverConfig.getQueryPlanThresholdMicros());
assertTrue(serverConfig.isQueryPlanCapture());
assertEquals(42, serverConfig.getQueryPlanCapturePeriodSecs());
assertEquals(560, serverConfig.getQueryPlanCaptureMaxTimeMillis());
assertEquals(7, serverConfig.getQueryPlanCaptureMaxCount());
assertThat(serverConfig.getMappingLocations()).containsExactly("classpath:/foo","bar");
serverConfig.setPersistBatch(PersistBatch.NONE);
@@ -137,6 +149,7 @@ public class ServerConfigTest {
ServerConfig serverConfig = new ServerConfig();
assertTrue(serverConfig.isIdGeneratorAutomatic());
assertTrue(serverConfig.isDefaultServer());
assertFalse(serverConfig.isAutoPersistUpdates());
serverConfig.setIdGeneratorAutomatic(false);
assertFalse(serverConfig.isIdGeneratorAutomatic());
@@ -144,10 +157,18 @@ public class ServerConfigTest {
assertEquals(JsonConfig.Date.ISO8601, serverConfig.getJsonDate());
assertTrue(serverConfig.getPlatformConfig().isCaseSensitiveCollation());
assertTrue(serverConfig.isAutoLoadModuleInfo());
assertEquals(Long.MAX_VALUE, serverConfig.getCollectQueryPlanThresholdMicros());
assertFalse(serverConfig.isQueryPlanEnable());
assertEquals(Long.MAX_VALUE, serverConfig.getQueryPlanThresholdMicros());
assertFalse(serverConfig.isQueryPlanCapture());
assertEquals(600, serverConfig.getQueryPlanCapturePeriodSecs());
assertEquals(10000L, serverConfig.getQueryPlanCaptureMaxTimeMillis());
assertEquals(10, serverConfig.getQueryPlanCaptureMaxCount());
serverConfig.setLoadModuleInfo(false);
assertFalse(serverConfig.isAutoLoadModuleInfo());
serverConfig.setAutoPersistUpdates(true);
assertTrue(serverConfig.isAutoPersistUpdates());
}
@Test
@@ -11,10 +11,10 @@ public class DtoQueryRequestTest {
public void testParse() {
assertEquals("foo", DtoQueryRequest.parseColumn("foo"));
assertEquals("bar", DtoQueryRequest.parseColumn("_e_t0_bar"));
assertEquals("BAR", DtoQueryRequest.parseColumn("_E_T0_BAR"));
assertEquals("baz", DtoQueryRequest.parseColumn("_e_t42_baz"));
assertEquals("BAZ", DtoQueryRequest.parseColumn("_E_T42_BAZ"));
assertEquals("bar", DtoQueryRequest.parseColumn("zx__t0_bar"));
assertEquals("BAR", DtoQueryRequest.parseColumn("ZX__T0_BAR"));
assertEquals("baz", DtoQueryRequest.parseColumn("zx__t42_baz"));
assertEquals("BAZ", DtoQueryRequest.parseColumn("ZX__T42_BAZ"));
assertEquals("e_t42_nope", DtoQueryRequest.parseColumn("e_t42_nope"));
assertEquals("_f_t42_nope", DtoQueryRequest.parseColumn("_f_t42_nope"));
@@ -4,21 +4,22 @@ import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.bean.EntityBean;
import io.ebean.plugin.Property;
import io.ebeaninternal.server.core.CacheOptions;
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import io.ebeaninternal.server.deploy.meta.DeployIdentityMode;
import io.ebeanservice.docstore.api.DocStoreBeanAdapter;
import org.junit.Test;
import org.tests.model.basic.Animal;
import org.tests.model.basic.AnimalShelter;
import org.tests.model.basic.Cat;
import org.tests.model.basic.Contact;
import org.tests.model.basic.Country;
import org.tests.model.basic.Customer;
import org.tests.model.basic.Dog;
import org.tests.model.basic.Order;
import org.tests.model.basic.*;
import org.tests.model.bridge.BSite;
import org.tests.model.bridge.BUser;
import java.util.Collection;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class BeanDescriptorTest extends BaseTestCase {
@@ -88,6 +89,42 @@ public class BeanDescriptorTest extends BaseTestCase {
assertThat(props).extracting("name").contains("id", "status", "orderDate", "shipDate");
}
@Test
public void matchBaseTable() {
BeanDescriptor<Customer> desc = getBeanDescriptor(Customer.class);
assertTrue(desc.matchBaseTable("o_customer"));
}
@Test
public void matchBaseTable_whenTableHasSchema_expect_matchRegardlessOfSchema() {
DeployBeanDescriptor<Customer> deploy = mockDeployCustomer();
when(deploy.getBaseTable()).thenReturn("foo.o_customer");
BeanDescriptor<?> desc1 = new BeanDescriptor<>(mockOwner(), deploy);
assertTrue(desc1.matchBaseTable("o_customer"));
when(deploy.getBaseTable()).thenReturn("bar.o_customer");
BeanDescriptor<?> desc2 = new BeanDescriptor<>(mockOwner(), deploy);
assertTrue(desc2.matchBaseTable("o_customer"));
}
@SuppressWarnings("unchecked")
private DeployBeanDescriptor<Customer> mockDeployCustomer() {
DeployBeanDescriptor<Customer> deploy = mock(DeployBeanDescriptor.class);
when(deploy.getBeanType()).thenReturn(Customer.class);
when(deploy.getIdentityMode()).thenReturn(DeployIdentityMode.auto());
when(deploy.buildIdentityMode()).thenReturn(IdentityMode.NONE);
when(deploy.getCacheOptions()).thenReturn(CacheOptions.NO_CACHING);
return deploy;
}
private BeanDescriptorMap mockOwner() {
BeanDescriptorMap owner = mock(BeanDescriptorMap.class);
when(owner.createDocStoreBeanAdapter(any(), any())).thenReturn(mock(DocStoreBeanAdapter.class));
return owner;
}
@Test
public void merge_when_empty() {
@@ -19,7 +19,7 @@ public class ParseFetchConfigTest {
@Test
public void parseLazy() {
FetchConfig lazy = ParseFetchConfig.parse("lazy");
assertThat(lazy.getBatchSize()).isEqualTo(10);
assertThat(lazy.getBatchSize()).isEqualTo(0);
}
@Test
@@ -46,7 +46,12 @@ public class MainMemoryLeak {
config.addClass(ECachedBean.class);
config.loadFromProperties();
Database server1 = DatabaseFactory.create(config);
assert server1 != null;
// create again for #2193
Database server = DatabaseFactory.create(config);
assert server != null;
// create a string with 10k chars
char[] c = new char[10_000];
@@ -25,6 +25,10 @@ public class PersonCacheEmail {
this.email = email;
}
public PersonCacheEmail(String id) {
this.id = id;
}
public String getId() {
return id;
}
@@ -0,0 +1,73 @@
package org.tests.cache.personinfo;
import io.ebean.annotation.WhenCreated;
import io.ebean.annotation.WhenModified;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Version;
import javax.validation.constraints.Size;
import java.time.Instant;
@Entity
public class PersonOther {
@Id
@Size(max=128)
private String id;
private String email;
@WhenCreated
private Instant whenCreated;
@WhenModified
private Instant whenModified;
@Version
private long version;
public PersonOther(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Instant getWhenCreated() {
return whenCreated;
}
public void setWhenCreated(Instant whenCreated) {
this.whenCreated = whenCreated;
}
public Instant getWhenModified() {
return whenModified;
}
public void setWhenModified(Instant whenModified) {
this.whenModified = whenModified;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
@@ -0,0 +1,30 @@
package org.tests.cache.personinfo;
import io.ebean.BaseTestCase;
import io.ebean.DB;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class TestStringIdOnly extends BaseTestCase {
@Test
public void insert() {
PersonCacheEmail b0 = new PersonCacheEmail("IdOnly");
DB.save(b0);
PersonCacheEmail found = DB.find(PersonCacheEmail.class, b0.getId());
assertThat(found).isNotNull();
}
@Test
public void insert_whenIdOnly() {
PersonOther b0 = new PersonOther("IdOnly");
DB.save(b0);
PersonOther found = DB.find(PersonOther.class, b0.getId());
assertThat(found).isNotNull();
}
}
@@ -1,12 +1,16 @@
package org.tests.model.embedded;
import io.ebean.annotation.NotNull;
import javax.persistence.Embeddable;
import java.util.Date;
@Embeddable
public class EEmbDatePeriod {
@NotNull
Date date1;
@NotNull
Date date2;
public Date getDate1() {
@@ -169,22 +169,40 @@ public class TestQueryFindEach extends BaseTestCase {
DB.find(OmBasicParent.class).delete();
insertData();
test_setLazyLoadBatchSize_withFetchLazy();
LoggedSqlCollector.start();
try (final Transaction transaction = DB.beginTransaction()) {
// DB.find(OmBasicParent.class).findList();
DB.find(OmBasicChild.class)
.setLazyLoadBatchSize(100)
//.fetchQuery("parent","name")
//.fetch("parent","name")
.findEach(child -> {
assertNotNull(child.getParent().getName());
});
}
DB.find(OmBasicChild.class)
.setLazyLoadBatchSize(100)
.findEach(child -> {
assertNotNull(child.getParent().getName());
});
final List<String> sql = LoggedSqlCollector.stop();
assertThat(sql.size()).isLessThan(50);
}
private void test_setLazyLoadBatchSize_withFetchLazy() {
LoggedSqlCollector.start();
DB.find(OmBasicParent.class)
.setLazyLoadBatchSize(5)
.fetchLazy("children")
.setMaxRows(50)
.findEach(it -> {
it.getChildren().size();
});
final List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(11);
assertThat(sql.get(0)).contains(" from om_basic_parent ");
for (int i = 1; i < 11; i++) {
assertThat(sql.get(i)).contains(" --bind(Array[5]");
}
}
@Transactional(batchSize = 40)
private void insertData() {
for (int i = 0; i < 150; i++) {
@@ -31,7 +31,7 @@ public class TestCustomerFinder extends BaseTestCase {
runQueries();
StringBuilder buffer0 = new StringBuilder();
server().getMetaInfoManager()
DB.getDefault().getMetaInfoManager()
.collectMetricsAsJson()
.withHeader(false)
.write(buffer0);
@@ -45,7 +45,7 @@ public class TestCustomerFinder extends BaseTestCase {
runQueries();
StringBuilder buffer1 = new StringBuilder();
server().getMetaInfoManager()
DB.getDefault().getMetaInfoManager()
.collectMetricsAsJson()
.withHeader(false)
.write(buffer1);
@@ -55,7 +55,6 @@ public class TestCustomerFinder extends BaseTestCase {
assertThat(json1).contains("\"name\":\"txn.main\"");
assertThat(json1).contains("\"name\":\"orm.Customer.findList\"");
assertThat(json1).doesNotContain("\"sql\":\"select t0.id, t0.status, t0.name");
}
@Test
@@ -85,7 +84,6 @@ public class TestCustomerFinder extends BaseTestCase {
assertThat(customer.getName()).isEqualTo(customer1.getName());
assertThat(Customer.find.db().getName()).isEqualTo(DB.getDefault().getName());
}
@Test
@@ -175,20 +173,27 @@ public class TestCustomerFinder extends BaseTestCase {
ResetBasicData.reset();
// change default collect query plan threshold to 200 micros
QueryPlanInit init0 = new QueryPlanInit();
init0.setAll(true);
init0.setThresholdMicros(2);
final List<MetaQueryPlan> plans = server().getMetaInfoManager().queryPlanInit(init0);
assertThat(plans.size()).isGreaterThan(1);
// the server has some plans
runQueries();
// enable query plan bind capture on all plans threshold 100 micros
// change query plan threshold to 100 micros
QueryPlanInit init = new QueryPlanInit();
init.setAll(true);
init.setThresholdMicros(100);
init.setThresholdMicros(1);
final List<MetaQueryPlan> appliedToPlans = server().getMetaInfoManager().queryPlanInit(init);
assertThat(appliedToPlans.size()).isGreaterThan(4);
// will collect bind captures
// run queries again
runQueries();
ServerMetrics metrics = server().getMetaInfoManager().collectMetrics();
ServerMetrics metrics = DB.getDefault().getMetaInfoManager().collectMetrics();
List<MetaQueryMetric> planStats = metrics.getQueryMetrics();
assertThat(planStats.size()).isGreaterThan(4);
@@ -203,12 +208,21 @@ public class TestCustomerFinder extends BaseTestCase {
// obtains db query plans ...
QueryPlanRequest request = new QueryPlanRequest();
List<MetaQueryPlan> plans = server().getMetaInfoManager().queryPlanCollectNow(request);
assertThat(plans).isNotEmpty();
// collect max 1000 plans (use something more like 10)
request.setMaxCount(1_000);
// don't collect any more plans if used 10 secs
request.setMaxTimeMillis(10_000);
List<MetaQueryPlan> plans0 = server().getMetaInfoManager().queryPlanCollectNow(request);
assertThat(plans0).isNotEmpty();
for (MetaQueryPlan plan : plans) {
logger.info("queryplan label:{}, queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}",
plan.getLabel(), plan.getQueryTimeMicros(), plan.getProfileLocation(),
plan.getSql(), plan.getBind(), plan.getPlan());
System.out.println(plan);
}
//DB.getBackgroundExecutor().scheduleWithFixedDelay(...)
}
@Test
@@ -1,38 +1,42 @@
package org.tests.rawsql;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.Query;
import io.ebean.DB;
import io.ebean.RawSql;
import io.ebean.RawSqlBuilder;
import org.junit.Test;
import org.tests.model.basic.Customer;
import org.tests.model.basic.ResetBasicData;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.assertNotNull;
import static org.assertj.core.api.Assertions.assertThat;
public class TestRawSqlPositionedParams extends BaseTestCase {
private static final RawSql RAWSQL_1 = RawSqlBuilder
.parse("select r.id, r.name from o_customer r where r.id >= ? and r.name like ?")
.create();
private static final RawSql RAW_SQL_2 = RawSqlBuilder
.unparsed("select r.id, r.name from o_customer r where r.id >= ? and r.name like ?")
.columnMapping("r.id", "id")
.columnMapping("r.name", "name")
.create();
@Test
public void test() {
ResetBasicData.reset();
RawSql rawSql = RawSqlBuilder
.parse("select r.id, r.name from o_customer r where r.id >= ? and r.name like ?")
.create();
List<Customer> list = DB.find(Customer.class)
.setRawSql(RAWSQL_1)
.setParameter(1)
.setParameter("R%")
.where().lt("id", 2001)
.findList();
Query<Customer> query = Ebean.find(Customer.class);
query.setRawSql(rawSql);
query.setParameter(1, 1);
query.setParameter(2, "R%");
query.where().lt("id", 2001);
List<Customer> list = query.findList();
assertNotNull(list);
assertThat(list).isNotNull();
}
@Test
@@ -40,18 +44,12 @@ public class TestRawSqlPositionedParams extends BaseTestCase {
ResetBasicData.reset();
RawSql rawSql = RawSqlBuilder
.unparsed("select r.id, r.name from o_customer r where r.id >= ? and r.name like ?")
.columnMapping("r.id", "id")
.columnMapping("r.name", "name").create();
List<Customer> list = DB.find(Customer.class)
.setRawSql(RAW_SQL_2)
.setParameter(1)
.setParameter("R%")
.findList();
Query<Customer> query = Ebean.find(Customer.class);
query.setRawSql(rawSql);
query.setParameter(1, 1);
query.setParameter(2, "R%");
List<Customer> list = query.findList();
assertNotNull(list);
assertThat(list).isNotNull();
}
}
@@ -1,19 +1,25 @@
package org.tests.rawsql;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.Query;
import io.ebean.DB;
import io.ebean.RawSql;
import io.ebean.RawSqlBuilder;
import org.junit.Test;
import org.tests.model.basic.Customer;
import org.tests.model.basic.ResetBasicData;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class TestRawSqlUnparsedQuery extends BaseTestCase {
private static final RawSql rawSql = RawSqlBuilder
.unparsed("select r.id, r.name from o_customer r where r.id >= :a and r.name like :b")
.columnMapping("r.id", "id")
.columnMapping("r.name", "name")
.create();
@Test
public void testDoubleUnparsedQuery() {
@@ -25,17 +31,14 @@ public class TestRawSqlUnparsedQuery extends BaseTestCase {
}
private static void test() {
RawSql rawSql = RawSqlBuilder
.unparsed("select r.id, r.name from o_customer r where r.id >= :a and r.name like :b")
.columnMapping("r.id", "id").columnMapping("r.name", "name").create();
Query<Customer> query = Ebean.find(Customer.class);
query.setRawSql(rawSql);
query.setParameter("a", 1);
query.setParameter("b", "R%");
List<Customer> list = DB.find(Customer.class)
.setRawSql(rawSql)
.setParameter("a", 1)
.setParameter("b", "R%")
.findList();
List<Customer> list = query.findList();
Assert.assertNotNull(list);
assertThat(list).isNotNull();
}
}
@@ -0,0 +1,298 @@
package org.tests.transparentpersist;
import io.ebean.*;
import io.ebean.annotation.*;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeantest.LoggedSql;
import org.junit.Test;
import org.tests.model.basic.Customer;
import org.tests.model.basic.EBasicVer;
import org.tests.model.basic.Order;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class TestTransparentPersist extends BaseTestCase {
@Test
public void insertFlush_mutateFlush_expect_update() {
LoggedSql.start();
EBasicVer newBean;
try (Transaction transaction = DB.beginTransaction()) {
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
newBean = new EBasicVer("insertMe");
DB.save(newBean);
// flush - new bean needs to get registered into persistence context
transaction.flush();
// make it dirty, we expect it to execute an update on flush()
newBean.setName("make it dirty - auto save");
// flush again, auto persist dirty bean in persistence context
transaction.commit();
}
List<String> sql = LoggedSql.stop();
EBasicVer found = DB.find(EBasicVer.class, newBean.getId());
assertThat(found.getName()).isEqualTo("make it dirty - auto save");
assertThat(sql).hasSize(4);
assertThat(sql.get(0)).contains("insert into e_basicver");
assertThat(sql.get(1)).contains(" -- bind(");
assertThat(sql.get(2)).contains("update e_basicver set name=?, last_update=? where id=? and last_update=?");
assertThat(sql.get(3)).contains(" -- bind(");
DB.delete(found);
}
@Test
public void delete_expect_beanRemovedFromPersistenceContext() {
EBasicVer b0 = new EBasicVer("simpleDelete");
DB.save(b0);
try (Transaction transaction = DB.beginTransaction()) {
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
EBasicVer found = DB.find(EBasicVer.class, b0.getId());
// make it dirty
found.setName("make it dirty");
// delete it, should remove it from the "live" part of persistence context
// with the expectation that no update is executed (no dirty in PC update)
DB.delete(found);
transaction.commit();
}
EBasicVer after = DB.find(EBasicVer.class, b0.getId());
assertThat(after).isNull();
}
@Test
public void simpleInsertUpdateDelete_experimental() {
EBasicVer b0 = new EBasicVer("simpleIUD_0");
b0.save();
EBasicVer b1 = new EBasicVer("simpleIUD_1");
b1.save();
EBasicVer b2 = new EBasicVer("simpleIUD_2");
b2.save();
EBasicVer newBean;
try (Transaction transaction = DB.beginTransaction()) {
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
EBasicVer found = DB.find(EBasicVer.class, b0.getId());
found.setName("auto dirty");
// delete by id
DB.delete(EBasicVer.class, b1.getId());
// find and delete, note the delete is batched up to execute later
DB.delete(DB.find(EBasicVer.class, b2.getId()));
// insert is batched up to execute later
newBean = new EBasicVer("simpleIUD_New1");
DB.save(newBean);
// can still mutate newBean before flush (but not after flush yet as new bean isn't put into Persistence context)
newBean.setName("simpleIUD_New2");
transaction.commit();
}
EBasicVer after = DB.find(EBasicVer.class, b0.getId());
assertThat(after.getName()).isEqualTo("auto dirty");
EBasicVer wasInserted = DB.find(EBasicVer.class, newBean.getId());
assertThat(wasInserted.getName()).isEqualTo("simpleIUD_New2");
assertThat(DB.find(EBasicVer.class, b1.getId())).isNull();
assertThat(DB.find(EBasicVer.class, b2.getId())).isNull();
DB.delete(after);
DB.delete(wasInserted);
}
@Test
public void simpleUpdate_experimental() {
EBasicVer transPersist = new EBasicVer("simulate_simpleUpdate");
transPersist.save();
try (Transaction transaction = DB.beginTransaction()) {
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
EBasicVer found = DB.find(EBasicVer.class, transPersist.getId());
found.setName("Persisted automatically as dirty");
transaction.commit();
}
EBasicVer after = DB.find(EBasicVer.class,transPersist.getId());
assertThat(after.getName()).isEqualTo("Persisted automatically as dirty");
DB.delete(after);
}
@Test
public void updateWithPersistCascadeInsert() {
// setup data
Customer c0 = new Customer();
c0.setName("firstCust");
Order order = new Order();
order.setStatus(Order.Status.NEW);
order.setCustomer(c0);
DB.save(order);
try (Transaction transaction = DB.beginTransaction()) {
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
Order foundOrder = DB.find(Order.class, order.getId());
foundOrder.setStatus(Order.Status.APPROVED);
// cascade persist will insert this customer (even though it isn't in the persistence context)
Customer c1 = new Customer();
c1.setName("newCust CascadePersist");
foundOrder.setCustomer(c1);
transaction.commit();
}
Order checkOrder = DB.find(Order.class, order.getId());
assertThat(checkOrder.getStatus()).isEqualTo(Order.Status.APPROVED);
assertThat(checkOrder.getCustomer().getName()).isEqualTo("newCust CascadePersist");
DB.delete(checkOrder);
DB.delete(Customer.class, checkOrder.getCustomer().getId());
DB.delete(Customer.class, c0.getId());
}
@Test
public void updateReferenceOnlyWithPersistCascade_Insert_andUpdateForeignKey() {
// setup data
Customer c0 = new Customer();
c0.setName("firstCust");
Order order = new Order();
order.setStatus(Order.Status.NEW);
order.setCustomer(c0);
DB.save(order);
LoggedSql.start();
try (Transaction transaction = DB.beginTransaction()) {
transaction.setAutoPersistUpdates(true); // EXPERIMENTAL feature
Order foundOrder = DB.find(Order.class, order.getId());
// we ONLY mutate the foreign key
// cascade persist will insert this customer (even though it isn't in the persistence context)
Customer c1 = new Customer();
c1.setName("newCust CascadePersist");
foundOrder.setCustomer(c1);
transaction.commit();
}
List<String> sql = LoggedSql.stop();
Order checkOrder = DB.find(Order.class, order.getId());
assertThat(checkOrder.getStatus()).isEqualTo(Order.Status.NEW);
assertThat(checkOrder.getCustomer().getName()).isEqualTo("newCust CascadePersist");
assertThat(sql).hasSize(5);
assertThat(sql.get(0)).contains("select t0.id, t0.status, t0.order_date");
assertThat(sql.get(1)).contains("insert into o_customer");
assertThat(sql.get(2)).contains(" -- bind(");
assertThat(sql.get(3)).contains("update o_order set updtime=?, kcustomer_id=? where id=? and updtime=?");
assertThat(sql.get(4)).contains(" -- bind(");
DB.delete(checkOrder);
DB.delete(Customer.class, checkOrder.getCustomer().getId());
DB.delete(Customer.class, c0.getId());
}
@Test
public void txScope_setAutoPersistUpdates() {
EBasicVer b0 = new EBasicVer("txScopeUpdate");
b0.save();
DB.execute(TxScope.required().setAutoPersistUpdates(TxOption.ON), () -> {
EBasicVer found = DB.find(EBasicVer.class, b0.getId());
found.setName("mutable txScope bean");
});
EBasicVer after = DB.find(EBasicVer.class, b0.getId());
assertThat(after.getName()).isEqualTo("mutable txScope bean");
DB.delete(after);
}
@Test
public void txn_setAutoPersistUpdates() {
EBasicVer b0 = new EBasicVer("txn autoPersistUpdates ON");
b0.save();
performUpdate(b0);
EBasicVer after = DB.find(EBasicVer.class, b0.getId());
assertThat(after.getName()).isEqualTo("mutated with autoPersistUpdates ON");
DB.delete(after);
}
@Transactional(autoPersistUpdates = TxOption.ON)
private void performUpdate(EBasicVer b0) {
EBasicVer found = DB.find(EBasicVer.class, b0.getId());
found.setName("mutated with autoPersistUpdates ON");
}
@Test
public void simulate_transparentPersistence_forSimpleUpdate() {
EBasicVer transPersist = new EBasicVer("simulate_simpleUpdate");
transPersist.save();
try (Transaction transaction = DB.beginTransaction()) {
transaction.setBatchMode(true);
transaction.setBatchSize(10);
EBasicVer found = DB.find(EBasicVer.class, transPersist.getId());
found.setName("Changed");
// simulate transparent persistence
List<Object> dirtyBeans = simulateTransparentPersist(transaction);
assertThat(dirtyBeans).hasSize(1);
assertThat(dirtyBeans).contains(found);
// would occur as first part of flush
transaction.flush();
transaction.commit();
}
EBasicVer after = DB.find(EBasicVer.class,transPersist.getId());
assertThat(after.getName()).isEqualTo("Changed");
DB.delete(after);
}
private List<Object> simulateTransparentPersist(Transaction transaction) {
Database db = DB.getDefault();
List<Object> dirtyBeans = getDirtyBeansFromPersistenceContext(transaction);
for (Object dirtyBean : dirtyBeans) {
db.update(dirtyBean, transaction);
}
return dirtyBeans;
}
private List<Object> getDirtyBeansFromPersistenceContext(Transaction transaction) {
return ((SpiTransaction)transaction).getPersistenceContext().dirtyBeans();
}
}