#1221 - ENH: Add a merge(bean) and merge(bean, paths)

This commit is contained in:
Rob Bygrave
2018-03-13 23:55:47 +13:00
parent 607cf3156c
commit afead2da41
37 changed files with 1804 additions and 66 deletions
@@ -11,6 +11,7 @@ import io.ebean.Filter;
import io.ebean.FutureIds;
import io.ebean.FutureList;
import io.ebean.FutureRowCount;
import io.ebean.MergeOptions;
import io.ebean.PagedList;
import io.ebean.PersistenceContextScope;
import io.ebean.Query;
@@ -236,6 +237,16 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
return 0;
}
@Override
public void merge(Object bean, MergeOptions options) {
}
@Override
public void merge(Object bean, MergeOptions options, Transaction transaction) {
}
@Override
public <T> List<Version<T>> findVersions(Query<T> query, Transaction transaction) {
return null;
@@ -551,6 +562,11 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
}
@Override
public boolean exists(Class<?> beanType, Object beanId, Transaction transaction) {
return false;
}
@Override
public <T> T find(Class<T> beanType, Object uid) {
return null;
@@ -6,8 +6,8 @@ import io.ebeaninternal.server.core.PersistRequest;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanManager;
import org.tests.model.basic.Customer;
import org.junit.Test;
import org.tests.model.basic.Customer;
import java.sql.Timestamp;
@@ -17,8 +17,7 @@ public class BatchedBeanHolderTest extends BaseTestCase {
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void testAppend() throws Exception {
public void testAppend() {
TDSpiEbeanServer server = new TDSpiEbeanServer("foo");
@@ -32,13 +31,13 @@ public class BatchedBeanHolderTest extends BaseTestCase {
Customer customer = new Customer();
customer.setUpdtime(new Timestamp(System.currentTimeMillis()));
PersistRequestBean req1 = new PersistRequestBean(server, customer, null, beanManager, null, null, PersistRequest.Type.INSERT, false, false);
PersistRequestBean req1 = new PersistRequestBean(server, customer, null, beanManager, null, null, PersistRequest.Type.INSERT, 0);
int size = holder.append(req1);
assertEquals(1, size);
PersistRequestBean req2 = new PersistRequestBean(server, customer, null, beanManager, null, null, PersistRequest.Type.INSERT, false, false);
PersistRequestBean req2 = new PersistRequestBean(server, customer, null, beanManager, null, null, PersistRequest.Type.INSERT, 0);
size = holder.append(req2);
assertEquals(0, size);
@@ -0,0 +1,44 @@
package io.ebeaninternal.server.persist;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class FlagsTest {
@Test
public void test() {
int state = 0;
state = Flags.setPublish(state);
assertThat(Flags.isSet(state, Flags.PUBLISH)).isTrue();
state = Flags.setMerge(state);
state = Flags.setInsert(state);
assertThat(Flags.isSet(state, Flags.PUBLISH)).isTrue();
assertThat(Flags.isSet(state, Flags.MERGE)).isTrue();
assertThat(Flags.isSet(state, Flags.INSERT)).isTrue();
state = Flags.unsetPublish(state);
assertThat(Flags.isSet(state, Flags.PUBLISH)).isFalse();
assertThat(Flags.isSet(state, Flags.MERGE)).isTrue();
assertThat(Flags.isSet(state, Flags.INSERT)).isTrue();
}
@Test
public void isPublishOrMerge() {
assertThat(Flags.isPublishOrMerge(0)).isFalse();
assertThat(Flags.isPublishOrMerge(Flags.INSERT)).isFalse();
assertThat(Flags.isPublishOrMerge(Flags.PUBLISH)).isTrue();
assertThat(Flags.isPublishOrMerge(Flags.MERGE)).isTrue();
int mergePublish = Flags.setMerge(Flags.setPublish(0));
assertThat(Flags.isPublishOrMerge(mergePublish)).isTrue();
}
}
@@ -0,0 +1,33 @@
package org.tests.merge;
import javax.persistence.Entity;
@Entity
public class MAddress extends MBase {
private String street;
private String city;
public MAddress(String street, String city) {
this.street = street;
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
+32
View File
@@ -0,0 +1,32 @@
package org.tests.merge;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;
import java.util.UUID;
@MappedSuperclass
public class MBase {
@Id
private UUID id;
@Version
private long version;
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
@@ -0,0 +1,67 @@
package org.tests.merge;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import java.util.List;
@Entity
public class MContact extends MBase {
String email;
String firstName;
String lastName;
@ManyToOne
MCustomer customer;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "contact")
List<MContactMessage> messages;
public MContact(String email, String firstName, String lastName) {
this.email = email;
this.firstName = firstName;
this.lastName = lastName;
}
public MCustomer getCustomer() {
return customer;
}
public void setCustomer(MCustomer customer) {
this.customer = customer;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public List<MContactMessage> getMessages() {
return messages;
}
public void setMessages(List<MContactMessage> messages) {
this.messages = messages;
}
}
@@ -0,0 +1,54 @@
package org.tests.merge;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
@Entity
public class MContactMessage extends MBase {
private String title;
private String subject;
private String notes;
@ManyToOne(optional = false)
private MContact contact;
public MContactMessage(String title, String subject) {
this.title = title;
this.subject = subject;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public MContact getContact() {
return contact;
}
public void setContact(MContact contact) {
this.contact = contact;
}
}
@@ -0,0 +1,68 @@
package org.tests.merge;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import java.util.List;
@Entity
public class MCustomer extends MBase {
private String name;
private String notes;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "customer")
private List<MContact> contacts;
@ManyToOne(cascade = CascadeType.ALL)
private MAddress shippingAddress;
@ManyToOne(cascade = CascadeType.ALL)
private MAddress billingAddress;
public MCustomer(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
public List<MContact> getContacts() {
return contacts;
}
public void setContacts(List<MContact> contacts) {
this.contacts = contacts;
}
public MAddress getShippingAddress() {
return shippingAddress;
}
public void setShippingAddress(MAddress shippingAddress) {
this.shippingAddress = shippingAddress;
}
public MAddress getBillingAddress() {
return billingAddress;
}
public void setBillingAddress(MAddress billingAddress) {
this.billingAddress = billingAddress;
}
}
@@ -0,0 +1,117 @@
package org.tests.merge;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.MergeOptions;
import io.ebean.MergeOptionsBuilder;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import org.tests.model.basic.UUOne;
import org.tests.model.basic.UUTwo;
import java.util.List;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
public class TestMergeBasic extends BaseTestCase {
private UUOne rebuildViaJson(UUOne input) {
String asJson = Ebean.json().toJson(input);
return Ebean.json().toBean(UUOne.class, asJson);
}
private UUOne build() {
UUOne uuOne = buildGraph();
Ebean.save(uuOne);
return rebuildViaJson(uuOne);
}
@Test
public void with_setClientGeneratedIds_expect_noDifferenceWithToMany() {
UUOne one = build();
one.setDescription("mod");
List<UUTwo> comments = one.getComments();
comments.remove(0);
comments.add(0, new UUTwo("twoMore", UUID.randomUUID()));
comments.add(new UUTwo("twoExtra", UUID.randomUUID()));
MergeOptions options = new MergeOptionsBuilder()
.addPath("comments")
.setClientGeneratedIds()
.build();
LoggedSqlCollector.start();
Ebean.merge(one, options);
List<String> sql = LoggedSqlCollector.stop();
// fetch the Ids ... used to identity inserts, updates and deletes
assertThat(sql.get(0)).contains("select t0.id, t1.id from uuone t0 left join uutwo t1 on t1.master_id = t0.id where t0.id = ?");
// deletes of Ids that are no longer in the graph
assertThat(sql.get(1)).contains("delete from uutwo where id=?");
// cascade persist ... master
assertThat(sql.get(2)).contains("update uuone set name=?, description=?, version=? where id=? and version=?");
// persist children ...
assertThat(sql.get(3)).contains("insert into uutwo (id, name, notes, version, master_id) values (?,?,?,?,?);");
assertThat(sql.get(4)).contains("insert into uutwo (id, name, notes, version, master_id) values (?,?,?,?,?);");
assertThat(sql.get(5)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?");
assertThat(sql.get(6)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?");
assertThat(sql.get(7)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?");
}
@Test
public void test() {
UUOne one = build();
one.setDescription("mod");
List<UUTwo> comments = one.getComments();
comments.remove(0);
comments.add(0, new UUTwo("twoMore", UUID.randomUUID()));
comments.add(new UUTwo("twoExtra", UUID.randomUUID()));
MergeOptions options = new MergeOptionsBuilder()
.addPath("comments")
.build();
LoggedSqlCollector.start();
Ebean.merge(one, options);
List<String> sql = LoggedSqlCollector.stop();
// fetch the Ids ... used to identity inserts, updates and deletes
assertThat(sql.get(0)).contains("select t0.id, t1.id from uuone t0 left join uutwo t1 on t1.master_id = t0.id where t0.id = ?");
// deletes of Ids that are no longer in the graph
assertThat(sql.get(1)).contains("delete from uutwo where id=?");
// cascade persist ... master
assertThat(sql.get(2)).contains("update uuone set name=?, description=?, version=? where id=? and version=?");
// persist children ...
assertThat(sql.get(3)).contains("insert into uutwo (id, name, notes, version, master_id) values (?,?,?,?,?);");
assertThat(sql.get(4)).contains("insert into uutwo (id, name, notes, version, master_id) values (?,?,?,?,?);");
assertThat(sql.get(5)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?");
assertThat(sql.get(6)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?");
assertThat(sql.get(7)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?");
}
private UUOne buildGraph() {
UUOne one = new UUOne("one1", UUID.randomUUID());
for (int i = 1; i < 5; i++) {
UUTwo two = new UUTwo("two" + i, UUID.randomUUID());
one.getComments().add(two);
}
return one;
}
}
@@ -0,0 +1,376 @@
package org.tests.merge;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.FetchPath;
import io.ebean.MergeOptions;
import io.ebean.MergeOptionsBuilder;
import io.ebean.text.PathProperties;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
public class TestMergeCustomer extends BaseTestCase {
private Random random = new Random();
/**
* So this is effectively the same as a stateless update.
*/
@Test
public void customerOnly_expect_updateOnly() {
MCustomer mCustomer = partial("cust1", "(id,name,version)");
mCustomer.setName("NotCust1");
MergeOptions options = new MergeOptionsBuilder().build();
LoggedSqlCollector.start();
Ebean.merge(mCustomer, options);
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains("update mcustomer set name=?, version=? where id=? and version=?");
}
@Test
public void customerOnly_setClientGeneratedIds_expect_selectAndUpdate() {
MCustomer mCustomer = partial("cust2", "(id,name,version)");
mCustomer.setName("NotCust2");
MergeOptions options = new MergeOptionsBuilder().setClientGeneratedIds().build();
LoggedSqlCollector.start();
server().merge(mCustomer, options);
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(2);
assertThat(sql.get(0)).contains("select t0.id from mcustomer t0 where t0.id = ?");
assertThat(sql.get(1)).contains("update mcustomer set name=?, version=? where id=? and version=?");
}
@Test
public void customerWithAddresses_setClientGeneratedIds_expect_selectAndUpdate() {
MCustomer mCustomer = partial("cust3", "(id,name,version,shippingAddress(*),billingAddress(*))");
mCustomer.setName("NotCust3");
mCustomer.getBillingAddress().setStreet("modBillStreet");
mCustomer.getShippingAddress().setCity("modShipCity");
MergeOptions options = new MergeOptionsBuilder()
.addPath("shippingAddress")
.addPath("billingAddress")
.setClientGeneratedIds()
.build();
LoggedSqlCollector.start();
server().merge(mCustomer, options);
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(4);
assertThat(sql.get(0)).contains("select t0.id, t2.id, t1.id from mcustomer t0 left join maddress t2 on t2.id = t0.shipping_address_id left join maddress t1 on t1.id = t0.billing_address_id where t0.id = ?");
assertThat(sql.get(1)).contains("update maddress set street=?, city=?, version=? where id=? and version=?");
assertThat(sql.get(2)).contains("update maddress set street=?, city=?, version=? where id=? and version=?");
assertThat(sql.get(3)).contains("update mcustomer set name=?, version=?, shipping_address_id=?, billing_address_id=? where id=? and version=?");
}
@Test
public void customerWithAddresses_newAddress_setClientGeneratedIds_expect_insertAddress() {
MCustomer mCustomer = partial("cust3", "(id,name,version,shippingAddress(*),billingAddress(*))");
mCustomer.setName("NotCust3");
// new billing address - no Id value so must be insert
mCustomer.setBillingAddress(new MAddress("Short", "Mid Wicket"));
mCustomer.getShippingAddress().setCity("modShipCity");
MergeOptions options = new MergeOptionsBuilder()
.addPath("shippingAddress")
.addPath("billingAddress")
.setClientGeneratedIds()
.build();
LoggedSqlCollector.start();
server().merge(mCustomer, options);
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(4);
assertThat(sql.get(0)).contains("select t0.id, t2.id, t1.id from mcustomer t0 left join maddress t2 on t2.id = t0.shipping_address_id left join maddress t1 on t1.id = t0.billing_address_id where t0.id = ?");
assertThat(sql.get(1)).contains("update maddress set street=?, city=?, version=? where id=? and version=?");
assertThat(sql.get(2)).contains("insert into maddress (id, street, city, version) values (?,?,?,?);");
assertThat(sql.get(3)).contains("update mcustomer set name=?, version=?, shipping_address_id=?, billing_address_id=? where id=? and version=?");
}
@Test
public void customerWithAddresses_newAddressWithId_setClientGeneratedIds_expect_additionalCheckForAddressInsert() {
MCustomer mCustomer = partial("cust3", "(id,name,version,shippingAddress(*),billingAddress(*))");
mCustomer.setName("NotCust3");
// new billing address - has Id value + setClientGeneratedIds ... so extra query to check
MAddress mAddress = new MAddress("Short", "Mid Wicket");
mAddress.setId(UUID.randomUUID());
mCustomer.setBillingAddress(mAddress);
mCustomer.getShippingAddress().setCity("modShipCity");
MergeOptions options = new MergeOptionsBuilder()
.addPath("shippingAddress")
.addPath("billingAddress")
.setClientGeneratedIds() // As we are using clientIds ... we don't know if the new UUID is an insert or update without checking
.build();
LoggedSqlCollector.start();
server().merge(mCustomer, options);
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(5);
assertThat(sql.get(0)).contains("select t0.id, t2.id, t1.id from mcustomer t0 left join maddress t2 on t2.id = t0.shipping_address_id left join maddress t1 on t1.id = t0.billing_address_id where t0.id = ?");
// Additional check to see if the address with the unknown UUID is 'insert' or 'update'
assertThat(sql.get(1)).contains("select t0.id from maddress t0 where t0.id = ?");
assertThat(sql.get(2)).contains("update maddress set street=?, city=?, version=? where id=? and version=?");
assertThat(sql.get(3)).contains("insert into maddress (id, street, city, version) values (?,?,?,?);");
assertThat(sql.get(4)).contains("update mcustomer set name=?, version=?, shipping_address_id=?, billing_address_id=? where id=? and version=?");
}
@Test
public void assocOne_onlineIsNull() {
MCustomer c = new MCustomer("Null Address");
c.setBillingAddress(new MAddress("Cow corner", "Mid Wicket"));
Ebean.save(c);
MCustomer mCustomer = rebuildViaJson(c);
MAddress mAddress = new MAddress("Silly", "Mid Wicket");
mAddress.setId(UUID.randomUUID());
mCustomer.setShippingAddress(mAddress);
MergeOptions options = new MergeOptionsBuilder()
.addPath("shippingAddress")
.addPath("billingAddress")
.setClientGeneratedIds()
.build();
Ebean.merge(mCustomer, options);
}
private MCustomer rebuildViaJson(MCustomer input) {
String asJson = Ebean.json().toJson(input);
return Ebean.json().toBean(MCustomer.class, asJson);
}
@Test
public void whenContacts_isNull_expect_deleteContacts() {
MCustomer mCustomer = partial("cust6", "(id,name,version,shippingAddress(id),billingAddress(id),contacts(*))");
// null contacts ... but in path so this means - delete all contacts
mCustomer.setContacts(null);
MergeOptions options = new MergeOptionsBuilder()
.addPath("contacts")
.build();
LoggedSqlCollector.start();
server().merge(mCustomer, options);
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql.get(0)).contains("select t0.id, t1.id from mcustomer t0 left join mcontact t1 on t1.customer_id = t0.id where t0.id = ?");
assertThat(sql.get(1)).contains("delete from mcontact_message where contact_id = ?");
assertThat(sql.get(2)).contains("delete from mcontact where id=?");
assertThat(sql.get(13)).contains("update mcustomer set name=?, version=?, shipping_address_id=?, billing_address_id=? where id=? and version=?");
}
@Test
public void whenContacts_isEmpty_expect_deleteContacts() {
MCustomer mCustomer = partial("cust6", "(id,name,version,shippingAddress(id),billingAddress(id),contacts(*))");
// empty contacts ... but in path so this means - delete all contacts
mCustomer.setContacts(new ArrayList<>());
MergeOptions options = new MergeOptionsBuilder()
.addPath("contacts")
.build();
LoggedSqlCollector.start();
server().merge(mCustomer, options);
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql.get(0)).contains("select t0.id, t1.id from mcustomer t0 left join mcontact t1 on t1.customer_id = t0.id where t0.id = ?");
assertThat(sql.get(1)).contains("delete from mcontact_message where contact_id = ?");
assertThat(sql.get(2)).contains("delete from mcontact where id=?");
assertThat(sql.get(13)).contains("update mcustomer set name=?, version=?, shipping_address_id=?, billing_address_id=? where id=? and version=?");
}
@Test
public void whenContacts_mixed_expect_deleteInsertUpdateContacts() {
MCustomer mCustomer = partial("cust6", "(id,name,version,shippingAddress(id),billingAddress(id),contacts(*))");
List<MContact> contacts = mCustomer.getContacts();
MContact mContact = new MContact("z@a.com", "z", "zed");
mContact.setId(UUID.randomUUID());
contacts.add(mContact);
contacts.get(0).setEmail("a@beta.com");
contacts.get(1).setEmail("b@beta.com");
contacts.remove(4);
contacts.remove(2);
MContact mContactEnd = new MContact("z@z.com", "zx", "zedXtra");
mContactEnd.setId(UUID.randomUUID());
contacts.add(mContactEnd);
MergeOptions options = new MergeOptionsBuilder()
.addPath("contacts")
.build();
LoggedSqlCollector.start();
server().merge(mCustomer, options);
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql.get(0)).contains("select t0.id, t1.id from mcustomer t0 left join mcontact t1 on t1.customer_id = t0.id where t0.id = ?");
assertThat(sql.get(1)).contains("delete from mcontact_message where contact_id = ?");
assertThat(sql.get(2)).contains("delete from mcontact where id=?");
assertThat(sql.get(5)).contains("update mcustomer set name=?, version=?, shipping_address_id=?, billing_address_id=? where id=? and version=?");
assertThat(sql.get(6)).contains("insert into mcontact");
assertThat(sql.get(7)).contains("insert into mcontact");
assertThat(sql.get(8)).contains("update mcontact set email=?, first_name=?, last_name=?, version=?, customer_id=? where id=? and version=?");
assertThat(sql.get(11)).contains("update mcontact set email=?, first_name=?, last_name=?, version=?, customer_id=? where id=? and version=?");
}
@Test
public void fullMonty() {
MCustomer cust1 = customer("monty1");
modify(cust1);
MergeOptions options = new MergeOptionsBuilder()
.addPath("billingAddress")
.addPath("shippingAddress")
.addPath("contacts")
.addPath("contacts.messages")
.setClientGeneratedIds()
.setDeletePermanent()
.build();
LoggedSqlCollector.start();
server().merge(cust1, options);
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql.get(0)).contains("select t0.id, t3.id, t1.id, t2.id from mcustomer t0 left join maddress t3 on t3.id = t0.shipping_address_id left join maddress t1 on t1.id = t0.billing_address_id left join mcontact t2 on t2.customer_id = t0.id where t0.id = ?");
if (isH2()) {
// with nested OneToMany .. we need a second query to read the contact message ids
assertThat(sql.get(1)).contains("select t0.contact_id, t0.id from mcontact_message t0 where (t0.contact_id) in (?, ?, ?, ?, ?, ?, ?, ?, ?, ? )");
}
assertThat(sql.get(2)).contains("delete from mcontact_message where contact_id = ?");
assertThat(sql.get(3)).contains("delete from mcontact where id=?");
assertThat(sql.get(4)).contains("delete from mcontact_message where contact_id = ?");
assertThat(sql.get(5)).contains("delete from mcontact where id=?");
assertThat(sql.get(6)).contains("update maddress set street=?, city=?, version=? where id=? and version=?");
assertThat(sql.get(7)).contains("update mcustomer set name=?, notes=?, version=?, shipping_address_id=?, billing_address_id=? where id=? and version=?");
assertThat(sql.get(8)).contains("insert into mcontact");
assertThat(sql.get(9)).contains("update mcontact set email=?, first_name=?, last_name=?, version=?, customer_id=? where id=? and version=?");
assertThat(sql.get(13)).contains("update mcontact_message set title=?, subject=?, notes=?, version=?, contact_id=? where id=? and version=?");
}
private void modify(MCustomer cust) {
List<MContact> contacts = cust.getContacts();
MContact mContact = new MContact("z@a.com", "z", "zed");
mContact.setId(UUID.randomUUID());
contacts.add(mContact);
contacts.get(0).setEmail("a@beta.com");
contacts.get(1).setEmail("b@beta.com");
contacts.remove(4);
contacts.remove(2);
cust.setName(cust.getName()+" modified");
cust.getBillingAddress().setStreet("Short");
MAddress mAddress = new MAddress("Broken", "Dreams");
mAddress.setId(UUID.randomUUID());
cust.setShippingAddress(null);
}
private MCustomer partial(String name, String fetchGraph) {
MCustomer cust1 = buildCustomer(name);
Ebean.save(cust1);
FetchPath fetchPath = PathProperties.parse(fetchGraph);
String asJson = Ebean.json().toJson(cust1, fetchPath);
return Ebean.json().toBean(MCustomer.class, asJson);
}
private MCustomer customer(String name) {
MCustomer cust1 = buildCustomer(name);
Ebean.save(cust1);
String asJson = Ebean.json().toJson(cust1);
return Ebean.json().toBean(MCustomer.class, asJson);
}
private MCustomer buildCustomer(String name) {
MCustomer c = new MCustomer(name);
c.setShippingAddress(new MAddress("Fleet st", "London"));
c.setBillingAddress(new MAddress("Cow corner", "Mid Wicket"));
c.getContacts().add(addContact("a@a.com", "a", "alligator"));
c.getContacts().add(addContact("b@a.com", "b", "beaver"));
c.getContacts().add(addContact("c@a.com", "c", "crow"));
c.getContacts().add(addContact("d@a.com", "d", "dog"));
c.getContacts().add(addContact("e@a.com", "e", "ent"));
c.getContacts().add(addContact("f@a.com", "f", "frog"));
return c;
}
private MContact addContact(String email, String first, String last) {
MContact mContact = new MContact(email, first, last);
int i = random.nextInt(2);
for (int j = 0; j < i; j++) {
mContact.getMessages().add(new MContactMessage(first+" "+i, last+" "+i));
}
return mContact;
}
}
@@ -6,6 +6,7 @@ import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Version;
import java.util.List;
import java.util.UUID;
@@ -18,10 +19,22 @@ public class UUOne {
String name;
String description;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "master")
List<UUTwo> comments;
@Version
long version;
public UUOne() {
}
public UUOne(String name, UUID id) {
this.name = name;
this.id = id;
}
public UUID getId() {
return id;
}
@@ -38,6 +51,14 @@ public class UUOne {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<UUTwo> getComments() {
return comments;
}
@@ -46,4 +67,11 @@ public class UUOne {
this.comments = comments;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
@@ -4,6 +4,7 @@ import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Version;
import java.util.UUID;
@Entity
@@ -14,9 +15,22 @@ public class UUTwo {
String name;
String notes;
@ManyToOne(cascade = CascadeType.PERSIST)
UUOne master;
@Version
long version;
public UUTwo() {
}
public UUTwo(String name, UUID id) {
this.name = name;
this.id = id;
}
public UUID getId() {
return id;
}
@@ -33,6 +47,14 @@ public class UUTwo {
this.name = name;
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
public UUOne getMaster() {
return master;
}
@@ -41,4 +63,11 @@ public class UUTwo {
this.master = master;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
@@ -3,10 +3,10 @@ package org.tests.query.joins;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.Query;
import org.tests.model.basic.UUOne;
import org.tests.model.basic.UUTwo;
import org.junit.Assert;
import org.junit.Test;
import org.tests.model.basic.UUOne;
import org.tests.model.basic.UUTwo;
import java.util.List;
@@ -58,11 +58,11 @@ public class TestDisjunctWhereOuterOnMany extends BaseTestCase {
Assert.assertEquals(2, rowCount);
if (isPostgres()) {
String expectedSql = "select distinct on (t0.id) t0.id, t0.name from uuone t0 left join uutwo u1 on u1.master_id = t0.id where (t0.name = ? or u1.name = ? ) ";
String expectedSql = "select distinct on (t0.id) t0.id, t0.name, t0.description, t0.version from uuone t0 left join uutwo u1 on u1.master_id = t0.id where (t0.name = ? or u1.name = ? ) ";
assertThat(sqlOf(query, 1)).contains(expectedSql);
} else {
String expectedSql = "select distinct t0.id, t0.name from uuone t0 left join uutwo u1 on u1.master_id = t0.id where (t0.name = ? or u1.name = ? ) ";
String expectedSql = "select distinct t0.id, t0.name, t0.description, t0.version from uuone t0 left join uutwo u1 on u1.master_id = t0.id where (t0.name = ? or u1.name = ? ) ";
assertThat(sqlOf(query, 1)).contains(expectedSql);
}