Files
ebean/ebean-test/src/test/java/org/tests/basic/TestPersistenceContext.java
T
Rob Bygrave 1f9cf1e4b7 Split DatabaseBuilder interface separating the setters n getters adding DatabaseBuilder.Settings
So DatabaseBuilder.Settings has all the getters for code looking to read
the configuration that has been set. So DatabaseBuilder now just has the
setter methods.

Use DatabaseBuilder.settings() to access the settings and read the config
that has been set.
2023-11-03 22:58:11 +13:00

332 lines
10 KiB
Java

package org.tests.basic;
import io.ebean.DB;
import io.ebean.DatabaseFactory;
import io.ebean.QueryIterator;
import io.ebean.Transaction;
import io.ebean.DatabaseBuilder;
import io.ebean.config.DatabaseConfig;
import io.ebean.xtest.BaseTestCase;
import io.ebeaninternal.api.SpiPersistenceContext;
import io.ebeaninternal.api.SpiTransaction;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.tests.model.basic.ContactNote;
import org.tests.model.basic.Customer;
import org.tests.model.basic.Order;
import org.tests.model.basic.ResetBasicData;
import jakarta.persistence.Embeddable;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import javax.validation.constraints.Size;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
public class TestPersistenceContext extends BaseTestCase {
@Test
void testReload() {
ResetBasicData.reset();
try (Transaction txn = DB.beginTransaction()) {
List<ContactNote> notes = new ArrayList<>();
DB.find(ContactNote.class).findEach(notes::add);
notes.get(0).setTitle("FooBar");
DB.find(ContactNote.class).findList();
assertThat(notes.get(0).getTitle()).isEqualTo("FooBar");
}
}
@Test
void test() {
ResetBasicData.reset();
// implicit transaction with its own persistence context
Order oBefore = DB.find(Order.class, 1);
// start a persistence context
DB.beginTransaction();
Order order;
try {
order = DB.find(Order.class, 1);
// not the same instance ...as a different persistence context
assertNotSame(order, oBefore);
// finds an existing bean in the persistence context
// ... so doesn't even execute a query
Order o2 = DB.find(Order.class, 1);
Order o3 = DB.reference(Order.class, 1);
// all the same instance
assertSame(order, o2);
assertSame(order, o3);
} finally {
DB.endTransaction();
}
// implicit transaction with its own persistence context
Order oAfter = DB.find(Order.class, 1);
assertNotSame(oAfter, oBefore);
assertNotSame(oAfter, order);
// start a persistence context
DB.beginTransaction();
try {
Order testOrder = ResetBasicData.createOrderCustAndOrder("testPC");
Integer id = testOrder.getCustomer().getId();
Integer orderId = testOrder.getId();
Customer customer = DB.find(Customer.class)
.setUseCache(false)
.setId(id)
.findOne();
assert customer != null;
System.gc();
Order order2 = DB.find(Order.class, orderId);
assert order2 != null;
Customer customer2 = order2.getCustomer();
assert customer2 != null;
assertEquals(customer.getId(), customer2.getId());
assertSame(customer, customer2);
} finally {
DB.endTransaction();
}
}
@Disabled
@Test
void findWithGcTest() {
for (int j = 0; j < 20; j++) {
for (int i = 0; i < 500; i++) {
Customer c = new Customer();
c.setName("Customer #" + i);
DB.save(c);
}
int customerCount = DB.find(Customer.class).findCount();
AtomicInteger count = new AtomicInteger(customerCount);
WeakHashMap<Customer, Integer> customers = new WeakHashMap<>();
DB.find(Customer.class).fetch("orders").findEach(customer -> {
customers.put(customer, customer.getId());
if (count.decrementAndGet() == 0) {
// Trigger garbage collection on last iteration and check if beans disappear from memory
System.gc();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
customers.size(); // expunge stale entries
System.out.println("Total instances: " + customerCount + ", instances left in memory: " + customers.size());
}
});
}
}
@Disabled // run manually
@Test
void testPcScopes_with_weakReferences() throws InterruptedException {
for (int i = 0; i < 5000; i++) {
Customer c = new Customer();
c.setName("Customer #" + i);
DB.save(c);
Order o = new Order();
o.setCustomer(c);
DB.save(o);
}
try (Transaction txn = DB.beginTransaction()) {
List<Customer> first100 = DB.find(Customer.class).where().le("id", 100).findList();
assertEquals(100, first100.size());
for (Customer c : first100) {
c.setSmallnote("one of the first 100");
}
// use lastBean to hold onto a weak reference bean
Customer[] lastBean = new Customer[1];
// findEach switches on use of weak reference in persistence context
DB.find(Customer.class).setLazyLoadBatchSize(1).findEach(customer -> {
if (customer.getId() <= 100) {
assertEquals("one of the first 100", customer.getSmallnote());
// nested finds
DB.find(Order.class).where().eq("customer", customer).findEach(20, consumer -> {
});
} else {
assertNotEquals("one of the first 100", customer.getSmallnote());
}
lastBean[0] = customer;
});
SpiPersistenceContext pc = ((SpiTransaction) txn).persistenceContext();
// the first 100 customers using strong references
assertThat(pc.toString()).contains("Customer=size:5000 (4900 weak)");
assertThat(pc.toString()).contains("Order=size:100 (100 weak)");
System.gc();
Thread.sleep(100);
pc.get(Customer.class, 1); // trigger expungeStaleEntries
// pc.get(Order.class, 1);
assertThat(pc.toString()).contains("Customer=size:101 (1 weak)");
assertThat(pc.toString()).contains("Order=size:0 (0 weak)");
first100 = DB.find(Customer.class).where().le("id", 100).findList();
for (Customer c : first100) {
assertEquals("one of the first 100", c.getSmallnote());
}
Customer lastBeanFromDb = DB.find(Customer.class).setId(lastBean[0].getId()).findOne();
assertSame(lastBeanFromDb, lastBean[0]);
// read 200
DB.find(Customer.class).where().le("id", 200).findList();
assertThat(pc.toString()).contains("Customer=size:201 (1 weak)");
lastBean[0] = null; // allow GC on this one
lastBeanFromDb = null;
System.gc();
Thread.sleep(100);
assertThat(pc.toString()).contains("Customer=size:200 (0 weak)");
}
}
@Embeddable
public static class TmId {
private final UUID id1;
private final UUID id2;
public TmId(UUID id1, UUID id2) {
this.id1 = id1;
this.id2 = id2;
}
public UUID getId1() {
return id1;
}
public UUID getId2() {
return id2;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TmId tmId = (TmId) o;
return Objects.equals(id1, tmId.id1) && Objects.equals(id2, tmId.id2);
}
@Override
public int hashCode() {
return Objects.hash(id1, id2);
}
}
@Entity
// @Cache(enableQueryCache = true, enableBeanCache = false)
public static class TestModel2 {
@EmbeddedId
private TmId id;
@Size(max = 255)
private String someData;
public String getSomeData() {
return someData;
}
public void setSomeData(String someData) {
this.someData = someData;
}
public TmId getId() {
return id;
}
public void setId(TmId id) {
this.id = id;
}
}
@Test
@Disabled
void initDb() {
DatabaseConfig config = new DatabaseConfig();
config.setName("h2-batch");
config.loadFromProperties();
config.setDdlExtra(false);
config.getDataSourceConfig().setUsername("sa");
config.getDataSourceConfig().setPassword("sa");
config.getDataSourceConfig().setUrl("jdbc:h2:file:./testsFile3;DB_CLOSE_ON_EXIT=FALSE;NON_KEYWORDS=KEY,VALUE");
config.addClass(TestModel2.class);
config.addClass(TmId.class);
DatabaseFactory.create(config);
String base = "x".repeat(240);
// 10 mio TestModel - each needs about 1/4 kbytes -> 2,5 GB in total
List<TestModel2> batch = new ArrayList<>();
for (int i = 0; i < 1_000_000; i++) {
TestModel2 m = new TestModel2();
TmId id = new TmId(UUID.randomUUID(), UUID.randomUUID());
m.setId(id);
m.setSomeData(base + i); // ensure we have not duplicates
batch.add(m);
if (i % 1000 == 0) {
DB.saveAll(batch);
batch.clear();
}
if (i % 100000 == 0) {
System.out.println(i);
}
}
DB.saveAll(batch);
}
@Test
@Disabled
void testFindEachFindList() {
DatabaseConfig config = new DatabaseConfig();
config.setName("h2-batch");
config.loadFromProperties();
config.setDdlRun(false);
config.getDataSourceConfig().setUsername("sa");
config.getDataSourceConfig().setPassword("sa");
config.getDataSourceConfig().setUrl("jdbc:h2:file:./testsFile3;DB_CLOSE_ON_EXIT=FALSE;NON_KEYWORDS=KEY,VALUE");
config.addClass(TestModel2.class);
config.addClass(TmId.class);
DatabaseFactory.create(config);
AtomicInteger i = new AtomicInteger();
System.out.println("Doing findEach");
DB.find(TestModel2.class).select("*").findEach(c -> {
i.incrementAndGet();
});
System.out.println("Read " + i + " entries");
i.set(0);
System.out.println("Doing findStream");
DB.find(TestModel2.class).select("*").findStream().forEach(c -> i.incrementAndGet());
System.out.println("Read " + i + " entries");
i.set(0);
System.out.println("Doing findIterate");
QueryIterator<TestModel2> iter = DB.find(TestModel2.class).select("*").findIterate();
while (iter.hasNext()) {
iter.next();
i.incrementAndGet();
}
System.out.println("Read " + i + " entries");
System.out.println("Doing FindList, will hold all entries in memory. Expect OOM with -Xmx100m.");
List<TestModel2> lst = DB.find(TestModel2.class).select("*").findList();
System.out.println("Read " + lst.size() + " entries");
}
}