/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2002, 2015 Oracle and/or its affiliates. All rights reserved. * */ package com.sleepycat.persist.test; import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.sleepycat.db.DatabaseException; import com.sleepycat.db.Environment; import com.sleepycat.db.EnvironmentConfig; import com.sleepycat.db.Transaction; import com.sleepycat.db.util.DualTestCase; import com.sleepycat.persist.EntityCursor; import com.sleepycat.persist.EntityStore; import com.sleepycat.persist.PrimaryIndex; import com.sleepycat.persist.SecondaryIndex; import com.sleepycat.persist.StoreConfig; import com.sleepycat.persist.model.AnnotationModel; import com.sleepycat.persist.model.Entity; import com.sleepycat.persist.model.EntityModel; import com.sleepycat.persist.model.Persistent; import com.sleepycat.persist.model.PrimaryKey; import com.sleepycat.persist.model.SecondaryKey; import com.sleepycat.util.test.SharedTestUtils; import com.sleepycat.util.test.TestEnv; public class SubclassIndexTest extends DualTestCase { private File envHome; private Environment env; private EntityStore store; @Before public void setUp() throws Exception { envHome = SharedTestUtils.getTestDir(); super.setUp(); } @After public void tearDown() throws Exception { super.tearDown(); envHome = null; env = null; } private void open() throws DatabaseException { EnvironmentConfig envConfig = TestEnv.TXN.getConfig(); envConfig.setAllowCreate(true); env = create(envHome, envConfig); EntityModel model = new AnnotationModel(); model.registerClass(Manager.class); model.registerClass(SalariedManager.class); StoreConfig storeConfig = new StoreConfig(); storeConfig.setModel(model); storeConfig.setAllowCreate(true); storeConfig.setTransactional(true); store = new EntityStore(env, "foo", storeConfig); } private void close() throws DatabaseException { store.close(); store = null; close(env); env = null; } @Test public void testSubclassIndex() throws DatabaseException { open(); PrimaryIndex<String, Employee> employeesById = store.getPrimaryIndex(String.class, Employee.class); employeesById.put(new Employee("1")); employeesById.put(new Manager("2", "a")); employeesById.put(new Manager("3", "a")); employeesById.put(new Manager("4", "b")); Employee e; Manager m; e = employeesById.get("1"); assertNotNull(e); assertTrue(!(e instanceof Manager)); /* Ensure DB exists BEFORE calling getSubclassIndex. [#15247] */ PersistTestUtils.assertDbExists (true, env, "foo", Employee.class.getName(), "dept"); /* Normal use: Subclass index for a key in the subclass. */ SecondaryIndex<String, String, Manager> managersByDept = store.getSubclassIndex (employeesById, Manager.class, String.class, "dept"); m = managersByDept.get("a"); assertNotNull(m); assertEquals("2", m.id); m = managersByDept.get("b"); assertNotNull(m); assertEquals("4", m.id); Transaction txn = env.beginTransaction(null, null); EntityCursor<Manager> managers = managersByDept.entities(txn, null); try { m = managers.next(); assertNotNull(m); assertEquals("2", m.id); m = managers.next(); assertNotNull(m); assertEquals("3", m.id); m = managers.next(); assertNotNull(m); assertEquals("4", m.id); m = managers.next(); assertNull(m); } finally { managers.close(); txn.commit(); } /* Getting a subclass index for the entity class is also allowed. */ store.getSubclassIndex (employeesById, Employee.class, String.class, "other"); /* Getting a subclass index for a base class key is not allowed. */ try { store.getSubclassIndex (employeesById, Manager.class, String.class, "other"); fail(); } catch (IllegalArgumentException expected) { } close(); } /** * Previously this tested that a secondary key database was added only * AFTER storing the first instance of the subclass that defines the key. * Now that we require registering the subclass up front, the database is * created up front also. So this test is somewhat less useful, but still * nice to have around. [#16399] */ @Test public void testAddSecKey() throws DatabaseException { open(); PrimaryIndex<String, Employee> employeesById = store.getPrimaryIndex(String.class, Employee.class); employeesById.put(new Employee("1")); assertTrue(hasEntityKey("dept")); close(); open(); employeesById = store.getPrimaryIndex(String.class, Employee.class); assertTrue(hasEntityKey("dept")); employeesById.put(new Manager("2", "a")); assertTrue(hasEntityKey("dept")); close(); open(); assertTrue(hasEntityKey("dept")); close(); open(); employeesById = store.getPrimaryIndex(String.class, Employee.class); assertTrue(hasEntityKey("salary")); employeesById.put(new SalariedManager("3", "a", "111")); assertTrue(hasEntityKey("salary")); close(); open(); assertTrue(hasEntityKey("dept")); assertTrue(hasEntityKey("salary")); close(); } private boolean hasEntityKey(String keyName) { return store.getModel(). getRawType(Employee.class.getName()). getEntityMetadata(). getSecondaryKeys(). keySet(). contains(keyName); } @Entity private static class Employee { @PrimaryKey String id; @SecondaryKey(relate=MANY_TO_ONE) String other; Employee(String id) { this.id = id; } private Employee() {} } @Persistent private static class Manager extends Employee { @SecondaryKey(relate=MANY_TO_ONE) String dept; Manager(String id, String dept) { super(id); this.dept = dept; } private Manager() {} } @Persistent private static class SalariedManager extends Manager { @SecondaryKey(relate=MANY_TO_ONE) String salary; SalariedManager(String id, String dept, String salary) { super(id, dept); this.salary = salary; } private SalariedManager() {} } }