diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
index 8079e6d5af77d5b0b039056bfe3479385bda5313..9b4de4ebc4ac8dbc96550360be5f57853a500d14 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
@@ -48,6 +48,7 @@
 import org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM;
 import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
 import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_TYPE_ENUM;
+import org.sqlite.SQLiteConfig;
 import org.sqlite.SQLiteJDBCLoader;
 
 /**
@@ -4098,6 +4099,19 @@ public void closeRunQuery(ResultSet resultSet) throws SQLException {
 		}
 	}
 
+	/**
+	 * This method allows developers to run arbitrary SQL "SELECT"
+	 * queries. The CaseDbQuery object will take care to acquiring
+	 * the necessary database lock and when used in a try-with-resources
+	 * block will automatically take care of releasing the lock.
+	 * @param query The query string to execute.
+	 * @return A CaseDbQuery instance.
+	 * @throws TskCoreException 
+	 */
+	public CaseDbQuery executeQuery(String query) throws TskCoreException {
+		return new CaseDbQuery(query);
+	}
+	
 	@Override
 	public void finalize() throws Throwable {
 		try {
@@ -4112,8 +4126,8 @@ public void finalize() throws Throwable {
 	 */
 	public void close() {
 		System.err.println(this.hashCode() + " closed"); //NON-NLS
-		acquireExclusiveLock();
 		System.err.flush();
+		acquireExclusiveLock();
 		connections.close();
 		fileSystemIdMap.clear();
 
@@ -5110,11 +5124,22 @@ String getSQL() {
 			this.preparedStatements = new EnumMap<PREPARED_STATEMENT, PreparedStatement>(PREPARED_STATEMENT.class);
 			Statement statement = null;
 			try {
-				this.connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath); //NON-NLS
-				statement = createStatement();
-				statement.execute("PRAGMA synchronous = OFF;"); // Reduce I/O operations, we have no OS crash recovery anyway. //NON-NLS			
-				statement.execute("PRAGMA read_uncommitted = True;"); // Allow query while in transaction. //NON-NLS
-				statement.execute("PRAGMA foreign_keys = ON;"); // Enforce foreign key constraints. //NON-NLS
+				SQLiteConfig config = new SQLiteConfig();
+				
+				// Reduce I/O operations, we have no OS crash recovery anyway.
+				config.setSynchronous(SQLiteConfig.SynchronousMode.OFF);
+				
+				// The original comment for "read_uncommited" indicating that it
+				// was being set to "allow query while in transaction". I don't fully
+				// understand why this is needed since all it does it expose dirty writes
+				// within one transaction to other queries. There was also the suggestion
+				// that it may have helped to increase performance.
+				config.setReadUncommited(true);
+				
+				// Enforce foreign key constraints.
+				config.enforceForeignKeys(true);
+				
+				this.connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath, config.toProperties()); //NON-NLS
 			} catch (SQLException ex) {
 				// The exception is caught and logged here because this 
 				// constructor will be called by an override of 
@@ -5131,8 +5156,6 @@ String getSQL() {
 					}
 					this.connection = null;
 				}
-			} finally {
-				closeStatement(statement);
 			}
 		}
 
@@ -5199,11 +5222,26 @@ void beginTransaction() throws SQLException {
 		}
 
 		void commitTransaction() throws SQLException {
+			boolean locked = true;
+
+			// Exceptions can be thrown on a call to commit so we will retry
+			// until it succeeds.
+			while (locked) {
+				try {
+					connection.commit();
+					locked = false;
+				} catch (SQLException ex) {
+					logger.log(Level.SEVERE, String.format("Exception commiting transaction: Error code: %d SQLState: %s", ex.getErrorCode(), ex.getSQLState()), ex);
+				}
+			}
+
+			// You must turn auto commit back on when done with the transaction.
 			try {
-				connection.commit();
-			} finally {
 				connection.setAutoCommit(true);
 			}
+			catch (SQLException ex) {
+				logger.log(Level.SEVERE, String.format("Exception resetting auto commit: Error code: %d SQLState: %s", ex.getErrorCode(), ex.getSQLState()), ex);
+			}
 		}
 
 		/**
@@ -5373,4 +5411,69 @@ public void rollback() throws TskCoreException {
 			}
 		}
 	}
+	
+	/**
+	 * The CaseDbQuery supports the use case where developers have a 
+	 * need for data that is not exposed through the SleuthkitCase API.
+	 * A CaseDbQuery instance gets created through the SleuthkitCase
+	 * executeDbQuery() method. It wraps the ResultSet and takes care
+	 * of acquiring and releasing the appropriate database lock.
+	 * It implements AutoCloseable so that it can be used in a try-with
+	 * -resources block freeing developers from having to remember to
+	 * close the result set and releasing the lock.
+	 * 
+	 */
+	public final class CaseDbQuery implements AutoCloseable {
+		private ResultSet resultSet;
+		
+		private CaseDbQuery(String query) throws TskCoreException {
+			if (!query.regionMatches(true, 0, "SELECT", 0, "SELECT".length())) {
+				throw new TskCoreException("Unsupported query: Only SELECT queries are supported.");
+			}
+			
+			CaseDbConnection connection;
+			
+			try {
+				connection = connections.getConnection();
+			} catch (TskCoreException ex) {
+				throw new TskCoreException("Error getting connection for query: ", ex);
+			}
+
+			try {
+				SleuthkitCase.this.acquireSharedLock();		
+				resultSet = connection.executeQuery(connection.createStatement(), query);
+			}
+			catch (SQLException ex)
+			{
+				SleuthkitCase.this.releaseSharedLock();
+				throw new TskCoreException("Error executing query: ", ex);				
+			}
+		}
+		
+		/**
+		 * Get the result set for this query.
+		 * @return The result set.
+		 */
+		public ResultSet getResultSet() {
+			return resultSet;
+		}
+		
+		@Override
+		public void close() throws TskCoreException {
+			try {
+				if (resultSet != null) {
+					final Statement statement = resultSet.getStatement();
+					if (statement != null) {
+						statement.close();
+					}
+					resultSet.close();
+				}
+
+				SleuthkitCase.this.releaseSharedLock();				
+			}
+			catch (SQLException ex) {
+				throw new TskCoreException("Error closing query: ", ex);
+			}
+		}	
+	}
 }