From f00d441cae2fb759981f4d1824a8fff156ce5e60 Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Tue, 18 Aug 2009 10:26:43 +0000 Subject: [PATCH] Nested collection (maps, sets, lists) property retrieval - Single-shot query retrieves ordered view - MLText, HashMap and ArrayList currently supported git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@15783 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- ...escoPostCreate-3.3-PropertyValueTables.sql | 28 +-- .../propval-common-SqlMap.xml | 70 +++---- .../propval/AbstractPropertyValueDAOImpl.java | 198 +++++++++++++----- .../domain/propval/PropertyIdSearchRow.java | 144 +++++++++++++ ...inkEntity.java => PropertyLinkEntity.java} | 69 +++--- .../repo/domain/propval/PropertyValueDAO.java | 2 - .../domain/propval/PropertyValueDAOTest.java | 81 ++++--- .../domain/propval/PropertyValueEntity.java | 59 +++--- .../propval/ibatis/PropertyValueDAOImpl.java | 75 ++----- 9 files changed, 462 insertions(+), 264 deletions(-) create mode 100644 source/java/org/alfresco/repo/domain/propval/PropertyIdSearchRow.java rename source/java/org/alfresco/repo/domain/propval/{PropertyCollectionLinkEntity.java => PropertyLinkEntity.java} (62%) diff --git a/config/alfresco/dbscripts/create/3.3/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoPostCreate-3.3-PropertyValueTables.sql b/config/alfresco/dbscripts/create/3.3/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoPostCreate-3.3-PropertyValueTables.sql index 666c2b70af..f3aada207b 100644 --- a/config/alfresco/dbscripts/create/3.3/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoPostCreate-3.3-PropertyValueTables.sql +++ b/config/alfresco/dbscripts/create/3.3/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoPostCreate-3.3-PropertyValueTables.sql @@ -68,24 +68,24 @@ CREATE TABLE alf_prop_value PRIMARY KEY (id) ) ENGINE=InnoDB; -CREATE TABLE alf_prop_collection_link +CREATE TABLE alf_prop_link ( - root_coll_prop_id BIGINT NOT NULL, - curr_coll_prop_id BIGINT NOT NULL, - key_prop_id BIGINT NOT NULL, + root_prop_id BIGINT NOT NULL, + current_prop_id BIGINT NOT NULL, value_prop_id BIGINT NOT NULL, - INDEX idx_alf_prop_coll_rev (value_prop_id, root_coll_prop_id), - PRIMARY KEY (root_coll_prop_id, curr_coll_prop_id, key_prop_id, value_prop_id) + key_prop_id BIGINT NOT NULL, + INDEX idx_alf_prop_coll_rev (value_prop_id, root_prop_id), + PRIMARY KEY (root_prop_id, current_prop_id, value_prop_id, key_prop_id) ) ENGINE=InnoDB; -- -- Record script finish -- -DELETE FROM alf_applied_patch WHERE id = 'patch.db-V3.3-PropertyValueTables'; -INSERT INTO alf_applied_patch - (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) - VALUES - ( - 'patch.db-V3.3-PropertyValueTables', 'Manually executed script upgrade V3.3: PropertyValue Tables', - 0, 3000, -1, 3001, null, 'UNKOWN', 1, 1, 'Script completed' - ); \ No newline at end of file +-- DELETE FROM alf_applied_patch WHERE id = 'patch.db-V3.3-PropertyValueTables'; +-- INSERT INTO alf_applied_patch +-- (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) +-- VALUES +-- ( +-- 'patch.db-V3.3-PropertyValueTables', 'Manually executed script upgrade V3.3: PropertyValue Tables', +-- 0, 3000, -1, 3001, null, 'UNKOWN', 1, 1, 'Script completed' +-- ); \ No newline at end of file diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/propval-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/propval-common-SqlMap.xml index ce1eb2cd89..ce63a42484 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/propval-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/propval-common-SqlMap.xml @@ -15,7 +15,8 @@ - + + @@ -57,27 +58,29 @@ - - - - + + + + + + + + + + + + + + + + - - @@ -260,32 +263,29 @@ - + select + cl.root_prop_id, cl.current_prop_id, cl.value_prop_id, cl.key_prop_id, + v.actual_type_id, v.persisted_type, + v.long_value, dv.double_value, sv.string_value, serv.serializable_value + from + alf_prop_link cl + join alf_prop_value v on (cl.value_prop_id = v.id) + left join alf_prop_double_value dv on (dv.id = v.long_value and v.persisted_type = 2) + left join alf_prop_string_value sv on (sv.id = v.long_value and (v.persisted_type = 3 || v.persisted_type = 5)) + left join alf_prop_serializable_value serv on (serv.id = v.long_value and v.persisted_type = 4) + where cl.root_prop_id = #id# + order by current_prop_id, value_prop_id, key_prop_id - - insert into alf_prop_collection_link + + insert into alf_prop_link ( - root_coll_prop_id, curr_coll_prop_id, key_prop_id, value_prop_id + root_prop_id, current_prop_id, value_prop_id, key_prop_id ) values ( - #rootCollectionPropId#, #currentCollectionPropId#, #keyPropId#, #valuePropId# + #rootPropId#, #currentPropId#, #valuePropId#, #keyPropId# ) diff --git a/source/java/org/alfresco/repo/domain/propval/AbstractPropertyValueDAOImpl.java b/source/java/org/alfresco/repo/domain/propval/AbstractPropertyValueDAOImpl.java index 99a9c32731..101e963358 100644 --- a/source/java/org/alfresco/repo/domain/propval/AbstractPropertyValueDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/propval/AbstractPropertyValueDAOImpl.java @@ -30,6 +30,7 @@ import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -41,6 +42,8 @@ import org.alfresco.repo.domain.CrcHelper; import org.alfresco.repo.domain.propval.PropertyValueEntity.PersistedType; import org.alfresco.service.cmr.repository.MLText; import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * Abstract implementation for Property Value DAO. @@ -59,6 +62,8 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO private static final String CACHE_REGION_PROPERTY_DOUBLE_VALUE = "PropertyDoubleValue"; private static final String CACHE_REGION_PROPERTY_VALUE = "PropertyValue"; + private static final Log logger = LogFactory.getLog(AbstractPropertyValueDAOImpl.class); + protected PropertyTypeConverter converter; private final PropertyClassCallbackDAO propertyClassDaoCallback; @@ -626,6 +631,29 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO */ private class PropertyValueCallbackDAO extends EntityLookupCallbackDAOAdaptor { + private final Serializable convertToValue(PropertyValueEntity entity) + { + if (entity == null) + { + return null; + } + final Serializable actualValue; + if (entity.getPersistedTypeEnum() == PersistedType.CONSTRUCTABLE) + { + actualValue = entity.getPersistedValue(); + } + else + { + Long actualTypeId = entity.getActualTypeId(); + Class actualType = getPropertyClassById(actualTypeId).getSecond(); + + Serializable entityValue = entity.getPersistedValue(); + actualValue = (Serializable) converter.convert(actualType, entityValue); + } + // Done + return actualValue; + } + private final Pair convertEntityToPair(PropertyValueEntity entity) { if (entity == null) @@ -633,13 +661,7 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO return null; } Long entityId = entity.getId(); - Serializable entityValue = entity.getPersistedValue(); - - // Dig out the class to convert the value to i.e. the actual type of the value - Long actualTypeId = entity.getActualTypeId(); - Class actualType = getPropertyClassById(actualTypeId).getSecond(); - // Convert it - Serializable actualValue = (Serializable) converter.convert(actualType, entityValue); + Serializable actualValue = convertToValue(entity); // Done return new Pair(entityId, actualValue); } @@ -669,15 +691,21 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO PropertyValueEntity entity = createPropertyValue(value); Long entityId = entity.getId(); // Create the link entry for the property - createPropertyLink(entityId, entityId, 0L, entityId); + createPropertyLink(entityId, entityId, entityId, 0L); // Done return new Pair(entity.getId(), value); } public Pair findByKey(Long key) { - PropertyValueEntity entity = findPropertyValueById(key); - return convertEntityToPair(entity); + List rows = findPropertyValueById(key); + if (rows.size() == 0) + { + // No results + return null; + } + Serializable value = convertPropertyIdSearchRows(rows); + return new Pair(key, value); } public Pair findByValue(Serializable value) @@ -694,10 +722,102 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO } } - protected abstract PropertyValueEntity findPropertyValueById(Long id); + protected abstract List findPropertyValueById(Long id); protected abstract PropertyValueEntity findPropertyValueByValue(Serializable value); protected abstract PropertyValueEntity createPropertyValue(Serializable value); + @SuppressWarnings("unchecked") + protected Serializable convertPropertyIdSearchRows(List rows) + { + /* + * The results are ordered by the root_prop_id, current_prop_id and value_prop_id. + * However, for safety (data patching, etc) we take a first pass to create the + * basic properties before hooking them all together in a second pass. + */ + final Map values = new HashMap(rows.size()); + List keyRows = new ArrayList(5); + Serializable result = null; + for (PropertyIdSearchRow row : rows) + { + PropertyLinkEntity linkEntity = row.getLinkEntity(); + PropertyValueEntity valueEntity = row.getValueEntity(); + // Construct the value (Maps and Collections should be CONSTRUCTABLE) + Serializable value = propertyValueCallback.convertToValue(valueEntity); + // Keep it + values.put(new Long(linkEntity.getValuePropId()), value); + + // If this row is a mapping row (the property value ID does not match the current property ID) + // then store it for quicker use later + if (linkEntity.getCurrentPropId() != linkEntity.getValuePropId()) + { + keyRows.add(linkEntity); + } + if (linkEntity.getRootPropId() == linkEntity.getValuePropId()) + { + // We found the root + result = value; + } + } + + // We expect a value to be found unless the results are empty + if (result == null) + { + return null; + } + + // Now we have all our constructed values and the mapping rows: build the collections + for (PropertyLinkEntity propertyLinkEntity : keyRows) + { + Serializable value = values.get(propertyLinkEntity.getValuePropId()); + Serializable currentProp = values.get(propertyLinkEntity.getCurrentPropId()); + if (value == null) + { + logger.error("No value found for link property: " + propertyLinkEntity); + continue; + } + if (currentProp == null) + { + logger.error("No current property found for link property: " + propertyLinkEntity); + continue; + } + Long keyId = propertyLinkEntity.getKeyPropId(); + // put the value into the container + if (currentProp instanceof Map) + { + Pair keyPair = getPropertyValueById(keyId); + if (keyPair == null) + { + logger.error("Current property (map) has key without a value: " + propertyLinkEntity); + continue; + } + Serializable key = keyPair.getSecond(); + Map map = (Map) currentProp; + map.put(key, value); + } + else if (currentProp instanceof Set) + { + // We can ignore the key - it won't make a difference + Set set = (Set) currentProp; + set.add(value); + } +// Results 'should' be ordered by key +// else if (currentProp instanceof List) +// { +// // The order is important +// List collection = (List) currentProp; +// collection.add(keyId.intValue(), value); +// } + else if (currentProp instanceof Collection) + { + // The order is important + Collection collection = (Collection) currentProp; + collection.add(value); + } + } + // This will have put the values into the correct containers + return result; + } + //================================ // Special handling of maps and collections //================================ @@ -726,7 +846,10 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO { clazz = HashMap.class; } - Long entityId = createPropertyMapRoot(clazz); + + // Can't use a cached instance as each map is unique. Go direct to entity creation. + Long entityId = createPropertyValue(clazz).getId(); + // Use this as the root if this is the first entry into this method if (rootId == null) { @@ -734,7 +857,7 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO } // Create the link entry for the root - createPropertyLink(rootId, entityId, 0L, entityId); + createPropertyLink(rootId, entityId, entityId, 0L); // Now iterate over the entries and create properties for the keys and values for (Map.Entry entry : map.entrySet()) @@ -752,7 +875,7 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO Long valueId = valuePair.getFirst(); // Now write the mapping entry - createPropertyLink(rootId, entityId, keyId, valueId); + createPropertyLink(rootId, entityId, valueId, keyId); } // Done @@ -783,7 +906,10 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO { clazz = ArrayList.class; } - Long entityId = createPropertyCollectionRoot(clazz); + + // Can't use a cached instance as each collection is unique. Go direct to entity creation. + Long entityId = createPropertyValue(clazz).getId(); + // Use this as the root if this is the first entry into this method if (rootId == null) { @@ -791,7 +917,7 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO } // Create the link entry for the root - createPropertyLink(rootId, entityId, 0L, entityId); + createPropertyLink(rootId, entityId, entityId, 0L); // Now iterate over the entries and create properties for the keys and values long index = 0L; @@ -806,48 +932,24 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO Long valueId = valuePair.getFirst(); // Now write the mapping entry Long keyId = new Long(index); - createPropertyLink(rootId, entityId, keyId, valueId); + createPropertyLink(rootId, entityId, valueId, keyId); // Keep iterating index++; } return entityId; } - /** - * Create a property value entry for maps. The class is assumed to - * be correct with a default constructor for later reconstruction. - *

