mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
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:
@@ -68,6 +68,16 @@ CREATE TABLE alf_prop_value
|
||||
PRIMARY KEY (id)
|
||||
) 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
|
||||
--
|
||||
|
@@ -15,6 +15,7 @@
|
||||
<typeAlias alias="PropertyStringValue" type="org.alfresco.repo.domain.propval.PropertyStringValueEntity"/>
|
||||
<typeAlias alias="PropertyDoubleValue" type="org.alfresco.repo.domain.propval.PropertyDoubleValueEntity"/>
|
||||
<typeAlias alias="PropertyValue" type="org.alfresco.repo.domain.propval.PropertyValueEntity"/>
|
||||
<typeAlias alias="PropertyCollectionLink" type="org.alfresco.repo.domain.propval.PropertyCollectionLinkEntity"/>
|
||||
|
||||
<!-- -->
|
||||
<!-- Result Maps -->
|
||||
@@ -56,6 +57,12 @@
|
||||
<result property="stringValue" column="string_value" jdbcType="VARCHAR" javaType="java.lang.String"/>
|
||||
<result property="serializableValue" column="serializable_value" jdbcType="BLOB" javaType="java.lang.Object"/>
|
||||
</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 -->
|
||||
@@ -271,4 +278,15 @@
|
||||
pv.id = #id#
|
||||
</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>
|
@@ -25,7 +25,13 @@
|
||||
package org.alfresco.repo.domain.propval;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
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.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.domain.CrcHelper;
|
||||
import org.alfresco.repo.domain.propval.PropertyValueEntity.PersistedType;
|
||||
import org.alfresco.service.cmr.repository.MLText;
|
||||
import org.alfresco.util.Pair;
|
||||
|
||||
/**
|
||||
@@ -489,7 +496,6 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
|
||||
return (Pair<Long, Double>) entityPair;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback for <b>alf_prop_double_value</b> DAO.
|
||||
*/
|
||||
@@ -559,10 +565,60 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
|
||||
return entityPair;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see #getOrCreatePropertyValueImpl(Serializable, int, int)
|
||||
*/
|
||||
public Pair<Long, Serializable> getOrCreatePropertyValue(Serializable value)
|
||||
{
|
||||
Pair<Long, Serializable> entityPair = propertyValueCache.getOrCreateByValue(value);
|
||||
return (Pair<Long, Serializable>) entityPair;
|
||||
return getOrCreatePropertyValueImpl(value, null, Integer.MAX_VALUE, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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)
|
||||
{
|
||||
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)
|
||||
@@ -622,6 +682,13 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
|
||||
|
||||
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);
|
||||
return convertEntityToPair(entity);
|
||||
}
|
||||
@@ -630,4 +697,157 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
|
||||
protected abstract PropertyValueEntity findPropertyValueById(Long id);
|
||||
protected abstract PropertyValueEntity findPropertyValueByValue(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);
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -26,7 +26,9 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -37,6 +39,9 @@ import org.alfresco.util.Pair;
|
||||
*/
|
||||
public interface PropertyValueDAO
|
||||
{
|
||||
//================================
|
||||
// 'alf_prop_class' accessors
|
||||
//================================
|
||||
/**
|
||||
* <b>alf_prop_class</b> accessor
|
||||
*
|
||||
@@ -56,6 +61,9 @@ public interface PropertyValueDAO
|
||||
*/
|
||||
Pair<Long, Class<?>> getOrCreatePropertyClass(Class<?> value);
|
||||
|
||||
//================================
|
||||
// 'alf_prop_date_value' accessors
|
||||
//================================
|
||||
/**
|
||||
* <b>alf_prop_date_value</b> accessor
|
||||
*
|
||||
@@ -75,6 +83,9 @@ public interface PropertyValueDAO
|
||||
*/
|
||||
Pair<Long, Date> getOrCreatePropertyDateValue(Date value);
|
||||
|
||||
//================================
|
||||
// 'alf_prop_string_value' accessors
|
||||
//================================
|
||||
/**
|
||||
* <b>alf_prop_string_value</b> accessor
|
||||
*
|
||||
@@ -94,6 +105,9 @@ public interface PropertyValueDAO
|
||||
*/
|
||||
Pair<Long, String> getOrCreatePropertyStringValue(String value);
|
||||
|
||||
//================================
|
||||
// 'alf_prop_double_value' accessors
|
||||
//================================
|
||||
/**
|
||||
* <b>alf_prop_double_value</b> accessor
|
||||
*
|
||||
@@ -113,22 +127,52 @@ public interface PropertyValueDAO
|
||||
*/
|
||||
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>)
|
||||
*/
|
||||
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>)
|
||||
*/
|
||||
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>)
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
@@ -25,7 +25,10 @@
|
||||
package org.alfresco.repo.domain.propval;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
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()
|
||||
{
|
||||
((AbstractPropertyValueDAOImpl)propertyValueDAO).setPropertyClassCache(null);
|
||||
|
@@ -25,6 +25,7 @@
|
||||
package org.alfresco.repo.domain.propval;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
@@ -55,6 +56,8 @@ 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;
|
||||
|
||||
/**
|
||||
* Enumeration of persisted types for <b>alf_prop_value.persisted_type</b>.
|
||||
@@ -131,6 +134,32 @@ public class PropertyValueEntity
|
||||
{
|
||||
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(String.class, PersistedType.STRING);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -250,18 +281,20 @@ public class PropertyValueEntity
|
||||
{
|
||||
switch (persistedTypeEnum)
|
||||
{
|
||||
case NULL:
|
||||
return null;
|
||||
case LONG:
|
||||
return longValue;
|
||||
case DOUBLE:
|
||||
return doubleValue;
|
||||
case STRING:
|
||||
return stringValue;
|
||||
case SERIALIZABLE:
|
||||
return serializableValue;
|
||||
default:
|
||||
throw new IllegalStateException("Should not be able to get through switch");
|
||||
case MAP:
|
||||
case COLLECTION:
|
||||
case NULL:
|
||||
return null;
|
||||
case LONG:
|
||||
return longValue;
|
||||
case DOUBLE:
|
||||
return doubleValue;
|
||||
case STRING:
|
||||
return stringValue;
|
||||
case SERIALIZABLE:
|
||||
return serializableValue;
|
||||
default:
|
||||
throw new IllegalStateException("Should not be able to get through switch");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -25,11 +25,14 @@
|
||||
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.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 INSERT_PROPERTY_VALUE = "insert.PropertyValue";
|
||||
|
||||
private static final String INSERT_PROPERTY_COLLECTION_LINK = "insert.PropertyCollectionLink";
|
||||
|
||||
private SqlMapClientTemplate template;
|
||||
|
||||
public void setSqlMapClientTemplate(SqlMapClientTemplate sqlMapClientTemplate)
|
||||
@@ -385,4 +390,62 @@ public class PropertyValueDAOImpl extends AbstractPropertyValueDAOImpl
|
||||
// Done
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@@ -32,7 +32,6 @@ import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -94,6 +93,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
private static Log loggerPaths = LogFactory.getLog(DbNodeServiceImpl.class.getName() + ".paths");
|
||||
|
||||
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 NodeService avmNodeService;
|
||||
private NodeIndexer nodeIndexer;
|
||||
|
Reference in New Issue
Block a user