1 /*- 2 * Copyright (c) 2002, 2020 Oracle and/or its affiliates. All rights reserved. 3 * 4 * See the file LICENSE for license information. 5 * 6 */ 7 8 package com.sleepycat.persist.test; 9 10 import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE; 11 import static org.junit.Assert.assertEquals; 12 import static org.junit.Assert.assertNotNull; 13 import static org.junit.Assert.assertNull; 14 import static org.junit.Assert.assertTrue; 15 import static org.junit.Assert.fail; 16 17 import java.io.File; 18 19 import org.junit.After; 20 import org.junit.Before; 21 import org.junit.Test; 22 23 import com.sleepycat.db.DatabaseException; 24 import com.sleepycat.db.Environment; 25 import com.sleepycat.db.EnvironmentConfig; 26 import com.sleepycat.db.Transaction; 27 import com.sleepycat.db.util.DualTestCase; 28 import com.sleepycat.persist.EntityCursor; 29 import com.sleepycat.persist.EntityStore; 30 import com.sleepycat.persist.PrimaryIndex; 31 import com.sleepycat.persist.SecondaryIndex; 32 import com.sleepycat.persist.StoreConfig; 33 import com.sleepycat.persist.model.AnnotationModel; 34 import com.sleepycat.persist.model.Entity; 35 import com.sleepycat.persist.model.EntityModel; 36 import com.sleepycat.persist.model.Persistent; 37 import com.sleepycat.persist.model.PrimaryKey; 38 import com.sleepycat.persist.model.SecondaryKey; 39 import com.sleepycat.util.test.SharedTestUtils; 40 import com.sleepycat.util.test.TestEnv; 41 42 public class SubclassIndexTest extends DualTestCase { 43 44 private File envHome; 45 private Environment env; 46 private EntityStore store; 47 48 @Before setUp()49 public void setUp() 50 throws Exception { 51 52 envHome = SharedTestUtils.getTestDir(); 53 super.setUp(); 54 } 55 56 @After tearDown()57 public void tearDown() 58 throws Exception { 59 60 super.tearDown(); 61 envHome = null; 62 env = null; 63 } 64 open()65 private void open() 66 throws DatabaseException { 67 68 EnvironmentConfig envConfig = TestEnv.TXN.getConfig(); 69 envConfig.setAllowCreate(true); 70 env = create(envHome, envConfig); 71 72 EntityModel model = new AnnotationModel(); 73 model.registerClass(Manager.class); 74 model.registerClass(SalariedManager.class); 75 76 StoreConfig storeConfig = new StoreConfig(); 77 storeConfig.setModel(model); 78 storeConfig.setAllowCreate(true); 79 storeConfig.setTransactional(true); 80 store = new EntityStore(env, "foo", storeConfig); 81 } 82 close()83 private void close() 84 throws DatabaseException { 85 86 store.close(); 87 store = null; 88 close(env); 89 env = null; 90 } 91 92 @Test testSubclassIndex()93 public void testSubclassIndex() 94 throws DatabaseException { 95 96 open(); 97 98 PrimaryIndex<String, Employee> employeesById = 99 store.getPrimaryIndex(String.class, Employee.class); 100 101 employeesById.put(new Employee("1")); 102 employeesById.put(new Manager("2", "a")); 103 employeesById.put(new Manager("3", "a")); 104 employeesById.put(new Manager("4", "b")); 105 106 Employee e; 107 Manager m; 108 109 e = employeesById.get("1"); 110 assertNotNull(e); 111 assertTrue(!(e instanceof Manager)); 112 113 /* Ensure DB exists BEFORE calling getSubclassIndex. [#15247] */ 114 PersistTestUtils.assertDbExists 115 (true, env, "foo", Employee.class.getName(), "dept"); 116 117 /* Normal use: Subclass index for a key in the subclass. */ 118 SecondaryIndex<String, String, Manager> managersByDept = 119 store.getSubclassIndex 120 (employeesById, Manager.class, String.class, "dept"); 121 122 m = managersByDept.get("a"); 123 assertNotNull(m); 124 assertEquals("2", m.id); 125 126 m = managersByDept.get("b"); 127 assertNotNull(m); 128 assertEquals("4", m.id); 129 130 Transaction txn = env.beginTransaction(null, null); 131 EntityCursor<Manager> managers = managersByDept.entities(txn, null); 132 try { 133 m = managers.next(); 134 assertNotNull(m); 135 assertEquals("2", m.id); 136 m = managers.next(); 137 assertNotNull(m); 138 assertEquals("3", m.id); 139 m = managers.next(); 140 assertNotNull(m); 141 assertEquals("4", m.id); 142 m = managers.next(); 143 assertNull(m); 144 } finally { 145 managers.close(); 146 txn.commit(); 147 } 148 149 /* Getting a subclass index for the entity class is also allowed. */ 150 store.getSubclassIndex 151 (employeesById, Employee.class, String.class, "other"); 152 153 /* Getting a subclass index for a base class key is not allowed. */ 154 try { 155 store.getSubclassIndex 156 (employeesById, Manager.class, String.class, "other"); 157 fail(); 158 } catch (IllegalArgumentException expected) { 159 } 160 161 close(); 162 } 163 164 /** 165 * Previously this tested that a secondary key database was added only 166 * AFTER storing the first instance of the subclass that defines the key. 167 * Now that we require registering the subclass up front, the database is 168 * created up front also. So this test is somewhat less useful, but still 169 * nice to have around. [#16399] 170 */ 171 @Test testAddSecKey()172 public void testAddSecKey() 173 throws DatabaseException { 174 175 open(); 176 PrimaryIndex<String, Employee> employeesById = 177 store.getPrimaryIndex(String.class, Employee.class); 178 employeesById.put(new Employee("1")); 179 assertTrue(hasEntityKey("dept")); 180 close(); 181 182 open(); 183 employeesById = store.getPrimaryIndex(String.class, Employee.class); 184 assertTrue(hasEntityKey("dept")); 185 employeesById.put(new Manager("2", "a")); 186 assertTrue(hasEntityKey("dept")); 187 close(); 188 189 open(); 190 assertTrue(hasEntityKey("dept")); 191 close(); 192 193 open(); 194 employeesById = store.getPrimaryIndex(String.class, Employee.class); 195 assertTrue(hasEntityKey("salary")); 196 employeesById.put(new SalariedManager("3", "a", "111")); 197 assertTrue(hasEntityKey("salary")); 198 close(); 199 200 open(); 201 assertTrue(hasEntityKey("dept")); 202 assertTrue(hasEntityKey("salary")); 203 close(); 204 } 205 hasEntityKey(String keyName)206 private boolean hasEntityKey(String keyName) { 207 return store.getModel(). 208 getRawType(Employee.class.getName()). 209 getEntityMetadata(). 210 getSecondaryKeys(). 211 keySet(). 212 contains(keyName); 213 } 214 215 @Entity 216 private static class Employee { 217 218 @PrimaryKey 219 String id; 220 221 @SecondaryKey(relate=MANY_TO_ONE) 222 String other; 223 Employee(String id)224 Employee(String id) { 225 this.id = id; 226 } 227 Employee()228 private Employee() {} 229 } 230 231 @Persistent 232 private static class Manager extends Employee { 233 234 @SecondaryKey(relate=MANY_TO_ONE) 235 String dept; 236 Manager(String id, String dept)237 Manager(String id, String dept) { 238 super(id); 239 this.dept = dept; 240 } 241 Manager()242 private Manager() {} 243 } 244 245 @Persistent 246 private static class SalariedManager extends Manager { 247 248 @SecondaryKey(relate=MANY_TO_ONE) 249 String salary; 250 SalariedManager(String id, String dept, String salary)251 SalariedManager(String id, String dept, String salary) { 252 super(id, dept); 253 this.salary = salary; 254 } 255 SalariedManager()256 private SalariedManager() {} 257 } 258 } 259