- * This method must not create the associated link property. - * - * @param clazz The map instance that must be used for re-instantiation. - * This must be an instance derived from {@link Map} with a default constructor. - * @return Returns the newly-created property ID - */ - protected abstract Long createPropertyMapRoot(Class clazz); - - /** - * Create a property value entry for collections. The class is assumed to - * be correct with a default constructor for later reconstruction. - *

- * This method must not create the associated link property. - * - * @param clazz The collection instance that must be used for re-instantiation. - * This must be an instance derived from {@link Collection} with a default constructor. - * @return Returns the newly-created property ID - */ - protected abstract Long createPropertyCollectionRoot(Class clazz); - /** * Create an entry for the map or collection link * - * @param rootCollectionId the root (entry-point) map or collection ID - * @param currentCollectionId the current map or collection ID - * @param keyId the map key entity ID or collection position count + * @param rootPropId the root (entry-point) map or collection ID + * @param currentPropId the current map or collection ID * @param valueId the ID of the entity storing the value (may be another map or collection) + * @param keyId the map key entity ID or collection position count */ protected abstract void createPropertyLink( - Long rootCollectionId, - Long currentCollectionId, - Long keyId, - Long valueId); + Long rootPropId, + Long currentPropId, + Long valueId, + Long keyId); } diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyIdSearchRow.java b/source/java/org/alfresco/repo/domain/propval/PropertyIdSearchRow.java new file mode 100644 index 0000000000..3f3f8df4aa --- /dev/null +++ b/source/java/org/alfresco/repo/domain/propval/PropertyIdSearchRow.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.domain.propval; + +import java.io.Serializable; + +/** + * Entity bean search results from alf_prop_collections_link and alf_prop_value tables. + * + * @author Derek Hulley + * @since 3.3 + */ +public class PropertyIdSearchRow +{ + private final PropertyLinkEntity linkEntity; + private final PropertyValueEntity valueEntity; + + public PropertyIdSearchRow() + { + linkEntity = new PropertyLinkEntity(); + valueEntity = new PropertyValueEntity(); + } + +// @Override +// public int hashCode() +// { +// return (int) rootCollectionPropId + (int) valuePropId; +// } +// +// @Override +// public boolean equals(Object obj) +// { +// if (this == obj) +// { +// return true; +// } +// else if (obj instanceof PropertyCollectionLinkEntity) +// { +// PropertyCollectionLinkEntity that = (PropertyCollectionLinkEntity) obj; +// return +// this.rootCollectionPropId == that.rootCollectionPropId && +// this.currentCollectionPropId == that.currentCollectionPropId && +// this.valuePropId == that.valuePropId && +// this.keyPropId == that.keyPropId; +// } +// else +// { +// return false; +// } +// } +// + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(512); + sb.append("PropertyIdSearchRow") + .append("[ ").append(linkEntity) + .append(", ").append(valueEntity) + .append("]"); + return sb.toString(); + } + + public PropertyLinkEntity getLinkEntity() + { + return linkEntity; + } + + public PropertyValueEntity getValueEntity() + { + return valueEntity; + } + + public void setRootPropId(long rootPropId) + { + linkEntity.setRootPropId(rootPropId); + } + + public void setCurrentPropId(long currentPropId) + { + linkEntity.setCurrentPropId(currentPropId); + } + + public void setValuePropId(long valuePropId) + { + linkEntity.setValuePropId(valuePropId); + } + + public void setKeyPropId(long keyPropId) + { + linkEntity.setKeyPropId(keyPropId); + } + + public void setActualTypeId(Long actualTypeId) + { + valueEntity.setActualTypeId(actualTypeId); + } + + public void setPersistedType(Short persistedType) + { + valueEntity.setPersistedType(persistedType); + } + + public void setLongValue(Long longValue) + { + valueEntity.setLongValue(longValue); + } + + public void setStringValue(String stringValue) + { + valueEntity.setStringValue(stringValue); + } + + public void setDoubleValue(Double doubleValue) + { + valueEntity.setDoubleValue(doubleValue); + } + + public void setSerializableValue(Serializable serializableValue) + { + valueEntity.setSerializableValue(serializableValue); + } +} diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyCollectionLinkEntity.java b/source/java/org/alfresco/repo/domain/propval/PropertyLinkEntity.java similarity index 62% rename from source/java/org/alfresco/repo/domain/propval/PropertyCollectionLinkEntity.java rename to source/java/org/alfresco/repo/domain/propval/PropertyLinkEntity.java index 1f57a4ac0d..8b3ded7335 100644 --- a/source/java/org/alfresco/repo/domain/propval/PropertyCollectionLinkEntity.java +++ b/source/java/org/alfresco/repo/domain/propval/PropertyLinkEntity.java @@ -25,26 +25,26 @@ package org.alfresco.repo.domain.propval; /** - * Entity bean for alf_prop_collections_link table. + * Entity bean for alf_prop_link table. * * @author Derek Hulley * @since 3.3 */ -public class PropertyCollectionLinkEntity +public class PropertyLinkEntity { - private long rootCollectionPropId; - private long currentCollectionPropId; - private long keyPropId; + private long rootPropId; + private long currentPropId; private long valuePropId; + private long keyPropId; - public PropertyCollectionLinkEntity() + public PropertyLinkEntity() { } @Override public int hashCode() { - return (int) rootCollectionPropId + (int) valuePropId; + return (int) rootPropId + (int) valuePropId; } @Override @@ -54,14 +54,14 @@ public class PropertyCollectionLinkEntity { return true; } - else if (obj instanceof PropertyCollectionLinkEntity) + else if (obj instanceof PropertyLinkEntity) { - PropertyCollectionLinkEntity that = (PropertyCollectionLinkEntity) obj; + PropertyLinkEntity that = (PropertyLinkEntity) obj; return - this.rootCollectionPropId == that.rootCollectionPropId && - this.currentCollectionPropId == that.currentCollectionPropId && - this.keyPropId == that.keyPropId && - this.valuePropId == that.valuePropId; + this.rootPropId == that.rootPropId && + this.currentPropId == that.currentPropId && + this.valuePropId == that.valuePropId && + this.keyPropId == that.keyPropId; } else { @@ -73,42 +73,33 @@ public class PropertyCollectionLinkEntity public String toString() { StringBuilder sb = new StringBuilder(512); - sb.append("PropertyCollectionsLinkEntity") - .append("[ currentCollectionPropId=").append(currentCollectionPropId) - .append(", keyPropId=").append(keyPropId) + sb.append("PropertyLinkEntity") + .append("[ rootPropId=").append(rootPropId) + .append(", currentPropId=").append(currentPropId) .append(", valuePropId=").append(valuePropId) + .append(", keyPropId=").append(keyPropId) .append("]"); return sb.toString(); } - public long getRootCollectionPropId() + public long getRootPropId() { - return rootCollectionPropId; + return rootPropId; } - public void setRootCollectionPropId(long rootCollectionPropId) + public void setRootPropId(long rootPropId) { - this.rootCollectionPropId = rootCollectionPropId; + this.rootPropId = rootPropId; } - public long getCurrentCollectionPropId() + public long getCurrentPropId() { - return currentCollectionPropId; + return currentPropId; } - public void setCurrentCollectionPropId(long currentCollectionPropId) + public void setCurrentPropId(long currentPropId) { - this.currentCollectionPropId = currentCollectionPropId; - } - - public long getKeyPropId() - { - return keyPropId; - } - - public void setKeyPropId(long keyPropId) - { - this.keyPropId = keyPropId; + this.currentPropId = currentPropId; } public long getValuePropId() @@ -120,4 +111,14 @@ public class PropertyCollectionLinkEntity { this.valuePropId = valuePropId; } + + public long getKeyPropId() + { + return keyPropId; + } + + public void setKeyPropId(long keyPropId) + { + this.keyPropId = keyPropId; + } } diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java index d0c0c41a28..8b01cb928f 100644 --- a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java +++ b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java @@ -26,9 +26,7 @@ package org.alfresco.repo.domain.propval; import java.io.Serializable; import java.util.Date; -import java.util.Map; -import org.alfresco.repo.action.evaluator.compare.PropertyValueComparator; import org.alfresco.util.Pair; /** diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java index abe28a8d57..3a2672f48f 100644 --- a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java +++ b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java @@ -64,6 +64,18 @@ public class PropertyValueDAOTest extends TestCase txnHelper = transactionService.getRetryingTransactionHelper(); propertyValueDAO = (PropertyValueDAO) ctx.getBean("propertyValueDAO"); + + // Remove the caches to test all functionality + removeCaches(); + } + + private void removeCaches() + { + ((AbstractPropertyValueDAOImpl)propertyValueDAO).setPropertyClassCache(null); + ((AbstractPropertyValueDAOImpl)propertyValueDAO).setPropertyDateValueCache(null); + ((AbstractPropertyValueDAOImpl)propertyValueDAO).setPropertyDoubleValueCache(null); + ((AbstractPropertyValueDAOImpl)propertyValueDAO).setPropertyStringValueCache(null); + ((AbstractPropertyValueDAOImpl)propertyValueDAO).setPropertyValueCache(null); } public void testPropertyClass() throws Exception @@ -238,6 +250,13 @@ public class PropertyValueDAOTest extends TestCase * Tests that the given value can be persisted and retrieved with the same resulting ID */ private void runPropertyValueTest(final Serializable value) throws Exception + { + runPropertyValueTest(value, true); + } + /** + * Tests that the given value can be persisted and retrieved with the same resulting ID + */ + private void runPropertyValueTest(final Serializable value, final boolean runValueRetrieval) throws Exception { // Create it (if it doesn't exist) RetryingTransactionCallback> createValueCallback = new RetryingTransactionCallback>() @@ -252,18 +271,21 @@ public class PropertyValueDAOTest extends TestCase assertNotNull(entityPair); assertEquals(value, entityPair.getSecond()); - // Retrieve it by value - RetryingTransactionCallback> getValueCallback = new RetryingTransactionCallback>() + if (runValueRetrieval) { - public Pair execute() throws Throwable + // Retrieve it by value + RetryingTransactionCallback> getValueCallback = new RetryingTransactionCallback>() { - // Get the classes - return propertyValueDAO.getPropertyValue(value); - } - }; - final Pair entityPairCheck = txnHelper.doInTransaction(getValueCallback, false); - assertNotNull(entityPairCheck); - assertEquals(entityPair, entityPairCheck); + public Pair execute() throws Throwable + { + // Get the classes + return propertyValueDAO.getPropertyValue(value); + } + }; + final Pair entityPairCheck = txnHelper.doInTransaction(getValueCallback, false); + assertNotNull(entityPairCheck); + assertEquals(entityPair, entityPairCheck); + } // Retrieve it by ID RetryingTransactionCallback> getByIdCallback = new RetryingTransactionCallback>() @@ -356,15 +378,7 @@ public class PropertyValueDAOTest extends TestCase String value = "MAP-VALUE-" + i; map.put(key, value); } - RetryingTransactionCallback createCallback = new RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - propertyValueDAO.getOrCreatePropertyValue(map); - return null; - } - }; - transactionService.getRetryingTransactionHelper().doInTransaction(createCallback); + runPropertyValueTest(map, false); } public void testPropertyValue_MapOfMapOfStrings() throws Exception @@ -382,15 +396,7 @@ public class PropertyValueDAOTest extends TestCase String key = "OUTERMAP-KEY-" + i; mapOuter.put(key, mapInner); } - RetryingTransactionCallback createCallback = new RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - propertyValueDAO.getOrCreatePropertyValue(mapOuter); - return null; - } - }; - transactionService.getRetryingTransactionHelper().doInTransaction(createCallback); + runPropertyValueTest(mapOuter, false); } public void testPropertyValue_CollectionOfStrings() throws Exception @@ -401,24 +407,7 @@ public class PropertyValueDAOTest extends TestCase String value = "COLL-VALUE-" + i; list.add(value); } - RetryingTransactionCallback createCallback = new RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - propertyValueDAO.getOrCreatePropertyValue(list); - return null; - } - }; - transactionService.getRetryingTransactionHelper().doInTransaction(createCallback); - } - - private void removeCaches() - { - ((AbstractPropertyValueDAOImpl)propertyValueDAO).setPropertyClassCache(null); - ((AbstractPropertyValueDAOImpl)propertyValueDAO).setPropertyDateValueCache(null); - ((AbstractPropertyValueDAOImpl)propertyValueDAO).setPropertyDoubleValueCache(null); - ((AbstractPropertyValueDAOImpl)propertyValueDAO).setPropertyStringValueCache(null); - ((AbstractPropertyValueDAOImpl)propertyValueDAO).setPropertyValueCache(null); + runPropertyValueTest(list, false); } public void testPropertyClass_NoCache() throws Exception diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyValueEntity.java b/source/java/org/alfresco/repo/domain/propval/PropertyValueEntity.java index 7e19279a5d..6376c81564 100644 --- a/source/java/org/alfresco/repo/domain/propval/PropertyValueEntity.java +++ b/source/java/org/alfresco/repo/domain/propval/PropertyValueEntity.java @@ -56,8 +56,7 @@ public class PropertyValueEntity public static final Short ORDINAL_DOUBLE = 2; public static final Short ORDINAL_STRING = 3; public static final Short ORDINAL_SERIALIZABLE = 4; - public static final Short ORDINAL_MAP = 5; - public static final Short ORDINAL_COLLECTION = 6; + public static final Short ORDINAL_CONSTRUCTABLE = 5; /** * Enumeration of persisted types for alf_prop_value.persisted_type. @@ -135,30 +134,17 @@ public class PropertyValueEntity return Serializable.class; } }, - MAP + CONSTRUCTABLE { @Override public Short getOrdinalNumber() { - return ORDINAL_MAP; + return ORDINAL_CONSTRUCTABLE; } @Override public Class getAssociatedClass() { - return Map.class; - } - }, - COLLECTION - { - @Override - public Short getOrdinalNumber() - { - return ORDINAL_COLLECTION; - } - @Override - public Class getAssociatedClass() - { - return Collection.class; + return Class.class; } }; @@ -213,6 +199,7 @@ public class PropertyValueEntity mapClass.put(Date.class, PersistedType.LONG); mapClass.put(Map.class, PersistedType.SERIALIZABLE); // Will be serialized if encountered mapClass.put(Collection.class, PersistedType.SERIALIZABLE); // Will be serialized if encountered + mapClass.put(Class.class, PersistedType.CONSTRUCTABLE); // Will construct a new instance persistedTypesByClass = Collections.unmodifiableMap(mapClass); } @@ -281,8 +268,6 @@ public class PropertyValueEntity { switch (persistedTypeEnum) { - case MAP: - case COLLECTION: case NULL: return null; case LONG: @@ -293,6 +278,21 @@ public class PropertyValueEntity return stringValue; case SERIALIZABLE: return serializableValue; + case CONSTRUCTABLE: + // Construct an instance + try + { + Class clazz = Class.forName(stringValue); + return (Serializable) clazz.newInstance(); + } + catch (ClassNotFoundException e) + { + throw new RuntimeException("Unable to construct instance of class " + stringValue, e); + } + catch (Throwable e) + { + throw new RuntimeException("Unable to create new instance of " + stringValue, e); + } default: throw new IllegalStateException("Should not be able to get through switch"); } @@ -304,16 +304,21 @@ public class PropertyValueEntity * * @param value the value to persist (may be null) * @param converter the converter that will perform and type conversion - * @return Returns the persisted type value */ - public Serializable setValue(Serializable value, PropertyTypeConverter converter) + public void setValue(Serializable value, PropertyTypeConverter converter) { if (value == null) { this.persistedType = ORDINAL_NULL; this.persistedTypeEnum = PersistedType.NULL; this.longValue = LONG_ZERO; - return longValue; + } + else if (value instanceof Class) + { + Class clazz = (Class) value; + stringValue = clazz.getName(); + persistedTypeEnum = PersistedType.CONSTRUCTABLE; + persistedType = persistedTypeEnum.getOrdinalNumber(); } else { @@ -329,16 +334,16 @@ public class PropertyValueEntity { case LONG: longValue = converter.convert(Long.class, value); - return longValue; + break; case DOUBLE: doubleValue = converter.convert(Double.class, value); - return doubleValue; + break; case STRING: stringValue = converter.convert(String.class, value); - return stringValue; + break; case SERIALIZABLE: serializableValue = value; - return serializableValue; + break; default: throw new IllegalStateException("Should not be able to get through switch"); } diff --git a/source/java/org/alfresco/repo/domain/propval/ibatis/PropertyValueDAOImpl.java b/source/java/org/alfresco/repo/domain/propval/ibatis/PropertyValueDAOImpl.java index 6ce0825a19..7e665f8592 100644 --- a/source/java/org/alfresco/repo/domain/propval/ibatis/PropertyValueDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/propval/ibatis/PropertyValueDAOImpl.java @@ -25,16 +25,15 @@ package org.alfresco.repo.domain.propval.ibatis; import java.io.Serializable; -import java.util.Collection; import java.util.Date; import java.util.List; -import java.util.Map; import org.alfresco.repo.domain.propval.AbstractPropertyValueDAOImpl; import org.alfresco.repo.domain.propval.PropertyClassEntity; -import org.alfresco.repo.domain.propval.PropertyCollectionLinkEntity; import org.alfresco.repo.domain.propval.PropertyDateValueEntity; import org.alfresco.repo.domain.propval.PropertyDoubleValueEntity; +import org.alfresco.repo.domain.propval.PropertyIdSearchRow; +import org.alfresco.repo.domain.propval.PropertyLinkEntity; import org.alfresco.repo.domain.propval.PropertyStringValueEntity; import org.alfresco.repo.domain.propval.PropertyValueEntity; import org.alfresco.repo.domain.propval.PropertyValueEntity.PersistedType; @@ -71,7 +70,7 @@ public class PropertyValueDAOImpl extends AbstractPropertyValueDAOImpl private static final String SELECT_PROPERTY_VALUE_BY_STRING_VALUE = "select.PropertyValueByStringValue"; private static final String INSERT_PROPERTY_VALUE = "insert.PropertyValue"; - private static final String INSERT_PROPERTY_COLLECTION_LINK = "insert.PropertyCollectionLink"; + private static final String INSERT_PROPERTY_LINK = "insert.PropertyLink"; private SqlMapClientTemplate template; @@ -255,16 +254,17 @@ public class PropertyValueDAOImpl extends AbstractPropertyValueDAOImpl // 'alf_prop_value' accessors //================================ + @SuppressWarnings("unchecked") @Override - protected PropertyValueEntity findPropertyValueById(Long id) + protected List findPropertyValueById(Long id) { PropertyValueEntity entity = new PropertyValueEntity(); entity.setId(id); - entity = (PropertyValueEntity) template.queryForObject( + List results = (List) template.queryForList( SELECT_PROPERTY_VALUE_BY_ID, entity); // Done - return entity; + return results; } @SuppressWarnings("unchecked") @@ -367,6 +367,7 @@ public class PropertyValueDAOImpl extends AbstractPropertyValueDAOImpl insertEntity.setLongValue(insertDoublePair.getFirst()); break; case STRING: + case CONSTRUCTABLE: String stringValue = insertEntity.getStringValue(); Pair insertStringPair = getOrCreatePropertyStringValue(stringValue); insertEntity.setLongValue(insertStringPair.getFirst()); @@ -391,61 +392,19 @@ public class PropertyValueDAOImpl extends AbstractPropertyValueDAOImpl return insertEntity; } - @Override - protected Long createPropertyMapRoot(Class clazz) - { - if (!Map.class.isAssignableFrom(clazz)) - { - throw new IllegalArgumentException("Map root must be a Map instance"); - } - final Pair> clazzPair = getOrCreatePropertyClass(clazz); - final Long actualTypeId = clazzPair.getFirst(); - - // Construct a property value to represent the collection - PropertyValueEntity insertEntity = new PropertyValueEntity(); - // We have to set the persisted type manually - insertEntity.setPersistedType(PersistedType.MAP.getOrdinalNumber()); - insertEntity.setLongValue(PropertyValueEntity.LONG_ZERO); - insertEntity.setActualTypeId(actualTypeId); - Long collectionId = (Long) template.insert(INSERT_PROPERTY_VALUE, insertEntity); - // Done - return collectionId; - } - - @Override - protected Long createPropertyCollectionRoot(Class clazz) - { - if (!Collection.class.isAssignableFrom(clazz)) - { - throw new IllegalArgumentException("Collection root must be a Collection instance"); - } - final Pair> clazzPair = getOrCreatePropertyClass(clazz); - final Long actualTypeId = clazzPair.getFirst(); - - // Construct a property value to represent the collection - PropertyValueEntity insertEntity = new PropertyValueEntity(); - // We have to set the persisted type manually - insertEntity.setPersistedType(PersistedType.COLLECTION.getOrdinalNumber()); - insertEntity.setLongValue(PropertyValueEntity.LONG_ZERO); - insertEntity.setActualTypeId(actualTypeId); - Long collectionId = (Long) template.insert(INSERT_PROPERTY_VALUE, insertEntity); - // Done - return collectionId; - } - @Override protected void createPropertyLink( - Long rootCollectionId, - Long currentCollectionId, - Long keyId, - Long valueId) + Long rootPropId, + Long currentPropId, + Long valueId, + Long keyId) { - PropertyCollectionLinkEntity insertEntity = new PropertyCollectionLinkEntity(); - insertEntity.setRootCollectionPropId(rootCollectionId); - insertEntity.setCurrentCollectionPropId(currentCollectionId); - insertEntity.setKeyPropId(keyId); + PropertyLinkEntity insertEntity = new PropertyLinkEntity(); + insertEntity.setRootPropId(rootPropId); + insertEntity.setCurrentPropId(currentPropId); insertEntity.setValuePropId(valueId); - template.insert(INSERT_PROPERTY_COLLECTION_LINK, insertEntity); + insertEntity.setKeyPropId(keyId); + template.insert(INSERT_PROPERTY_LINK, insertEntity); // Done } }