Support for searchable, arbitrarily-nested properties

- TODO: ID-based caching for entities that can't be found by value (e.g. maps, serializable)
 - TODO: Serializable table
 - TODO: Dedicated tests for node properties (QName-Serializable maps with nested MLText)


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@15753 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2009-08-14 14:53:12 +00:00
parent f3f4b2b043
commit 5f8bfcae7a
9 changed files with 600 additions and 20 deletions

View File

@@ -68,6 +68,16 @@ CREATE TABLE alf_prop_value
PRIMARY KEY (id) PRIMARY KEY (id)
) ENGINE=InnoDB; ) ENGINE=InnoDB;
CREATE TABLE alf_prop_collection_link
(
root_coll_prop_id BIGINT NOT NULL,
curr_coll_prop_id BIGINT NOT NULL,
key_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)
) ENGINE=InnoDB;
-- --
-- Record script finish -- Record script finish
-- --

View File

@@ -15,6 +15,7 @@
<typeAlias alias="PropertyStringValue" type="org.alfresco.repo.domain.propval.PropertyStringValueEntity"/> <typeAlias alias="PropertyStringValue" type="org.alfresco.repo.domain.propval.PropertyStringValueEntity"/>
<typeAlias alias="PropertyDoubleValue" type="org.alfresco.repo.domain.propval.PropertyDoubleValueEntity"/> <typeAlias alias="PropertyDoubleValue" type="org.alfresco.repo.domain.propval.PropertyDoubleValueEntity"/>
<typeAlias alias="PropertyValue" type="org.alfresco.repo.domain.propval.PropertyValueEntity"/> <typeAlias alias="PropertyValue" type="org.alfresco.repo.domain.propval.PropertyValueEntity"/>
<typeAlias alias="PropertyCollectionLink" type="org.alfresco.repo.domain.propval.PropertyCollectionLinkEntity"/>
<!-- --> <!-- -->
<!-- Result Maps --> <!-- Result Maps -->
@@ -56,6 +57,12 @@
<result property="stringValue" column="string_value" jdbcType="VARCHAR" javaType="java.lang.String"/> <result property="stringValue" column="string_value" jdbcType="VARCHAR" javaType="java.lang.String"/>
<result property="serializableValue" column="serializable_value" jdbcType="BLOB" javaType="java.lang.Object"/> <result property="serializableValue" column="serializable_value" jdbcType="BLOB" javaType="java.lang.Object"/>
</resultMap> </resultMap>
<resultMap id="result.PropertyCollectionLink" class="PropertyCollectionLink">
<result property="rootCollectionPropId" column="root_coll_prop_id" jdbcType="BIGINT" javaType="long"/>
<result property="currentCollectionPropId" column="persisted_type" jdbcType="BIGINT" javaType="long"/>
<result property="keyPropId" column="key_prop_id" jdbcType="BIGINT" javaType="long"/>
<result property="valuePropId" column="value_prop_id" jdbcType="BIGINT" javaType="long"/>
</resultMap>
<!-- --> <!-- -->
<!-- Parameter Maps --> <!-- Parameter Maps -->
@@ -271,4 +278,15 @@
pv.id = #id# pv.id = #id#
</select> </select>
<insert id="insert.PropertyCollectionLink" parameterClass="PropertyCollectionLink" >
insert into alf_prop_collection_link
(
root_coll_prop_id, curr_coll_prop_id, key_prop_id, value_prop_id
)
values
(
#rootCollectionPropId#, #currentCollectionPropId#, #keyPropId#, #valuePropId#
)
</insert>
</sqlMap> </sqlMap>

View File

