mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Fix SAIL-389 (SAIL-294): NodeDAO: single-valued, d:any properties don't handle increasing array values
- Incorrect translation of raw values back to Serializable for cache purposes - Addition of Savepoint around alf_node insert git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@21136 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -938,13 +938,16 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
|
|||||||
}
|
}
|
||||||
|
|
||||||
Long id = null;
|
Long id = null;
|
||||||
|
Savepoint savepoint = controlDAO.createSavepoint("newNodeImpl");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// First try a straight insert and risk the constraint violation if the node exists
|
// First try a straight insert and risk the constraint violation if the node exists
|
||||||
id = insertNode(node);
|
id = insertNode(node);
|
||||||
|
controlDAO.releaseSavepoint(savepoint);
|
||||||
}
|
}
|
||||||
catch (Throwable e)
|
catch (Throwable e)
|
||||||
{
|
{
|
||||||
|
controlDAO.rollbackToSavepoint(savepoint);
|
||||||
// This is probably because there is an existing node. We can handle existing deleted nodes.
|
// This is probably because there is an existing node. We can handle existing deleted nodes.
|
||||||
NodeRef targetNodeRef = node.getNodeRef();
|
NodeRef targetNodeRef = node.getNodeRef();
|
||||||
NodeEntity deletedNode = selectNodeByNodeRef(targetNodeRef, true); // Only look for deleted nodes
|
NodeEntity deletedNode = selectNodeByNodeRef(targetNodeRef, true); // Only look for deleted nodes
|
||||||
@@ -3071,7 +3074,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
|
|||||||
protected abstract Map<NodePropertyKey, NodePropertyValue> selectNodeProperties(Long nodeId);
|
protected abstract Map<NodePropertyKey, NodePropertyValue> selectNodeProperties(Long nodeId);
|
||||||
protected abstract Map<NodePropertyKey, NodePropertyValue> selectNodeProperties(Long nodeId, Set<Long> qnameIds);
|
protected abstract Map<NodePropertyKey, NodePropertyValue> selectNodeProperties(Long nodeId, Set<Long> qnameIds);
|
||||||
protected abstract int deleteNodeProperties(Long nodeId, Set<Long> qnameIds);
|
protected abstract int deleteNodeProperties(Long nodeId, Set<Long> qnameIds);
|
||||||
protected abstract void deleteNodeProperties(Long nodeId, List<NodePropertyKey> propKeys);
|
protected abstract int deleteNodeProperties(Long nodeId, List<NodePropertyKey> propKeys);
|
||||||
protected abstract void insertNodeProperties(Long nodeId, Map<NodePropertyKey, NodePropertyValue> persistableProps);
|
protected abstract void insertNodeProperties(Long nodeId, Map<NodePropertyKey, NodePropertyValue> persistableProps);
|
||||||
protected abstract Set<Long> selectNodeAspectIds(Long nodeId);
|
protected abstract Set<Long> selectNodeAspectIds(Long nodeId);
|
||||||
protected abstract void insertNodeAspect(Long nodeId, Long qnameId);
|
protected abstract void insertNodeAspect(Long nodeId, Long qnameId);
|
||||||
|
@@ -191,9 +191,10 @@ public class NodePropertyHelper
|
|||||||
// Check that multi-valued properties are supported if the property is a collection
|
// Check that multi-valued properties are supported if the property is a collection
|
||||||
if (!isMultiValued)
|
if (!isMultiValued)
|
||||||
{
|
{
|
||||||
throw new DictionaryException("A single-valued property of this type may not be a collection: \n"
|
throw new DictionaryException("A single-valued property of this type may not be a collection: \n" +
|
||||||
+ " Property: " + propertyDef + "\n" + " Type: " + propertyTypeQName + "\n" + " Value: "
|
" Property: " + propertyDef + "\n" +
|
||||||
+ value);
|
" Type: " + propertyTypeQName + "\n" +
|
||||||
|
" Value: " + value);
|
||||||
}
|
}
|
||||||
// We have an allowable collection.
|
// We have an allowable collection.
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -327,8 +328,7 @@ public class NodePropertyHelper
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Serializable getPublicProperty(
|
public Serializable getPublicProperty(
|
||||||
Map<NodePropertyKey,
|
Map<NodePropertyKey, NodePropertyValue> propertyValues,
|
||||||
NodePropertyValue> propertyValues,
|
|
||||||
QName propertyQName)
|
QName propertyQName)
|
||||||
{
|
{
|
||||||
// Get the qname ID
|
// Get the qname ID
|
||||||
@@ -409,16 +409,24 @@ public class NodePropertyHelper
|
|||||||
// There is more than one value so the list indexes need to be collapsed
|
// There is more than one value so the list indexes need to be collapsed
|
||||||
collapsedValue = collapsePropertiesWithSameQName(currentPropertyDef, scratch);
|
collapsedValue = collapsePropertiesWithSameQName(currentPropertyDef, scratch);
|
||||||
}
|
}
|
||||||
|
boolean forceCollection = false;
|
||||||
// If the property is multi-valued then the output property must be a collection
|
// If the property is multi-valued then the output property must be a collection
|
||||||
if (currentPropertyDef != null && currentPropertyDef.isMultiValued())
|
if (currentPropertyDef != null && currentPropertyDef.isMultiValued())
|
||||||
{
|
{
|
||||||
if (collapsedValue != null && !(collapsedValue instanceof Collection<?>))
|
forceCollection = true;
|
||||||
{
|
}
|
||||||
// Can't use Collections.singletonList: ETHREEOH-1172
|
else if (scratch.size() == 1 && scratch.firstKey().getListIndex().intValue() > -1)
|
||||||
ArrayList<Serializable> collection = new ArrayList<Serializable>(1);
|
{
|
||||||
collection.add(collapsedValue);
|
// This is to handle cases of collections where the property is d:any but not
|
||||||
collapsedValue = collection;
|
// declared as multiple.
|
||||||
}
|
forceCollection = true;
|
||||||
|
}
|
||||||
|
if (forceCollection && collapsedValue != null && !(collapsedValue instanceof Collection<?>))
|
||||||
|
{
|
||||||
|
// Can't use Collections.singletonList: ETHREEOH-1172
|
||||||
|
ArrayList<Serializable> collection = new ArrayList<Serializable>(1);
|
||||||
|
collection.add(collapsedValue);
|
||||||
|
collapsedValue = collection;
|
||||||
}
|
}
|
||||||
// Store the value
|
// Store the value
|
||||||
propertyMap.put(currentQName, collapsedValue);
|
propertyMap.put(currentQName, collapsedValue);
|
||||||
@@ -534,12 +542,23 @@ public class NodePropertyHelper
|
|||||||
if (propertyValuesSize == 0)
|
if (propertyValuesSize == 0)
|
||||||
{
|
{
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
Integer listIndex = null;
|
||||||
for (Map.Entry<NodePropertyKey, NodePropertyValue> entry : propertyValues.entrySet())
|
for (Map.Entry<NodePropertyKey, NodePropertyValue> entry : propertyValues.entrySet())
|
||||||
{
|
{
|
||||||
NodePropertyKey propertyKey = entry.getKey();
|
NodePropertyKey propertyKey = entry.getKey();
|
||||||
NodePropertyValue propertyValue = entry.getValue();
|
NodePropertyValue propertyValue = entry.getValue();
|
||||||
|
|
||||||
|
if (listIndex == null)
|
||||||
|
{
|
||||||
|
listIndex = propertyKey.getListIndex();
|
||||||
|
}
|
||||||
|
else if (!listIndex.equals(propertyKey.getListIndex()))
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("Expecting to collapse properties with same list index: " + propertyValues);
|
||||||
|
}
|
||||||
|
|
||||||
if (propertyValuesSize == 1
|
if (propertyValuesSize == 1
|
||||||
&& (propertyDef == null || !propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)))
|
&& (propertyDef == null || !propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)))
|
||||||
{
|
{
|
||||||
|
@@ -468,14 +468,14 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void deleteNodeProperties(Long nodeId, List<NodePropertyKey> propKeys)
|
protected int deleteNodeProperties(Long nodeId, List<NodePropertyKey> propKeys)
|
||||||
{
|
{
|
||||||
Assert.notNull(nodeId, "Must have 'nodeId'");
|
Assert.notNull(nodeId, "Must have 'nodeId'");
|
||||||
Assert.notNull(nodeId, "Must have 'propKeys'");
|
Assert.notNull(nodeId, "Must have 'propKeys'");
|
||||||
|
|
||||||
if (propKeys.size() == 0)
|
if (propKeys.size() == 0)
|
||||||
{
|
{
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodePropertyEntity prop = new NodePropertyEntity();
|
NodePropertyEntity prop = new NodePropertyEntity();
|
||||||
@@ -483,18 +483,20 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
|
|||||||
prop.setNodeId(nodeId);
|
prop.setNodeId(nodeId);
|
||||||
|
|
||||||
startBatch();
|
startBatch();
|
||||||
|
int count = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
for (NodePropertyKey propKey : propKeys)
|
for (NodePropertyKey propKey : propKeys)
|
||||||
{
|
{
|
||||||
prop.setKey(propKey);
|
prop.setKey(propKey);
|
||||||
template.delete(DELETE_NODE_PROPERTIES, prop);
|
count += template.delete(DELETE_NODE_PROPERTIES, prop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
executeBatch();
|
executeBatch();
|
||||||
}
|
}
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -1892,6 +1892,40 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
|||||||
assertEquals("MLText collection didn't come back correctly.", mlTextCollection, mlTextCollectionCheck);
|
assertEquals("MLText collection didn't come back correctly.", mlTextCollection, mlTextCollectionCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that d:any types are handled correctly when adding values
|
||||||
|
*/
|
||||||
|
public void testMultivaluedSerializable() throws Exception
|
||||||
|
{
|
||||||
|
ArrayList<String> listProp = new ArrayList<String>();
|
||||||
|
|
||||||
|
listProp.clear();
|
||||||
|
nodeService.addProperties(
|
||||||
|
rootNodeRef,
|
||||||
|
Collections.singletonMap(PROP_QNAME_ANY_PROP_MULTIPLE, (Serializable) listProp));
|
||||||
|
listProp.add("ONE");
|
||||||
|
nodeService.addProperties(
|
||||||
|
rootNodeRef,
|
||||||
|
Collections.singletonMap(PROP_QNAME_ANY_PROP_MULTIPLE, (Serializable) listProp));
|
||||||
|
listProp.add("TWO");
|
||||||
|
nodeService.addProperties(
|
||||||
|
rootNodeRef,
|
||||||
|
Collections.singletonMap(PROP_QNAME_ANY_PROP_MULTIPLE, (Serializable) listProp));
|
||||||
|
|
||||||
|
listProp.clear();
|
||||||
|
nodeService.addProperties(
|
||||||
|
rootNodeRef,
|
||||||
|
Collections.singletonMap(PROP_QNAME_ANY_PROP_SINGLE, (Serializable) listProp));
|
||||||
|
listProp.add("ONE");
|
||||||
|
nodeService.addProperties(
|
||||||
|
rootNodeRef,
|
||||||
|
Collections.singletonMap(PROP_QNAME_ANY_PROP_SINGLE, (Serializable) listProp));
|
||||||
|
listProp.add("TWO");
|
||||||
|
nodeService.addProperties(
|
||||||
|
rootNodeRef,
|
||||||
|
Collections.singletonMap(PROP_QNAME_ANY_PROP_SINGLE, (Serializable) listProp));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks that the {@link ContentModel#ASPECT_REFERENCEABLE referencable} properties
|
* Checks that the {@link ContentModel#ASPECT_REFERENCEABLE referencable} properties
|
||||||
* are present
|
* are present
|
||||||
|
Reference in New Issue
Block a user