mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-23 10:03:59 +00:00
Refactor detection of deferred persist relationship (circular imported ids)
This commit is contained in:
@@ -283,10 +283,12 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
* Check for batch escalation on cascade.
|
||||
*/
|
||||
public void checkBatchEscalationOnCascade() {
|
||||
if (transaction.checkBatchEscalationOnCascade(this)) {
|
||||
// we escalated to use batch mode so flush when done
|
||||
// but if createdTransaction then commit will flush it
|
||||
batchOnCascadeSet = !createdTransaction;
|
||||
if (type != Type.INSERT || beanDescriptor.isCascadeBatchEscalateSupported()) {
|
||||
if (transaction.checkBatchEscalationOnCascade(this)) {
|
||||
// we escalated to use batch mode so flush when done
|
||||
// but if createdTransaction then commit will flush it
|
||||
batchOnCascadeSet = !createdTransaction;
|
||||
}
|
||||
}
|
||||
persistCascade = transaction.isPersistCascade();
|
||||
}
|
||||
|
||||
@@ -140,6 +140,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
private final short profileBeanId;
|
||||
|
||||
private final boolean multiValueSupported;
|
||||
private boolean cascadeBatchEscalateSupported;
|
||||
|
||||
public enum EntityType {
|
||||
ORM, EMBEDDED, VIEW, SQL, DOC
|
||||
@@ -752,6 +753,24 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasCircularImportedId() {
|
||||
for (BeanPropertyAssocOne<?> assocOne : propertiesOneImportedSave) {
|
||||
if (assocOne.hasCircularImportedId(this)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean hasCircularImportedIdTo(BeanDescriptor sourceDesc) {
|
||||
for (BeanPropertyAssocOne<?> assocOne : propertiesOneImportedSave) {
|
||||
if (assocOne.getTargetDescriptor() == sourceDesc) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void registerColumn(String dbColumn, String path) {
|
||||
String key = dbColumn.toLowerCase();
|
||||
// check for clash with imported OneToOne PK
|
||||
@@ -812,6 +831,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
void initialiseDocMapping() {
|
||||
cascadeBatchEscalateSupported = supportCascadeBatch();
|
||||
for (BeanPropertyAssocMany<?> many : propertiesMany) {
|
||||
many.initialisePostTarget();
|
||||
}
|
||||
@@ -826,6 +846,18 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
cacheHelp.deriveNotifyFlags();
|
||||
}
|
||||
|
||||
private boolean supportCascadeBatch() {
|
||||
return idType == IdType.IDENTITY || !hasCircularImportedId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return false if JDBC batch can't be implicitly escalated to.
|
||||
* This happens when we have circular import id situation (need to defer setting identity value).
|
||||
*/
|
||||
public boolean isCascadeBatchEscalateSupported() {
|
||||
return cascadeBatchEscalateSupported;
|
||||
}
|
||||
|
||||
void initInheritInfo() {
|
||||
if (inheritInfo != null) {
|
||||
// need to check every BeanDescriptor in the inheritance hierarchy
|
||||
|
||||
@@ -807,4 +807,8 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasCircularImportedId(BeanDescriptor<?> sourceDesc) {
|
||||
return targetDescriptor.hasCircularImportedIdTo(sourceDesc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -643,6 +643,7 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
// implicit transaction, no gain by batching where depth <= 0
|
||||
return false;
|
||||
}
|
||||
//
|
||||
return batchMode;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,12 @@ package org.tests.basic.one2one;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
public class TestOne2OneBookingInvoice extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
@@ -25,23 +27,23 @@ public class TestOne2OneBookingInvoice extends BaseTestCase {
|
||||
Ebean.save(b);
|
||||
|
||||
Invoice invoice = Ebean.find(Invoice.class, ai.getId());
|
||||
Assert.assertEquals(b.getId(), invoice.getBooking().getId());
|
||||
assertEquals(b.getId(), invoice.getBooking().getId());
|
||||
|
||||
Booking b1 = Ebean.find(Booking.class, b.getId());
|
||||
|
||||
Invoice ai1 = b1.getAgentInvoice();
|
||||
Assert.assertNotNull(ai1);
|
||||
assertNotNull(ai1);
|
||||
|
||||
Booking b2 = ai1.getBooking();
|
||||
Assert.assertNotNull(b2);
|
||||
Assert.assertEquals(b1.getId(), b2.getId());
|
||||
Assert.assertSame(b1, b2);
|
||||
assertNotNull(b2);
|
||||
assertEquals(b1.getId(), b2.getId());
|
||||
assertSame(b1, b2);
|
||||
|
||||
Invoice ci1 = b1.getClientInvoice();
|
||||
Booking b3 = ci1.getBooking();
|
||||
Assert.assertNotNull(b3);
|
||||
Assert.assertEquals(b1.getId(), b2.getId());
|
||||
Assert.assertSame(b1, b2);
|
||||
assertNotNull(b3);
|
||||
assertEquals(b1.getId(), b2.getId());
|
||||
assertSame(b1, b2);
|
||||
|
||||
// cleanup
|
||||
ai.setBooking(null);
|
||||
@@ -49,7 +51,5 @@ public class TestOne2OneBookingInvoice extends BaseTestCase {
|
||||
Ebean.save(ai);
|
||||
Ebean.save(ci);
|
||||
Ebean.delete(b);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.tests.iud;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.OneToMany;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static javax.persistence.CascadeType.ALL;
|
||||
|
||||
@Entity
|
||||
public class PcfCalendar extends PcfModel {
|
||||
|
||||
@OneToMany(cascade = ALL, orphanRemoval = true)
|
||||
private List<PcfEvent> events = new ArrayList<>();
|
||||
|
||||
public void addEvent(PcfEvent event) {
|
||||
events.add(event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.tests.iud;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.OneToOne;
|
||||
|
||||
import static javax.persistence.CascadeType.ALL;
|
||||
|
||||
@Entity
|
||||
public class PcfCity extends PcfModel {
|
||||
|
||||
private final String name;
|
||||
|
||||
@OneToOne(cascade = ALL, optional = false)
|
||||
private PcfPerson mayor;
|
||||
|
||||
@OneToOne(cascade = ALL, optional = false)
|
||||
private PcfPerson viceMayor;
|
||||
|
||||
public PcfCity(String name, PcfPerson mayor, PcfPerson viceMayor) {
|
||||
this.name = name;
|
||||
this.mayor = mayor;
|
||||
this.viceMayor = viceMayor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.tests.iud;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.OneToMany;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
public class PcfCountry extends PcfModel {
|
||||
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL)
|
||||
private List<PcfCity> cities = new ArrayList<>();
|
||||
|
||||
public void addCity(PcfCity city) {
|
||||
cities.add(city);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.tests.iud;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
|
||||
@Entity
|
||||
public class PcfEvent extends PcfModel {
|
||||
|
||||
final String name;
|
||||
|
||||
public PcfEvent(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.tests.iud;
|
||||
|
||||
import io.ebean.Model;
|
||||
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.Version;
|
||||
|
||||
@MappedSuperclass
|
||||
public class PcfModel extends Model {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
@Version
|
||||
long version;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.tests.iud;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.OneToMany;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static javax.persistence.CascadeType.ALL;
|
||||
|
||||
@Entity
|
||||
public class PcfPerson extends PcfModel {
|
||||
|
||||
private final String name;
|
||||
|
||||
@OneToMany(cascade = ALL, orphanRemoval = true)
|
||||
private List<PcfCalendar> calendars = new ArrayList<>();
|
||||
|
||||
public PcfPerson(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void addCalendar(PcfCalendar calendar) {
|
||||
calendars.add(calendar);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.tests.iud;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.Transaction;
|
||||
import org.avaje.moduuid.ModUUID;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestPersistCascade extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void insert() throws InterruptedException {
|
||||
|
||||
PcfCountry country = new PcfCountry();
|
||||
for (int a = 0; a < 3; a++) {
|
||||
PcfPerson mayor = createPerson();
|
||||
PcfPerson viceMayor = createPerson();
|
||||
country.addCity(new PcfCity("c_" + ModUUID.newShortId(), mayor, viceMayor));
|
||||
}
|
||||
// try (Transaction txn = DB.beginTransaction()) {
|
||||
// txn.setBatchSize(20);
|
||||
// country.save();
|
||||
// txn.commit();
|
||||
// }
|
||||
|
||||
country.save();
|
||||
|
||||
|
||||
Thread.sleep(2000);
|
||||
|
||||
country.deletePermanent();
|
||||
}
|
||||
|
||||
private static PcfPerson createPerson() {
|
||||
PcfPerson person = new PcfPerson("per_" + ModUUID.newShortId());
|
||||
for (int a = 0; a < 2; a++) {
|
||||
PcfCalendar calendar = new PcfCalendar();
|
||||
for (int b = 0; b < 10; b++) {
|
||||
calendar.addEvent(new PcfEvent("evt_" + ModUUID.newShortId()));
|
||||
}
|
||||
person.addCalendar(calendar);
|
||||
}
|
||||
return person;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -79,9 +79,9 @@
|
||||
<logger name="io.ebean.docker" level="TRACE"/>
|
||||
<!-- <logger name="io.ebean.DDL" level="DEBUG"/>-->
|
||||
|
||||
<!-- <logger name="io.ebean.SQL" level="TRACE"/>-->
|
||||
<!-- <logger name="io.ebean.TXN" level="TRACE"/>-->
|
||||
<!-- <logger name="io.ebean.SUM" level="TRACE"/> -->
|
||||
<logger name="io.ebean.SQL" level="TRACE"/>
|
||||
<logger name="io.ebean.TXN" level="TRACE"/>
|
||||
<logger name="io.ebean.SUM" level="TRACE"/>
|
||||
|
||||
<!-- <logger name="io.ebean.cache" level="TRACE"/> -->
|
||||
<!-- <logger name="io.ebean.cache.REGION" level="TRACE"/>-->
|
||||
|
||||
Reference in New Issue
Block a user