@@ -25,7 +25,13 @@
package org.alfresco.repo.domain.propval; package org.alfresco.repo.domain.propval;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.cache.SimpleCache;
@@ -33,6 +39,7 @@ import org.alfresco.repo.cache.lookup.EntityLookupCache;
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAOAdaptor; import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAOAdaptor;
import org.alfresco.repo.domain.CrcHelper; import org.alfresco.repo.domain.CrcHelper;
import org.alfresco.repo.domain.propval.PropertyValueEntity.PersistedType; import org.alfresco.repo.domain.propval.PropertyValueEntity.PersistedType;
import org.alfresco.service.cmr.repository.MLText;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
/** /**
@@ -489,7 +496,6 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
return (Pair<Long, Double>) entityPair; return (Pair<Long, Double>) entityPair;
} }
/** /**
* Callback for <b>alf_prop_double_value</b> DAO. * Callback for <b>alf_prop_double_value</b> DAO.
*/ */
@@ -559,10 +565,60 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
return entityPair; return entityPair;
} }
/**
* {@inheritDoc}
* @see #getOrCreatePropertyValueImpl(Serializable, int, int)
*/
public Pair<Long, Serializable> getOrCreatePropertyValue(Serializable value) public Pair<Long, Serializable> getOrCreatePropertyValue(Serializable value)
{ {
Pair<Long, Serializable> entityPair = propertyValueCache.getOrCreateByValue(value); return getOrCreatePropertyValueImpl(value, null, Integer.MAX_VALUE, 0);
return (Pair<Long, Serializable>) entityPair; }
/**
* {@inheritDoc}
* @see #getOrCreatePropertyValueImpl(Serializable, int, int)
*/
public Pair<Long, Serializable> getOrCreatePropertyValue(Serializable value, int maxDepth)
{
return getOrCreatePropertyValueImpl(value, null, maxDepth, 0);
}
@SuppressWarnings("unchecked")
private Pair<Long, Serializable> getOrCreatePropertyValueImpl(
Serializable value,
Long rootId,
int maxDepth,
int currentDepth)
{
if (value != null && maxDepth > currentDepth && value instanceof Map<?, ?>)
{
// The default is to do a deep expansion
Long mapId = createPropertyMapImpl(
(Map<? extends Serializable, ? extends Serializable>)value,
rootId,
maxDepth,
currentDepth);
Pair<Long, Serializable> entityPair = new Pair<Long, Serializable>(mapId, value);
// TODO: Go through cache?
return entityPair;
}
else if (value != null && maxDepth > currentDepth && value instanceof Collection<?>)
{
// The default is to do a deep expansion
Long collectionId = createPropertyCollectionImpl(
(Collection<? extends Serializable>)value,
rootId,
maxDepth,
currentDepth);
Pair<Long, Serializable> entityPair = new Pair<Long, Serializable>(collectionId, value);
// TODO: Go through cache?
return entityPair;
}
else
{
Pair<Long, Serializable> entityPair = propertyValueCache.getOrCreateByValue(value);
return (Pair<Long, Serializable>) entityPair;
}
} }
/** /**
@@ -611,7 +667,11 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
public Pair<Long, Serializable> createValue(Serializable value) public Pair<Long, Serializable> createValue(Serializable value)
{ {
PropertyValueEntity entity = createPropertyValue(value); PropertyValueEntity entity = createPropertyValue(value);
return convertEntityToPair(entity); Long entityId = entity.getId();
// Create the link entry for the property
createPropertyLink(entityId, entityId, 0L, entityId);
// Done
return new Pair<Long, Serializable>(entity.getId(), value);
} }
public Pair<Long, Serializable> findByKey(Long key) public Pair<Long, Serializable> findByKey(Long key)
@@ -622,6 +682,13 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
public Pair<Long, Serializable> findByValue(Serializable value) public Pair<Long, Serializable> findByValue(Serializable value)
{ {
if (value != null)
{
if (value instanceof Map<?, ?> || value instanceof Collection<?>)
{
throw new IllegalArgumentException("Should not be searching for Maps or Collections");
}
}
PropertyValueEntity entity = findPropertyValueByValue(value); PropertyValueEntity entity = findPropertyValueByValue(value);
return convertEntityToPair(entity); return convertEntityToPair(entity);
} }
@@ -630,4 +697,157 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
protected abstract PropertyValueEntity findPropertyValueById(Long id); protected abstract PropertyValueEntity findPropertyValueById(Long id);
protected abstract PropertyValueEntity findPropertyValueByValue(Serializable value); protected abstract PropertyValueEntity findPropertyValueByValue(Serializable value);
protected abstract PropertyValueEntity createPropertyValue(Serializable value); protected abstract PropertyValueEntity createPropertyValue(Serializable value);
//================================
// Special handling of maps and collections
//================================
/**
* Recursive method to write a map out. If the root entity ID is <tt>null</tt> then the current
* map ID is used as the root.
*
* @param value the map to write
* @param rootId the root entity ID, which may be a map or a collection
* @return Returns the ID of the newly-written map
*/
private <K extends Serializable, V extends Serializable> Long createPropertyMapImpl(
Map<K, V> map,
Long rootId,
int maxDepth,
int currentDepth)
{
// Create the root of the map
Class<?> clazz = null;
if (map instanceof MLText)
{
clazz = MLText.class;
}
else
{
clazz = HashMap.class;
}
Long entityId = createPropertyMapRoot(clazz);
// Use this as the root if this is the first entry into this method
if (rootId == null)
{
rootId = entityId;
}
// Create the link entry for the root
createPropertyLink(rootId, entityId, 0L, entityId);
// Now iterate over the entries and create properties for the keys and values
for (Map.Entry<K, V> entry : map.entrySet())
{
K key = entry.getKey();
Long keyId = getOrCreatePropertyValue(key).getFirst();
V value = entry.getValue();
// Callback with this level, incrementing the current depth
Pair<Long, Serializable> valuePair = getOrCreatePropertyValueImpl(
(Serializable) value,
rootId,
maxDepth,
currentDepth + 1);
Long valueId = valuePair.getFirst();
// Now write the mapping entry
createPropertyLink(rootId, entityId, keyId, valueId);
}
// Done
return entityId;
}
/**
* Recursive method to write a collection out. If the root entity ID is <tt>null</tt> then the current
* collection ID is used as the root.
*
* @param value the collection to write
* @param rootId the root property ID
* @return Returns the ID of the newly-written collection
*/
private <V extends Serializable> Long createPropertyCollectionImpl(
Collection<V> collection,
Long rootId,
int maxDepth,
int currentDepth)
{
// Create the root of the collection
Class<?> clazz = null;
if (collection instanceof Set<?>)
{
clazz = HashSet.class;
}
else
{
clazz = ArrayList.class;
}
Long entityId = createPropertyCollectionRoot(clazz);
// Use this as the root if this is the first entry into this method
if (rootId == null)
{
rootId = entityId;
}
// Create the link entry for the root
createPropertyLink(rootId, entityId, 0L, entityId);
// Now iterate over the entries and create properties for the keys and values
long index = 0L;
for (V value : collection)
{
// Callback with this level, incrementing the current depth
Pair<Long, Serializable> valuePair = getOrCreatePropertyValueImpl(
(Serializable) value,
rootId,
maxDepth,
currentDepth + 1);
Long valueId = valuePair.getFirst();
// Now write the mapping entry
Long keyId = new Long(index);
createPropertyLink(rootId, entityId, keyId, valueId);
// Keep iterating
index++;
}
return entityId;
}
/**
* Create a property value entry for <b>maps</b>. The class is assumed to
* be correct with a default constructor for later reconstruction.
* <p/>
* 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 <b>collections</b>. The class is assumed to
* be correct with a default constructor for later reconstruction.
* <p/>
* 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 valueId the ID of the entity storing the value (may be another map or collection)
*/
protected abstract void createPropertyLink(
Long rootCollectionId,
Long currentCollectionId,
Long keyId,
Long valueId);
} }

