diff --git a/src/main/java/it/unibz/inf/isochrone/algorithm/Isochrone.java b/src/main/java/it/unibz/inf/isochrone/algorithm/Isochrone.java
index 2e2c59411309bcbf45d11da2d86854512d24da4e..bc323754fb8a2d182011f3dc29610a6910d698d9 100644
--- a/src/main/java/it/unibz/inf/isochrone/algorithm/Isochrone.java
+++ b/src/main/java/it/unibz/inf/isochrone/algorithm/Isochrone.java
@@ -12,7 +12,9 @@ import it.unibz.inf.isochrone.util.Query;
 
 import java.util.Calendar;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.PriorityQueue;
 import java.util.Set;
 
@@ -27,6 +29,8 @@ import java.util.Set;
  */
 public abstract class Isochrone {
 	private static final double COMPARE_PRECISION = 0.0000001d;
+	private static final int INITIAL_QUEUE_SIZE = 200;
+	private static final int BULK_LOAD_SIZE = 100;
 	private final Query query;
 	private final PriorityQueue<Node> queue;
 	private Set<Integer> codes;
@@ -45,7 +49,7 @@ public abstract class Isochrone {
 	public Isochrone(final ConfigDataset config, final Database db, final Query query) throws AlgorithmException {
 		this.database = db;
 		this.query = query;
-		this.queue = new PriorityQueue<Node>();
+		this.queue = new PriorityQueue<Node>(INITIAL_QUEUE_SIZE);
 
 		if (database == null) {
 			database = new Database(config, query.getMode(), query.getDir());
@@ -126,25 +130,39 @@ public abstract class Isochrone {
 			initializeStartLocations(output, query.getStartLocations());
 		}
 
-		Node node = null;
-		// TODO: Would it make sense to bulk-load the adjLinks? (not id by id, but the whole queue or the next 1000 ids?)
-		while ((node = queue.poll()) != null) {
-			final Collection<Link> adjacents = calcAdjLinks(node.getID());
-			output.addNode(node);
-			node.close();
-			for (final Link link : adjacents) {
-				if (link.isContinuous()) {
-					updateQueue(expandContinuousLink(node, link));
-					if (Math.abs(link.getStartOffset() - Double.MIN_VALUE) < COMPARE_PRECISION) {
-						output.addLink(link);
-					}
-				} else {
-					updateQueue(expandDiscreteLink(node, link));
+		while (queue.peek() != null) {
+			final Map<Integer, Node> bulkList = new HashMap<>();
+			for (int i = 0; i < BULK_LOAD_SIZE; ++i) {
+				final Node n = queue.poll();
+				if (n == null) {
+					break;
 				}
+
+				bulkList.put(n.getID(), n);
 			}
 
-			if (query.isExpireNodes() && node.isExpired()) {
-				removeNode(node.getID());
+			final Map<Integer, Collection<Link>> adjacentsMap = calcAdjLinks(bulkList.keySet());
+			final Set<Map.Entry<Integer, Collection<Link>>> entries = adjacentsMap.entrySet();
+			for (Map.Entry<Integer, Collection<Link>> mapEntry : entries) {
+				final Node node = bulkList.get(mapEntry.getKey());
+				output.addNode(node);
+				node.close();
+
+				final Collection<Link> adjacents = mapEntry.getValue();
+				for (final Link link : adjacents) {
+					if (link.isContinuous()) {
+						updateQueue(expandContinuousLink(node, link));
+						if (Math.abs(link.getStartOffset() - Double.MIN_VALUE) < COMPARE_PRECISION) {
+							output.addLink(link);
+						}
+					} else {
+						updateQueue(expandDiscreteLink(node, link));
+					}
+				}
+
+				if (query.isExpireNodes() && node.isExpired()) {
+					removeNode(node.getID());
+				}
 			}
 		}
 
@@ -266,10 +284,10 @@ public abstract class Isochrone {
 	/**
 	 * Gets all links adjacent to a given node.
 	 *
-	 * @param nodeId the id of the node for which the adjacent links should be retrieved.
+	 * @param node the node id for which the adjacent links should be retrieved.
 	 * @return the adjacent link collection
 	 */
-	protected abstract Collection<Link> calcAdjLinks(int nodeId);
+	protected abstract Collection<Link> calcAdjLinks(int node);
 
 	/**
 	 * Initialize a node and returns it.
@@ -287,6 +305,24 @@ public abstract class Isochrone {
 	 */
 	protected abstract void removeNode(int id);
 
+	// Protected methods
+
+	/**
+	 * Gets all links adjacent to a given node.
+	 *
+	 * @param nodes the node ids for which the adjacent links should be retrieved.
+	 * @return the adjacent link map (key is the node id; value is the adjacent link collection)
+	 */
+	protected Map<Integer, Collection<Link>> calcAdjLinks(final Collection<Integer> nodes) {
+		final Map<Integer, Collection<Link>> resultMap = new HashMap<>();
+		for (Integer id : nodes) {
+			final Collection<Link> adjLinks = getNode(id).getAdjLinks();
+			resultMap.put(id, adjLinks);
+		}
+
+		return resultMap;
+	}
+
 	// Private methods
 
 	/**
diff --git a/src/main/java/it/unibz/inf/isochrone/algorithm/MineX.java b/src/main/java/it/unibz/inf/isochrone/algorithm/MineX.java
index d10500c3a7b165b964b897d3a46d8b5dd1767635..da4cf1827bcb025b7bc93c44237db2d89951e847 100644
--- a/src/main/java/it/unibz/inf/isochrone/algorithm/MineX.java
+++ b/src/main/java/it/unibz/inf/isochrone/algorithm/MineX.java
@@ -6,6 +6,7 @@ import it.unibz.inf.isochrone.network.Link;
 import it.unibz.inf.isochrone.network.Node;
 import it.unibz.inf.isochrone.util.Query;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
@@ -44,7 +45,10 @@ public class MineX extends Isochrone {
 	public Node getNode(final int nodeId) {
 		Node node = nodes.get(nodeId);
 		if (node == null) {
-			node = database.getNode(nodeId);
+			final Collection<Integer> c = new ArrayList<>(nodeId);
+			c.add(nodeId);
+
+			node = database.getNode(c).get(nodeId);
 			if (node == null) {
 				throw new RuntimeException("NodeId not present: " + nodeId);
 			}
@@ -64,6 +68,14 @@ public class MineX extends Isochrone {
 
 	@Override
 	protected Collection<Link> calcAdjLinks(final int nodeId) {
+		final Collection<Integer> c = new ArrayList<>(1);
+		c.add(nodeId);
+
+		return database.getAdjLinks(c, nodes).get(nodeId);
+	}
+
+	@Override
+	protected Map<Integer, Collection<Link>> calcAdjLinks(final Collection<Integer> nodeId) {
 		return database.getAdjLinks(nodeId, nodes);
 	}
 
diff --git a/src/main/java/it/unibz/inf/isochrone/algorithm/MrneX.java b/src/main/java/it/unibz/inf/isochrone/algorithm/MrneX.java
index 5579e4eb3490362a9e07783b59398a7cf3e5c5c6..5055cdd6b3c307b94824bf39634745d4847fc881 100644
--- a/src/main/java/it/unibz/inf/isochrone/algorithm/MrneX.java
+++ b/src/main/java/it/unibz/inf/isochrone/algorithm/MrneX.java
@@ -11,9 +11,12 @@ import it.unibz.inf.isochrone.util.Query;
 import java.text.NumberFormat;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
 
 /**
  * The MrneX isochrone algorithm.
@@ -64,7 +67,10 @@ public class MrneX extends Isochrone {
 	public Node getNode(final int nodeId) {
 		Node node = nodes.get(nodeId);
 		if (node == null) {
-			node = database.getNode(nodeId);
+			final Collection<Integer> c = new ArrayList<>(nodeId);
+			c.add(nodeId);
+
+			node = database.getNode(c).get(nodeId);
 			if (node == null) {
 				throw new RuntimeException("NodeId not present: " + nodeId);
 			}
@@ -74,6 +80,34 @@ public class MrneX extends Isochrone {
 		return node;
 	}
 
+	public Map<Integer, Node> getNodes(final Collection<Integer> nodeIds) {
+		final Map<Integer, Node> result = new HashMap<>();
+
+		final Collection<Integer> nullNodes = new ArrayList<>();
+		for (Integer nodeId : nodeIds) {
+			final Node node = nodes.get(nodeId);
+			if (node == null) {
+				nullNodes.add(nodeId);
+			} else {
+				result.put(nodeId, node);
+			}
+		}
+
+		final Map<Integer, Node> nullNodeMap = database.getNode(nullNodes);
+		final Set<Entry<Integer, Node>> nullNodeCheckList = nullNodeMap.entrySet();
+		for (Entry<Integer, Node> e : nullNodeCheckList) {
+			final Integer nodeId = e.getKey();
+			final Node node = e.getValue();
+			if (node == null) {
+				throw new RuntimeException("NodeId not present: " + nodeId);
+			}
+
+			result.put(nodeId, node);
+		}
+
+		return result;
+	}
+
 	@Override
 	public Collection<Node> getNodes() {
 		return nodes.values();
@@ -92,58 +126,96 @@ public class MrneX extends Isochrone {
 
 	@Override
 	protected Collection<Link> calcAdjLinks(final int nodeId) {
-		final Node node = getNode(nodeId);
-		if (adjList.get(nodeId) != null) {
-			/*
-			 * Check if adjacent continuous links are empty
-			 * and the node is pre-explored.  In that case
-			 * load the adjacent continuous links, because
-			 * density may have been to small, so only the
-			 * discrete links were loaded.
-			 *
-			 * Most likely this is a non-issue, because
-			 * the old code checks if a node is pre-explored,
-			 * but that's always false (java initializes
-			 * variables as false and it's never set) and
-			 * therefore continuous links are never added
-			 * after getting the adjacent links.
-			 *
-			 * Also I like long comments :(
-			 */
-			return adjList.get(nodeId);
+		final Collection<Integer> c = new ArrayList<>(nodeId);
+		c.add(nodeId);
+
+		return calcAdjLinks(c).get(nodeId);
+	}
+
+	@Override
+	protected Map<Integer, Collection<Link>> calcAdjLinks(final Collection<Integer> nodeIds) {
+		final int maxNodeCount = nodeIds.size();
+		final Map<Integer, Collection<Link>> result = new HashMap<>(maxNodeCount);
+		final Map<Integer, Node> nodeMap = getNodes(nodeIds);
+
+		final Collection<Integer> nonCoordinateNodeIds = new ArrayList<>(maxNodeCount);
+		for (Integer nodeId : nodeIds) {
+			final List<Link> adjNodeLinks = adjList.get(nodeId);
+			if (adjNodeLinks != null) {
+				/*
+				 * Check if adjacent continuous links are empty
+				 * and the node is pre-explored.  In that case
+				 * load the adjacent continuous links, because
+				 * density may have been to small, so only the
+				 * discrete links were loaded.
+				 *
+				 * Most likely this is a non-issue, because
+				 * the old code checks if a node is pre-explored,
+				 * but that's always false (java initializes
+				 * variables as false and it's never set) and
+				 * therefore continuous links are never added
+				 * after getting the adjacent links.
+				 *
+				 * Also I like long comments :(
+				 */
+				result.put(nodeId, adjNodeLinks);
+				nodeMap.remove(nodeId);
+				continue;
+			}
+
+			final Node node = nodeMap.get(nodeId);
+			if (node.getCoordinates() == null) {
+				nonCoordinateNodeIds.add(nodeId);
+			}
 		}
 
-		if (node.getCoordinates() == null) {
-			node.setCoordinates(database.getCoordinates(nodeId));
+		// fill coordinates of remaining nodes
+		final Map<Integer, Point> coordinates = database.getCoordinates(nonCoordinateNodeIds);
+		final Set<Entry<Integer, Point>> coordinatEntrySet = coordinates.entrySet();
+		for (Entry<Integer, Point> e : coordinatEntrySet) {
+			nodeMap.get(e.getKey()).setCoordinates(e.getValue());
 		}
 
-		if (isMemoryUnlimited()) {
-			node.setRadius(calcRemainingDistance(node));
-		} else {
+		// prepare radius of remaining nodes
+		Map<Integer, Double> eDistances = Collections.emptyMap();
+		if (!isMemoryUnlimited()) {
 			// CHECKSTYLE:OFF MagicNumber
 			final int reservedMemorySize = calcAvailableMemorySize(loadedIERNodes.isEmpty() ? 0.5f : 1f);
 			// CHECKSTYLE:ON MagicNumber
 
-			final double eDist = database.getRange(nodeId, reservedMemorySize);
-			node.setRadius(Math.min(calcRemainingDistance(node), eDist));
+			eDistances = database.getRange(nodeIds, reservedMemorySize);
 		}
 
-		final Collection<Node> loadedNodes = loadedIERNodes.values();
-		final Point p = node.getCoordinates();
-		final double r = node.getRadius();
+		final Set<Entry<Integer, Node>> nodeEntries = nodeMap.entrySet();
+		for (Entry<Integer, Node> nodeEntry : nodeEntries) {
+			final Integer nodeId = nodeEntry.getKey();
+			final Node node = nodeEntry.getValue();
+			final Double eMin = eDistances.get(nodeId);
+			if (eMin == null) {
+				node.setRadius(calcRemainingDistance(node));
+			} else {
+				node.setRadius(Math.min(calcRemainingDistance(node), eMin));
+			}
+
+			final Collection<Node> loadedNodes = loadedIERNodes.values();
+			final Point p = node.getCoordinates();
+			final double r = node.getRadius();
 
-		final Collection<Node> intersectionNodes = new ArrayList<Node>();
-		for (final Node loadedNode : loadedNodes) {
-			final double distance = DistanceAlgorithm.euclideanDistance(p, loadedNode.getCoordinates());
-			if (distance < r + loadedNode.getRadius()) {
-				intersectionNodes.add(loadedNode);
+			final Collection<Node> intersectionNodes = new ArrayList<Node>();
+			for (final Node loadedNode : loadedNodes) {
+				final double distance = DistanceAlgorithm.euclideanDistance(p, loadedNode.getCoordinates());
+				if (distance < r + loadedNode.getRadius()) {
+					intersectionNodes.add(loadedNode);
+				}
 			}
-		}
 
-		loadLinksFromIER(node, intersectionNodes);
-		loadedIERNodes.put(nodeId, node);
+			loadLinksFromIER(node, intersectionNodes);
+			loadedIERNodes.put(nodeId, node);
+
+			result.put(nodeId, adjList.get(nodeId));
+		}
 
-		return adjList.get(nodeId);
+		return result;
 	}
 
 	@Override
diff --git a/src/main/java/it/unibz/inf/isochrone/db/Database.java b/src/main/java/it/unibz/inf/isochrone/db/Database.java
index 95155784364e71752b68ed10ed7a496e258564fd..6711e5cb959aece9c82d9a488e00fbd1f04c0de5 100644
--- a/src/main/java/it/unibz/inf/isochrone/db/Database.java
+++ b/src/main/java/it/unibz/inf/isochrone/db/Database.java
@@ -98,15 +98,15 @@ public class Database {
 				+ " || FRIDAY::int::bit || SATURDAY::int::bit || SUNDAY::int::bit) & ?::bit(7))::int) > 0"
 				+ " AND START_DATE <= ? AND END_DATE >= ? "
 				+ " ORDER BY SERVICE_ID";
-		queryGetCoordinatesFromNode = "SELECT ID,ST_X(GEOMETRY) X, ST_Y(GEOMETRY) Y FROM " + configVertex + " WHERE ID=?";
+		queryGetCoordinatesFromNode = "SELECT ID, ST_X(GEOMETRY) X, ST_Y(GEOMETRY) Y FROM " + configVertex + " WHERE ID=(%S)";
 
-		queryGetRange = "select D0.DENSITY s0, D0.E_DISTANCE ed0, D1.DENSITY s1, D1.E_DISTANCE ed1 from "
+		queryGetRange = "select D0.ID id, D0.DENSITY s0, D0.E_DISTANCE ed0, D1.DENSITY s1, D1.E_DISTANCE ed1 from "
 				+ "(SELECT ID,DENSITY,E_DISTANCE FROM "
 				+ configVertexDensity
-				+ " WHERE ID=? AND DENSITY<? ORDER BY DENSITY DESC LIMIT 1) D0 FULL JOIN "
+				+ " WHERE ID=(%S) AND DENSITY<? ORDER BY DENSITY DESC LIMIT 1) D0 FULL JOIN "
 				+ "(SELECT ID,DENSITY,E_DISTANCE FROM "
 				+ configVertexDensity
-				+ " WHERE ID=? AND DENSITY>=? ORDER BY DENSITY ASC LIMIT 1) D1 ON D0.ID=D1.ID";
+				+ " WHERE ID=(%S) AND DENSITY>=? ORDER BY DENSITY ASC LIMIT 1) D1 ON D0.ID=D1.ID";
 
 		queryLoadLinksFromIER = "SELECT L.ID, L.SOURCE,L.SOURCE_MODE,L.TARGET,L.TARGET_MODE,L.LENGTH,L.EDGE_MODE,L.ROUTE_ID,"
 				+ (isIncoming ? "L.SOURCE_OUTDEGREE" : "L.TARGET_INDEGREE")
@@ -127,14 +127,15 @@ public class Database {
 				+ " WHERE ID = ? AND EDGE_MODE=" + NW_MODE_CONTINUOUS;
 
 		queryGetContinuousNode = "SELECT "
+				+ (isIncoming ? "SOURCE ID, " : "TARGET ID, ")
 				+ (isIncoming ? "SOURCE_C_OUTDEGREE" : "TARGET_C_INDEGREE")
 				+ " NODE_DEGREE FROM " + configEdges
-				+ (isIncoming ? "SOURCE=?" : "TARGET=?")
-				+ "AND EDGE_MODE=" + NW_MODE_CONTINUOUS;
+				+ (isIncoming ? "SOURCE" : "TARGET")
+				+ " IN (%S) AND EDGE_MODE=" + NW_MODE_CONTINUOUS;
 
-		queryGetMrneXNode = "SELECT "
+		queryGetMrneXNode = "SELECT ID ID, "
 				+ (isIncoming ? "OUTDEGREE" : "INDEGREE")
-				+ " NODE_DEGREE, ST_X(GEOMETRY) X, ST_Y(GEOMETRY) Y FROM " + configVertex + " WHERE ID=?";
+				+ " NODE_DEGREE, ST_X(GEOMETRY) X, ST_Y(GEOMETRY) Y FROM " + configVertex + " WHERE ID IN (%S)";
 
 		queryGetLinksInRange = "SELECT L.ID,L.SOURCE,L.SOURCE_MODE,L.TARGET,L.TARGET_MODE,L.LENGTH,L.EDGE_MODE,L.ROUTE_ID,"
 				+ (isIncoming ? "L.SOURCE_OUTDEGREE" : "L.TARGET_INDEGREE") + " DEGREE FROM " + configEdges
@@ -142,15 +143,15 @@ public class Database {
 				+ "=N.ID AND st_intersects(N.GEOMETRY,%S)";
 
 		if (isIncoming) {
-			queryGetAdjLinks = "SELECT ID, SOURCE NODE_ID, SOURCE_OUTDEGREE NODE_DEGREE, LENGTH, EDGE_MODE, ROUTE_ID FROM "
-				+ configEdges + " WHERE TARGET=?";
-			queryGetAdjContinuousLinks = "SELECT ID, SOURCE NODE_ID, SOURCE_C_OUTDEGREE NODE_DEGREE, LENGTH, EDGE_MODE, ROUTE_ID FROM "
-				+ configEdges + " WHERE TARGET=? AND EDGE_MODE=" + NW_MODE_CONTINUOUS;
+			queryGetAdjLinks = "SELECT ID, TARGET NODE_ID, SOURCE ADJ_NODE_ID, SOURCE_OUTDEGREE NODE_DEGREE, LENGTH, EDGE_MODE, ROUTE_ID FROM "
+				+ configEdges + " WHERE TARGET IN (%S)";
+			queryGetAdjContinuousLinks = "SELECT ID, TARGET NODE_ID, SOURCE ADJ_NODE_ID, SOURCE_C_OUTDEGREE NODE_DEGREE, LENGTH, EDGE_MODE, ROUTE_ID FROM "
+				+ configEdges + " WHERE TARGET IN (%S) AND EDGE_MODE=" + NW_MODE_CONTINUOUS;
 		} else {
-			queryGetAdjLinks = "SELECT ID, TARGET NODE_ID, TARGET_INDEGREE NODE_DEGREE, LENGTH, EDGE_MODE, ROUTE_ID FROM "
-				+ configEdges + " WHERE SOURCE=?";
-			queryGetAdjContinuousLinks = "SELECT ID, TARGET NODE_ID, TARGET_C_INDEGREE NODE_DEGREE, LENGTH FROM "
-				+ configEdges + " WHERE TARGET=? AND EDGE_MODE=";
+			queryGetAdjLinks = "SELECT ID, SOURCE NODE_ID, TARGET ADJ_NODE_ID, TARGET_INDEGREE NODE_DEGREE, LENGTH, EDGE_MODE, ROUTE_ID FROM "
+				+ configEdges + " WHERE SOURCE IN (%S)";
+			queryGetAdjContinuousLinks = "SELECT ID, SOURCE NODE_ID, TARGET ADJ_NODE_ID, TARGET_C_INDEGREE NODE_DEGREE,  EDGE_MODE, ROUTE_ID FROM "
+				+ configEdges + " WHERE SOURCE IN (%S) AND EDGE_MODE=" + NW_MODE_CONTINUOUS;
 		}
 	}
 
@@ -192,23 +193,30 @@ public class Database {
 	/**
 	 * Gets all links adjacent to a given node.
 	 *
-	 * @param nodeId the node to which the links should be adjacent
+	 * @param nodeIds the identifiers of the nodes to which the links should be adjacent
 	 * @param nodes the nodes HashMap to which eventual new nodes should be added
-	 * @return a list of links adjacent to the given nodeId
+	 * @return a map containing the links adjacent to the given nodeIds (key == nodeId, value == adjacent links)
 	 */
-	public Collection<Link> getAdjLinks(final int nodeId, final Map<Integer, Node> nodes) {
-		final PreparedStatement stmt = getPstmt((mode == Mode.UNIMODAL) ? queryGetAdjContinuousLinks : queryGetAdjLinks);
-		final Collection<Link> adjLinks = new ArrayList<Link>();
+	public Map<Integer, Collection<Link>> getAdjLinks(final Collection<Integer> nodeIds, final Map<Integer, Node> nodes) {
+		final Map<Integer, Collection<Link>> adjLinks = new HashMap<Integer, Collection<Link>>();
+
+		final int countIds = nodeIds.size();
+		final String query = String.format((mode == Mode.UNIMODAL) ? queryGetAdjContinuousLinks : queryGetAdjLinks, preparePlaceHolders(countIds));
+		final PreparedStatement stmt = getPstmt(query);
 
 		ResultSet rs = null;
 		try {
-			stmt.setInt(1, nodeId);
+			setValues(1, stmt, nodeIds.toArray());
 			rs = stmt.executeQuery();
 			while (rs.next()) {
-				final int adjNodeId = rs.getInt("NODE_ID");
+				final int nodeId = rs.getInt("NODE_ID");
+				final int adjNodeId = rs.getInt("ADJ_NODE_ID");
 				final short degree = rs.getShort("NODE_DEGREE");
 				addNode(adjNodeId, degree, nodes);
-				adjLinks.add(createLinkFromResultSet(nodeId, adjNodeId, rs));
+
+				final Collection<Link> links = (adjLinks.containsKey(nodeId)) ? adjLinks.get(nodeId) : new ArrayList<Link>();
+				links.add(createLinkFromResultSet(nodeId, adjNodeId, rs));
+				adjLinks.put(nodeId, links);
 			}
 		} catch (final SQLException e) {
 			e.printStackTrace();
@@ -276,19 +284,22 @@ public class Database {
 	/**
 	 * Gets the coordinates of a node.
 	 *
-	 * @param nodeId the nodeId of the node for which we should find the coordinates
-	 * @return a point with the coordinates of the node
+	 * @param nodeIds the identifiers of the nodes for which we should find the coordinates
+	 * @return a map with points (key == nodeId, value == coordinate) with the coordinates of the node
 	 */
-	public Point getCoordinates(final int nodeId) {
-		final PreparedStatement stmt = getPstmt(queryGetCoordinatesFromNode);
+	public Map<Integer, Point> getCoordinates(final Collection<Integer> nodeIds) {
+		final Map<Integer, Point> result = new HashMap<Integer, Point>();
+
+		final int countIds = nodeIds.size();
+		final PreparedStatement stmt = getPstmt(String.format(queryGetCoordinatesFromNode, preparePlaceHolders(countIds)));
 
-		Point gp = null;
 		ResultSet rs = null;
 		try {
-			stmt.setInt(1, nodeId);
+			setValues(1, stmt, nodeIds.toArray());
 			rs = stmt.executeQuery();
 			if (rs.next()) {
-				gp = new Point(rs.getDouble("X"), rs.getDouble("Y"));
+				final int nodeId = rs.getInt("ID");
+				result.put(nodeId, new Point(rs.getDouble("X"), rs.getDouble("Y")));
 			}
 		} catch (final SQLException e) {
 			e.printStackTrace();
@@ -296,7 +307,7 @@ public class Database {
 			DbUtils.closeQuietly(rs);
 		}
 
-		return gp;
+		return result;
 	}
 
 	/**
@@ -428,24 +439,29 @@ public class Database {
 	}
 
 	/**
-	 * Gets a node from the database.
+	 * Gets nodes from the database.
 	 *
-	 * @param nodeId the id of the node that should be read from the database
-	 * @return the node from the database
+	 * @param nodeIds the ids of the nodes that should be read from the database
+	 * @return the nodes from the database (key == nodeId, value == node)
 	 */
-	public Node getNode(final int nodeId) {
-		final PreparedStatement stmt = getPstmt((mode == Mode.UNIMODAL) ? queryGetContinuousNode : queryGetMrneXNode);
+	public Map<Integer, Node> getNode(final Collection<Integer> nodeIds) {
+		final Map<Integer, Node> result = new HashMap<>();
+
+		final int countIds = nodeIds.size();
+		final String query = String.format((mode == Mode.UNIMODAL) ? queryGetContinuousNode : queryGetMrneXNode, preparePlaceHolders(countIds));
+		final PreparedStatement stmt = getPstmt(query);
 
-		Node node = null;
 		ResultSet rs = null;
 		try {
-			stmt.setInt(1, nodeId);
+			setValues(1, stmt, nodeIds.toArray());
 			rs = stmt.executeQuery();
 			if (rs.next()) {
-				node = new Node(nodeId, rs.getShort("NODE_DEGREE"));
+				final int nodeId = rs.getInt("ID");
+				final Node node = new Node(nodeId, rs.getShort("NODE_DEGREE"));
 				if (mode == Mode.MULTIMODAL) {
 					node.setCoordinates(new Point(rs.getDouble("X"), rs.getDouble("Y")));
 				}
+				result.put(nodeId, node);
 			}
 		} catch (final SQLException e) {
 			e.printStackTrace();
@@ -453,45 +469,50 @@ public class Database {
 			DbUtils.closeQuietly(rs);
 		}
 
-		return node;
+		return result;
 	}
 
 	/**
 	 * Returns the maximal range for the given vertex with a maximal number of specified vertices.
 	 * The database query used finds the last two tuples having a memory size smaller than the specified memorySize and then interpolates the range.
 	 *
-	 * @param nodeId the vertex identifier
+	 * @param nodeIds the vertex identifiers
 	 * @param memorySize the maximal number of vertices
-	 * @return the available range as the euclidean distance
+	 * @return a map containing the available range as the euclidean distance (key == nodeId, value == distance)
 	 */
-	public double getRange(final int nodeId, final int memorySize) {
-		final PreparedStatement stmt = getPstmt(queryGetRange);
+	public Map<Integer, Double> getRange(final Collection<Integer> nodeIds, final int memorySize) {
+		final Map<Integer, Double> result = new HashMap<>();
+
+		final int countIds = nodeIds.size();
+		final PreparedStatement stmt = getPstmt(String.format(queryGetRange, preparePlaceHolders(countIds)));
+		final Object[] nodeIdArr = nodeIds.toArray();
 
 		ResultSet rs = null;
 		try {
-			// CHECKSTYLE:OFF MagicNumber
-			stmt.setInt(1, nodeId);
-			stmt.setInt(2, memorySize);
-			stmt.setInt(3, nodeId);
-			stmt.setInt(4, memorySize);
-			// CHECKSTYLE:ON MagicNumber
+			setValues(1, stmt, nodeIdArr);
+			stmt.setInt(countIds  + 1, memorySize);
+			setValues(countIds + 2, stmt, nodeIdArr);
+			stmt.setInt(2 * (countIds + 1), memorySize);
+
 			rs = stmt.executeQuery();
 			int s0 = 0, s1 = 0;
 			double ed0 = 0, ed1 = 0;
 			if (rs.next()) {
+				final int nodeId = rs.getInt("id");
 				s0 = rs.getInt("s0");
 				ed0 = rs.getDouble("ed0");
 				s1 = rs.getInt("s1");
 				ed1 = rs.getDouble("ed1");
+
+				result.put(nodeId, ed0 + (ed1 - ed0) / (s1 - s0) * (memorySize - s0));
 			}
-			return ed0 + (ed1 - ed0) / (s1 - s0) * (memorySize - s0);
 		} catch (final SQLException e) {
 			e.printStackTrace();
 		} finally {
 			DbUtils.closeQuietly(rs);
 		}
 
-		return 0;
+		return result;
 	}
 
 	/**
@@ -627,11 +648,13 @@ public class Database {
 	 * each time a query needs to be called, and recalculating the
 	 * queryplan each time.
 	 *
+	 * @author t.gummerer
 	 * @param query The prepared statement that is needed
 	 * @return the cached prepared statement object
 	 */
-	// FIXME: Find a fix for prepared statements with parameters
-	// (we should NOT set strings in preparedStatemens just because we want to cache something)
+	// TODO: maybe we should NOT set strings (tableNames) in preparedStatemens just because we want to cache something
+	// -> what is called internally to prevent sql injection (we should use this for tableNames, since they are configured in a config file)
+	// Inspired by: http://tutorials.jenkov.com/jdbc/preparedstatement.html
 	protected PreparedStatement getPstmt(final String query) {
 		initConnection();
 		if (pstmtsCacheMap == null) {