PageRenderTime 55ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/src/com/google/appengine/datanucleus/mapping/DatastoreTable.java

http://datanucleus-appengine.googlecode.com/
Java | 1430 lines | 1026 code | 156 blank | 248 comment | 351 complexity | 0653b973053f01eba9e16fc25da0dd34 MD5 | raw file
Possible License(s): Apache-2.0
  1. /**********************************************************************
  2. Copyright (c) 2009 Google Inc.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. **********************************************************************/
  13. package com.google.appengine.datanucleus.mapping;
  14. import org.datanucleus.ClassLoaderResolver;
  15. import org.datanucleus.exceptions.NucleusException;
  16. import org.datanucleus.exceptions.NucleusUserException;
  17. import org.datanucleus.exceptions.NucleusFatalUserException;
  18. import org.datanucleus.identity.OID;
  19. import org.datanucleus.metadata.AbstractClassMetaData;
  20. import org.datanucleus.metadata.AbstractMemberMetaData;
  21. import org.datanucleus.metadata.ClassMetaData;
  22. import org.datanucleus.metadata.ColumnMetaData;
  23. import org.datanucleus.metadata.ColumnMetaDataContainer;
  24. import org.datanucleus.metadata.DiscriminatorMetaData;
  25. import org.datanucleus.metadata.DiscriminatorStrategy;
  26. import org.datanucleus.metadata.FieldPersistenceModifier;
  27. import org.datanucleus.metadata.FieldRole;
  28. import org.datanucleus.metadata.IdentityStrategy;
  29. import org.datanucleus.metadata.IdentityType;
  30. import org.datanucleus.metadata.InheritanceStrategy;
  31. import org.datanucleus.metadata.MetaData;
  32. import org.datanucleus.metadata.MetaDataManager;
  33. import org.datanucleus.metadata.OrderMetaData;
  34. import org.datanucleus.metadata.PropertyMetaData;
  35. import org.datanucleus.metadata.RelationType;
  36. import org.datanucleus.metadata.VersionMetaData;
  37. import org.datanucleus.plugin.ConfigurationElement;
  38. import org.datanucleus.store.types.SCOUtils;
  39. import org.datanucleus.store.mapped.exceptions.NoSuchPersistentFieldException;
  40. import org.datanucleus.store.mapped.exceptions.NoTableManagedException;
  41. import org.datanucleus.store.mapped.DatastoreAdapter;
  42. import org.datanucleus.store.mapped.DatastoreClass;
  43. import org.datanucleus.store.mapped.DatastoreField;
  44. import org.datanucleus.store.mapped.DatastoreIdentifier;
  45. import org.datanucleus.store.mapped.IdentifierFactory;
  46. import org.datanucleus.store.mapped.IdentifierType;
  47. import org.datanucleus.store.mapped.MappedStoreManager;
  48. import org.datanucleus.store.mapped.exceptions.DuplicateDatastoreFieldException;
  49. import org.datanucleus.store.mapped.mapping.CorrespondentColumnsMapper;
  50. import org.datanucleus.store.mapped.mapping.DatastoreMapping;
  51. import org.datanucleus.store.mapped.mapping.DiscriminatorLongMapping;
  52. import org.datanucleus.store.mapped.mapping.DiscriminatorStringMapping;
  53. import org.datanucleus.store.mapped.mapping.IndexMapping;
  54. import org.datanucleus.store.mapped.mapping.IntegerMapping;
  55. import org.datanucleus.store.mapped.mapping.JavaTypeMapping;
  56. import org.datanucleus.store.mapped.mapping.LongMapping;
  57. import org.datanucleus.store.mapped.mapping.MappingConsumer;
  58. import org.datanucleus.store.mapped.mapping.OIDMapping;
  59. import org.datanucleus.store.mapped.mapping.PersistableMapping;
  60. import org.datanucleus.util.MultiMap;
  61. import org.datanucleus.util.NucleusLogger;
  62. import org.datanucleus.util.StringUtils;
  63. import com.google.appengine.datanucleus.DatastoreManager;
  64. import com.google.appengine.datanucleus.MetaDataUtils;
  65. import com.google.appengine.datanucleus.Utils;
  66. import java.text.MessageFormat;
  67. import java.util.Collection;
  68. import java.util.HashMap;
  69. import java.util.HashSet;
  70. import java.util.LinkedHashMap;
  71. import java.util.LinkedList;
  72. import java.util.List;
  73. import java.util.Map;
  74. import java.util.Set;
  75. /**
  76. * Describes a 'table' in the datastore. We don't actually have tables, but
  77. * we need to represent this logically in order to take care of all the nice
  78. * mapping logic that datanucleus does for us.
  79. *
  80. * This code is largely copied from AbstractClassTable, ClassTable, TableImpl, and AbstractTable
  81. *
  82. * TODO : Drop the code adding "foreign key" when we drop support for old storage versions
  83. *
  84. * @author Max Ross <maxr@google.com>
  85. */
  86. public class DatastoreTable implements DatastoreClass {
  87. /** All callbacks for class tables waiting to be performed. */
  88. private static final MultiMap callbacks = new MultiMap();
  89. private final DatastoreManager storeMgr;
  90. private final AbstractClassMetaData cmd;
  91. private final ClassLoaderResolver clr;
  92. private final DatastoreAdapter dba;
  93. private final DatastoreIdentifier identifier;
  94. /**
  95. * Mappings for fields mapped to this table, keyed by the FieldMetaData.
  96. * Supports fast lookup but also preserves order.
  97. */
  98. private final Map<AbstractMemberMetaData, JavaTypeMapping> fieldMappingsMap =
  99. new LinkedHashMap<AbstractMemberMetaData, JavaTypeMapping>();
  100. /**
  101. * Similar to {@link #fieldMappingsMap} except primary key fields are added as
  102. * well. This is needed to support persistence capable classes that can be
  103. * persisted as top-level classes and as embedded classes.
  104. */
  105. private final Map<AbstractMemberMetaData, JavaTypeMapping> embeddedFieldMappingsMap =
  106. new LinkedHashMap<AbstractMemberMetaData, JavaTypeMapping>();
  107. /**
  108. * All the properties in the table. Even though the datastore is schemaless,
  109. * the mappings provided by the ORM effectively impose a schema. This allows
  110. * us to know, up front, what properties we can expect.
  111. */
  112. private final List<DatastoreProperty> datastoreProperties = Utils.newArrayList();
  113. /**
  114. * Index to the props, keyed by name.
  115. */
  116. protected Map<String, DatastoreProperty> datastorePropertiesByName = Utils.newHashMap();
  117. /** Mapping for datastore identity (optional). */
  118. private JavaTypeMapping datastoreIDMapping;
  119. /** Mappings for application identity (optional). */
  120. private JavaTypeMapping[] pkMappings;
  121. /** Mapping for the id of the table. */
  122. private JavaTypeMapping idMapping;
  123. /** MetaData for discriminator for objects stored in this kind. */
  124. protected DiscriminatorMetaData discriminatorMetaData;
  125. /** Mapping for any discriminator column. */
  126. private JavaTypeMapping discriminatorMapping;
  127. /** Highest absolute field number managed by this table */
  128. private int highestFieldNumber = 0;
  129. /** Mappings to the owner where this is an element. */
  130. private final Map<AbstractMemberMetaData, JavaTypeMapping> externalFkMappings = Utils.newHashMap();
  131. /** Mappings of the position where this is an element in a List. */
  132. private final Map<AbstractMemberMetaData, JavaTypeMapping> externalOrderMappings = Utils.newHashMap();
  133. private AbstractMemberMetaData parentMappingMemberMetaData;
  134. /** MetaData for all classes being managed here. */
  135. private final Collection<AbstractClassMetaData> managedClassMetaData = new HashSet<AbstractClassMetaData>();
  136. public DatastoreTable(String kind, DatastoreManager storeMgr, AbstractClassMetaData cmd,
  137. ClassLoaderResolver clr, DatastoreAdapter dba) {
  138. this.storeMgr = storeMgr;
  139. this.cmd = cmd;
  140. this.clr = clr;
  141. this.dba = dba;
  142. this.identifier = new DatastoreKind(kind);
  143. }
  144. public String getType() {
  145. return cmd.getFullClassName();
  146. }
  147. public IdentityType getIdentityType() {
  148. return cmd.getIdentityType();
  149. }
  150. public boolean isObjectIdDatastoreAttributed() {
  151. return true;
  152. }
  153. public boolean isBaseDatastoreClass() {
  154. return true;
  155. }
  156. public DatastoreClass getBaseDatastoreClass() {
  157. return this;
  158. }
  159. public DatastoreClass getBaseDatastoreClassWithMember(AbstractMemberMetaData fmd) {
  160. if (fieldMappingsMap.get(fmd) != null) {
  161. return this;
  162. }
  163. return null;
  164. }
  165. public DatastoreClass getSuperDatastoreClass() {
  166. return null;
  167. }
  168. public Collection getSecondaryDatastoreClasses() {
  169. return null;
  170. }
  171. public boolean managesClass(String className) {
  172. return cmd.getFullClassName().equals(className);
  173. }
  174. public JavaTypeMapping getDatastoreObjectIdMapping() {
  175. return datastoreIDMapping;
  176. }
  177. public JavaTypeMapping getMemberMapping(String fieldName) {
  178. AbstractMemberMetaData fmd = getFieldMetaData(fieldName);
  179. JavaTypeMapping m = getMemberMapping(fmd);
  180. if (m == null) {
  181. throw new NoSuchPersistentFieldException(cmd.getFullClassName(), fieldName);
  182. }
  183. return m;
  184. }
  185. private AbstractMemberMetaData getFieldMetaData(String fieldName) {
  186. return cmd.getMetaDataForMember(fieldName);
  187. }
  188. public JavaTypeMapping getMemberMapping(AbstractMemberMetaData mmd) {
  189. if (mmd == null) {
  190. return null;
  191. }
  192. return fieldMappingsMap.get(mmd);
  193. }
  194. public JavaTypeMapping getMemberMappingInDatastoreClass(AbstractMemberMetaData mmd) {
  195. return getMemberMapping(mmd);
  196. }
  197. // Mostly copied from AbstractTable.addDatastoreField
  198. public DatastoreProperty addDatastoreField(String storedJavaType, DatastoreIdentifier name,
  199. JavaTypeMapping mapping, ColumnMetaData colmd) {
  200. // Create the column
  201. DatastoreProperty prop =
  202. new DatastoreProperty(this, mapping.getJavaType().getName(), name, (ColumnMetaData) colmd);
  203. if (hasColumnName(name)) {
  204. if (colmd == null || !(colmd instanceof ColumnMetaData)) {
  205. throw new NucleusException(
  206. "invalid column meta data property name on class " + getType() + " : " + colmd);
  207. }
  208. if (((ColumnMetaData) colmd).getInsertable() || ((ColumnMetaData) colmd).getUpdateable()) {
  209. // duplicate property names are ok if the field is neither insertable nor updatable
  210. if (!isSuperclassColumn(prop, mapping, colmd)) {
  211. throw new NucleusException(
  212. "Duplicate property name on class " + getType() + " : " + name);
  213. }
  214. }
  215. }
  216. DatastoreIdentifier colName = prop.getIdentifier();
  217. datastoreProperties.add(prop);
  218. datastorePropertiesByName.put(colName.getIdentifierName(), prop);
  219. return prop;
  220. }
  221. protected boolean hasColumnName(DatastoreIdentifier colName) {
  222. return getDatastoreField(colName) != null;
  223. }
  224. public boolean hasDatastoreField(DatastoreIdentifier identifier) {
  225. return (hasColumnName(identifier));
  226. }
  227. public boolean isSuperclassColumn(DatastoreField col, JavaTypeMapping mapping, MetaData colmd) {
  228. // Verify if a duplicate column is valid. A duplicate column name is (currently) valid when :-
  229. // 1. subclasses defining the duplicated column are using "super class table" strategy
  230. //
  231. // Find the MetaData for the existing column
  232. DatastoreIdentifier name = col.getIdentifier();
  233. DatastoreField existingCol = getDatastoreField(name);
  234. MetaData md = existingCol.getColumnMetaData().getParent();
  235. while (!(md instanceof AbstractClassMetaData)) {
  236. if (md == null) {
  237. // ColumnMetaData for existing column has no parent class somehow!
  238. throw new NucleusUserException(MessageFormat.format(
  239. "The property \"{0}\" exists in entity \"{1}\" and has invalid metadata. The existing property is \"{2}\"",
  240. name, this.identifier, colmd.toString()));
  241. }
  242. md = md.getParent();
  243. }
  244. // Find the MetaData for the column to be added
  245. MetaData dupMd = colmd.getParent();
  246. while (!(dupMd instanceof AbstractClassMetaData)) {
  247. dupMd = dupMd.getParent();
  248. if (dupMd == null) {
  249. // ColumnMetaData for required column has no parent class somehow!
  250. throw new NucleusUserException(MessageFormat.format(
  251. "The column \"{0}\" exists in table \"{1}\" and cannot be validated because a duplicated column has been specified and the metadata is invalid. The column is \"{2}\"",
  252. name, this.identifier, colmd.toString()));
  253. }
  254. }
  255. if (((AbstractClassMetaData) md).getFullClassName().equals(
  256. ((AbstractClassMetaData) dupMd).getFullClassName())) {
  257. // compare the current column defining class and the duplicated column defining class. if the same class,
  258. // we raise an exception when within one class it is defined a column twice
  259. // in some cases it could still be possible to have these duplicated columns, but does not make too
  260. // much sense in most of the cases. (this whole block of duplicated column check, could be optional, like a pmf property)
  261. throw new DuplicateDatastoreFieldException(this.toString(), existingCol, col);
  262. }
  263. // Make sure the field JavaTypeMappings are compatible
  264. if (mapping != null &&
  265. !mapping.getClass().isAssignableFrom(existingCol.getJavaTypeMapping().getClass()) &&
  266. !existingCol.getJavaTypeMapping().getClass().isAssignableFrom(mapping.getClass())) {
  267. // the mapping class must be the same (not really required, but to avoid user mistakes)
  268. throw new DuplicateDatastoreFieldException(this.toString(), existingCol, col);
  269. }
  270. // Make sure the field java types are compatible
  271. Class<?> fieldStoredJavaTypeClass = null;
  272. Class<?> existingColStoredJavaTypeClass = null;
  273. try {
  274. ClassLoaderResolver clr = storeMgr.getNucleusContext().getClassLoaderResolver(null);
  275. fieldStoredJavaTypeClass = clr.classForName(col.getStoredJavaType());
  276. existingColStoredJavaTypeClass = clr.classForName(col.getStoredJavaType());
  277. }
  278. catch (RuntimeException cnfe) {
  279. // Do nothing
  280. }
  281. if (fieldStoredJavaTypeClass != null && existingColStoredJavaTypeClass != null &&
  282. !fieldStoredJavaTypeClass.isAssignableFrom(existingColStoredJavaTypeClass) &&
  283. !existingColStoredJavaTypeClass.isAssignableFrom(fieldStoredJavaTypeClass)) {
  284. // the stored java type must be the same (not really required, but to avoid user mistakes)
  285. throw new DuplicateDatastoreFieldException(this.toString(), existingCol, col);
  286. }
  287. return true;
  288. }
  289. public DatastoreField getDatastoreField(String colName) {
  290. return datastorePropertiesByName.get(colName);
  291. }
  292. public DatastoreField getDatastoreField(DatastoreIdentifier identifier) {
  293. return getDatastoreField(identifier.getIdentifierName());
  294. }
  295. public JavaTypeMapping getIdMapping() {
  296. return idMapping;
  297. }
  298. public MappedStoreManager getStoreManager() {
  299. return storeMgr;
  300. }
  301. public DatastoreIdentifier getIdentifier() {
  302. return identifier;
  303. }
  304. public boolean isSuperDatastoreClass(DatastoreClass datastoreClass) {
  305. return false;
  306. }
  307. public boolean managesMapping(JavaTypeMapping javaTypeMapping) {
  308. return true;
  309. }
  310. public DatastoreField[] getDatastoreFields() {
  311. return datastoreProperties.toArray(new DatastoreField[datastoreProperties.size()]);
  312. }
  313. public void buildMapping() {
  314. initializePK();
  315. initializeNonPK();
  316. initializeDiscriminatorMapping(cmd.getDiscriminatorMetaDataForTable());
  317. runCallBacks();
  318. this.managedClassMetaData.add(cmd);
  319. }
  320. public void manageClass(AbstractClassMetaData cmd) {
  321. // We demand DiscriminatorMetaData, because in cases where an object of the
  322. // top class in the inheritance hierarchy will be persisted and the subclasses
  323. // are not yet known to datanucleus, we already need to know about the discriminator
  324. // property. At this point here the exception can be too late, if an object of
  325. // the top class has been persisted already. But at least the developer is
  326. // now informed, that he should add the DiscriminatorMetaData.
  327. if (getDiscriminatorMetaData() == null) {
  328. throw new NucleusUserException("Descriminator meta data for " +
  329. cmd.getFullClassName() +
  330. " is missing. Please specify at least the discriminator column.");
  331. }
  332. // Go through the fields for this class and add columns for them
  333. for (AbstractMemberMetaData fmd : cmd.getManagedMembers()) {
  334. addFieldMapping(cmd, fmd);
  335. }
  336. this.managedClassMetaData.add(cmd);
  337. }
  338. /**
  339. * returns the names of all classes managed by this table.
  340. * @return Names of the classes managed (stored) here
  341. */
  342. public String[] getManagedClasses() {
  343. List<String> list = Utils.transform(managedClassMetaData, new Utils.Function<AbstractClassMetaData, String>() {
  344. public String apply(AbstractClassMetaData cmd) {
  345. return cmd.getFullClassName();
  346. }
  347. });
  348. return list.toArray(new String[list.size()]);
  349. }
  350. private void initializeNonPK() {
  351. // We only support inheritance strategies that resolve to all fields for
  352. // a class plus all its subclasses living in every "table," so we'll
  353. // iterate over all managed fields in the entire chain, topmost classes
  354. // first so that overrides work properly.
  355. List<AbstractClassMetaData> cmdl = buildClassMetaDataList();
  356. // get the class level overridden members
  357. // simply reversing the order we iterate through the hierarchy
  358. // of ClassMetaData disorders the field numbers
  359. Map<String, AbstractMemberMetaData> overriddenFieldMap = new HashMap<String, AbstractMemberMetaData>();
  360. for (AbstractClassMetaData curCmd : cmdl) {
  361. for (AbstractMemberMetaData fmd : curCmd.getOverriddenMembers()) {
  362. overriddenFieldMap.put(fmd.getFullFieldName(), fmd);
  363. }
  364. }
  365. for (AbstractClassMetaData curCmd : cmdl) {
  366. // Go through the fields for this class and add columns for them
  367. for (AbstractMemberMetaData fmd : curCmd.getManagedMembers()) {
  368. if (overriddenFieldMap.containsKey(fmd.getFullFieldName())) {
  369. fmd = overriddenFieldMap.get(fmd.getFullFieldName());
  370. }
  371. addFieldMapping(curCmd, fmd);
  372. }
  373. }
  374. Class curClass = clr.classForName(cmd.getFullClassName()).getSuperclass();
  375. // see if any of our superclasses have a parentMappingField
  376. while (!Object.class.equals(curClass)) {
  377. DatastoreTable dt = null;
  378. try {
  379. dt = (DatastoreTable) storeMgr.getDatastoreClass(curClass.getName(), clr);
  380. } catch (NoTableManagedException ntme) {
  381. // this is ok, not all parent classes need to be managed
  382. }
  383. // sometimes the parent table is not yet initialized
  384. if (dt != null) {
  385. // inherit the parentMappingField
  386. if (parentMappingMemberMetaData == null) {
  387. if (dt.parentMappingMemberMetaData != null) {
  388. parentMappingMemberMetaData = dt.parentMappingMemberMetaData;
  389. break;
  390. }
  391. }
  392. // inherit any external fk mappings
  393. if (!dt.externalFkMappings.isEmpty()) {
  394. externalFkMappings.putAll(dt.externalFkMappings);
  395. }
  396. }
  397. curClass = curClass.getSuperclass();
  398. }
  399. }
  400. private void addFieldMapping(AbstractClassMetaData curCmd, AbstractMemberMetaData fmd) {
  401. // Primary key fields are added by the initialisePK method
  402. if (fmd.isPrimaryKey()) {
  403. // We need to know about this mapping when accessing the class as an embedded field.
  404. embeddedFieldMappingsMap.put(fmd, pkMappings[0]);
  405. } else {
  406. if (managesField(fmd.getFullFieldName())) {
  407. if (!fmd.getClassName(true).equals(curCmd.getFullClassName())) {
  408. throw new UnsupportedOperationException("Overrides not currently supported.");
  409. }
  410. } else {
  411. MetaDataManager mmgr = storeMgr.getMetaDataManager();
  412. // Manage the field if not already managed (may already exist if overriding a superclass field)
  413. JavaTypeMapping mapping = null;
  414. if (fmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT) {
  415. boolean isPrimary = true;
  416. if (fmd.getTable() != null && fmd.getJoinMetaData() == null) {
  417. // Field has a table specified and is not a 1-N with join table so is mapped to a secondary table
  418. isPrimary = false;
  419. }
  420. if (isPrimary) {
  421. // Add the field to this table
  422. mapping = dba.getMappingManager(storeMgr).getMapping(this, fmd, clr, FieldRole.ROLE_FIELD);
  423. addFieldMapping(mapping);
  424. embeddedFieldMappingsMap.put(fmd, fieldMappingsMap.get(fmd));
  425. } else {
  426. throw new UnsupportedOperationException("No support for secondary tables.");
  427. }
  428. } else if (fmd.getPersistenceModifier() != FieldPersistenceModifier.TRANSACTIONAL) {
  429. throw new NucleusException("Invalid persistence-modifier for field ").setFatal();
  430. }
  431. // Calculate if we need a FK adding due to a 1-N relationship TODO Remove this when no longer supporting old storageVersion
  432. // Note that we ignore any "join" setting since we don't use join tables
  433. boolean needsFKToContainerOwner = false;
  434. RelationType relationType = fmd.getRelationType(clr);
  435. if (relationType == RelationType.ONE_TO_MANY_BI) {
  436. /*AbstractMemberMetaData[] relatedMmds = fmd.getRelatedMemberMetaData(clr);
  437. if (fmd.getJoinMetaData() == null && relatedMmds[0].getJoinMetaData() == null) {*/
  438. needsFKToContainerOwner = true;
  439. /*}*/
  440. } else if (relationType == RelationType.ONE_TO_MANY_UNI) {
  441. /*if (fmd.getJoinMetaData() == null) {*/
  442. needsFKToContainerOwner = true;
  443. /*}*/
  444. } else if (relationType == RelationType.ONE_TO_ONE_BI) {
  445. if (fmd.getMappedBy() != null && MetaDataUtils.isOwnedRelation(fmd, storeMgr)) {
  446. // This element type has a many-to-one pointing back.
  447. // We assume that our pk is part of the pk of the element type.
  448. DatastoreTable dt = storeMgr.getDatastoreClass(fmd.getAbstractClassMetaData().getFullClassName(), clr);
  449. dt.runCallBacks();
  450. dt.markFieldAsParentKeyProvider(fmd.getName());
  451. }
  452. } else if (relationType == RelationType.MANY_TO_ONE_BI) {
  453. }
  454. if (needsFKToContainerOwner) {
  455. // 1-N uni/bidirectional using FK, so update the element side with a FK
  456. if ((fmd.getCollection() != null && !SCOUtils.collectionHasSerialisedElements(fmd)) ||
  457. (fmd.getArray() != null && !SCOUtils.arrayIsStoredInSingleColumn(fmd, mmgr))) {
  458. // 1-N ForeignKey collection/array, so add FK to element table
  459. AbstractClassMetaData elementCmd;
  460. if (fmd.hasCollection()) {
  461. // Collection
  462. elementCmd = mmgr.getMetaDataForClass(fmd.getCollection().getElementType(), clr);
  463. } else {
  464. // Array
  465. elementCmd = mmgr.getMetaDataForClass(fmd.getType().getComponentType(), clr);
  466. }
  467. if (elementCmd == null) {
  468. // Elements that are reference types or non-PC will come through here
  469. } else {
  470. AbstractClassMetaData[] elementCmds;
  471. // TODO : Cater for interface elements, and get the metadata for the implementation classes here
  472. if (elementCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUBCLASS_TABLE) {
  473. elementCmds = storeMgr.getClassesManagingTableForClass(elementCmd, clr);
  474. } else {
  475. elementCmds = new ClassMetaData[1];
  476. elementCmds[0] = elementCmd;
  477. }
  478. // Run callbacks for each of the element classes.
  479. for (AbstractClassMetaData elementCmd1 : elementCmds) {
  480. callbacks.put(elementCmd1.getFullClassName(), new CallBack(fmd, cmd.getFullClassName()));
  481. DatastoreTable dt =
  482. (DatastoreTable) storeMgr.getDatastoreClass(elementCmd1.getFullClassName(), clr);
  483. dt.runCallBacks();
  484. if (fmd.getMappedBy() != null && MetaDataUtils.isOwnedRelation(fmd, storeMgr)) {
  485. // This element type has a many-to-one pointing back.
  486. // We assume that our pk is part of the pk of the element type.
  487. dt.markFieldAsParentKeyProvider(fmd.getMappedBy());
  488. }
  489. }
  490. }
  491. } else if (fmd.getMap() != null && !SCOUtils.mapHasSerialisedKeysAndValues(fmd)) {
  492. // 1-N ForeignKey map, so add FK to value table
  493. if (fmd.getKeyMetaData() != null && fmd.getKeyMetaData().getMappedBy() != null) {
  494. // Key is stored in the value table so add the FK to the value table
  495. AbstractClassMetaData valueCmd = mmgr.getMetaDataForClass(fmd.getMap().getValueType(), clr);
  496. if (valueCmd == null) {
  497. // Interface elements will come through here and java.lang.String and others as well
  498. }
  499. } else if (fmd.getValueMetaData() != null
  500. && fmd.getValueMetaData().getMappedBy() != null) {
  501. // Value is stored in the key table so add the FK to the key table
  502. AbstractClassMetaData keyCmd = mmgr.getMetaDataForClass(fmd.getMap().getKeyType(), clr);
  503. if (keyCmd == null) {
  504. // Interface elements will come through here and java.lang.String and others as well
  505. } else {
  506. }
  507. }
  508. }
  509. }
  510. }
  511. }
  512. }
  513. /**
  514. * Constructs a list containing the class meta data of {@link #cmd} and all
  515. * its superclasses. We add to the beginning of the list as we iterate up so
  516. * the class at the top of the hierarchy is returned first.
  517. */
  518. private LinkedList<AbstractClassMetaData> buildClassMetaDataList() {
  519. LinkedList<AbstractClassMetaData> stack = Utils.newLinkedList();
  520. AbstractClassMetaData curCmd = cmd;
  521. while (curCmd != null) {
  522. stack.addFirst(curCmd);
  523. curCmd = curCmd.getSuperAbstractClassMetaData();
  524. }
  525. return stack;
  526. }
  527. private void markFieldAsParentKeyProvider(String mappedBy) {
  528. AbstractMemberMetaData newParentMappingField = getFieldMetaData(mappedBy);
  529. if (parentMappingMemberMetaData == null) {
  530. parentMappingMemberMetaData = newParentMappingField;
  531. } else if (parentMappingMemberMetaData != newParentMappingField) { // intentional reference compare
  532. throw new NucleusException(
  533. "App Engine ORM does not support multiple parent key provider fields : class=" + cmd.getFullClassName() +
  534. " requested to have member=" + mappedBy +
  535. " but already have member=" + parentMappingMemberMetaData.getName());
  536. }
  537. }
  538. protected void addFieldMapping(JavaTypeMapping fieldMapping) {
  539. AbstractMemberMetaData fmd = fieldMapping.getMemberMetaData();
  540. fieldMappingsMap.put(fmd, fieldMapping);
  541. // Update highest field number if this is higher
  542. int absoluteFieldNumber = fmd.getAbsoluteFieldNumber();
  543. if (absoluteFieldNumber > highestFieldNumber) {
  544. highestFieldNumber = absoluteFieldNumber;
  545. }
  546. }
  547. public boolean managesField(String fieldName) {
  548. return fieldName != null && getMappingForFullFieldName(fieldName) != null;
  549. }
  550. /**
  551. * Accessor for the JavaTypeMapping that is handling the field of the specified name. Returns the
  552. * first one that matches.
  553. *
  554. * @param fieldName Name of the field
  555. * @return The java type mapping
  556. */
  557. public JavaTypeMapping getMappingForFullFieldName(String fieldName) {
  558. Set fields = fieldMappingsMap.keySet();
  559. for (Object field : fields) {
  560. AbstractMemberMetaData fmd = (AbstractMemberMetaData) field;
  561. if (fmd.getFullFieldName().equals(fieldName)) {
  562. return fieldMappingsMap.get(fmd);
  563. }
  564. }
  565. return null;
  566. }
  567. public JavaTypeMapping getMappingForSimpleFieldName(String fieldName) {
  568. Set fields = fieldMappingsMap.keySet();
  569. for (Object field : fields) {
  570. AbstractMemberMetaData fmd = (AbstractMemberMetaData) field;
  571. if (fmd.getName().equals(fieldName)) {
  572. return fieldMappingsMap.get(fmd);
  573. }
  574. }
  575. return null;
  576. }
  577. void addDatastoreId(ColumnMetaData columnMetaData, DatastoreClass refTable,
  578. AbstractClassMetaData cmd) {
  579. datastoreIDMapping = new OIDMapping();
  580. datastoreIDMapping.initialize(storeMgr, cmd.getFullClassName());
  581. // Create a ColumnMetaData in the container if none is defined
  582. ColumnMetaData colmd;
  583. if (columnMetaData == null) {
  584. colmd = new ColumnMetaData();
  585. } else {
  586. colmd = columnMetaData;
  587. }
  588. if (colmd.getName() == null) {
  589. // Provide default column naming if none is defined
  590. if (refTable != null) {
  591. colmd.setName(storeMgr.getIdentifierFactory()
  592. .newDatastoreFieldIdentifier(refTable.getIdentifier().getIdentifierName(),
  593. this.storeMgr.getNucleusContext().getTypeManager().isDefaultEmbeddedType(OID.class),
  594. FieldRole.ROLE_OWNER).getIdentifierName());
  595. } else {
  596. colmd.setName(
  597. storeMgr.getIdentifierFactory().newDatastoreFieldIdentifier(identifier.getIdentifierName(),
  598. this.storeMgr.getNucleusContext().getTypeManager().isDefaultEmbeddedType(OID.class),
  599. FieldRole.ROLE_NONE).getIdentifierName());
  600. }
  601. }
  602. // Add the datastore identity column as the PK
  603. DatastoreField idColumn = addDatastoreField(OID.class.getName(),
  604. storeMgr.getIdentifierFactory().newIdentifier(IdentifierType.COLUMN, colmd.getName()),
  605. datastoreIDMapping, colmd);
  606. idColumn.setAsPrimaryKey();
  607. // Set the identity column type based on the IdentityStrategy
  608. String strategyName = cmd.getIdentityMetaData().getValueStrategy().toString();
  609. if (cmd.getIdentityMetaData().getValueStrategy().equals(IdentityStrategy.CUSTOM)) {
  610. strategyName = cmd.getIdentityMetaData().getValueStrategy().getCustomName();
  611. }
  612. // Check the POID type being stored
  613. Class poidClass = Long.class;
  614. ConfigurationElement elem =
  615. storeMgr.getNucleusContext().getPluginManager().getConfigurationElementForExtension(
  616. "org.datanucleus.store_valuegenerator",
  617. new String[]{"name", "unique"}, new String[]{strategyName, "true"});
  618. if (elem == null) {
  619. // Not datastore-independent, so try for this datastore
  620. elem = storeMgr.getNucleusContext().getPluginManager().getConfigurationElementForExtension(
  621. "org.datanucleus.store_valuegenerator",
  622. new String[]{"name", "datastore"},
  623. new String[]{strategyName, storeMgr.getStoreManagerKey()});
  624. }
  625. if (elem != null) {
  626. // Set the generator name (for use by the PoidManager)
  627. String generatorClassName = elem.getAttribute("class-name");
  628. Class generatorClass =
  629. getStoreManager().getNucleusContext().getClassLoaderResolver(null)
  630. .classForName(generatorClassName);
  631. try {
  632. poidClass = (Class) generatorClass.getMethod("getStorageClass").invoke(null);
  633. }
  634. catch (Exception e) {
  635. // Unable to get the storage class from the PoidGenerator class
  636. NucleusLogger.VALUEGENERATION
  637. .warn("Error retrieving storage class for POID generator " + generatorClassName +
  638. " " + e.getMessage());
  639. }
  640. }
  641. dba.getMappingManager(storeMgr)
  642. .createDatastoreMapping(datastoreIDMapping, idColumn, poidClass.getName());
  643. // Handle any auto-increment requirement
  644. if (isObjectIdDatastoreAttributed()) {
  645. // Only the base class can be autoincremented
  646. // idColumn.setAutoIncrement(true);
  647. }
  648. }
  649. private void initializePK() {
  650. AbstractMemberMetaData[] fieldsToAdd = new AbstractMemberMetaData[cmd.getNoOfPrimaryKeyMembers()];
  651. // Initialise Primary Key mappings for application id with PK fields in this class
  652. int pkFieldNum = 0;
  653. int fieldCount = cmd.getNoOfManagedMembers();
  654. boolean hasPrimaryKeyInThisClass = false;
  655. if (cmd.getNoOfPrimaryKeyMembers() > 0) {
  656. pkMappings = new JavaTypeMapping[cmd.getNoOfPrimaryKeyMembers()];
  657. if (cmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.COMPLETE_TABLE) {
  658. // COMPLETE-TABLE so use root class metadata and add PK members
  659. // TODO Does this allow for overridden PK field info ?
  660. AbstractClassMetaData baseCmd = cmd.getBaseAbstractClassMetaData();
  661. fieldCount = baseCmd.getNoOfManagedMembers();
  662. for (int relFieldNum = 0; relFieldNum < fieldCount; ++relFieldNum) {
  663. AbstractMemberMetaData mmd = baseCmd.getMetaDataForManagedMemberAtRelativePosition(relFieldNum);
  664. if (mmd.isPrimaryKey()) {
  665. if (mmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT) {
  666. fieldsToAdd[pkFieldNum++] = mmd;
  667. hasPrimaryKeyInThisClass = true;
  668. } else if (mmd.getPersistenceModifier() != FieldPersistenceModifier.TRANSACTIONAL) {
  669. throw new NucleusException("Invalid persistence-modifier for field " + mmd.getName()).setFatal();
  670. }
  671. }
  672. }
  673. } else {
  674. for (int relFieldNum = 0; relFieldNum < fieldCount; ++relFieldNum) {
  675. AbstractMemberMetaData fmd = cmd.getMetaDataForManagedMemberAtRelativePosition(relFieldNum);
  676. if (fmd.isPrimaryKey()) {
  677. if (fmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT) {
  678. fieldsToAdd[pkFieldNum++] = fmd;
  679. hasPrimaryKeyInThisClass = true;
  680. } else if (fmd.getPersistenceModifier() != FieldPersistenceModifier.TRANSACTIONAL) {
  681. throw new NucleusException("Invalid persistence-modifier for field" + fmd.getName()).setFatal();
  682. }
  683. }
  684. }
  685. }
  686. }
  687. // No Primary Key defined, so search for superclass or handle datastore id
  688. if (!hasPrimaryKeyInThisClass) {
  689. if (cmd.getIdentityType() == IdentityType.APPLICATION) {
  690. // application-identity
  691. DatastoreClass elementCT =
  692. storeMgr.getDatastoreClass(cmd.getPersistenceCapableSuperclass(), clr);
  693. if (elementCT != null) {
  694. // Superclass has a table so copy its PK mappings
  695. ColumnMetaDataContainer colContainer = null;
  696. if (cmd.getInheritanceMetaData() != null) {
  697. // Try via <inheritance><join>...</join></inheritance>
  698. colContainer = cmd.getInheritanceMetaData().getJoinMetaData();
  699. }
  700. if (colContainer == null) {
  701. // Try via <primary-key>...</primary-key>
  702. colContainer = cmd.getPrimaryKeyMetaData();
  703. }
  704. addApplicationIdUsingClassTableId(colContainer, elementCT, clr, cmd);
  705. } else {
  706. // Superclass has no table so create new mappings and columns
  707. AbstractClassMetaData pkCmd =
  708. storeMgr.getClassWithPrimaryKeyForClass(cmd.getSuperAbstractClassMetaData(), clr);
  709. if (pkCmd != null) {
  710. pkMappings = new JavaTypeMapping[pkCmd.getNoOfPrimaryKeyMembers()];
  711. pkFieldNum = 0;
  712. fieldCount = pkCmd.getNoOfInheritedManagedMembers() + pkCmd.getNoOfManagedMembers();
  713. for (int absFieldNum = 0; absFieldNum < fieldCount; ++absFieldNum) {
  714. AbstractMemberMetaData fmd = pkCmd
  715. .getMetaDataForManagedMemberAtAbsolutePosition(absFieldNum);
  716. if (fmd.isPrimaryKey()) {
  717. AbstractMemberMetaData overriddenFmd = cmd.getOverriddenMember(fmd.getName());
  718. if (overriddenFmd != null) {
  719. // PK field is overridden so use the overriding definition
  720. fmd = overriddenFmd;
  721. }
  722. if (fmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT) {
  723. fieldsToAdd[pkFieldNum++] = fmd;
  724. } else if (fmd.getPersistenceModifier() != FieldPersistenceModifier.TRANSACTIONAL) {
  725. throw new NucleusException("Invalid persistence-modifier for field " + fmd.getName()).setFatal();
  726. }
  727. }
  728. }
  729. }
  730. }
  731. } else if (cmd.getIdentityType() == IdentityType.DATASTORE) {
  732. // datastore-identity
  733. ColumnMetaData colmd = null;
  734. if (cmd.getIdentityMetaData() != null
  735. && cmd.getIdentityMetaData().getColumnMetaData() != null) {
  736. // Try via <datastore-identity>...</datastore-identity>
  737. colmd = cmd.getIdentityMetaData().getColumnMetaData();
  738. }
  739. if (colmd == null) {
  740. // Try via <primary-key>...</primary-key>
  741. if (cmd.getPrimaryKeyMetaData() != null && cmd.getPrimaryKeyMetaData().getColumnMetaData() != null &&
  742. cmd.getPrimaryKeyMetaData().getColumnMetaData().length > 0) {
  743. colmd = cmd.getPrimaryKeyMetaData().getColumnMetaData()[0];
  744. }
  745. }
  746. addDatastoreId(colmd, null, cmd);
  747. } else if (cmd.getIdentityType() == IdentityType.NONDURABLE) {
  748. // Do nothing since no identity!
  749. }
  750. }
  751. //add field mappings in the end, so we compute all columns after the post initialize
  752. for (int i = 0; i < fieldsToAdd.length; i++) {
  753. if (fieldsToAdd[i] != null) {
  754. try {
  755. DatastoreClass datastoreClass = getStoreManager()
  756. .getDatastoreClass(fieldsToAdd[i].getType().getName(), clr);
  757. if (datastoreClass.getIdMapping() == null) {
  758. throw new NucleusException(
  759. "Unsupported relationship with field " + fieldsToAdd[i].getFullFieldName())
  760. .setFatal();
  761. }
  762. }
  763. catch (NoTableManagedException ex) {
  764. //do nothing
  765. }
  766. JavaTypeMapping fieldMapping = dba.getMappingManager(storeMgr)
  767. .getMapping(this, fieldsToAdd[i], clr, FieldRole.ROLE_FIELD);
  768. addFieldMapping(fieldMapping);
  769. pkMappings[i] = fieldMapping;
  770. }
  771. }
  772. initializeIDMapping();
  773. }
  774. private void initializeDiscriminatorMapping(DiscriminatorMetaData dismd) {
  775. if (dismd != null) {
  776. discriminatorMetaData = dismd;
  777. if (dismd.getStrategy() == DiscriminatorStrategy.CLASS_NAME) {
  778. discriminatorMapping = new DiscriminatorStringMapping(
  779. dba, this, dba.getMappingManager(storeMgr).getMapping(String.class));
  780. } else if (dismd.getStrategy() == DiscriminatorStrategy.VALUE_MAP) {
  781. ColumnMetaData disColmd = dismd.getColumnMetaData();
  782. if (disColmd != null && disColmd.getJdbcType() != null) {
  783. if (disColmd.getJdbcType().equalsIgnoreCase("INTEGER")
  784. || disColmd.getJdbcType().equalsIgnoreCase("BIGINT")
  785. || disColmd.getJdbcType().equalsIgnoreCase("NUMERIC")) {
  786. discriminatorMapping = new DiscriminatorLongMapping(dba, this, dba
  787. .getMappingManager(storeMgr).getMapping(Long.class));
  788. } else {
  789. discriminatorMapping = new DiscriminatorStringMapping(
  790. dba, this, dba.getMappingManager(storeMgr).getMapping(String.class));
  791. }
  792. } else {
  793. discriminatorMapping = new DiscriminatorStringMapping(
  794. dba, this, dba.getMappingManager(storeMgr).getMapping(String.class));
  795. }
  796. }
  797. }
  798. }
  799. /**
  800. * Initialize the ID Mapping
  801. */
  802. private void initializeIDMapping() {
  803. if (idMapping != null) {
  804. return;
  805. }
  806. final PersistableMapping mapping = new PersistableMapping();
  807. mapping.initialize(getStoreManager(), cmd.getFullClassName());
  808. if (getIdentityType() == IdentityType.DATASTORE) {
  809. mapping.addJavaTypeMapping(datastoreIDMapping);
  810. } else if (getIdentityType() == IdentityType.APPLICATION) {
  811. for (JavaTypeMapping pkMapping : pkMappings) {
  812. mapping.addJavaTypeMapping(pkMapping);
  813. }
  814. } else {
  815. // Nothing to do for nondurable since no identity
  816. }
  817. idMapping = mapping;
  818. }
  819. final void addApplicationIdUsingClassTableId(ColumnMetaDataContainer columnContainer,
  820. DatastoreClass refTable, ClassLoaderResolver clr, AbstractClassMetaData cmd) {
  821. ColumnMetaData[] userdefinedCols = null;
  822. int nextUserdefinedCol = 0;
  823. if (columnContainer != null) {
  824. userdefinedCols = columnContainer.getColumnMetaData();
  825. }
  826. pkMappings = new JavaTypeMapping[cmd.getPKMemberPositions().length];
  827. for (int i = 0; i < cmd.getPKMemberPositions().length; i++) {
  828. AbstractMemberMetaData fmd = cmd
  829. .getMetaDataForManagedMemberAtAbsolutePosition(cmd.getPKMemberPositions()[i]);
  830. JavaTypeMapping mapping = refTable.getMemberMapping(fmd);
  831. if (mapping == null) {
  832. //probably due to invalid metadata defined by the user
  833. throw new NucleusUserException("Cannot find mapping for field " + fmd.getFullFieldName() +
  834. " in table " + refTable.toString() + " " +
  835. StringUtils.objectArrayToString(refTable.getDatastoreFields()));
  836. }
  837. JavaTypeMapping masterMapping = storeMgr.getMappingManager()
  838. .getMapping(clr.classForName(mapping.getType()));
  839. masterMapping.setMemberMetaData(fmd); // Update field info in mapping
  840. pkMappings[i] = masterMapping;
  841. // Loop through each id column in the reference table and add the same here
  842. // applying the required names from the columnContainer
  843. for (int j = 0; j < mapping.getNumberOfDatastoreMappings(); j++) {
  844. JavaTypeMapping m = masterMapping;
  845. DatastoreField refColumn = mapping.getDatastoreMapping(j).getDatastoreField();
  846. if (mapping instanceof PersistableMapping) {
  847. m = storeMgr.getMappingManager()
  848. .getMapping(clr.classForName(refColumn.getJavaTypeMapping().getType()));
  849. ((PersistableMapping) masterMapping).addJavaTypeMapping(m);
  850. }
  851. ColumnMetaData userdefinedColumn = null;
  852. if (userdefinedCols != null) {
  853. for (ColumnMetaData userdefinedCol : userdefinedCols) {
  854. if (refColumn.getIdentifier().toString().equals(userdefinedCol.getTarget())) {
  855. userdefinedColumn = userdefinedCol;
  856. break;
  857. }
  858. }
  859. if (userdefinedColumn == null && nextUserdefinedCol < userdefinedCols.length) {
  860. userdefinedColumn = userdefinedCols[nextUserdefinedCol++];
  861. }
  862. }
  863. // Add this application identity column
  864. DatastoreField idColumn;
  865. if (userdefinedColumn != null) {
  866. // User has provided a name for this column
  867. // Currently we only use the column namings from the users definition but we could easily
  868. // take more of their details.
  869. idColumn = addDatastoreField(refColumn.getStoredJavaType(),
  870. storeMgr.getIdentifierFactory().newIdentifier(IdentifierType.COLUMN,
  871. userdefinedColumn.getName()),
  872. m, refColumn.getColumnMetaData());
  873. } else {
  874. // No name provided so take same as superclass
  875. idColumn = addDatastoreField(refColumn.getStoredJavaType(), refColumn.getIdentifier(),
  876. m, refColumn.getColumnMetaData());
  877. }
  878. if (mapping != null
  879. && mapping.getDatastoreMapping(j).getDatastoreField().getColumnMetaData() != null) {
  880. refColumn.copyConfigurationTo(idColumn);
  881. }
  882. idColumn.setAsPrimaryKey();
  883. // Set the column type based on the field.getType()
  884. getStoreManager().getMappingManager()
  885. .createDatastoreMapping(m, idColumn, refColumn.getJavaTypeMapping().getType());
  886. }
  887. // Update highest field number if this is higher
  888. int absoluteFieldNumber = fmd.getAbsoluteFieldNumber();
  889. if (absoluteFieldNumber > highestFieldNumber) {
  890. highestFieldNumber = absoluteFieldNumber;
  891. }
  892. }
  893. }
  894. public void provideDatastoreIdMappings(MappingConsumer consumer) {
  895. consumer.preConsumeMapping(highestFieldNumber + 1);
  896. if (getIdentityType() == IdentityType.DATASTORE) {
  897. consumer.consumeMapping(getDatastoreObjectIdMapping(), MappingConsumer.MAPPING_TYPE_DATASTORE_ID);
  898. }
  899. }
  900. public void providePrimaryKeyMappings(MappingConsumer consumer) {
  901. consumer.preConsumeMapping(highestFieldNumber + 1);
  902. if (pkMappings != null) {
  903. // Application identity
  904. int[] primaryKeyFieldNumbers = cmd.getPKMemberPositions();
  905. for (int i = 0; i < pkMappings.length; i++) {
  906. // Make the assumption that the pkMappings are in the same order as the absolute field numbers
  907. AbstractMemberMetaData fmd = cmd
  908. .getMetaDataForManagedMemberAtAbsolutePosition(primaryKeyFieldNumbers[i]);
  909. consumer.consumeMapping(pkMappings[i], fmd);
  910. }
  911. } else {
  912. // Datastore identity
  913. int[] primaryKeyFieldNumbers = cmd.getPKMemberPositions();
  914. int countPkFields = cmd.getNoOfPrimaryKeyMembers();
  915. for (int i = 0; i < countPkFields; i++) {
  916. AbstractMemberMetaData pkfmd = cmd
  917. .getMetaDataForManagedMemberAtAbsolutePosition(primaryKeyFieldNumbers[i]);
  918. consumer.consumeMapping(getMemberMapping(pkfmd), pkfmd);
  919. }
  920. }
  921. }
  922. public void provideNonPrimaryKeyMappings(MappingConsumer consumer) {
  923. provideNonPrimaryKeyMappings(consumer, false);
  924. }
  925. public void provideNonPrimaryKeyMappings(MappingConsumer consumer, boolean isEmbedded) {
  926. consumer.preConsumeMapping(highestFieldNumber + 1);
  927. Set<Map.Entry<AbstractMemberMetaData, JavaTypeMapping>> entries =
  928. isEmbedded ? embeddedFieldMappingsMap.entrySet() : fieldMappingsMap.entrySet();
  929. for (Map.Entry<AbstractMemberMetaData, JavaTypeMapping> entry : entries) {
  930. if (entry.getValue() != null) {
  931. if (!entry.getKey().isPrimaryKey() || isEmbedded) {
  932. consumer.consumeMapping(entry.getValue(), entry.getKey());
  933. }
  934. }
  935. }
  936. }
  937. public void provideMappingsForMembers(MappingConsumer consumer, AbstractMemberMetaData[] mmds,
  938. boolean includeSecondaryTables) {
  939. for (AbstractMemberMetaData aFieldMetaData : mmds) {
  940. JavaTypeMapping fieldMapping = fieldMappingsMap.get(aFieldMetaData);
  941. if (fieldMapping != null) {
  942. if (!aFieldMetaData.isPrimaryKey()) {
  943. consumer.consumeMapping(fieldMapping, aFieldMetaData);
  944. }
  945. }
  946. }
  947. }
  948. public void provideVersionMappings(MappingConsumer consumer) {
  949. }
  950. public void provideDiscriminatorMappings(MappingConsumer consumer) {
  951. consumer.preConsumeMapping(highestFieldNumber + 1);
  952. if (getDiscriminatorMapping(false) != null) {
  953. consumer.consumeMapping(getDiscriminatorMapping(false),
  954. MappingConsumer.MAPPING_TYPE_DISCRIMINATOR);
  955. }
  956. }
  957. public void provideUnmappedDatastoreFields(MappingConsumer consumer) {
  958. }
  959. public void provideExternalMappings(MappingConsumer consumer, int mappingType) {
  960. }
  961. public JavaTypeMapping getExternalMapping(AbstractMemberMetaData fmd, int mappingType) {
  962. if (mappingType == MappingConsumer.MAPPING_TYPE_EXTERNAL_FK) {
  963. return getExternalFkMappings().get(fmd);
  964. } else if (mappingType == MappingConsumer.MAPPING_TYPE_EXTERNAL_FK_DISCRIM) {
  965. return null; //getExternalFkDiscriminatorMappings().get(fmd);
  966. } else if (mappingType == MappingConsumer.MAPPING_TYPE_EXTERNAL_INDEX) {
  967. return getExternalOrderMappings().get(fmd);
  968. } else {
  969. return null;
  970. }
  971. }
  972. public AbstractMemberMetaData getMetaDataForExternalMapping(JavaTypeMapping mapping,
  973. int mappingType) {
  974. if (mappingType == MappingConsumer.MAPPING_TYPE_EXTERNAL_FK) {
  975. Set entries = getExternalFkMappings().entrySet();
  976. for (Object entry1 : entries) {
  977. Map.Entry entry = (Map.Entry) entry1;
  978. if (entry.getValue() == mapping) {
  979. return (AbstractMemberMetaData) entry.getKey();
  980. }
  981. }
  982. }
  983. return null;
  984. }
  985. public DiscriminatorMetaData getDiscriminatorMetaData() {
  986. return discriminatorMetaData;
  987. }
  988. public JavaTypeMapping getDiscriminatorMapping(boolean allowSuperclasses) {
  989. return discriminatorMapping;
  990. }
  991. public VersionMetaData getVersionMetaData() {
  992. return null;
  993. }
  994. public JavaTypeMapping getVersionMapping(boolean allowSuperclasses) {
  995. return null;
  996. }
  997. /**
  998. * Accessor for all of the external FK mappings (used by FK Lists, Collections, Arrays)
  999. * @return The mappings for the FKs of 1-Ns, keyed by the field/property metadata.
  1000. */
  1001. public Map<AbstractMemberMetaData, JavaTypeMapping> getExternalFkMappings() {
  1002. return externalFkMappings;
  1003. }
  1004. /**
  1005. * Accessor for all of the order mappings (used by FK Lists, Collections, Arrays)
  1006. * @return The mappings for the order columns of Lists, keyed by the field/property metadata.
  1007. */
  1008. public Map<AbstractMemberMetaData, JavaTypeMapping> getExternalOrderMappings() {
  1009. return externalOrderMappings;
  1010. }
  1011. /**
  1012. * Execute the callbacks for the classes that this table maps to.
  1013. */
  1014. private void runCallBacks() {
  1015. Collection c = (Collection) callbacks.remove(cmd.getFullClassName());
  1016. if (c == null) {
  1017. return;
  1018. }
  1019. for (Object aC : c) {
  1020. CallBack callback = (CallBack) aC;
  1021. /* if (callback.fmd.getJoinMetaData() == null) {*/
  1022. // 1-N relationship
  1023. AbstractMemberMetaData ownerFmd = callback.fmd;
  1024. if (ownerFmd.getMappedBy() != null) {
  1025. // Bidirectional (element has a PC mapping to the owner)
  1026. // Check that the "mapped-by" field in the other class actually exists
  1027. AbstractMemberMetaData fmd = cmd.getMetaDataForMember(ownerFmd.getMappedBy());
  1028. if (fmd == null) {
  1029. throw new NucleusUserException(
  1030. String.format(
  1031. "Unable to find the field \"{0}\" in the class \"{1}\" with a relationship to the field \"{2}\"",
  1032. ownerFmd.getMappedBy(),
  1033. cmd.getFullClassName(),
  1034. ownerFmd.getFullFieldName()));
  1035. }
  1036. // Add the order mapping as necessary
  1037. addOrderMapping(ownerFmd, null);
  1038. } else {
  1039. // Unidirectional (element knows nothing about the owner)
  1040. String ownerClassName = callback.ownerClassName;
  1041. JavaTypeMapping fkMapping = new PersistableMapping();
  1042. fkMapping.initialize(storeMgr, ownerClassName);
  1043. JavaTypeMapping orderMapping = null;
  1044. // Get the owner id mapping of the "1" end
  1045. JavaTypeMapping ownerIdMapping =
  1046. storeMgr.getDatastoreClass(ownerClassName, clr).getIdMapping();
  1047. ColumnMetaDataContainer colmdContainer = null;
  1048. if (ownerFmd.hasCollection() || ownerFmd.hasArray()) {
  1049. // 1-N Collection/array
  1050. colmdContainer = ownerFmd.getElementMetaData();
  1051. } else if (ownerFmd.hasMap() && ownerFmd.getKeyMetaData() != null
  1052. && ownerFmd.getKeyMetaData().getMappedBy() != null) {
  1053. // 1-N Map with key stored in the value
  1054. colmdContainer = ownerFmd.getValueMetaData();
  1055. } else if (ownerFmd.hasMap() && ownerFmd.getValueMetaData() != null
  1056. && ownerFmd.getValueMetaData().getMappedBy() != null) {
  1057. // 1-N Map with value stored in the key
  1058. colmdContainer = ownerFmd.getKeyMetaData();
  1059. }
  1060. CorrespondentColumnsMapper correspondentColumnsMapping =
  1061. new CorrespondentColumnsMapper(colmdContainer, ownerIdMapping, true);
  1062. int countIdFields = ownerIdMapping.getNumberOfDatastoreMappings();
  1063. for (int i = 0; i < countIdFields; i++) {
  1064. DatastoreMapping refDatastoreMapping = ownerIdMapping.getDatastoreMapping(i);
  1065. JavaTypeMapping mapping = storeMgr.getMappingManager()
  1066. .getMapping(refDatastoreMapping.getJavaTypeMapping().getJavaType());
  1067. ColumnMetaData colmd = correspondentColumnsMapping.getColumnMetaDataByIdentifier(
  1068. refDatastoreMapping.getDatastoreField().getIdentifier());
  1069. if (colmd == null) {
  1070. throw new NucleusFatalUserException(
  1071. String.format("Primary Key column \"%s\" for table \"%s\" is not mapped.",
  1072. refDatastoreMapping.getDatastoreField().getIdentifier(),
  1073. toString()));
  1074. }
  1075. DatastoreIdentifier identifier;
  1076. IdentifierFactory idFactory = storeMgr.getIdentifierFactory();
  1077. if (colmd.getName() == null || colmd.getName().length() < 1) {
  1078. // No user provided name so generate one
  1079. identifier = idFactory.newForeignKeyFieldIdentifier(
  1080. ownerFmd, null, refDatastoreMapping.getDatastoreField().getIdentifier(),
  1081. storeMgr.getNucleusContext().getTypeManager().isDefaultEmbeddedType(mapping.getJavaType()),
  1082. FieldRole.ROLE_OWNER);
  1083. } else {
  1084. // User-defined name
  1085. identifier = idFactory.newDatastoreFieldIdentifier(colmd.getName());
  1086. }
  1087. // When we have an inherited relationship we end up
  1088. // trying to add an owner property twice - once from the super-class
  1089. // and once from the sub-class. This generates an exception for
  1090. // duplicate property names. To avoid this we check to see if
  1091. // the table already has a property with this name before attempting
  1092. // to add the mapping
  1093. if (!datastorePropertiesByName.containsKey(identifier.getIdentifierName())) {
  1094. DatastoreProperty refColumn =
  1095. addDatastoreField(mapping.getJavaType().getName(), identifier, mapping, colmd);
  1096. refDatastoreMapping.getDatastoreField().copyConfigurationTo(refColumn);
  1097. if ((colmd.getAllowsNull() == null) ||
  1098. (colmd.getAllowsNull() != null && colmd.isAllowsNull())) {
  1099. // User either wants it nullable, or havent specified anything, so make it nullable
  1100. refColumn.setNullable();
  1101. }
  1102. // this is needed for one-to-many sets
  1103. fkMapping.addDatastoreMapping(getStoreManager().getMappingManager()
  1104. .createDatastoreMapping(mapping, refColumn,
  1105. refDatastoreMapping.getJavaTypeMapping().getJavaType().getName()));
  1106. ((PersistableMapping) fkMapping).addJavaTypeMapping(mapping);
  1107. }
  1108. }
  1109. // Save the external FK
  1110. getExternalFkMappings().put(ownerFmd, fkMapping);
  1111. // Add the order mapping as necessary
  1112. addOrderMapping(ownerFmd, orderMapping);
  1113. }
  1114. }
  1115. /*}*/
  1116. }
  1117. private JavaTypeMapping addOrderMapping(AbstractMemberMetaData fmd, JavaTypeMapping orderMapping) {
  1118. boolean needsOrderMapping = false;
  1119. OrderMetaData omd = fmd.getOrderMetaData();
  1120. if (fmd.hasArray()) {
  1121. // Array field always has the index mapping
  1122. needsOrderMapping = true;
  1123. } else if (List.class.isAssignableFrom(fmd.getType())) {
  1124. // List field
  1125. needsOrderMapping = !(omd != null && !omd.isIndexedList());
  1126. } else if (java.util.Collection.class.isAssignableFrom(fmd.getType()) &&
  1127. omd != null && omd.isIndexedList() && omd.getMappedBy() == null) {
  1128. // Collection field with <order> and is indexed list so needs order mapping
  1129. needsOrderMapping = true;
  1130. }
  1131. if (needsOrderMapping) {
  1132. // if the field is list or array type, add index column
  1133. if (orderMapping == null) {
  1134. // Create new order mapping since we need one and we aren't using a shared FK
  1135. orderMapping = addOrderColumn(fmd);
  1136. }
  1137. if (!MetaDataUtils.readRelatedKeysFromParent(storeMgr, fmd)) {
  1138. getExternalOrderMappings().put(fmd, orderMapping);
  1139. }
  1140. }
  1141. return orderMapping;
  1142. }
  1143. /**
  1144. * Adds an ordering column to the element table (this) in inverse list relationships. Used to
  1145. * store the position of the element in the List. If the &lt;order&gt; provides a mapped-by, this
  1146. * will return the existing column mapping.
  1147. *
  1148. * @param fmd The MetaData for the column to map to
  1149. * @return The Mapping for the order column
  1150. */
  1151. private JavaTypeMapping addOrderColumn(AbstractMemberMetaData fmd) {
  1152. Class indexType = Integer.class;
  1153. JavaTypeMapping indexMapping = new IndexMapping();
  1154. indexMapping.initialize(storeMgr, indexType.getName());
  1155. IdentifierFactory idFactory = storeMgr.getIdentifierFactory();
  1156. DatastoreIdentifier indexColumnName = null;
  1157. ColumnMetaData colmd = null;
  1158. // Allow for any user definition in OrderMetaData
  1159. OrderMetaData omd = fmd.getOrderMetaData();
  1160. if (omd != null) {
  1161. colmd =
  1162. (omd.getColumnMetaData() != null && omd.getColumnMetaData().length > 0 ? omd.getColumnMetaData()[0] : null);
  1163. if (omd.getMappedBy() != null) {
  1164. // User has defined ordering using the column(s) of an existing field.
  1165. JavaTypeMapping orderMapping = getMemberMapping(omd.getMappedBy());
  1166. if (orderMapping == null) {
  1167. throw new NucleusUserException(String.format(
  1168. "Field \"{0}\" has an <order> defined to be persisted into the columns in the element table for element field \"{1}\". This field is not found in the element class.",
  1169. fmd.getFullFieldName(), omd.getMappedBy()));
  1170. }
  1171. if (!(orderMapping instanceof IntegerMapping) && !(orderMapping instanceof LongMapping)) {
  1172. throw new NucleusUserException(
  1173. String.format(
  1174. "Field \"{0}\" has an <order> defined to be persisted into the column of field \"{1}\". This field is of an invalid type. Must be an int/Integer.",
  1175. fmd.getFullFieldName(), omd.getMappedBy()));
  1176. }
  1177. return orderMapping;
  1178. }
  1179. String colName;
  1180. if (omd.getColumnMetaData() != null && omd.getColumnMetaData().length > 0
  1181. && omd.getColumnMetaData()[0].getName() != null) {
  1182. // User-defined name so create an identifier using it
  1183. colName = omd.getColumnMetaData()[0].getName();
  1184. indexColumnName = idFactory.newDatastoreFieldIdentifier(colName);
  1185. }
  1186. }
  1187. if (indexColumnName == null) {
  1188. // No index column name defined so generate one
  1189. indexColumnName = idFactory.newForeignKeyFieldIdentifier(fmd, null, null, true, FieldRole.ROLE_INDEX);
  1190. }
  1191. // if the relationship is in a base class with multiple subclasses, each
  1192. // subclass will try to add the index column. We need to avoid adding
  1193. // the same column twice.
  1194. DatastoreField column = datastorePropertiesByName.get(indexColumnName.getIdentifierName());
  1195. if (column == null) {
  1196. column = addDatastoreField(indexType.getName(), indexColumnName, indexMapping, colmd);
  1197. }
  1198. if (colmd == null || (colmd.getAllowsNull() == null) ||
  1199. (colmd.getAllowsNull() != null && colmd.isAllowsNull())) {
  1200. // User either wants it nullable, or havent specified anything, so make it nullable
  1201. column.setNullable();
  1202. }
  1203. DatastoreFKMapping fkMapping =
  1204. (DatastoreFKMapping) storeMgr.getMappingManager().createDatastoreMapping(
  1205. indexMapping, column, indexType.getName());
  1206. DatastoreProperty field = fkMapping.getDatastoreField();
  1207. DatastoreTable elementTable = field.getDatastoreContainerObject();
  1208. PropertyMetaData pmd = new PropertyMetaData(elementTable.getClassMetaData(), indexColumnName.getIdentifierName());
  1209. field.setMemberMetaData(pmd);
  1210. return indexMapping;
  1211. }
  1212. public boolean isParentKeyProvider(AbstractMemberMetaData ammd) {
  1213. return ammd.equals(parentMappingMemberMetaData);
  1214. }
  1215. public AbstractMemberMetaData getParentMappingMemberMetaData() {
  1216. return parentMappingMemberMetaData;
  1217. }
  1218. /**
  1219. * Callbacks is used for inverse relationships to run some operation in the target table. The
  1220. * operation is creation of columns, indexes, or whatever needed.
  1221. */
  1222. private static class CallBack {
  1223. final AbstractMemberMetaData fmd;
  1224. final String ownerClassName;
  1225. /**
  1226. * Default constructor
  1227. *
  1228. * @param fmd The FieldMetaData
  1229. * @param ownerClassName The concrete type of the relationship
  1230. */
  1231. public CallBack(AbstractMemberMetaData fmd, String ownerClassName) {
  1232. this.fmd = fmd;
  1233. this.ownerClassName = ownerClassName;
  1234. }
  1235. }
  1236. public String toString() {
  1237. return cmd.getFullClassName();
  1238. }
  1239. public AbstractClassMetaData getClassMetaData() {
  1240. return cmd;
  1241. }
  1242. public JavaTypeMapping getMultitenancyMapping() {
  1243. // Don't support multitenancy via discriminator currently
  1244. return null;
  1245. }
  1246. public void provideMultitenancyMapping(MappingConsumer consumer) {
  1247. // Don't support multitenancy via discriminator currently
  1248. }
  1249. class DatastoreKind implements DatastoreIdentifier {
  1250. private final String identifier;
  1251. public DatastoreKind(String identifier) {
  1252. this.identifier = identifier;
  1253. }
  1254. public String getIdentifierName() {
  1255. return identifier;
  1256. }
  1257. public String getFullyQualifiedName(boolean adapterCase) {
  1258. return getIdentifierName();
  1259. }
  1260. public void setCatalogName(String s) {
  1261. }
  1262. public void setSchemaName(String s) {
  1263. }
  1264. public String getCatalogName() {
  1265. return null;
  1266. }
  1267. public String getSchemaName() {
  1268. return null;
  1269. }
  1270. }
  1271. }