View File

@@ -0,0 +1,123 @@
/*
* 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;
/**
* Entity bean for <b>alf_prop_collections_link</b> table.
*
* @author Derek Hulley
* @since 3.3
*/
public class PropertyCollectionLinkEntity
{
private long rootCollectionPropId;
private long currentCollectionPropId;
private long keyPropId;
private long valuePropId;
public PropertyCollectionLinkEntity()
{
}
@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.keyPropId == that.keyPropId &&
this.valuePropId == that.valuePropId;
}
else
{
return false;
}
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder(512);
sb.append("PropertyCollectionsLinkEntity")
.append("[ currentCollectionPropId=").append(currentCollectionPropId)
.append(", keyPropId=").append(keyPropId)
.append(", valuePropId=").append(valuePropId)
.append("]");
return sb.toString();
}
public long getRootCollectionPropId()
{
return rootCollectionPropId;
}
public void setRootCollectionPropId(long rootCollectionPropId)
{
this.rootCollectionPropId = rootCollectionPropId;
}
public long getCurrentCollectionPropId()
{
return currentCollectionPropId;
}
public void setCurrentCollectionPropId(long currentCollectionPropId)
{
this.currentCollectionPropId = currentCollectionPropId;
}
public long getKeyPropId()
{
return keyPropId;
}
public void setKeyPropId(long keyPropId)
{
this.keyPropId = keyPropId;
}
public long getValuePropId()
{
return valuePropId;
}
public void setValuePropId(long valuePropId)
{
this.valuePropId = valuePropId;
}
}

