package org.tests.singleTableInheritance; import io.ebean.BaseTestCase; import io.ebean.Ebean; import org.tests.singleTableInheritance.model.PalletLocation; import org.tests.singleTableInheritance.model.PalletLocationExternal; import org.tests.singleTableInheritance.model.Warehouse; import org.tests.singleTableInheritance.model.Zone; import org.tests.singleTableInheritance.model.ZoneExternal; import org.tests.singleTableInheritance.model.ZoneInternal; import org.junit.Assert; import org.junit.Test; import java.util.List; public class TestInheritQuery extends BaseTestCase { @Test public void test() { ZoneExternal zone = new ZoneExternal(); zone.setAttribute("ABC"); Ebean.save(zone); PalletLocationExternal location = new PalletLocationExternal(); location.setZone(zone); location.setAttribute("123"); Ebean.save(location); // This line should work too: List<PalletLocation> locations = Ebean.find(PalletLocation.class).where().eq("zone", zone) .findList(); // List<PalletLocation> locations = // Ebean.find(PalletLocation.class).where().eq("zone.id", // zone.getId()).findList(); Assert.assertNotNull(locations); Assert.assertEquals(1, locations.size()); PalletLocation rereadLoc = locations.get(0); Assert.assertTrue(rereadLoc instanceof PalletLocation); Zone rereadZone = rereadLoc.getZone(); Assert.assertNotNull(rereadZone); Assert.assertTrue(rereadZone instanceof ZoneExternal); } @Test public void testDiscriminator_bug417() { Ebean.deleteAll(Ebean.find(Warehouse.class).findList()); Ebean.deleteAll(Ebean.find(PalletLocation.class).findList()); Ebean.deleteAll(Ebean.find(Zone.class).findList()); Ebean.deleteAll(Ebean.find(ZoneInternal.class).findList()); Ebean.deleteAll(Ebean.find(ZoneInternal.class).findList()); ZoneInternal zoneInt = new ZoneInternal(); zoneInt.setAttribute("some zone 1"); Ebean.save(zoneInt); ZoneExternal zoneExt = new ZoneExternal(); zoneExt.setAttribute("some zone 2"); Ebean.save(zoneExt); // queries of Zone and subclasses as root node of query // query abstract class on attribute (root of heirarchy) List<Zone> zones = Ebean.find(Zone.class).where().startsWith("attribute", "some zone").findList(); // select t0.type c0, t0.ID c1, t0.attribute c2, t0.attribute c3 from zones t0 where t0.attribute like ? ; --bind(some zone%) Assert.assertEquals(2, zones.size()); Assert.assertTrue(zones.contains(zoneInt)); Assert.assertTrue(zones.contains(zoneExt)); // query internal zones only // discriminator is in WHERE clause where it belongs List<ZoneInternal> internalZones = Ebean.find(ZoneInternal.class).where().startsWith("attribute", "some zone").findList(); // select t0.type c0, t0.ID c1, t0.attribute c2 from zones t0 where t0.type = 'INT' and t0.attribute like ? ; --bind(some zone%) Assert.assertEquals(1, internalZones.size()); Assert.assertTrue(internalZones.contains(zoneInt)); Assert.assertFalse(internalZones.contains(zoneExt)); Assert.assertTrue(internalZones.get(0) instanceof ZoneInternal); // query external zones only List<ZoneExternal> externalZones = Ebean.find(ZoneExternal.class).where().startsWith("attribute", "some zone").findList(); // select t0.type c0, t0.ID c1, t0.attribute c2 from zones t0 where t0.type = 'EXT' and t0.attribute like ? ; --bind(some zone%) Assert.assertEquals(1, externalZones.size()); Assert.assertTrue(externalZones.contains(zoneExt)); Assert.assertFalse(externalZones.contains(zoneInt)); Assert.assertTrue(externalZones.get(0) instanceof ZoneExternal); // parents with children of Zones and subclasses Warehouse wh = new Warehouse(); wh.setOfficeZone(zoneInt); // many-to-one wh.getShippingZones().add(zoneExt); // many-to-many Ebean.save(wh); // JOIN clause, no discriminator // parent with many-to-one, doesn't put in discriminator, why not, PK sufficient? // eager join Warehouse wh2 = Ebean.find(Warehouse.class, wh.getId()); Assert.assertNotNull(wh2); Assert.assertEquals(wh.getId(), wh2.getId()); Assert.assertEquals(wh.getOfficeZone(), wh2.getOfficeZone()); Assert.assertEquals(wh.getOfficeZone().getAttribute(), wh2.getOfficeZone().getAttribute()); // before the fix, next assertion runs this lazy query: // select t0.ID c0, t1.type c1, t1.ID c2 from warehouses t0 // left join WarehousesShippingZones t1z_ on t1z_.warehouseId = t0.ID // left join zones t1 on t1.ID = t1z_.shippingZoneId // where t1.type = 'EXT' // this should be in the join clause // and t0.ID = ? // order by t0.ID; --bind(1) // this works here because we have at least one shipping zone Assert.assertEquals(1, wh2.getShippingZones().size()); Assert.assertTrue(wh2.getShippingZones().contains(zoneExt)); // set optional concrete to null to set stage for failure wh.setOfficeZone(null); Ebean.save(wh); // no discriminator here wh2 = Ebean.find(Warehouse.class) .where().eq("id", wh.getId()) .findUnique(); Assert.assertNotNull(wh2); // discriminator is used here, should be in join // assuming this "manual" fetch is equivalent to autofetch (i.e., autofetch should work the same way) // before Daryl's fix // select t0.ID c0, t1.type c1, t1.ID c2, t1.attribute c3 from warehouses t0 left join zones t1 on t1.ID = t0.officeZoneId where t1.type = 'INT' and t0.ID = ? // todo: after Daryl's fix, not sure if this is proper, no discriminator at all, isn't PK/FK sufficient? // select t0.ID c0, t1.type c1, t1.ID c2, t1.attribute c3 from warehouses t0 left join zones t1 on t1.ID = t0.officeZoneId where t0.ID = ? ; --bind(1) wh2 = Ebean.find(Warehouse.class) .fetch("officeZone") .where().eq("id", wh.getId()) .findUnique(); // key assertion #1 - fails due to left join with discriminator in WHERE Assert.assertNotNull(wh2); // clear children to set the stage for left join failure wh.getShippingZones().clear(); Ebean.save(wh); wh2 = Ebean.find(Warehouse.class, wh.getId()); Assert.assertNotNull(wh2); Assert.assertEquals(wh.getId(), wh2.getId()); Assert.assertEquals(0, wh.getShippingZones().size()); // query with lazy load of abstract children wh = Ebean.find(Warehouse.class) .where().eq("id", wh.getId()) .findUnique(); Assert.assertNotNull(wh); Assert.assertEquals(wh.getId(), wh2.getId()); Assert.assertEquals(0, wh.getShippingZones().size()); // query with fetch of abstract children wh = Ebean.find(Warehouse.class) .fetch("shippingZones") .where().eq("id", wh.getId()) .findUnique(); // key assertion #2 - fails due to left join with discriminator in WHERE Assert.assertNotNull(wh); Assert.assertEquals(wh.getId(), wh2.getId()); Assert.assertEquals(0, wh.getShippingZones().size()); } }