/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and Cambridge Semantics Incorporated.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* File: $Source: /cvsroot/slrp/boca/com.ibm.adtech.boca.common/src/com/ibm/adtech/boca/rdb/layout/CompositeNodeLayout.java,v $
* Created by: Stephen Evanchik <evanchik@us.ibm.com>
* Created on: 9/30/2005
* Revision: $Id: CompositeNodeLayout.java 178 2007-07-31 14:22:33Z mroy $
*
* Contributors:
* IBM Corporation - initial API and implementation
* Cambridge Semantics Incorporated - Fork to Anzo
*******************************************************************************/
package org.openanzo.jdbc.layout;
import java.security.InvalidParameterException;
import java.sql.Connection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.openanzo.cache.ICache;
import org.openanzo.exceptions.ExceptionConstants;
import org.openanzo.exceptions.LogUtils;
import org.openanzo.jdbc.container.sql.NodeSQL;
import org.openanzo.jdbc.container.sql.NodeSQL.GetAllLiteralsResult;
import org.openanzo.jdbc.layout.indexer.LiteralIndexer;
import org.openanzo.jdbc.query.NodeConverter;
import org.openanzo.jdbc.utils.ClosableIterator;
import org.openanzo.jdbc.utils.PreparedStatementProvider;
import org.openanzo.jdbc.utils.RdbException;
import org.openanzo.jdbc.utils.UnhandledRdbException;
import org.openanzo.rdf.BlankNode;
import org.openanzo.rdf.PlainLiteral;
import org.openanzo.rdf.TypedLiteral;
import org.openanzo.rdf.URI;
import org.openanzo.rdf.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provides cached access to RDF Nodes persisted in a relational database according to the 'node centric layout' schema. RDF Nodes are allocated IDs when they
* are stored allowing any database table to store a reference to a node using a BIGINT column and this class for converting Nodes to and from the IDs stored in
* the database.
*
* @author <a href="mailto:evanchik@us.ibm.com">Stephen Evanchik</a>
* @author Joe Betz
*/
public class CompositeNodeLayout {
//private static final Logger log = LoggerFactory.getLogger(CompositeNodeLayout.class);
// The name of the temporary table that is used during bulk node insertion
private final static String RESOURCE_TEMP_TABLE = "RES_TMP";
private final static String LITERAL_TEMP_TABLE = "LIT_TMP";
private final static String LOCKED_IDS_TABLE = "_USED_IDS";
private final static String ID_TEMP_TABLE = "ID_TMP";
private static final Logger log = LoggerFactory.getLogger(CompositeNodeLayout.class);
@SuppressWarnings("unchecked")
private final ILayoutCache[] layoutTypesToCaches;
private final String URI_TABLE_NAME;
private final String LONG_URI_TABLE_NAME;
private final String BLANK_TABLE_NAME;
private final String LITERAL_TABLE_NAME;
private final String LONG_LIT_TABLE_NAME;
private final String TYPED_LITERAL_TABLE_NAME;
private final String LONG_TYPED_LIT_TABLE_NAME;
private final String LOCK_ID_TABLE_NAME;
private final String LANGUAGE_TABLE;
private final String DATATYPE_TABLE;
private final String[] tables;
private final PreparedStatementProvider stmtProvider;
private int maxLength;
private final boolean supportsTempTables;
private final NodeLayoutCacheProxy<URI> nodeURILayout;
private final NodeLayoutCacheProxy<BlankNode> nodeBlankLayout;
private final NodeLayoutCacheProxy<PlainLiteral> nodePlainLiteralLayout;
private final NodeLayoutCacheProxy<TypedLiteral> nodeTypedLiteralLayout;
private final ValueLayoutCacheProxy datatypeLayout;
private final ValueLayoutCacheProxy languageLayout;
private final NodeConverter nodeConverter;
private final String optimizationString;
private final LiteralIndexer literalIndexer;
private final ICache<Long, URI> uriCache;
private final ICache<URI, Long> uriIdCache;
private final ICache<Long, BlankNode> blankCache;
private final ICache<BlankNode, Long> blankIdCache;
private final ICache<Long, PlainLiteral> literalCache;
private final ICache<PlainLiteral, Long> literalIdCache;
private final ICache<Long, TypedLiteral> typedLiteralCache;
private final ICache<TypedLiteral, Long> typedLiteralIdCache;
private final ICache<Long, String> languageCache;
private final ICache<String, Long> languageIdCache;
private final ICache<Long, String> datatypeCache;
private final ICache<String, Long> datatypeIdCache;
/**
* Construct a layout to store nodes in a database
*
* @param stmtProvider
* The interface to the SQL prepared statement cache
* @param supportsSequences
* Does the underlying database support sequences to get IDs
* @param literalIndexer
* Literal indexer for the underlying connection
* @param containerName
* The name of the logical container for the nodes. This container name is used to generate the table names for each node type (i.e. Node_URI or
* Node_Literal, etc.)
* @param maxLength
* The maximum length of a node's string representation before it is considered long
* @param optimizationString
* String prepended to queries to enable database specific optimizations
* @param supportsTempTables
* true if the database supports temporary tables, false otherwise.
* @param supportsIdentity
* @param sessionPrefix
* prefix for references to temporary tables within queries
* @param uriCache
* cache of uri values and ids
* @param literalCache
* cache of literal values and ids
* @param languageCache
* cache of language values and ids
* @param datatypeCache
* cache of datatype values and ids
*
* @param uriIdCache
* cache of uri values and ids
* @param blankCache
* @param blankIdCache
* @param literalIdCache
* cache of literal values and ids
* @param typedLiteralCache
* @param typedLiteralIdCache
* @param languageIdCache
* cache of language values and ids
* @param datatypeIdCache
* cachoe of datatype values and ids
*
*/
public CompositeNodeLayout(PreparedStatementProvider stmtProvider, boolean supportsSequences, LiteralIndexer literalIndexer, String containerName, int maxLength, String optimizationString, boolean supportsTempTables, boolean supportsIdentity, String sessionPrefix, ICache<Long, URI> uriCache, ICache<URI, Long> uriIdCache, ICache<Long, BlankNode> blankCache, ICache<BlankNode, Long> blankIdCache, ICache<Long, PlainLiteral> literalCache, ICache<PlainLiteral, Long> literalIdCache,
ICache<Long, TypedLiteral> typedLiteralCache, ICache<TypedLiteral, Long> typedLiteralIdCache, ICache<Long, String> languageCache, ICache<String, Long> languageIdCache, ICache<Long, String> datatypeCache, ICache<String, Long> datatypeIdCache) {
if (stmtProvider == null)
throw new InvalidParameterException("stmtProvider must not be null");
this.literalIndexer = literalIndexer;
this.optimizationString = optimizationString;
this.stmtProvider = stmtProvider;
this.maxLength = maxLength;
this.supportsTempTables = supportsTempTables;
this.uriCache = uriCache;
this.uriIdCache = uriIdCache;
this.blankCache = blankCache;
this.blankIdCache = blankIdCache;
this.literalCache = literalCache;
this.literalIdCache = literalIdCache;
this.typedLiteralCache = typedLiteralCache;
this.typedLiteralIdCache = typedLiteralIdCache;
this.datatypeCache = datatypeCache;
this.datatypeIdCache = datatypeIdCache;
this.languageCache = languageCache;
this.languageIdCache = languageIdCache;
URI_TABLE_NAME = NodeType.URI.getName(containerName);
LONG_URI_TABLE_NAME = NodeType.LONG_URI.getName(containerName);
BLANK_TABLE_NAME = NodeType.BLANK.getName(containerName);
LITERAL_TABLE_NAME = NodeType.LITERAL.getName(containerName);
LONG_LIT_TABLE_NAME = NodeType.LONG_LITERAL.getName(containerName);
TYPED_LITERAL_TABLE_NAME = NodeType.TYPED_LITERAL.getName(containerName);
LONG_TYPED_LIT_TABLE_NAME = NodeType.TYPED_LONG_LITERAL.getName(containerName);
LOCK_ID_TABLE_NAME = containerName + LOCKED_IDS_TABLE;
LANGUAGE_TABLE = containerName + "_LANGUAGE";
DATATYPE_TABLE = containerName + "_DATATYPE";
tables = new String[] { URI_TABLE_NAME, LONG_URI_TABLE_NAME, BLANK_TABLE_NAME, LITERAL_TABLE_NAME, LONG_LIT_TABLE_NAME, TYPED_LITERAL_TABLE_NAME, LONG_TYPED_LIT_TABLE_NAME, DATATYPE_TABLE, LANGUAGE_TABLE };
datatypeLayout = new ValueLayoutCacheProxy(new ValueLayout(stmtProvider, DATATYPE_TABLE, optimizationString, "DATATYPE_SEQ", supportsSequences), datatypeCache, datatypeIdCache);
languageLayout = new ValueLayoutCacheProxy(new ValueLayout(stmtProvider, LANGUAGE_TABLE, optimizationString, "LANG_SEQ", supportsSequences), languageCache, languageIdCache);
nodeURILayout = new NodeLayoutCacheProxy<URI>(new NodeURILayout<URI>(NodeType.URI, NodeType.LONG_URI, stmtProvider, supportsSequences, NodeType.uriSequence, NodeType.longUriSequence, URI_TABLE_NAME, LONG_URI_TABLE_NAME, sessionPrefix, RESOURCE_TEMP_TABLE, ID_TEMP_TABLE, maxLength, optimizationString, LOCK_ID_TABLE_NAME), uriCache, uriIdCache);
nodeBlankLayout = new NodeLayoutCacheProxy<BlankNode>(new NodeURILayout<BlankNode>(NodeType.BLANK, null, stmtProvider, supportsSequences, NodeType.blankSequence, NodeType.blankSequence, BLANK_TABLE_NAME, sessionPrefix, RESOURCE_TEMP_TABLE, ID_TEMP_TABLE, optimizationString, LOCK_ID_TABLE_NAME), blankCache, blankIdCache);
nodePlainLiteralLayout = new NodeLayoutCacheProxy<PlainLiteral>(new NodeLiteralLayout<PlainLiteral>(NodeType.LITERAL, NodeType.LONG_LITERAL, PlainLiteral.class, literalIndexer, stmtProvider, supportsSequences, NodeType.literalSequence, NodeType.longLiteralSequence, LITERAL_TABLE_NAME, LONG_LIT_TABLE_NAME, sessionPrefix, LITERAL_TEMP_TABLE, ID_TEMP_TABLE, maxLength, optimizationString, datatypeLayout, languageLayout, LOCK_ID_TABLE_NAME, false), literalCache, literalIdCache);
nodeTypedLiteralLayout = new NodeLayoutCacheProxy<TypedLiteral>(new NodeLiteralLayout<TypedLiteral>(NodeType.TYPED_LITERAL, NodeType.TYPED_LONG_LITERAL, TypedLiteral.class, literalIndexer, stmtProvider, supportsSequences, NodeType.typedliteralSequence, NodeType.longTypedLiteralSequence, TYPED_LITERAL_TABLE_NAME, LONG_TYPED_LIT_TABLE_NAME, sessionPrefix, LITERAL_TEMP_TABLE, ID_TEMP_TABLE, maxLength, optimizationString, datatypeLayout, languageLayout, LOCK_ID_TABLE_NAME, true), typedLiteralCache,
typedLiteralIdCache);
layoutTypesToCaches = new ILayoutCache[NodeType.typeCount];
layoutTypesToCaches[NodeType.URI.getRange()] = nodeURILayout;
layoutTypesToCaches[NodeType.LONG_URI.getRange()] = nodeURILayout;
layoutTypesToCaches[NodeType.BLANK.getRange()] = nodeBlankLayout;
layoutTypesToCaches[NodeType.LITERAL.getRange()] = nodePlainLiteralLayout;
layoutTypesToCaches[NodeType.LONG_LITERAL.getRange()] = nodePlainLiteralLayout;
layoutTypesToCaches[NodeType.TYPED_LITERAL.getRange()] = nodeTypedLiteralLayout;
layoutTypesToCaches[NodeType.TYPED_LONG_LITERAL.getRange()] = nodeTypedLiteralLayout;
this.nodeConverter = new NodeConverter(this);
}
/**
* Construct a layout to store nodes in a database
*
* @param stmtProvider
* The interface to the SQL prepared statement cache
* @param supportsSequences
* Does the underlying database support sequences to get IDs
* @param literalIndexer
* Literal indexer for the underlying connection
* @param containerName
* The name of the logical container for the nodes. This container name is used to generate the table names for each node type (i.e. Node_URI or
* Node_Literal, etc.)
* @param uriCache
* cache of uri values and ids
* @param literalCache
* cache of literal values and ids
* @param languageCache
* cache of language values and ids
* @param datatypeCache
* cachoe of datatype values and ids
* @param uriIdCache
* cache of uri values and ids
* @param blankCache
* @param blankIdCache
* @param literalIdCache
* cache of literal values and ids
* @param typedLiteralCache
* @param typedLiteralIdCache
* @param languageIdCache
* cache of language values and ids
* @param datatypeIdCache
* cachoe of datatype values and ids
* @param maxLength
* The maximum length of a node's string representation before it is considered long
* @param optimizationString
* String prepended to queries to enable database specific optimizations
* @param supportsTempTables
* true if the database supports temporary tables, false otherwise.
* @param supportsIdentity
* @param sessionPrefix
* prefix for references to temporary tables within queries
*/
public CompositeNodeLayout(PreparedStatementProvider stmtProvider, boolean supportsSequences, LiteralIndexer literalIndexer, String containerName, ICache<Long, URI> uriCache, ICache<URI, Long> uriIdCache, ICache<Long, BlankNode> blankCache, ICache<BlankNode, Long> blankIdCache, ICache<Long, PlainLiteral> literalCache, ICache<PlainLiteral, Long> literalIdCache, ICache<Long, TypedLiteral> typedLiteralCache, ICache<TypedLiteral, Long> typedLiteralIdCache, ICache<Long, String> languageCache,
ICache<String, Long> languageIdCache, ICache<Long, String> datatypeCache, ICache<String, Long> datatypeIdCache, int maxLength, String optimizationString, boolean supportsTempTables, boolean supportsIdentity, String sessionPrefix) {
this(stmtProvider, supportsSequences, literalIndexer, containerName, maxLength, optimizationString, supportsTempTables, supportsIdentity, sessionPrefix, uriCache, uriIdCache, blankCache, blankIdCache, literalCache, literalIdCache, typedLiteralCache, typedLiteralIdCache, languageCache, languageIdCache, datatypeCache, datatypeIdCache);
}
/**
* Determine the NodeType for a given node
*
* @param n
* The node to retrieve its specific type
* @return The type of the node
* @throws RdbException
*/
public NodeType getNodeType(Value n) throws RdbException {
if (n == null) {
throw new RdbException(ExceptionConstants.CORE.NULL_PARAMETER, "n");
}
NodeType layoutType;
if (isLong(n)) {
layoutType = NodeType.getLongLayout(n);
} else {
layoutType = NodeType.getShortLayout(n);
}
if (layoutType == null) {
log.error(LogUtils.RDB_MARKER, "Unable to map node type to ID for Class:{} ", n.getClass().getName());
throw new UnhandledRdbException(ExceptionConstants.RDB.NO_NODETYPE_CLASS, n.getClass().getName());
}
return layoutType;
}
/**
* Determine whether or not a node's string representation is longer than the maximum length.
*
* @param n
* The node to test
* @return true of the node is a 'long' node, false otherwise
* @throws RdbException
*/
private boolean isLong(Value n) throws RdbException {
if (n == null) {
throw new RdbException(ExceptionConstants.CORE.NULL_PARAMETER, "n");
}
return n.toString().length() > maxLength;
}
/**
* Get the array of table names where the nodes are stored
*
* @return An array of table names where each node type is stored
*/
public String[] listTables() {
return tables;
}
/**
* Get the DataType layout
*
* @return the DataType layout
*/
public ValueLayoutCacheProxy getDatatypeLayout() {
return datatypeLayout;
}
/**
* Get the Language layout
*
* @return the Language layout
*/
public ValueLayoutCacheProxy getLanguageLayout() {
return languageLayout;
}
/**
* Clear all of the in memory node caches as well as the datatype and language caches
*
*/
public void clearCache() {
uriCache.clear();
blankCache.clear();
literalCache.clear();
typedLiteralCache.clear();
datatypeCache.clear();
languageCache.clear();
uriIdCache.clear();
blankIdCache.clear();
literalIdCache.clear();
typedLiteralIdCache.clear();
datatypeIdCache.clear();
languageIdCache.clear();
clear(nodeURILayout);
clear(nodeBlankLayout);
clear(nodePlainLiteralLayout);
clear(nodeTypedLiteralLayout);
clear(datatypeLayout);
clear(languageLayout);
nodeConverter.clearCache();
}
/**
* Clear the in-memory uncommitted cache data
*/
public void clearUncommittedCache() {
clearUncommitted(nodeURILayout);
clearUncommitted(nodeBlankLayout);
clearUncommitted(nodePlainLiteralLayout);
clearUncommitted(nodeTypedLiteralLayout);
clearUncommitted(datatypeLayout);
clearUncommitted(languageLayout);
}
/**
* Commit the in-memory uncommitted cache data
*/
public void commitUncommittedCache() {
commitUncommitted(nodeURILayout);
commitUncommitted(nodeBlankLayout);
commitUncommitted(nodePlainLiteralLayout);
commitUncommitted(nodeTypedLiteralLayout);
commitUncommitted(datatypeLayout);
commitUncommitted(languageLayout);
}
private void clear(ILayoutCache<?> valueLayout) {
valueLayout.clearCache();
}
private void clearUncommitted(ILayoutCache<?> valueLayout) {
valueLayout.clearUncommittedCache();
}
private void commitUncommitted(ILayoutCache<?> valueLayout) {
valueLayout.commitUncommittedCache();
}
/**
* Get the NodeLayoutCacheProxy for the node in question.
*
* @param n
* The node to retrieve the NodeLayoutCacheProxy
* @return The NodeLayoutCacheProxy for that type of node
*/
private NodeLayoutCacheProxy<?> getNodeLayoutByNodeClass(Value n) throws RdbException {
if (n == null) {
throw new RdbException(ExceptionConstants.CORE.NULL_PARAMETER, "n");
}
if (n instanceof URI) {
return nodeURILayout;
} else if (n instanceof PlainLiteral) {
return nodePlainLiteralLayout;
} else if (n instanceof TypedLiteral) {
return nodeTypedLiteralLayout;
} else if (n instanceof BlankNode) {
return nodeBlankLayout;
}
throw new UnhandledRdbException(ExceptionConstants.RDB.NO_NODETYPE_CLASS, n.getClass().getName());
}
/**
* Get the NodeLayoutCacheProxy for the NodeType in question.
*
* @param layoutType
* The layout for a particular type of node
* @return The NodeLayoutCacheProxy for that type of node
*/
@SuppressWarnings("unchecked")
private NodeLayoutCacheProxy getNodeLayoutByType(NodeType layoutType) {
return (NodeLayoutCacheProxy) layoutTypesToCaches[layoutType.getRange()];
}
/**
* Store value in appropriate table and return ID
*
* @param value
* value to store
* @param connection
* connection to the database
* @param transactionId
* id of the transaction for which this storage operation is a part
* @return ID of value stored in table
* @throws RdbException
*/
@SuppressWarnings("unchecked")
public Long store(Value value, Connection connection, long transactionId) throws RdbException {
if (value == null) {
throw new RdbException(ExceptionConstants.CORE.NULL_PARAMETER, "value");
}
INodeLayout nodeLayout = getNodeLayoutByNodeClass(value);
Long id = nodeLayout.store(value, connection, transactionId);
return id;
}
/**
* Fetch the ID of the Value from the appropriate table
*
* @param value
* value to store
* @param connection
* connection to the database
* @return ID of value stored in table
* @throws RdbException
*/
@SuppressWarnings("unchecked")
public Long fetchId(Value value, Connection connection) throws RdbException {
if (value == null) {
throw new RdbException(ExceptionConstants.CORE.NULL_PARAMETER, "value");
}
INodeLayout nodeLayout = getNodeLayoutByNodeClass(value);
Long id = nodeLayout.fetchId(value, connection);
return id;
}
/**
* Fetch the stored Value for the provided ID
*
* @param id
* ID of value to retrieve
* @param connection
* connection to the database
* @return Value for provided ID
* @throws RdbException
*/
public Value fetchValue(Long id, Connection connection) throws RdbException {
if (id == null || id.longValue() == 0)
return null;
NodeType layoutType = NodeType.getById(id.longValue());
INodeLayout<?> nodeLayout = getNodeLayoutByType(layoutType);
Value n = nodeLayout.fetchValue(id, connection);
return n;
}
/**
* Resolve the IDs of the set of provided Values,optionally storing Values if they are not already stored based.
*
* @param nodesToResolve
* Set of Values to resolve
* @param storeUnresolvedNodes
* Store Values that are not already stored
* @param connection
* connection to the database
* @param transactionId
* id of the transaction for which this storage operation is a part
* @return Map of Values to their resolved IDs
* @throws RdbException
*/
public Map<Value, Long> resolveStoredNodes(Set<Value> nodesToResolve, boolean storeUnresolvedNodes, Connection connection, long transactionId) throws RdbException {
Map<Value, Long> alreadyStored = new HashMap<Value, Long>();
if (supportsTempTables) {
alreadyStored = resolveStoredNodesFast(nodesToResolve, storeUnresolvedNodes, connection, transactionId);
} else {
alreadyStored = resolveStoredNodesSlow(nodesToResolve, storeUnresolvedNodes, connection, transactionId);
}
return alreadyStored;
}
/**
* Resolve the Values of the set of provided IDs
*
* @param ids
* Set of IDs to resolve
* @param connection
* connection to the database
*
* @return Map of IDs to their resolved Values
* @throws RdbException
*/
public Map<Long, Value> resolveStoredIds(Set<Long> ids, Connection connection) throws RdbException {
Map<Long, Value> alreadyStored = null;
if (supportsTempTables) {
alreadyStored = findDeferredIdsFast(ids, connection);
} else {
alreadyStored = findDeferredIdsSlow(ids, connection);
}
return alreadyStored;
}
/**
* Finds all the node using a bulk lookup. 'Slow' indicates this method is inferior in performance compared to 'findDeferredNodesFast', which optimizes
* insert by using temporary tables. This methods should be used only for databases that do not not support temporary tables.
*
* @param nodesToResolve
* nodes to resolved
* @param storeUnresolvedNodes
* store Values that are not currently stored
* @param connection
* connection to the database
* @return Map of Value and their resolved IDs.
* @throws RdbException
*/
@SuppressWarnings("unchecked")
private Map<Value, Long> resolveStoredNodesSlow(Set<Value> deferredNodes, boolean storeUnresolvedNodes, Connection connection, long transactionId) throws RdbException {
Map<Value, Long> alreadyStored = new HashMap<Value, Long>();
for (Value n : deferredNodes) {
INodeLayout nodeLayout = getNodeLayoutByNodeClass(n);
Long id = (storeUnresolvedNodes) ? nodeLayout.store(n, connection, transactionId) : nodeLayout.fetchId(n, connection);
if (id != null) {
alreadyStored.put(n, id);
}
}
return alreadyStored;
}
/**
* Finds all the node values using a bulk lookup. 'Fast' indicates this method is superior in performance compared with 'findDeferredNodesSlow'. This
* methods requires database support for temporary tables.
*
* @param nodesToResolve
* nodes to resolved
* @param storeUnresolvedNodes
* store Values that are not currently stored
* @param connection
* connection to the database
*
* @return Map of Value and their resolved IDs.
* @throws RdbException
*/
@SuppressWarnings("unchecked")
private Map<Value, Long> resolveStoredNodesFast(Set<Value> nodesToResolve, boolean storeUnresolvedNodes, Connection connection, long transactionId) throws RdbException {
Map<Value, Long> alreadyStored = new HashMap<Value, Long>();
Map<INodeLayout, Set<Value>> pendingNodes = new HashMap<INodeLayout, Set<Value>>();
if (nodesToResolve == null)
return alreadyStored;
for (Value n : nodesToResolve) {
INodeLayout nodeLayout = getNodeLayoutByNodeClass(n);
Set<Value> nodeSet = pendingNodes.get(nodeLayout);
if (nodeSet == null) {
nodeSet = new HashSet<Value>();
pendingNodes.put(nodeLayout, nodeSet);
}
nodeSet.add(n);
}
// Now, for each node type put the nodes in a temporary table so that we can resolve them
for (INodeLayout nodeLayout : pendingNodes.keySet()) {
Set<Value> nodeSet = pendingNodes.get(nodeLayout);
if (nodeSet.size() == 0)
continue;
alreadyStored.putAll(nodeLayout.resolveStoredNodes(nodeSet, storeUnresolvedNodes, connection, transactionId));
}
return alreadyStored;
}
/**
* Commit the entries for any nodes that were created during this transactions
*
* @param connection
* Connection for operation
* @param transactionId
* id of the transaction for which this storage operation is a part
* @return number of committed referenced ids
* @throws RdbException
*/
public long commitReferencedIds(Connection connection, long transactionId) throws RdbException {
long count = 0;
count += nodeURILayout.commitReferencedIds(connection, transactionId);
count += nodeBlankLayout.commitReferencedIds(connection, transactionId);
count += nodePlainLiteralLayout.commitReferencedIds(connection, transactionId);
count += nodeTypedLiteralLayout.commitReferencedIds(connection, transactionId);
NodeSQL.purge(stmtProvider, connection, transactionId, LOCK_ID_TABLE_NAME);
return count;
}
/**
* Abort the addition of any nodes that were created during this transaction, and not any other transaction
*
* @param connection
* Connection for operation
* @param transactionId
* id of the transaction for which this storage operation is a part* @return
* @return number of aborted referenced ids
* @throws RdbException
*/
public long abortReferencedIds(Connection connection, long transactionId) throws RdbException {
long count = 0;
count += nodeURILayout.abortReferencedIds(connection, transactionId);
count += nodeBlankLayout.abortReferencedIds(connection, transactionId);
count += nodePlainLiteralLayout.abortReferencedIds(connection, transactionId);
count += nodeTypedLiteralLayout.abortReferencedIds(connection, transactionId);
NodeSQL.purge(stmtProvider, connection, transactionId, LOCK_ID_TABLE_NAME);
return count;
}
/**
* Finds all the node values using a bulk lookup. 'Fast' indicates this method is superior in performance compared with 'findDeferredNodesSlow'. This
* methods requires database support for temporary tables.
*
* @param idsToResolve
* IDs that need to be resolved
* @param connection
* connection to the database
*
* @return A map of IDs to their resolved Values
* @throws RdbException
*/
@SuppressWarnings("unchecked")
private Map<Long, Value> findDeferredIdsFast(Set<Long> idsToResolve, Connection connection) throws RdbException {
Map<INodeLayout, Set<Long>> pendingIds = new HashMap<INodeLayout, Set<Long>>();
Map<Long, Value> resolved = new HashMap<Long, Value>();
if (idsToResolve == null) {
return resolved;
}
for (Long l : idsToResolve) {
Value val = (Value) getIfCached(l);
if (val == null) {
INodeLayout nodeLayout = getNodeLayoutByType(NodeType.getById(l.longValue()));
Set<Long> nodeSet = pendingIds.get(nodeLayout);
if (nodeSet == null) {
nodeSet = new HashSet<Long>();
pendingIds.put(nodeLayout, nodeSet);
}
nodeSet.add(l);
} else {
resolved.put(l, val);
}
}
// Now, for each node type put the nodes in a temporary table so that we can resolve them
for (INodeLayout nodeLayout : pendingIds.keySet()) {
Set<Long> nodeSet = pendingIds.get(nodeLayout);
if (nodeSet.size() == 0)
continue;
resolved.putAll(nodeLayout.resolveStoredIds(nodeSet, connection));
}
return resolved;
}
/**
* Finds all the node using a bulk lookup. 'Slow' indicates this method is inferior in performance compared to 'findDeferredNodesFast', which optimizes
* insert by using temporary tables. This methods should be used only for databases that do not not support temporary tables.
*
* @param idsToResolve
* IDs that need to be resolved
* @param connection
* connection to the database
*
* @return A map of IDs to their resolved Values
* @throws RdbException
*/
@SuppressWarnings("unchecked")
private Map<Long, Value> findDeferredIdsSlow(Set<Long> deferredIds, Connection connection) throws RdbException {
Map<Long, Value> resolved = new HashMap<Long, Value>();
if (deferredIds == null) {
throw new RdbException(ExceptionConstants.CORE.NULL_PARAMETER, "deferredIds");
}
for (Long l : deferredIds) {
Value val = (Value) getIfCached(l);
if (val == null) {
INodeLayout nodeLayout = getNodeLayoutByType(NodeType.getById(l.longValue()));
val = nodeLayout.fetchValue(l, connection);
resolved.put(l, val);
} else {
resolved.put(l, val);
}
}
return resolved;
}
/**
* Create Value object from provided ID and value and cache result
*
* @param id
* ID of node
* @param value
* String value of node
* @param modifierId
* modifierId for literals
* @param connection
* connection to the database
* @return convert Value for provided data
* @throws RdbException
*/
@SuppressWarnings("unchecked")
public Value cache(Long id, String value, Long modifierId, Connection connection) throws RdbException {
if (id == null) {
throw new RdbException(ExceptionConstants.CORE.NULL_PARAMETER, "id");
}
NodeType layoutType = NodeType.getById(id.longValue());
ILayoutCache cache = layoutTypesToCaches[layoutType.getRange()];
return (Value) cache.cache(id, value, modifierId, connection);
}
/**
* Get the Object for this ID, if already cached
*
* @param id
* of Object to retrieve
* @return the Object for this ID, if already cached
* @throws RdbException
*/
@SuppressWarnings("unchecked")
public Object getIfCached(Long id) throws RdbException {
if (id == null) {
throw new RdbException(ExceptionConstants.CORE.NULL_PARAMETER, "id");
}
NodeType layoutType = NodeType.getById(id.longValue());
NodeLayoutCacheProxy nodeLayout = getNodeLayoutByType(layoutType);
return nodeLayout.getIfCached(id);
}
/**
* Get the ID for this Value, if already cached
*
* @param value
* node value to find cached ID
* @return the ID for this Value, if already cached
* @throws RdbException
*/
@SuppressWarnings("unchecked")
public Long getIfCached(Value value) throws RdbException {
if (value == null) {
throw new RdbException(ExceptionConstants.CORE.NULL_PARAMETER, "value");
}
NodeLayoutCacheProxy nodeLayout = getNodeLayoutByNodeClass(value);
return nodeLayout.getIfCached(value);
}
/**
* Return true if a Value with this ID is already cached
*
* @param id
* to check
* @return true if a Value with this ID is already cached
* @throws RdbException
*/
@SuppressWarnings("unchecked")
public boolean isCached(Long id) throws RdbException {
if (id == null) {
throw new RdbException(ExceptionConstants.CORE.NULL_PARAMETER, "id");
}
NodeType layoutType = NodeType.getById(id.longValue());
NodeLayoutCacheProxy nodeLayout = getNodeLayoutByType(layoutType);
return nodeLayout.isCached(id);
}
/**
* Return true if a ID for this Value is already cached
*
* @param value
* node value to check
* @return true if a ID for this Value is already cached
* @throws RdbException
*/
@SuppressWarnings("unchecked")
public boolean isCached(Value value) throws RdbException {
if (value == null) {
throw new RdbException(ExceptionConstants.CORE.NULL_PARAMETER, "value");
}
NodeLayoutCacheProxy nodeLayout = getNodeLayoutByNodeClass(value);
return nodeLayout.isCached(value);
}
/**
* Get the nodeURILayout
*
* @return the nodeURILayout
*/
public NodeLayoutCacheProxy<URI> getNodeURILayout() {
return nodeURILayout;
}
/**
* Get the nodeConverter
*
* @return the nodeConverter
*/
public NodeConverter getNodeConverter() {
return nodeConverter;
}
/**
* Get the nodeLiteralLayout
*
* @return the nodeLiteralLayout
*/
public NodeLayoutCacheProxy<PlainLiteral> getPlainNodeLiteralLayout() {
return nodePlainLiteralLayout;
}
/**
* Get the TypedLiteral layout
*
* @return the TypedLiteral layout
*/
public NodeLayoutCacheProxy<TypedLiteral> getTypedNodeLiteralLayout() {
return nodeTypedLiteralLayout;
}
/**
* Get an Iterator over all the Literal values stored in the database
*
* @param connection
* connections to from which to retrieve literals
* @return an Iterator over all the Literal values stored in the database
* @throws RdbException
*/
public ClosableIterator<GetAllLiteralsResult> getAllLiterals(Connection connection) throws RdbException {
return NodeSQL.getAllLiterals(stmtProvider, connection, LITERAL_TABLE_NAME, optimizationString);
}
/**
* Get the literalIndexer
*
* @return the literalIndexer
*/
public LiteralIndexer getLiteralIndexer() {
return literalIndexer;
}
/**
* Get the URI table name
*
* @return URI table name
*/
public String getURITableName() {
return URI_TABLE_NAME;
}
/**
* Get the Long URI table name
*
* @return Long URI table name
*/
public String getLongURITableName() {
return LONG_URI_TABLE_NAME;
}
/**
* Get the Literal table name
*
* @return Literal table name
*/
public String getLiteralTableName() {
return LITERAL_TABLE_NAME;
}
/**
* Get the Long Literal table name
*
* @return Long Literal table name
*/
public String getLongLiteralTableName() {
return LONG_LIT_TABLE_NAME;
}
/**
* Get the Blank Node table name
*
* @return Blank Node table name
*/
public String getBlankNodeTableName() {
return BLANK_TABLE_NAME;
}
/**
* @param maxLength
* the maxLength to set
*/
public void setMaxLength(int maxLength) {
this.maxLength = maxLength;
nodeURILayout.setMaxLength(maxLength);
nodeBlankLayout.setMaxLength(maxLength);
nodePlainLiteralLayout.setMaxLength(maxLength);
nodeTypedLiteralLayout.setMaxLength(maxLength);
}
}