View File

@@ -26,7 +26,9 @@ package org.alfresco.repo.domain.propval;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
import java.util.Map;
import org.alfresco.repo.action.evaluator.compare.PropertyValueComparator;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
/** /**
@@ -37,6 +39,9 @@ import org.alfresco.util.Pair;
*/ */
public interface PropertyValueDAO public interface PropertyValueDAO
{ {
//================================
// 'alf_prop_class' accessors
//================================
/** /**
* <b>alf_prop_class</b> accessor * <b>alf_prop_class</b> accessor
* *
@@ -56,6 +61,9 @@ public interface PropertyValueDAO
*/ */
Pair<Long, Class<?>> getOrCreatePropertyClass(Class<?> value); Pair<Long, Class<?>> getOrCreatePropertyClass(Class<?> value);
//================================
// 'alf_prop_date_value' accessors
//================================
/** /**
* <b>alf_prop_date_value</b> accessor * <b>alf_prop_date_value</b> accessor
* *
@@ -75,6 +83,9 @@ public interface PropertyValueDAO
*/ */
Pair<Long, Date> getOrCreatePropertyDateValue(Date value); Pair<Long, Date> getOrCreatePropertyDateValue(Date value);
//================================
// 'alf_prop_string_value' accessors
//================================
/** /**
* <b>alf_prop_string_value</b> accessor * <b>alf_prop_string_value</b> accessor
* *
@@ -94,6 +105,9 @@ public interface PropertyValueDAO
*/ */
Pair<Long, String> getOrCreatePropertyStringValue(String value); Pair<Long, String> getOrCreatePropertyStringValue(String value);
//================================
// 'alf_prop_double_value' accessors
//================================
/** /**
* <b>alf_prop_double_value</b> accessor * <b>alf_prop_double_value</b> accessor
* *
@@ -113,22 +127,52 @@ public interface PropertyValueDAO
*/ */
Pair<Long, Double> getOrCreatePropertyDoubleValue(Double value); Pair<Long, Double> getOrCreatePropertyDoubleValue(Double value);
//================================
// 'alf_prop_value' accessors
//================================
/** /**
* <b>alf_prop_value</b> accessor * <b>alf_prop_value</b> accessor: get a property based on the database ID
* *
* @param id the ID (may not be <tt>null</tt>) * @param id the ID (may not be <tt>null</tt>)
*/ */
Pair<Long, Serializable> getPropertyValueById(Long id); Pair<Long, Serializable> getPropertyValueById(Long id);
/** /**
* <b>alf_prop_value</b> accessor * <b>alf_prop_value</b> accessor: find a property based on the value
* *
* @param value the value to find the ID for (may be <tt>null</tt>) * @param value the value to find the ID for (may be <tt>null</tt>)
*/ */
Pair<Long, Serializable> getPropertyValue(Serializable value); Pair<Long, Serializable> getPropertyValue(Serializable value);
/** /**
* <b>alf_prop_value</b> accessor * <b>alf_prop_value</b> accessor: find or create a property based on the value.
* <b>Note:</b> This method will not recurse into maps or collections. Use the
* dedicated methods if you want recursion; otherwise maps and collections will
* be serialized and probably stored as BLOB values.
* <p/>
* All collections and maps will be opened up to any depth. To limit this behaviour,
* use {@link #getOrCreatePropertyValue(Serializable, int)}.
* *
* @param value the value to find the ID for (may be <tt>null</tt>) * @param value the value to find the ID for (may be <tt>null</tt>)
*/ */
Pair<Long, Serializable> getOrCreatePropertyValue(Serializable value); Pair<Long, Serializable> getOrCreatePropertyValue(Serializable value);
/**
* <b>alf_prop_value</b> accessor: find or create a property based on the value.
* <b>Note:</b> This method will not recurse into maps or collections. Use the
* dedicated methods if you want recursion; otherwise maps and collections will
* be serialized and probably stored as BLOB values.
* <p>
* <u>Max depth examples</u> (there is no upper limit):
* <ul>
* <li>0: don't expand the value if it's a map or collection</li>
* <li>1: open the value up if it is a map or collection but don't do any more</li>
* <li>...</li>
* <li>10: open up 10 levels of maps or collections</li>
* </ul>
* All collections that are not opened up will be serialized unless there is a
* custom {@link PropertyTypeConverter converter} which can serialize it in an
* alternative format.
*
* @param value the value to find the ID for (may be <tt>null</tt>)
* @param maxDepth the maximum depth of collections and maps to iterate into
*/
Pair<Long, Serializable> getOrCreatePropertyValue(Serializable value, int maxDepth);
} }

View File

@@ -25,7 +25,10 @@
package org.alfresco.repo.domain.propval; package org.alfresco.repo.domain.propval;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random; import java.util.Random;
import junit.framework.TestCase; import junit.framework.TestCase;
@@ -344,6 +347,71 @@ public class PropertyValueDAOTest extends TestCase
} }
} }
public void testPropertyValue_MapOfStrings() throws Exception
{
final HashMap<String, String> map = new HashMap<String, String>(15);
for (int i = 0; i < 20; i++)
{
String key = "MAP-KEY-" + i;
String value = "MAP-VALUE-" + i;
map.put(key, value);
}
RetryingTransactionCallback<Void> createCallback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
propertyValueDAO.getOrCreatePropertyValue(map);
return null;
}
};
transactionService.getRetryingTransactionHelper().doInTransaction(createCallback);
}
public void testPropertyValue_MapOfMapOfStrings() throws Exception
{
final HashMap<String, String> mapInner = new HashMap<String, String>(15);
for (int i = 0; i < 20; i++)
{
String key = "INNERMAP-KEY-" + i;
String value = "INNERMAP-VALUE-" + i;
mapInner.put(key, value);
}
final HashMap<String, Map<?, ?>> mapOuter = new HashMap<String, Map<?, ?>>(37);
for (int i = 0; i < 2; i++)
{
String key = "OUTERMAP-KEY-" + i;
mapOuter.put(key, mapInner);
}
RetryingTransactionCallback<Void> createCallback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
propertyValueDAO.getOrCreatePropertyValue(mapOuter);
return null;
}
};
transactionService.getRetryingTransactionHelper().doInTransaction(createCallback);
}
public void testPropertyValue_CollectionOfStrings() throws Exception
{
final ArrayList<String> list = new ArrayList<String>(20);
for (int i = 0; i < 20; i++)
{
String value = "COLL-VALUE-" + i;
list.add(value);
}
RetryingTransactionCallback<Void> createCallback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
propertyValueDAO.getOrCreatePropertyValue(list);
return null;
}
};
transactionService.getRetryingTransactionHelper().doInTransaction(createCallback);
}
private void removeCaches() private void removeCaches()
{ {
((AbstractPropertyValueDAOImpl)propertyValueDAO).setPropertyClassCache(null); ((AbstractPropertyValueDAOImpl)propertyValueDAO).setPropertyClassCache(null);

View File

@@ -25,6 +25,7 @@
package org.alfresco.repo.domain.propval; package org.alfresco.repo.domain.propval;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
@@ -55,6 +56,8 @@ public class PropertyValueEntity
public static final Short ORDINAL_DOUBLE = 2; public static final Short ORDINAL_DOUBLE = 2;
public static final Short ORDINAL_STRING = 3; public static final Short ORDINAL_STRING = 3;
public static final Short ORDINAL_SERIALIZABLE = 4; public static final Short ORDINAL_SERIALIZABLE = 4;
public static final Short ORDINAL_MAP = 5;
public static final Short ORDINAL_COLLECTION = 6;
/** /**
* Enumeration of persisted types for <b>alf_prop_value.persisted_type</b>. * Enumeration of persisted types for <b>alf_prop_value.persisted_type</b>.
@@ -131,6 +134,32 @@ public class PropertyValueEntity
{ {
return Serializable.class; return Serializable.class;
} }
},
MAP
{
@Override
public Short getOrdinalNumber()
{
return ORDINAL_MAP;
}
@Override
public Class<?> getAssociatedClass()
{
return Map.class;
}
},
COLLECTION
{
@Override
public Short getOrdinalNumber()
{
return ORDINAL_COLLECTION;
}
@Override
public Class<?> getAssociatedClass()
{
return Collection.class;
}
}; };
/** /**
@@ -182,6 +211,8 @@ public class PropertyValueEntity
mapClass.put(Double.class, PersistedType.DOUBLE); mapClass.put(Double.class, PersistedType.DOUBLE);
mapClass.put(String.class, PersistedType.STRING); mapClass.put(String.class, PersistedType.STRING);
mapClass.put(Date.class, PersistedType.LONG); 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
persistedTypesByClass = Collections.unmodifiableMap(mapClass); persistedTypesByClass = Collections.unmodifiableMap(mapClass);
} }
@@ -250,18 +281,20 @@ public class PropertyValueEntity
{ {
switch (persistedTypeEnum) switch (persistedTypeEnum)
{ {
case NULL: case MAP:
return null; case COLLECTION:
case LONG: case NULL:
return longValue; return null;
case DOUBLE: case LONG:
return doubleValue; return longValue;
case STRING: case DOUBLE:
return stringValue; return doubleValue;
case SERIALIZABLE: case STRING:
return serializableValue; return stringValue;
default: case SERIALIZABLE:
throw new IllegalStateException("Should not be able to get through switch"); return serializableValue;
default:
throw new IllegalStateException("Should not be able to get through switch");
} }
} }

View File

@@ -25,11 +25,14 @@
package org.alfresco.repo.domain.propval.ibatis; package org.alfresco.repo.domain.propval.ibatis;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map;
import org.alfresco.repo.domain.propval.AbstractPropertyValueDAOImpl; import org.alfresco.repo.domain.propval.AbstractPropertyValueDAOImpl;
import org.alfresco.repo.domain.propval.PropertyClassEntity; 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.PropertyDateValueEntity;
import org.alfresco.repo.domain.propval.PropertyDoubleValueEntity; import org.alfresco.repo.domain.propval.PropertyDoubleValueEntity;
import org.alfresco.repo.domain.propval.PropertyStringValueEntity; import org.alfresco.repo.domain.propval.PropertyStringValueEntity;
@@ -68,6 +71,8 @@ public class PropertyValueDAOImpl extends AbstractPropertyValueDAOImpl
private static final String SELECT_PROPERTY_VALUE_BY_STRING_VALUE = "select.PropertyValueByStringValue"; 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_VALUE = "insert.PropertyValue";
private static final String INSERT_PROPERTY_COLLECTION_LINK = "insert.PropertyCollectionLink";
private SqlMapClientTemplate template; private SqlMapClientTemplate template;
public void setSqlMapClientTemplate(SqlMapClientTemplate sqlMapClientTemplate) public void setSqlMapClientTemplate(SqlMapClientTemplate sqlMapClientTemplate)
@@ -385,4 +390,62 @@ public class PropertyValueDAOImpl extends AbstractPropertyValueDAOImpl
// Done // Done
return insertEntity; 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<Long, Class<?>> 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<Long, Class<?>> 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)
{
PropertyCollectionLinkEntity insertEntity = new PropertyCollectionLinkEntity();
insertEntity.setRootCollectionPropId(rootCollectionId);
insertEntity.setCurrentCollectionPropId(currentCollectionId);
insertEntity.setKeyPropId(keyId);
insertEntity.setValuePropId(valueId);
template.insert(INSERT_PROPERTY_COLLECTION_LINK, insertEntity);
// Done
}
} }

View File

@@ -32,7 +32,6 @@ import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -94,6 +93,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
private static Log loggerPaths = LogFactory.getLog(DbNodeServiceImpl.class.getName() + ".paths"); private static Log loggerPaths = LogFactory.getLog(DbNodeServiceImpl.class.getName() + ".paths");
private NodeDaoService nodeDaoService; private NodeDaoService nodeDaoService;
/** A set of aspects, the presence of which indicate that nodes must not be archived */
private Set<QName> ignoreArchiveAspects;
private StoreArchiveMap storeArchiveMap; private StoreArchiveMap storeArchiveMap;
private NodeService avmNodeService; private NodeService avmNodeService;
private NodeIndexer nodeIndexer; private NodeIndexer nodeIndexer;