diff --git a/src/common/src/main/kotlin/Configuration.kt b/src/common/src/main/kotlin/Configuration.kt
index 683a386..c4969a1 100644
--- a/src/common/src/main/kotlin/Configuration.kt
+++ b/src/common/src/main/kotlin/Configuration.kt
@@ -2,7 +2,6 @@ package solutions.bitbadger.documents.common
 
 import java.sql.Connection
 import java.sql.DriverManager
-import kotlin.jvm.Throws
 
 /**
  * Configuration for the document library
diff --git a/src/java/pom.xml b/src/java/pom.xml
index 0b272c0..b382510 100644
--- a/src/java/pom.xml
+++ b/src/java/pom.xml
@@ -71,18 +71,6 @@
                         </configuration>
                     </execution>
                 </executions>
-                <configuration>
-                    <compilerPlugins>
-                        <plugin>kotlinx-serialization</plugin>
-                    </compilerPlugins>
-                </configuration>
-                <dependencies>
-                    <dependency>
-                        <groupId>org.jetbrains.kotlin</groupId>
-                        <artifactId>kotlin-maven-serialization</artifactId>
-                        <version>${kotlin.version}</version>
-                    </dependency>
-                </dependencies>
             </plugin>
             <plugin>
                 <artifactId>maven-surefire-plugin</artifactId>
diff --git a/src/java/src/main/kotlin/ConnectionExtensions.kt b/src/java/src/main/kotlin/ConnectionExtensions.kt
index a5230c2..f25e658 100644
--- a/src/java/src/main/kotlin/ConnectionExtensions.kt
+++ b/src/java/src/main/kotlin/ConnectionExtensions.kt
@@ -1,11 +1,8 @@
+@file:JvmName("ConnExt")
+
 package solutions.bitbadger.documents.java
 
-import solutions.bitbadger.documents.Custom
-import solutions.bitbadger.documents.common.DocumentIndex
-import solutions.bitbadger.documents.common.Field
-import solutions.bitbadger.documents.common.FieldMatch
-import solutions.bitbadger.documents.common.Parameter
-import solutions.bitbadger.documents.java.*
+import solutions.bitbadger.documents.common.*
 import java.sql.Connection
 import java.sql.ResultSet
 
@@ -16,24 +13,32 @@ import java.sql.ResultSet
  *
  * @param query The query to retrieve the results
  * @param parameters Parameters to use for the query
+ * @param clazz The class of the document to be returned
  * @param mapFunc The mapping function between the document and the domain item
  * @return A list of results for the given query
  */
-inline fun <reified TDoc> Connection.customList(
-    query: String, parameters: Collection<Parameter<*>> = listOf(), noinline mapFunc: (ResultSet, Class<TDoc>) -> TDoc
-) = Custom.list(query, parameters, this, mapFunc)
+fun <TDoc> Connection.customList(
+    query: String,
+    parameters: Collection<Parameter<*>> = listOf(),
+    clazz: Class<TDoc>,
+    mapFunc: (ResultSet, Class<TDoc>) -> TDoc
+) = Custom.list(query, parameters, clazz, this, mapFunc)
 
 /**
  * Execute a query that returns one or no results
  *
  * @param query The query to retrieve the results
  * @param parameters Parameters to use for the query
+ * @param clazz The class of the document to be returned
  * @param mapFunc The mapping function between the document and the domain item
  * @return The document if one matches the query, `null` otherwise
  */
-inline fun <reified TDoc> Connection.customSingle(
-    query: String, parameters: Collection<Parameter<*>> = listOf(), noinline mapFunc: (ResultSet, Class<TDoc>) -> TDoc
-) = Custom.single(query, parameters, this, mapFunc)
+fun <TDoc> Connection.customSingle(
+    query: String,
+    parameters: Collection<Parameter<*>> = listOf(),
+    clazz: Class<TDoc>,
+    mapFunc: (ResultSet, Class<TDoc>) -> TDoc
+) = Custom.single(query, parameters, clazz, this, mapFunc)
 
 /**
  * Execute a query that returns no results
@@ -49,14 +54,16 @@ fun Connection.customNonQuery(query: String, parameters: Collection<Parameter<*>
  *
  * @param query The query to retrieve the result
  * @param parameters Parameters to use for the query
+ * @param clazz The class of the document to be returned
  * @param mapFunc The mapping function between the document and the domain item
  * @return The scalar value from the query
  */
-inline fun <reified T : Any> Connection.customScalar(
+fun <T : Any> Connection.customScalar(
     query: String,
     parameters: Collection<Parameter<*>> = listOf(),
-    noinline mapFunc: (ResultSet, Class<T>) -> T
-) = Custom.scalar(query, parameters, this, mapFunc)
+    clazz: Class<T>,
+    mapFunc: (ResultSet, Class<T>) -> T
+) = Custom.scalar(query, parameters, clazz, this, mapFunc)
 
 // ~~~ DEFINITION QUERIES ~~~
 
@@ -66,7 +73,7 @@ inline fun <reified T : Any> Connection.customScalar(
  * @param tableName The table whose existence should be ensured (may include schema)
  */
 fun Connection.ensureTable(tableName: String) =
-    solutions.bitbadger.documents.java.Definition.ensureTable(tableName, this)
+    Definition.ensureTable(tableName, this)
 
 /**
  * Create an index on field(s) within documents in the specified table if necessary
@@ -76,7 +83,7 @@ fun Connection.ensureTable(tableName: String) =
  * @param fields One or more fields to be indexed<
  */
 fun Connection.ensureFieldIndex(tableName: String, indexName: String, fields: Collection<String>) =
-    solutions.bitbadger.documents.java.Definition.ensureFieldIndex(tableName, indexName, fields, this)
+    Definition.ensureFieldIndex(tableName, indexName, fields, this)
 
 /**
  * Create a document index on a table (PostgreSQL only)
@@ -85,8 +92,9 @@ fun Connection.ensureFieldIndex(tableName: String, indexName: String, fields: Co
  * @param indexType The type of index to ensure
  * @throws DocumentException If called on a SQLite connection
  */
+@Throws(DocumentException::class)
 fun Connection.ensureDocumentIndex(tableName: String, indexType: DocumentIndex) =
-    solutions.bitbadger.documents.java.Definition.ensureDocumentIndex(tableName, indexType, this)
+    Definition.ensureDocumentIndex(tableName, indexType, this)
 
 // ~~~ DOCUMENT MANIPULATION QUERIES ~~~
 
@@ -96,7 +104,7 @@ fun Connection.ensureDocumentIndex(tableName: String, indexType: DocumentIndex)
  * @param tableName The table into which the document should be inserted (may include schema)
  * @param document The document to be inserted
  */
-inline fun <reified TDoc> Connection.insert(tableName: String, document: TDoc) =
+fun <TDoc> Connection.insert(tableName: String, document: TDoc) =
     Document.insert(tableName, document, this)
 
 /**
@@ -105,7 +113,7 @@ inline fun <reified TDoc> Connection.insert(tableName: String, document: TDoc) =
  * @param tableName The table in which the document should be saved (may include schema)
  * @param document The document to be saved
  */
-inline fun <reified TDoc> Connection.save(tableName: String, document: TDoc) =
+fun <TDoc> Connection.save(tableName: String, document: TDoc) =
     Document.save(tableName, document, this)
 
 /**
@@ -115,7 +123,7 @@ inline fun <reified TDoc> Connection.save(tableName: String, document: TDoc) =
  * @param docId The ID of the document to be replaced
  * @param document The document to be replaced
  */
-inline fun <TKey, reified TDoc> Connection.update(tableName: String, docId: TKey, document: TDoc) =
+fun <TKey, TDoc> Connection.update(tableName: String, docId: TKey, document: TDoc) =
     Document.update(tableName, docId, document, this)
 
 // ~~~ DOCUMENT COUNT QUERIES ~~~
@@ -148,7 +156,8 @@ fun Connection.countByFields(tableName: String, fields: Collection<Field<*>>, ho
  * @return A count of the matching documents in the table
  * @throws DocumentException If called on a SQLite connection
  */
-inline fun <reified TContains> Connection.countByContains(tableName: String, criteria: TContains) =
+@Throws(DocumentException::class)
+fun <TContains> Connection.countByContains(tableName: String, criteria: TContains) =
     Count.byContains(tableName, criteria, this)
 
 /**
@@ -159,6 +168,7 @@ inline fun <reified TContains> Connection.countByContains(tableName: String, cri
  * @return A count of the matching documents in the table
  * @throws DocumentException If called on a SQLite connection
  */
+@Throws(DocumentException::class)
 fun Connection.countByJsonPath(tableName: String, path: String) =
     Count.byJsonPath(tableName, path, this)
 
@@ -193,7 +203,8 @@ fun Connection.existsByFields(tableName: String, fields: Collection<Field<*>>, h
  * @return True if any matching documents exist, false if not
  * @throws DocumentException If called on a SQLite connection
  */
-inline fun <reified TContains> Connection.existsByContains(tableName: String, criteria: TContains) =
+@Throws(DocumentException::class)
+fun <TContains> Connection.existsByContains(tableName: String, criteria: TContains) =
     Exists.byContains(tableName, criteria, this)
 
 /**
@@ -204,6 +215,7 @@ inline fun <reified TContains> Connection.existsByContains(tableName: String, cr
  * @return True if any matching documents exist, false if not
  * @throws DocumentException If called on a SQLite connection
  */
+@Throws(DocumentException::class)
 fun Connection.existsByJsonPath(tableName: String, path: String) =
     Exists.byJsonPath(tableName, path, this)
 
@@ -213,119 +225,145 @@ fun Connection.existsByJsonPath(tableName: String, path: String) =
  * Retrieve all documents in the given table, ordering results by the optional given fields
  *
  * @param tableName The table from which documents should be retrieved
+ * @param clazz The class of the document to be returned
  * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
  * @return A list of documents from the given table
  */
-inline fun <reified TDoc> Connection.findAll(tableName: String, orderBy: Collection<Field<*>>? = null) =
-    solutions.bitbadger.documents.java.Find.all<TDoc>(tableName, orderBy, this)
+@JvmOverloads
+fun <TDoc> Connection.findAll(tableName: String, clazz: Class<TDoc>, orderBy: Collection<Field<*>>? = null) =
+    Find.all(tableName, clazz, orderBy, this)
 
 /**
  * Retrieve a document by its ID
  *
  * @param tableName The table from which the document should be retrieved
  * @param docId The ID of the document to retrieve
+ * @param clazz The class of the document to be returned
  * @return The document if it is found, `null` otherwise
  */
-inline fun <TKey, reified TDoc> Connection.findById(tableName: String, docId: TKey) =
-    solutions.bitbadger.documents.java.Find.byId<TKey, TDoc>(tableName, docId, this)
+fun <TKey, TDoc> Connection.findById(tableName: String, docId: TKey, clazz: Class<TDoc>) =
+    Find.byId(tableName, docId, clazz, this)
 
 /**
  * Retrieve documents using a field comparison, ordering results by the optional given fields
  *
  * @param tableName The table from which the document should be retrieved
  * @param fields The fields which should be compared
+ * @param clazz The class of the document to be returned
  * @param howMatched How the fields should be matched
  * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
  * @return A list of documents matching the field comparison
  */
-inline fun <reified TDoc> Connection.findByFields(
+@JvmOverloads
+fun <TDoc> Connection.findByFields(
     tableName: String,
     fields: Collection<Field<*>>,
+    clazz: Class<TDoc>,
     howMatched: FieldMatch? = null,
     orderBy: Collection<Field<*>>? = null
 ) =
-    solutions.bitbadger.documents.java.Find.byFields<TDoc>(tableName, fields, howMatched, orderBy, this)
+    Find.byFields(tableName, fields, clazz, howMatched, orderBy, this)
 
 /**
  * Retrieve documents using a JSON containment query, ordering results by the optional given fields (PostgreSQL only)
  *
  * @param tableName The name of the table in which document existence should be checked
  * @param criteria The object for which JSON containment should be checked
+ * @param clazz The class of the document to be returned
  * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
  * @return A list of documents matching the JSON containment query
  * @throws DocumentException If called on a SQLite connection
  */
-inline fun <reified TDoc, reified TContains> Connection.findByContains(
+@Throws(DocumentException::class)
+@JvmOverloads
+fun <TDoc, TContains> Connection.findByContains(
     tableName: String,
     criteria: TContains,
+    clazz: Class<TDoc>,
     orderBy: Collection<Field<*>>? = null
 ) =
-    solutions.bitbadger.documents.java.Find.byContains<TDoc, TContains>(tableName, criteria, orderBy, this)
+    Find.byContains(tableName, criteria, clazz, orderBy, this)
 
 /**
  * Retrieve documents using a JSON Path match query, ordering results by the optional given fields (PostgreSQL only)
  *
  * @param tableName The table from which documents should be retrieved
  * @param path The JSON path comparison to match
+ * @param clazz The class of the document to be returned
  * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
  * @return A list of documents matching the JSON Path match query
  * @throws DocumentException If called on a SQLite connection
  */
-inline fun <reified TDoc> Connection.findByJsonPath(
+@Throws(DocumentException::class)
+@JvmOverloads
+fun <TDoc> Connection.findByJsonPath(
     tableName: String,
     path: String,
+    clazz: Class<TDoc>,
     orderBy: Collection<Field<*>>? = null
 ) =
-    solutions.bitbadger.documents.java.Find.byJsonPath<TDoc>(tableName, path, orderBy, this)
+    Find.byJsonPath(tableName, path, clazz, orderBy, this)
 
 /**
  * Retrieve the first document using a field comparison and optional ordering fields
  *
  * @param tableName The table from which documents should be retrieved
  * @param fields The fields which should be compared
+ * @param clazz The class of the document to be returned
  * @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
  * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
  * @return The first document matching the field comparison, or `null` if no matches are found
  */
-inline fun <reified TDoc> Connection.findFirstByFields(
+@Throws(DocumentException::class)
+@JvmOverloads
+fun <TDoc> Connection.findFirstByFields(
     tableName: String,
     fields: Collection<Field<*>>,
+    clazz: Class<TDoc>,
     howMatched: FieldMatch? = null,
     orderBy: Collection<Field<*>>? = null
 ) =
-    solutions.bitbadger.documents.java.Find.firstByFields<TDoc>(tableName, fields, howMatched, orderBy, this)
+    Find.firstByFields(tableName, fields, clazz, howMatched, orderBy, this)
 
 /**
  * Retrieve the first document using a JSON containment query and optional ordering fields (PostgreSQL only)
  *
  * @param tableName The table from which documents should be retrieved
  * @param criteria The object for which JSON containment should be checked
+ * @param clazz The class of the document to be returned
  * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
  * @return The first document matching the JSON containment query, or `null` if no matches are found
  * @throws DocumentException If called on a SQLite connection
  */
-inline fun <reified TDoc, reified TContains> Connection.findFirstByContains(
+@Throws(DocumentException::class)
+@JvmOverloads
+fun <TDoc, TContains> Connection.findFirstByContains(
     tableName: String,
     criteria: TContains,
+    clazz: Class<TDoc>,
     orderBy: Collection<Field<*>>? = null
 ) =
-    solutions.bitbadger.documents.java.Find.firstByContains<TDoc, TContains>(tableName, criteria, orderBy, this)
+    Find.firstByContains(tableName, criteria, clazz, orderBy, this)
 
 /**
  * Retrieve the first document using a JSON Path match query and optional ordering fields (PostgreSQL only)
  *
  * @param tableName The table from which documents should be retrieved
  * @param path The JSON path comparison to match
+ * @param clazz The class of the document to be returned
  * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
  * @return The first document matching the JSON Path match query, or `null` if no matches are found
  * @throws DocumentException If called on a SQLite connection
  */
-inline fun <reified TDoc> Connection.findFirstByJsonPath(
+@Throws(DocumentException::class)
+@JvmOverloads
+fun <TDoc> Connection.findFirstByJsonPath(
     tableName: String,
     path: String,
+    clazz: Class<TDoc>,
     orderBy: Collection<Field<*>>? = null
 ) =
-    solutions.bitbadger.documents.java.Find.firstByJsonPath<TDoc>(tableName, path, orderBy, this)
+    Find.firstByJsonPath(tableName, path, clazz, orderBy, this)
 
 // ~~~ DOCUMENT PATCH (PARTIAL UPDATE) QUERIES ~~~
 
@@ -336,7 +374,7 @@ inline fun <reified TDoc> Connection.findFirstByJsonPath(
  * @param docId The ID of the document to be patched
  * @param patch The object whose properties should be replaced in the document
  */
-inline fun <TKey, reified TPatch> Connection.patchById(tableName: String, docId: TKey, patch: TPatch) =
+fun <TKey, TPatch> Connection.patchById(tableName: String, docId: TKey, patch: TPatch) =
     Patch.byId(tableName, docId, patch, this)
 
 /**
@@ -347,7 +385,7 @@ inline fun <TKey, reified TPatch> Connection.patchById(tableName: String, docId:
  * @param patch The object whose properties should be replaced in the document
  * @param howMatched How the fields should be matched
  */
-inline fun <reified TPatch> Connection.patchByFields(
+fun <TPatch> Connection.patchByFields(
     tableName: String,
     fields: Collection<Field<*>>,
     patch: TPatch,
@@ -363,7 +401,8 @@ inline fun <reified TPatch> Connection.patchByFields(
  * @param patch The object whose properties should be replaced in the document
  * @throws DocumentException If called on a SQLite connection
  */
-inline fun <reified TContains, reified TPatch> Connection.patchByContains(
+@Throws(DocumentException::class)
+fun <TContains, TPatch> Connection.patchByContains(
     tableName: String,
     criteria: TContains,
     patch: TPatch
@@ -378,7 +417,8 @@ inline fun <reified TContains, reified TPatch> Connection.patchByContains(
  * @param patch The object whose properties should be replaced in the document
  * @throws DocumentException If called on a SQLite connection
  */
-inline fun <reified TPatch> Connection.patchByJsonPath(tableName: String, path: String, patch: TPatch) =
+@Throws(DocumentException::class)
+fun <TPatch> Connection.patchByJsonPath(tableName: String, path: String, patch: TPatch) =
     Patch.byJsonPath(tableName, path, patch, this)
 
 // ~~~ DOCUMENT FIELD REMOVAL QUERIES ~~~
@@ -417,7 +457,8 @@ fun Connection.removeFieldsByFields(
  * @param toRemove The names of the fields to be removed
  * @throws DocumentException If called on a SQLite connection
  */
-inline fun <reified TContains> Connection.removeFieldsByContains(
+@Throws(DocumentException::class)
+fun <TContains> Connection.removeFieldsByContains(
     tableName: String,
     criteria: TContains,
     toRemove: Collection<String>
@@ -432,6 +473,7 @@ inline fun <reified TContains> Connection.removeFieldsByContains(
  * @param toRemove The names of the fields to be removed
  * @throws DocumentException If called on a SQLite connection
  */
+@Throws(DocumentException::class)
 fun Connection.removeFieldsByJsonPath(tableName: String, path: String, toRemove: Collection<String>) =
     RemoveFields.byJsonPath(tableName, path, toRemove, this)
 
@@ -463,7 +505,8 @@ fun Connection.deleteByFields(tableName: String, fields: Collection<Field<*>>, h
  * @param criteria The object for which JSON containment should be checked
  * @throws DocumentException If called on a SQLite connection
  */
-inline fun <reified TContains> Connection.deleteByContains(tableName: String, criteria: TContains) =
+@Throws(DocumentException::class)
+fun <TContains> Connection.deleteByContains(tableName: String, criteria: TContains) =
     Delete.byContains(tableName, criteria, this)
 
 /**
diff --git a/src/java/src/main/kotlin/Count.kt b/src/java/src/main/kotlin/Count.kt
index 5d42b38..df9bc16 100644
--- a/src/java/src/main/kotlin/Count.kt
+++ b/src/java/src/main/kotlin/Count.kt
@@ -1,10 +1,7 @@
 package solutions.bitbadger.documents.java
 
-import solutions.bitbadger.documents.Results
-import solutions.bitbadger.documents.common.Field
-import solutions.bitbadger.documents.common.FieldMatch
-import solutions.bitbadger.documents.common.Parameter
-import solutions.bitbadger.documents.common.ParameterType
+import solutions.bitbadger.documents.common.*
+import solutions.bitbadger.documents.common.query.Count
 import java.sql.Connection
 
 /**
@@ -21,7 +18,7 @@ object Count {
      */
     @JvmStatic
     fun all(tableName: String, conn: Connection) =
-        conn.customScalar(all(tableName), mapFunc = Results::toCount)
+        conn.customScalar(Count.all(tableName), listOf(), Long::class.java, Results::toCount)
 
     /**
      * Count all documents in the table
@@ -52,8 +49,9 @@ object Count {
     ): Long {
         val named = Parameters.nameFields(fields)
         return conn.customScalar(
-            byFields(tableName, named, howMatched),
+            Count.byFields(tableName, named, howMatched),
             Parameters.addFields(named),
+            Long::class.java,
             Results::toCount
         )
     }
@@ -81,8 +79,13 @@ object Count {
      * @throws DocumentException If called on a SQLite connection
      */
     @JvmStatic
-    inline fun <reified TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
-        conn.customScalar(Count.byContains(tableName), listOf(Parameters.json(":criteria", criteria)), Results::toCount)
+    fun <TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
+        conn.customScalar(
+            Count.byContains(tableName),
+            listOf(Parameters.json(":criteria", criteria)),
+            Long::class.java,
+            Results::toCount
+        )
 
     /**
      * Count documents using a JSON containment query (PostgreSQL only)
@@ -93,7 +96,7 @@ object Count {
      * @throws DocumentException If called on a SQLite connection
      */
     @JvmStatic
-    inline fun <reified TContains> byContains(tableName: String, criteria: TContains) =
+    fun <TContains> byContains(tableName: String, criteria: TContains) =
         Configuration.dbConn().use { byContains(tableName, criteria, it) }
 
     /**
@@ -110,6 +113,7 @@ object Count {
         conn.customScalar(
             Count.byJsonPath(tableName),
             listOf(Parameter(":path", ParameterType.STRING, path)),
+            Long::class.java,
             Results::toCount
         )
 
diff --git a/src/java/src/main/kotlin/Definition.kt b/src/java/src/main/kotlin/Definition.kt
index 7cd749b..531415d 100644
--- a/src/java/src/main/kotlin/Definition.kt
+++ b/src/java/src/main/kotlin/Definition.kt
@@ -1,6 +1,9 @@
 package solutions.bitbadger.documents.java
 
+import solutions.bitbadger.documents.common.Configuration
+import solutions.bitbadger.documents.common.DocumentException
 import solutions.bitbadger.documents.common.DocumentIndex
+import solutions.bitbadger.documents.common.query.Definition
 import java.sql.Connection
 
 /**
@@ -14,6 +17,7 @@ object Definition {
      * @param tableName The table whose existence should be ensured (may include schema)
      * @param conn The connection on which the query should be executed
      */
+    @JvmStatic
     fun ensureTable(tableName: String, conn: Connection) =
         Configuration.dialect("ensure $tableName exists").let {
             conn.customNonQuery(Definition.ensureTable(tableName, it))
@@ -25,6 +29,7 @@ object Definition {
      *
      * @param tableName The table whose existence should be ensured (may include schema)
      */
+    @JvmStatic
     fun ensureTable(tableName: String) =
         Configuration.dbConn().use { ensureTable(tableName, it) }
 
@@ -33,9 +38,10 @@ object Definition {
      *
      * @param tableName The table to be indexed (may include schema)
      * @param indexName The name of the index to create
-     * @param fields One or more fields to be indexed<
+     * @param fields One or more fields to be indexed
      * @param conn The connection on which the query should be executed
      */
+    @JvmStatic
     fun ensureFieldIndex(tableName: String, indexName: String, fields: Collection<String>, conn: Connection) =
         conn.customNonQuery(Definition.ensureIndexOn(tableName, indexName, fields))
 
@@ -44,8 +50,9 @@ object Definition {
      *
      * @param tableName The table to be indexed (may include schema)
      * @param indexName The name of the index to create
-     * @param fields One or more fields to be indexed<
+     * @param fields One or more fields to be indexed
      */
+    @JvmStatic
     fun ensureFieldIndex(tableName: String, indexName: String, fields: Collection<String>) =
         Configuration.dbConn().use { ensureFieldIndex(tableName, indexName, fields, it) }
 
@@ -57,6 +64,8 @@ object Definition {
      * @param conn The connection on which the query should be executed
      * @throws DocumentException If called on a SQLite connection
      */
+    @Throws(DocumentException::class)
+    @JvmStatic
     fun ensureDocumentIndex(tableName: String, indexType: DocumentIndex, conn: Connection) =
         conn.customNonQuery(Definition.ensureDocumentIndexOn(tableName, indexType))
 
@@ -67,6 +76,8 @@ object Definition {
      * @param indexType The type of index to ensure
      * @throws DocumentException If called on a SQLite connection
      */
+    @Throws(DocumentException::class)
+    @JvmStatic
     fun ensureDocumentIndex(tableName: String, indexType: DocumentIndex) =
         Configuration.dbConn().use { ensureDocumentIndex(tableName, indexType, it) }
 }
diff --git a/src/java/src/main/kotlin/Delete.kt b/src/java/src/main/kotlin/Delete.kt
index 6f9bd52..0587fb7 100644
--- a/src/java/src/main/kotlin/Delete.kt
+++ b/src/java/src/main/kotlin/Delete.kt
@@ -1,9 +1,7 @@
 package solutions.bitbadger.documents.java
 
-import solutions.bitbadger.documents.common.Field
-import solutions.bitbadger.documents.common.FieldMatch
-import solutions.bitbadger.documents.common.Parameter
-import solutions.bitbadger.documents.common.ParameterType
+import solutions.bitbadger.documents.common.*
+import solutions.bitbadger.documents.common.query.Delete
 import java.sql.Connection
 
 /**
@@ -18,9 +16,10 @@ object Delete {
      * @param docId The ID of the document to be deleted
      * @param conn The connection on which the deletion should be executed
      */
+    @JvmStatic
     fun <TKey> byId(tableName: String, docId: TKey, conn: Connection) =
         conn.customNonQuery(
-            byId(tableName, docId),
+            Delete.byId(tableName, docId),
             Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id")))
         )
 
@@ -30,6 +29,7 @@ object Delete {
      * @param tableName The name of the table from which documents should be deleted
      * @param docId The ID of the document to be deleted
      */
+    @JvmStatic
     fun <TKey> byId(tableName: String, docId: TKey) =
         Configuration.dbConn().use { byId(tableName, docId, it) }
 
@@ -41,9 +41,11 @@ object Delete {
      * @param howMatched How the fields should be matched
      * @param conn The connection on which the deletion should be executed
      */
+    @JvmStatic
+    @JvmOverloads
     fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null, conn: Connection) {
         val named = Parameters.nameFields(fields)
-        conn.customNonQuery(byFields(tableName, named, howMatched), Parameters.addFields(named))
+        conn.customNonQuery(Delete.byFields(tableName, named, howMatched), Parameters.addFields(named))
     }
 
     /**
@@ -53,6 +55,8 @@ object Delete {
      * @param fields The fields which should be compared
      * @param howMatched How the fields should be matched
      */
+    @JvmStatic
+    @JvmOverloads
     fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
         Configuration.dbConn().use { byFields(tableName, fields, howMatched, it) }
 
@@ -64,7 +68,9 @@ object Delete {
      * @param conn The connection on which the deletion should be executed
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
         conn.customNonQuery(Delete.byContains(tableName), listOf(Parameters.json(":criteria", criteria)))
 
     /**
@@ -74,7 +80,9 @@ object Delete {
      * @param criteria The object for which JSON containment should be checked
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TContains> byContains(tableName: String, criteria: TContains) =
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TContains> byContains(tableName: String, criteria: TContains) =
         Configuration.dbConn().use { byContains(tableName, criteria, it) }
 
     /**
@@ -85,6 +93,8 @@ object Delete {
      * @param conn The connection on which the deletion should be executed
      * @throws DocumentException If called on a SQLite connection
      */
+    @Throws(DocumentException::class)
+    @JvmStatic
     fun byJsonPath(tableName: String, path: String, conn: Connection) =
         conn.customNonQuery(Delete.byJsonPath(tableName), listOf(Parameter(":path", ParameterType.STRING, path)))
 
@@ -95,6 +105,8 @@ object Delete {
      * @param path The JSON path comparison to match
      * @throws DocumentException If called on a SQLite connection
      */
+    @Throws(DocumentException::class)
+    @JvmStatic
     fun byJsonPath(tableName: String, path: String) =
         Configuration.dbConn().use { byJsonPath(tableName, path, it) }
 }
diff --git a/src/java/src/main/kotlin/Exists.kt b/src/java/src/main/kotlin/Exists.kt
index 5f9deda..7e55f89 100644
--- a/src/java/src/main/kotlin/Exists.kt
+++ b/src/java/src/main/kotlin/Exists.kt
@@ -1,10 +1,7 @@
 package solutions.bitbadger.documents.java
 
-import solutions.bitbadger.documents.Results
-import solutions.bitbadger.documents.common.Field
-import solutions.bitbadger.documents.common.FieldMatch
-import solutions.bitbadger.documents.common.Parameter
-import solutions.bitbadger.documents.common.ParameterType
+import solutions.bitbadger.documents.common.*
+import solutions.bitbadger.documents.common.query.Exists
 import java.sql.Connection
 
 /**
@@ -20,10 +17,12 @@ object Exists {
      * @param conn The connection on which the existence check should be executed
      * @return True if the document exists, false if not
      */
+    @JvmStatic
     fun <TKey> byId(tableName: String, docId: TKey, conn: Connection) =
         conn.customScalar(
-            byId(tableName, docId),
+            Exists.byId(tableName, docId),
             Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))),
+            Boolean::class.java,
             Results::toExists
         )
 
@@ -34,6 +33,7 @@ object Exists {
      * @param docId The ID of the document to be checked
      * @return True if the document exists, false if not
      */
+    @JvmStatic
     fun <TKey> byId(tableName: String, docId: TKey) =
         Configuration.dbConn().use { byId(tableName, docId, it) }
 
@@ -46,6 +46,8 @@ object Exists {
      * @param conn The connection on which the existence check should be executed
      * @return True if any matching documents exist, false if not
      */
+    @JvmStatic
+    @JvmOverloads
     fun byFields(
         tableName: String,
         fields: Collection<Field<*>>,
@@ -54,8 +56,9 @@ object Exists {
     ): Boolean {
         val named = Parameters.nameFields(fields)
         return conn.customScalar(
-            byFields(tableName, named, howMatched),
+            Exists.byFields(tableName, named, howMatched),
             Parameters.addFields(named),
+            Boolean::class.java,
             Results::toExists
         )
     }
@@ -68,6 +71,8 @@ object Exists {
      * @param howMatched How the fields should be matched
      * @return True if any matching documents exist, false if not
      */
+    @JvmStatic
+    @JvmOverloads
     fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
         Configuration.dbConn().use { byFields(tableName, fields, howMatched, it) }
 
@@ -80,10 +85,13 @@ object Exists {
      * @return True if any matching documents exist, false if not
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
         conn.customScalar(
             Exists.byContains(tableName),
             listOf(Parameters.json(":criteria", criteria)),
+            Boolean::class.java,
             Results::toExists
         )
 
@@ -95,7 +103,9 @@ object Exists {
      * @return True if any matching documents exist, false if not
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TContains> byContains(tableName: String, criteria: TContains) =
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TContains> byContains(tableName: String, criteria: TContains) =
         Configuration.dbConn().use { byContains(tableName, criteria, it) }
 
     /**
@@ -107,10 +117,13 @@ object Exists {
      * @return True if any matching documents exist, false if not
      * @throws DocumentException If called on a SQLite connection
      */
+    @Throws(DocumentException::class)
+    @JvmStatic
     fun byJsonPath(tableName: String, path: String, conn: Connection) =
         conn.customScalar(
             Exists.byJsonPath(tableName),
             listOf(Parameter(":path", ParameterType.STRING, path)),
+            Boolean::class.java,
             Results::toExists
         )
 
@@ -122,6 +135,8 @@ object Exists {
      * @return True if any matching documents exist, false if not
      * @throws DocumentException If called on a SQLite connection
      */
+    @Throws(DocumentException::class)
+    @JvmStatic
     fun byJsonPath(tableName: String, path: String) =
         Configuration.dbConn().use { byJsonPath(tableName, path, it) }
 }
diff --git a/src/java/src/main/kotlin/Find.kt b/src/java/src/main/kotlin/Find.kt
index 87f74a5..70cef9c 100644
--- a/src/java/src/main/kotlin/Find.kt
+++ b/src/java/src/main/kotlin/Find.kt
@@ -1,12 +1,9 @@
 package solutions.bitbadger.documents.java
 
-import solutions.bitbadger.documents.Results
-import solutions.bitbadger.documents.common.Field
-import solutions.bitbadger.documents.common.FieldMatch
-import solutions.bitbadger.documents.common.Parameter
-import solutions.bitbadger.documents.common.ParameterType
-import java.sql.Connection
+import solutions.bitbadger.documents.common.*
+import solutions.bitbadger.documents.common.query.Find
 import solutions.bitbadger.documents.common.query.orderBy
+import java.sql.Connection
 
 /**
  * Functions to find and retrieve documents
@@ -17,45 +14,55 @@ object Find {
      * Retrieve all documents in the given table, ordering results by the optional given fields
      *
      * @param tableName The table from which documents should be retrieved
+     * @param clazz The class of the document to be returned
      * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
      * @param conn The connection over which documents should be retrieved
      * @return A list of documents from the given table
      */
-    inline fun <reified TDoc> all(tableName: String, orderBy: Collection<Field<*>>? = null, conn: Connection) =
-        conn.customList<TDoc>(all(tableName) + (orderBy?.let(::orderBy) ?: ""), mapFunc = Results::fromData)
+    @JvmStatic
+    fun <TDoc> all(tableName: String, clazz: Class<TDoc>, orderBy: Collection<Field<*>>? = null, conn: Connection) =
+        conn.customList(Find.all(tableName) + (orderBy?.let(::orderBy) ?: ""), listOf(), clazz, Results::fromData)
 
     /**
      * Retrieve all documents in the given table
      *
      * @param tableName The table from which documents should be retrieved
+     * @param clazz The class of the document to be returned
      * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
      * @return A list of documents from the given table
      */
-    inline fun <reified TDoc> all(tableName: String, orderBy: Collection<Field<*>>? = null) =
-        Configuration.dbConn().use { all<TDoc>(tableName, orderBy, it) }
+    @JvmStatic
+    @JvmOverloads
+    fun <TDoc> all(tableName: String, clazz: Class<TDoc>, orderBy: Collection<Field<*>>? = null) =
+        Configuration.dbConn().use { all(tableName, clazz, orderBy, it) }
 
     /**
      * Retrieve all documents in the given table
      *
      * @param tableName The table from which documents should be retrieved
+     * @param clazz The class of the document to be returned
      * @param conn The connection over which documents should be retrieved
      * @return A list of documents from the given table
      */
-    inline fun <reified TDoc> all(tableName: String, conn: Connection) =
-        all<TDoc>(tableName, null, conn)
+    @JvmStatic
+    fun <TDoc> all(tableName: String, clazz: Class<TDoc>, conn: Connection) =
+        all(tableName, clazz, null, conn)
 
     /**
      * Retrieve a document by its ID
      *
      * @param tableName The table from which the document should be retrieved
      * @param docId The ID of the document to retrieve
+     * @param clazz The class of the document to be returned
      * @param conn The connection over which documents should be retrieved
      * @return The document if it is found, `null` otherwise
      */
-    inline fun <TKey, reified TDoc> byId(tableName: String, docId: TKey, conn: Connection) =
-        conn.customSingle<TDoc>(
-            byId(tableName, docId),
+    @JvmStatic
+    fun <TKey, TDoc> byId(tableName: String, docId: TKey, clazz: Class<TDoc>, conn: Connection) =
+        conn.customSingle(
+            Find.byId(tableName, docId),
             Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))),
+            clazz,
             Results::fromData
         )
 
@@ -64,32 +71,38 @@ object Find {
      *
      * @param tableName The table from which the document should be retrieved
      * @param docId The ID of the document to retrieve
+     * @param clazz The class of the document to be returned
      * @return The document if it is found, `null` otherwise
      */
-    inline fun <TKey, reified TDoc> byId(tableName: String, docId: TKey) =
-        Configuration.dbConn().use { byId<TKey, TDoc>(tableName, docId, it) }
+    @JvmStatic
+    fun <TKey, TDoc> byId(tableName: String, docId: TKey, clazz: Class<TDoc>) =
+        Configuration.dbConn().use { byId(tableName, docId, clazz, it) }
 
     /**
      * Retrieve documents using a field comparison, ordering results by the given fields
      *
      * @param tableName The table from which documents should be retrieved
      * @param fields The fields which should be compared
+     * @param clazz The class of the document to be returned
      * @param howMatched How the fields should be matched
      * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
      * @param conn The connection over which documents should be retrieved
      * @return A list of documents matching the field comparison
      */
-    inline fun <reified TDoc> byFields(
+    @JvmStatic
+    fun <TDoc> byFields(
         tableName: String,
         fields: Collection<Field<*>>,
+        clazz: Class<TDoc>,
         howMatched: FieldMatch? = null,
         orderBy: Collection<Field<*>>? = null,
         conn: Connection
     ): List<TDoc> {
         val named = Parameters.nameFields(fields)
-        return conn.customList<TDoc>(
-            byFields(tableName, named, howMatched) + (orderBy?.let(::orderBy) ?: ""),
+        return conn.customList(
+            Find.byFields(tableName, named, howMatched) + (orderBy?.let(::orderBy) ?: ""),
             Parameters.addFields(named),
+            clazz,
             Results::fromData
         )
     }
@@ -99,69 +112,66 @@ object Find {
      *
      * @param tableName The table from which documents should be retrieved
      * @param fields The fields which should be compared
+     * @param clazz The class of the document to be returned
      * @param howMatched How the fields should be matched
      * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
      * @return A list of documents matching the field comparison
      */
-    inline fun <reified TDoc> byFields(
+    @JvmStatic
+    @JvmOverloads
+    fun <TDoc> byFields(
         tableName: String,
         fields: Collection<Field<*>>,
+        clazz: Class<TDoc>,
         howMatched: FieldMatch? = null,
         orderBy: Collection<Field<*>>? = null
     ) =
-        Configuration.dbConn().use { byFields<TDoc>(tableName, fields, howMatched, orderBy, it) }
+        Configuration.dbConn().use { byFields(tableName, fields, clazz, howMatched, orderBy, it) }
 
     /**
      * Retrieve documents using a field comparison
      *
      * @param tableName The table from which documents should be retrieved
      * @param fields The fields which should be compared
+     * @param clazz The class of the document to be returned
      * @param howMatched How the fields should be matched
      * @param conn The connection over which documents should be retrieved
      * @return A list of documents matching the field comparison
      */
-    inline fun <reified TDoc> byFields(
+    @JvmStatic
+    fun <TDoc> byFields(
         tableName: String,
         fields: Collection<Field<*>>,
+        clazz: Class<TDoc>,
         howMatched: FieldMatch? = null,
         conn: Connection
     ) =
-        byFields<TDoc>(tableName, fields, howMatched, null, conn)
-
-    /**
-     * Retrieve documents using a field comparison
-     *
-     * @param tableName The table from which documents should be retrieved
-     * @param fields The fields which should be compared
-     * @param howMatched How the fields should be matched
-     * @return A list of documents matching the field comparison
-     */
-    inline fun <reified TDoc> byFields(
-        tableName: String,
-        fields: Collection<Field<*>>,
-        howMatched: FieldMatch? = null
-    ) =
-        Configuration.dbConn().use { byFields<TDoc>(tableName, fields, howMatched, null, it) }
+        byFields(tableName, fields, clazz, howMatched, null, conn)
 
     /**
      * Retrieve documents using a JSON containment query, ordering results by the given fields (PostgreSQL only)
      *
      * @param tableName The table from which documents should be retrieved
      * @param criteria The object for which JSON containment should be checked
+     * @param clazz The class of the document to be returned
      * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
      * @param conn The connection over which documents should be retrieved
      * @return A list of documents matching the JSON containment query
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TDoc, reified TContains> byContains(
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TDoc, TContains> byContains(
         tableName: String,
         criteria: TContains,
+        clazz: Class<TDoc>,
         orderBy: Collection<Field<*>>? = null,
         conn: Connection
     ) =
-        conn.customList<TDoc>(
+        conn.customList(
             Find.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""),
             listOf(Parameters.json(":criteria", criteria)),
+            clazz,
             Results::fromData
         )
 
@@ -170,59 +180,61 @@ object Find {
      *
      * @param tableName The table from which documents should be retrieved
      * @param criteria The object for which JSON containment should be checked
+     * @param clazz The class of the document to be returned
      * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
      * @return A list of documents matching the JSON containment query
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TDoc, reified TContains> byContains(
+    @Throws(DocumentException::class)
+    @JvmStatic
+    @JvmOverloads
+    fun <TDoc, TContains> byContains(
         tableName: String,
         criteria: TContains,
+        clazz: Class<TDoc>,
         orderBy: Collection<Field<*>>? = null
     ) =
-        Configuration.dbConn().use { byContains<TDoc, TContains>(tableName, criteria, orderBy, it) }
+        Configuration.dbConn().use { byContains(tableName, criteria, clazz, orderBy, it) }
 
     /**
      * Retrieve documents using a JSON containment query (PostgreSQL only)
      *
      * @param tableName The table from which documents should be retrieved
      * @param criteria The object for which JSON containment should be checked
+     * @param clazz The class of the document to be returned
      * @param conn The connection over which documents should be retrieved
      * @return A list of documents matching the JSON containment query
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TDoc, reified TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
-        byContains<TDoc, TContains>(tableName, criteria, null, conn)
-
-    /**
-     * Retrieve documents using a JSON containment query (PostgreSQL only)
-     *
-     * @param tableName The table from which documents should be retrieved
-     * @param criteria The object for which JSON containment should be checked
-     * @return A list of documents matching the JSON containment query
-     * @throws DocumentException If called on a SQLite connection
-     */
-    inline fun <reified TDoc, reified TContains> byContains(tableName: String, criteria: TContains) =
-        Configuration.dbConn().use { byContains<TDoc, TContains>(tableName, criteria, it) }
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TDoc, TContains> byContains(tableName: String, criteria: TContains, clazz: Class<TDoc>, conn: Connection) =
+        byContains(tableName, criteria, clazz, null, conn)
 
     /**
      * Retrieve documents using a JSON Path match query, ordering results by the given fields (PostgreSQL only)
      *
      * @param tableName The table from which documents should be retrieved
      * @param path The JSON path comparison to match
+     * @param clazz The class of the document to be returned
      * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
      * @param conn The connection over which documents should be retrieved
      * @return A list of documents matching the JSON Path match query
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TDoc> byJsonPath(
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TDoc> byJsonPath(
         tableName: String,
         path: String,
+        clazz: Class<TDoc>,
         orderBy: Collection<Field<*>>? = null,
         conn: Connection
     ) =
-        conn.customList<TDoc>(
-            byJsonPath(tableName) + (orderBy?.let(::orderBy) ?: ""),
+        conn.customList(
+            Find.byJsonPath(tableName) + (orderBy?.let(::orderBy) ?: ""),
             listOf(Parameter(":path", ParameterType.STRING, path)),
+            clazz,
             Results::fromData
         )
 
@@ -231,46 +243,57 @@ object Find {
      *
      * @param tableName The table from which documents should be retrieved
      * @param path The JSON path comparison to match
+     * @param clazz The class of the document to be returned
      * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
      * @return A list of documents matching the JSON Path match query
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TDoc> byJsonPath(tableName: String, path: String, orderBy: Collection<Field<*>>? = null) =
-        Configuration.dbConn().use { byJsonPath<TDoc>(tableName, path, orderBy, it) }
+    @Throws(DocumentException::class)
+    @JvmStatic
+    @JvmOverloads
+    fun <TDoc> byJsonPath(tableName: String, path: String, clazz: Class<TDoc>, orderBy: Collection<Field<*>>? = null) =
+        Configuration.dbConn().use { byJsonPath(tableName, path, clazz, orderBy, it) }
 
     /**
      * Retrieve documents using a JSON Path match query (PostgreSQL only)
      *
      * @param tableName The table from which documents should be retrieved
      * @param path The JSON path comparison to match
+     * @param clazz The class of the document to be returned
      * @param conn The connection over which documents should be retrieved
      * @return A list of documents matching the JSON Path match query
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TDoc> byJsonPath(tableName: String, path: String, conn: Connection) =
-        byJsonPath<TDoc>(tableName, path, null, conn)
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TDoc> byJsonPath(tableName: String, path: String, clazz: Class<TDoc>, conn: Connection) =
+        byJsonPath(tableName, path, clazz, null, conn)
 
     /**
      * Retrieve the first document using a field comparison and optional ordering fields
      *
      * @param tableName The table from which documents should be retrieved
      * @param fields The fields which should be compared
+     * @param clazz The class of the document to be returned
      * @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
      * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
      * @param conn The connection over which documents should be retrieved
      * @return The first document matching the field comparison, or `null` if no matches are found
      */
-    inline fun <reified TDoc> firstByFields(
+    @JvmStatic
+    fun <TDoc> firstByFields(
         tableName: String,
         fields: Collection<Field<*>>,
+        clazz: Class<TDoc>,
         howMatched: FieldMatch? = null,
         orderBy: Collection<Field<*>>? = null,
         conn: Connection
     ): TDoc? {
         val named = Parameters.nameFields(fields)
-        return conn.customSingle<TDoc>(
-            byFields(tableName, named, howMatched) + (orderBy?.let(::orderBy) ?: ""),
+        return conn.customSingle(
+            Find.byFields(tableName, named, howMatched) + (orderBy?.let(::orderBy) ?: ""),
             Parameters.addFields(named),
+            clazz,
             Results::fromData
         )
     }
@@ -280,34 +303,41 @@ object Find {
      *
      * @param tableName The table from which documents should be retrieved
      * @param fields The fields which should be compared
+     * @param clazz The class of the document to be returned
      * @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
      * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
      * @return The first document matching the field comparison, or `null` if no matches are found
      */
-    inline fun <reified TDoc> firstByFields(
+    @JvmStatic
+    @JvmOverloads
+    fun <TDoc> firstByFields(
         tableName: String,
         fields: Collection<Field<*>>,
+        clazz: Class<TDoc>,
         howMatched: FieldMatch? = null,
         orderBy: Collection<Field<*>>? = null
     ) =
-        Configuration.dbConn().use { firstByFields<TDoc>(tableName, fields, howMatched, orderBy, it) }
+        Configuration.dbConn().use { firstByFields(tableName, fields, clazz, howMatched, orderBy, it) }
 
     /**
      * Retrieve the first document using a field comparison
      *
      * @param tableName The table from which documents should be retrieved
      * @param fields The fields which should be compared
+     * @param clazz The class of the document to be returned
      * @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
      * @param conn The connection over which documents should be retrieved
      * @return The first document matching the field comparison, or `null` if no matches are found
      */
-    inline fun <reified TDoc> firstByFields(
+    @JvmStatic
+    fun <TDoc> firstByFields(
         tableName: String,
         fields: Collection<Field<*>>,
+        clazz: Class<TDoc>,
         howMatched: FieldMatch? = null,
         conn: Connection
     ) =
-        firstByFields<TDoc>(tableName, fields, howMatched, null, conn)
+        firstByFields(tableName, fields, clazz, howMatched, null, conn)
 
     /**
      * Retrieve the first document using a JSON containment query and optional ordering fields (PostgreSQL only)
@@ -319,15 +349,19 @@ object Find {
      * @return The first document matching the JSON containment query, or `null` if no matches are found
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TDoc, reified TContains> firstByContains(
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TDoc, TContains> firstByContains(
         tableName: String,
         criteria: TContains,
+        clazz: Class<TDoc>,
         orderBy: Collection<Field<*>>? = null,
         conn: Connection
     ) =
-        conn.customSingle<TDoc>(
+        conn.customSingle(
             Find.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""),
             listOf(Parameters.json(":criteria", criteria)),
+            clazz,
             Results::fromData
         )
 
@@ -336,44 +370,66 @@ object Find {
      *
      * @param tableName The table from which documents should be retrieved
      * @param criteria The object for which JSON containment should be checked
+     * @param clazz The class of the document to be returned
      * @param conn The connection over which documents should be retrieved
      * @return The first document matching the JSON containment query, or `null` if no matches are found
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TDoc, reified TContains> firstByContains(tableName: String, criteria: TContains, conn: Connection) =
-        firstByContains<TDoc, TContains>(tableName, criteria, null, conn)
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TDoc, TContains> firstByContains(
+        tableName: String,
+        criteria: TContains,
+        clazz: Class<TDoc>,
+        conn: Connection
+    ) =
+        firstByContains(tableName, criteria, clazz, null, conn)
 
     /**
      * Retrieve the first document using a JSON containment query and optional ordering fields (PostgreSQL only)
      *
      * @param tableName The table from which documents should be retrieved
      * @param criteria The object for which JSON containment should be checked
+     * @param clazz The class of the document to be returned
      * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
      * @return The first document matching the JSON containment query, or `null` if no matches are found
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TDoc, reified TContains> firstByContains(tableName: String, criteria: TContains, orderBy: Collection<Field<*>>? = null) =
-        Configuration.dbConn().use { firstByContains<TDoc, TContains>(tableName, criteria, orderBy, it) }
+    @Throws(DocumentException::class)
+    @JvmStatic
+    @JvmOverloads
+    fun <TDoc, TContains> firstByContains(
+        tableName: String,
+        criteria: TContains,
+        clazz: Class<TDoc>,
+        orderBy: Collection<Field<*>>? = null
+    ) =
+        Configuration.dbConn().use { firstByContains(tableName, criteria, clazz, orderBy, it) }
 
     /**
      * Retrieve the first document using a JSON Path match query and optional ordering fields (PostgreSQL only)
      *
      * @param tableName The table from which documents should be retrieved
      * @param path The JSON path comparison to match
+     * @param clazz The class of the document to be returned
      * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
      * @param conn The connection over which documents should be retrieved
      * @return The first document matching the JSON Path match query, or `null` if no matches are found
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TDoc> firstByJsonPath(
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TDoc> firstByJsonPath(
         tableName: String,
         path: String,
+        clazz: Class<TDoc>,
         orderBy: Collection<Field<*>>? = null,
         conn: Connection
     ) =
-        conn.customSingle<TDoc>(
-            byJsonPath(tableName) + (orderBy?.let(::orderBy) ?: ""),
+        conn.customSingle(
+            Find.byJsonPath(tableName) + (orderBy?.let(::orderBy) ?: ""),
             listOf(Parameter(":path", ParameterType.STRING, path)),
+            clazz,
             Results::fromData
         )
 
@@ -382,22 +438,34 @@ object Find {
      *
      * @param tableName The table from which documents should be retrieved
      * @param path The JSON path comparison to match
+     * @param clazz The class of the document to be returned
      * @param conn The connection over which documents should be retrieved
      * @return The first document matching the JSON Path match query, or `null` if no matches are found
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TDoc> firstByJsonPath(tableName: String, path: String, conn: Connection) =
-        firstByJsonPath<TDoc>(tableName, path, null, conn)
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TDoc> firstByJsonPath(tableName: String, path: String, clazz: Class<TDoc>, conn: Connection) =
+        firstByJsonPath(tableName, path, clazz, null, conn)
 
     /**
      * Retrieve the first document using a JSON Path match query and optional ordering fields (PostgreSQL only)
      *
      * @param tableName The table from which documents should be retrieved
      * @param path The JSON path comparison to match
+     * @param clazz The class of the document to be returned
      * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
      * @return The first document matching the JSON Path match query, or `null` if no matches are found
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TDoc> firstByJsonPath(tableName: String, path: String, orderBy: Collection<Field<*>>? = null) =
-        Configuration.dbConn().use { firstByJsonPath<TDoc>(tableName, path, orderBy, it) }
+    @Throws(DocumentException::class)
+    @JvmStatic
+    @JvmOverloads
+    fun <TDoc> firstByJsonPath(
+        tableName: String,
+        path: String,
+        clazz: Class<TDoc>,
+        orderBy: Collection<Field<*>>? = null
+    ) =
+        Configuration.dbConn().use { firstByJsonPath(tableName, path, clazz, orderBy, it) }
 }
diff --git a/src/java/src/main/kotlin/Patch.kt b/src/java/src/main/kotlin/Patch.kt
index 7b48698..a59fc2f 100644
--- a/src/java/src/main/kotlin/Patch.kt
+++ b/src/java/src/main/kotlin/Patch.kt
@@ -1,9 +1,7 @@
 package solutions.bitbadger.documents.java
 
-import solutions.bitbadger.documents.common.Field
-import solutions.bitbadger.documents.common.FieldMatch
-import solutions.bitbadger.documents.common.Parameter
-import solutions.bitbadger.documents.common.ParameterType
+import solutions.bitbadger.documents.common.*
+import solutions.bitbadger.documents.common.query.Patch
 import java.sql.Connection
 
 /**
@@ -19,7 +17,8 @@ object Patch {
      * @param patch The object whose properties should be replaced in the document
      * @param conn The connection on which the update should be executed
      */
-    inline fun <TKey, reified TPatch> byId(tableName: String, docId: TKey, patch: TPatch, conn: Connection) =
+    @JvmStatic
+    fun <TKey, TPatch> byId(tableName: String, docId: TKey, patch: TPatch, conn: Connection) =
         conn.customNonQuery(
             Patch.byId(tableName, docId),
             Parameters.addFields(
@@ -35,7 +34,8 @@ object Patch {
      * @param docId The ID of the document to be patched
      * @param patch The object whose properties should be replaced in the document
      */
-    inline fun <TKey, reified TPatch> byId(tableName: String, docId: TKey, patch: TPatch) =
+    @JvmStatic
+    fun <TKey, TPatch> byId(tableName: String, docId: TKey, patch: TPatch) =
         Configuration.dbConn().use { byId(tableName, docId, patch, it) }
 
     /**
@@ -47,7 +47,8 @@ object Patch {
      * @param howMatched How the fields should be matched
      * @param conn The connection on which the update should be executed
      */
-    inline fun <reified TPatch> byFields(
+    @JvmStatic
+    fun <TPatch> byFields(
         tableName: String,
         fields: Collection<Field<*>>,
         patch: TPatch,
@@ -56,7 +57,7 @@ object Patch {
     ) {
         val named = Parameters.nameFields(fields)
         conn.customNonQuery(
-            byFields(tableName, named, howMatched), Parameters.addFields(
+            Patch.byFields(tableName, named, howMatched), Parameters.addFields(
                 named,
                 mutableListOf(Parameters.json(":data", patch))
             )
@@ -71,7 +72,9 @@ object Patch {
      * @param patch The object whose properties should be replaced in the document
      * @param howMatched How the fields should be matched
      */
-    inline fun <reified TPatch> byFields(
+    @JvmStatic
+    @JvmOverloads
+    fun <TPatch> byFields(
         tableName: String,
         fields: Collection<Field<*>>,
         patch: TPatch,
@@ -88,12 +91,9 @@ object Patch {
      * @param conn The connection on which the update should be executed
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TContains, reified TPatch> byContains(
-        tableName: String,
-        criteria: TContains,
-        patch: TPatch,
-        conn: Connection
-    ) =
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TContains, TPatch> byContains(tableName: String, criteria: TContains, patch: TPatch, conn: Connection) =
         conn.customNonQuery(
             Patch.byContains(tableName),
             listOf(Parameters.json(":criteria", criteria), Parameters.json(":data", patch))
@@ -107,7 +107,9 @@ object Patch {
      * @param patch The object whose properties should be replaced in the document
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TContains, reified TPatch> byContains(tableName: String, criteria: TContains, patch: TPatch) =
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TContains, TPatch> byContains(tableName: String, criteria: TContains, patch: TPatch) =
         Configuration.dbConn().use { byContains(tableName, criteria, patch, it) }
 
     /**
@@ -119,7 +121,9 @@ object Patch {
      * @param conn The connection on which the update should be executed
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TPatch> byJsonPath(tableName: String, path: String, patch: TPatch, conn: Connection) =
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TPatch> byJsonPath(tableName: String, path: String, patch: TPatch, conn: Connection) =
         conn.customNonQuery(
             Patch.byJsonPath(tableName),
             listOf(Parameter(":path", ParameterType.STRING, path), Parameters.json(":data", patch))
@@ -133,6 +137,8 @@ object Patch {
      * @param patch The object whose properties should be replaced in the document
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TPatch> byJsonPath(tableName: String, path: String, patch: TPatch) =
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TPatch> byJsonPath(tableName: String, path: String, patch: TPatch) =
         Configuration.dbConn().use { byJsonPath(tableName, path, patch, it) }
 }
diff --git a/src/java/src/main/kotlin/RemoveFields.kt b/src/java/src/main/kotlin/RemoveFields.kt
index 0bb10f0..3a52eba 100644
--- a/src/java/src/main/kotlin/RemoveFields.kt
+++ b/src/java/src/main/kotlin/RemoveFields.kt
@@ -1,6 +1,7 @@
 package solutions.bitbadger.documents.java
 
 import solutions.bitbadger.documents.common.*
+import solutions.bitbadger.documents.common.query.RemoveFields
 import java.sql.Connection
 
 /**
@@ -18,7 +19,7 @@ object RemoveFields {
         val dialect = Configuration.dialect("remove fields")
         return when (dialect) {
             Dialect.POSTGRESQL -> parameters
-            Dialect.SQLITE     -> parameters.map { Parameter(it.name, it.type, "$.${it.value}") }.toMutableList()
+            Dialect.SQLITE -> parameters.map { Parameter(it.name, it.type, "$.${it.value}") }.toMutableList()
         }
     }
 
@@ -30,10 +31,11 @@ object RemoveFields {
      * @param toRemove The names of the fields to be removed
      * @param conn The connection on which the update should be executed
      */
+    @JvmStatic
     fun <TKey> byId(tableName: String, docId: TKey, toRemove: Collection<String>, conn: Connection) {
         val nameParams = Parameters.fieldNames(toRemove)
         conn.customNonQuery(
-            byId(tableName, nameParams, docId),
+            RemoveFields.byId(tableName, nameParams, docId),
             Parameters.addFields(
                 listOf(Field.equal(Configuration.idField, docId, ":id")),
                 translatePath(nameParams)
@@ -48,6 +50,7 @@ object RemoveFields {
      * @param docId The ID of the document to have fields removed
      * @param toRemove The names of the fields to be removed
      */
+    @JvmStatic
     fun <TKey> byId(tableName: String, docId: TKey, toRemove: Collection<String>) =
         Configuration.dbConn().use { byId(tableName, docId, toRemove, it) }
 
@@ -60,6 +63,7 @@ object RemoveFields {
      * @param howMatched How the fields should be matched
      * @param conn The connection on which the update should be executed
      */
+    @JvmStatic
     fun byFields(
         tableName: String,
         fields: Collection<Field<*>>,
@@ -70,7 +74,7 @@ object RemoveFields {
         val named = Parameters.nameFields(fields)
         val nameParams = Parameters.fieldNames(toRemove)
         conn.customNonQuery(
-            byFields(tableName, nameParams, named, howMatched),
+            RemoveFields.byFields(tableName, nameParams, named, howMatched),
             Parameters.addFields(named, translatePath(nameParams))
         )
     }
@@ -83,6 +87,8 @@ object RemoveFields {
      * @param toRemove The names of the fields to be removed
      * @param howMatched How the fields should be matched
      */
+    @JvmStatic
+    @JvmOverloads
     fun byFields(
         tableName: String,
         fields: Collection<Field<*>>,
@@ -100,7 +106,9 @@ object RemoveFields {
      * @param conn The connection on which the update should be executed
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TContains> byContains(
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TContains> byContains(
         tableName: String,
         criteria: TContains,
         toRemove: Collection<String>,
@@ -121,7 +129,9 @@ object RemoveFields {
      * @param toRemove The names of the fields to be removed
      * @throws DocumentException If called on a SQLite connection
      */
-    inline fun <reified TContains> byContains(tableName: String, criteria: TContains, toRemove: Collection<String>) =
+    @Throws(DocumentException::class)
+    @JvmStatic
+    fun <TContains> byContains(tableName: String, criteria: TContains, toRemove: Collection<String>) =
         Configuration.dbConn().use { byContains(tableName, criteria, toRemove, it) }
 
     /**
@@ -133,6 +143,8 @@ object RemoveFields {
      * @param conn The connection on which the update should be executed
      * @throws DocumentException If called on a SQLite connection
      */
+    @Throws(DocumentException::class)
+    @JvmStatic
     fun byJsonPath(tableName: String, path: String, toRemove: Collection<String>, conn: Connection) {
         val nameParams = Parameters.fieldNames(toRemove)
         conn.customNonQuery(
@@ -149,6 +161,8 @@ object RemoveFields {
      * @param toRemove The names of the fields to be removed
      * @throws DocumentException If called on a SQLite connection
      */
+    @Throws(DocumentException::class)
+    @JvmStatic
     fun byJsonPath(tableName: String, path: String, toRemove: Collection<String>) =
         Configuration.dbConn().use { byJsonPath(tableName, path, toRemove, it) }
 }
diff --git a/src/java/src/main/kotlin/Results.kt b/src/java/src/main/kotlin/Results.kt
index 6438eaa..028007c 100644
--- a/src/java/src/main/kotlin/Results.kt
+++ b/src/java/src/main/kotlin/Results.kt
@@ -63,12 +63,13 @@ object Results {
      * Extract a count from the first column
      *
      * @param rs A `ResultSet` set to the row with the count to retrieve
+     * @param clazz The type parameter (ignored; this always returns `Long`)
      * @return The count from the row
      * @throws DocumentException If the dialect has not been set
      */
     @Throws(DocumentException::class)
     @JvmStatic
-    fun toCount(rs: ResultSet) =
+    fun toCount(rs: ResultSet, clazz: Class<*>) =
         when (Configuration.dialect()) {
             Dialect.POSTGRESQL -> rs.getInt("it").toLong()
             Dialect.SQLITE -> rs.getLong("it")
@@ -78,14 +79,15 @@ object Results {
      * Extract a true/false value from the first column
      *
      * @param rs A `ResultSet` set to the row with the true/false value to retrieve
+     * @param clazz The type parameter (ignored; this always returns `Boolean`)
      * @return The true/false value from the row
      * @throws DocumentException If the dialect has not been set
      */
     @Throws(DocumentException::class)
     @JvmStatic
-    fun toExists(rs: ResultSet) =
+    fun toExists(rs: ResultSet, clazz: Class<*>) =
         when (Configuration.dialect()) {
             Dialect.POSTGRESQL -> rs.getBoolean("it")
-            Dialect.SQLITE -> toCount(rs) > 0L
+            Dialect.SQLITE -> toCount(rs, Long::class.java) > 0L
         }
 }
diff --git a/src/java/src/test/kotlin/integration/common/Custom.kt b/src/java/src/test/kotlin/integration/common/Custom.kt
index 05073d3..a66e4e8 100644
--- a/src/java/src/test/kotlin/integration/common/Custom.kt
+++ b/src/java/src/test/kotlin/integration/common/Custom.kt
@@ -1,9 +1,12 @@
 package solutions.bitbadger.documents.java.integration.common
 
 import solutions.bitbadger.documents.common.*
+import solutions.bitbadger.documents.common.query.Count
+import solutions.bitbadger.documents.common.query.Delete
+import solutions.bitbadger.documents.common.query.Find
+import solutions.bitbadger.documents.java.*
 import solutions.bitbadger.documents.java.integration.JsonDocument
 import solutions.bitbadger.documents.java.integration.TEST_TABLE
-import solutions.bitbadger.documents.java.Results
 import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
 import kotlin.test.assertEquals
 import kotlin.test.assertNotNull
@@ -17,26 +20,26 @@ object Custom {
     fun listEmpty(db: ThrowawayDatabase) {
         JsonDocument.load(db)
         db.conn.deleteByFields(TEST_TABLE, listOf(Field.exists(Configuration.idField)))
-        val result = db.conn.customList<JsonDocument>(Find.all(TEST_TABLE), mapFunc = Results::fromData)
+        val result = db.conn.customList(Find.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData)
         assertEquals(0, result.size, "There should have been no results")
     }
 
     fun listAll(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val result = db.conn.customList<JsonDocument>(Find.all(TEST_TABLE), mapFunc = Results::fromData)
+        val result = db.conn.customList(Find.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData)
         assertEquals(5, result.size, "There should have been 5 results")
     }
 
     fun singleNone(db: ThrowawayDatabase) =
         assertNull(
-            db.conn.customSingle(Find.all(TEST_TABLE), mapFunc = Results::fromData),
+            db.conn.customSingle(Find.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData),
             "There should not have been a document returned"
         )
 
     fun singleOne(db: ThrowawayDatabase) {
         JsonDocument.load(db)
         assertNotNull(
-            db.conn.customSingle<JsonDocument>(Find.all(TEST_TABLE), mapFunc = Results::fromData),
+            db.conn.customSingle(Find.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData),
             "There should not have been a document returned"
         )
     }
@@ -44,12 +47,12 @@ object Custom {
     fun nonQueryChanges(db: ThrowawayDatabase) {
         JsonDocument.load(db)
         assertEquals(
-            5L, db.conn.customScalar(Count.all(TEST_TABLE), mapFunc = Results::toCount),
+            5L, db.conn.customScalar(Count.all(TEST_TABLE), listOf(), Long::class.java, Results::toCount),
             "There should have been 5 documents in the table"
         )
         db.conn.customNonQuery("DELETE FROM $TEST_TABLE")
         assertEquals(
-            0L, db.conn.customScalar(Count.all(TEST_TABLE), mapFunc = Results::toCount),
+            0L, db.conn.customScalar(Count.all(TEST_TABLE), listOf(), Long::class.java, Results::toCount),
             "There should have been no documents in the table"
         )
     }
@@ -57,7 +60,7 @@ object Custom {
     fun nonQueryNoChanges(db: ThrowawayDatabase) {
         JsonDocument.load(db)
         assertEquals(
-            5L, db.conn.customScalar(Count.all(TEST_TABLE), mapFunc = Results::toCount),
+            5L, db.conn.customScalar(Count.all(TEST_TABLE), listOf(), Long::class.java, Results::toCount),
             "There should have been 5 documents in the table"
         )
         db.conn.customNonQuery(
@@ -65,7 +68,7 @@ object Custom {
             listOf(Parameter(":id", ParameterType.STRING, "eighty-two"))
         )
         assertEquals(
-            5L, db.conn.customScalar(Count.all(TEST_TABLE), mapFunc = Results::toCount),
+            5L, db.conn.customScalar(Count.all(TEST_TABLE), listOf(), Long::class.java, Results::toCount),
             "There should still have been 5 documents in the table"
         )
     }
@@ -74,7 +77,12 @@ object Custom {
         JsonDocument.load(db)
         assertEquals(
             3L,
-            db.conn.customScalar("SELECT 3 AS it FROM $TEST_TABLE LIMIT 1", mapFunc = Results::toCount),
+            db.conn.customScalar(
+                "SELECT 3 AS it FROM $TEST_TABLE LIMIT 1",
+                listOf(),
+                Long::class.java,
+                Results::toCount
+            ),
             "The number 3 should have been returned"
         )
     }
diff --git a/src/java/src/test/kotlin/integration/common/Document.kt b/src/java/src/test/kotlin/integration/common/Document.kt
index e698525..2a1eb98 100644
--- a/src/java/src/test/kotlin/integration/common/Document.kt
+++ b/src/java/src/test/kotlin/integration/common/Document.kt
@@ -1,8 +1,6 @@
 package solutions.bitbadger.documents.java.integration.common
 
-import solutions.bitbadger.documents.*
-import solutions.bitbadger.documents.common.Field
-import solutions.bitbadger.documents.integration.*
+import solutions.bitbadger.documents.common.*
 import solutions.bitbadger.documents.java.*
 import solutions.bitbadger.documents.java.integration.*
 import kotlin.test.*
@@ -16,7 +14,7 @@ object Document {
         assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table")
         val doc = JsonDocument("turkey", "", 0, SubDocument("gobble", "gobble"))
         db.conn.insert(TEST_TABLE, doc)
-        val after = db.conn.findAll<JsonDocument>(TEST_TABLE)
+        val after = db.conn.findAll(TEST_TABLE, JsonDocument::class.java)
         assertEquals(1, after.size, "There should be one document in the table")
         assertEquals(doc, after[0], "The document should be what was inserted")
     }
@@ -42,7 +40,7 @@ object Document {
             db.conn.insert(TEST_TABLE, NumIdDocument(77, "three"))
             db.conn.insert(TEST_TABLE, NumIdDocument(0, "four"))
 
-            val after = db.conn.findAll<NumIdDocument>(TEST_TABLE, listOf(Field.named("key")))
+            val after = db.conn.findAll(TEST_TABLE, NumIdDocument::class.java, listOf(Field.named("key")))
             assertEquals(4, after.size, "There should have been 4 documents returned")
             assertEquals(
                 "1|2|77|78", after.joinToString("|") { it.key.toString() },
@@ -61,7 +59,7 @@ object Document {
 
             db.conn.insert(TEST_TABLE, JsonDocument(""))
 
-            val after = db.conn.findAll<JsonDocument>(TEST_TABLE)
+            val after = db.conn.findAll(TEST_TABLE, JsonDocument::class.java)
             assertEquals(1, after.size, "There should have been 1 document returned")
             assertEquals(32, after[0].id.length, "The ID was not generated correctly")
         } finally {
@@ -79,7 +77,7 @@ object Document {
             Configuration.idStringLength = 21
             db.conn.insert(TEST_TABLE, JsonDocument(""))
 
-            val after = db.conn.findAll<JsonDocument>(TEST_TABLE)
+            val after = db.conn.findAll(TEST_TABLE, JsonDocument::class.java)
             assertEquals(2, after.size, "There should have been 2 documents returned")
             assertEquals(16, after[0].id.length, "The first document's ID was not generated correctly")
             assertEquals(21, after[1].id.length, "The second document's ID was not generated correctly")
@@ -92,7 +90,7 @@ object Document {
     fun saveMatch(db: ThrowawayDatabase) {
         JsonDocument.load(db)
         db.conn.save(TEST_TABLE, JsonDocument("two", numValue = 44))
-        val doc = db.conn.findById<String, JsonDocument>(TEST_TABLE, "two")
+        val doc = db.conn.findById(TEST_TABLE, "two", JsonDocument::class.java)
         assertNotNull(doc, "There should have been a document returned")
         assertEquals("two", doc.id, "An incorrect document was returned")
         assertEquals("", doc.value, "The \"value\" field was not updated")
@@ -104,7 +102,7 @@ object Document {
         JsonDocument.load(db)
         db.conn.save(TEST_TABLE, JsonDocument("test", sub = SubDocument("a", "b")))
         assertNotNull(
-            db.conn.findById<String, JsonDocument>(TEST_TABLE, "test"),
+            db.conn.findById(TEST_TABLE, "test", JsonDocument::class.java),
             "The test document should have been saved"
         )
     }
@@ -112,7 +110,7 @@ object Document {
     fun updateMatch(db: ThrowawayDatabase) {
         JsonDocument.load(db)
         db.conn.update(TEST_TABLE, "one", JsonDocument("one", "howdy", 8, SubDocument("y", "z")))
-        val doc = db.conn.findById<String, JsonDocument>(TEST_TABLE, "one")
+        val doc = db.conn.findById(TEST_TABLE, "one", JsonDocument::class.java)
         assertNotNull(doc, "There should have been a document returned")
         assertEquals("one", doc.id, "An incorrect document was returned")
         assertEquals("howdy", doc.value, "The \"value\" field was not updated")
diff --git a/src/java/src/test/kotlin/integration/common/Find.kt b/src/java/src/test/kotlin/integration/common/Find.kt
index d7f124e..3be673c 100644
--- a/src/java/src/test/kotlin/integration/common/Find.kt
+++ b/src/java/src/test/kotlin/integration/common/Find.kt
@@ -1,9 +1,6 @@
 package solutions.bitbadger.documents.java.integration.common
 
-import solutions.bitbadger.documents.*
-import solutions.bitbadger.documents.common.Field
-import solutions.bitbadger.documents.common.FieldMatch
-import solutions.bitbadger.documents.integration.*
+import solutions.bitbadger.documents.common.*
 import solutions.bitbadger.documents.java.*
 import solutions.bitbadger.documents.java.integration.*
 import kotlin.test.assertEquals
@@ -18,12 +15,16 @@ object Find {
 
     fun allDefault(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        assertEquals(5, db.conn.findAll<JsonDocument>(TEST_TABLE).size, "There should have been 5 documents returned")
+        assertEquals(
+            5,
+            db.conn.findAll(TEST_TABLE, JsonDocument::class.java).size,
+            "There should have been 5 documents returned"
+        )
     }
 
     fun allAscending(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val docs = db.conn.findAll<JsonDocument>(TEST_TABLE, listOf(Field.named("id")))
+        val docs = db.conn.findAll(TEST_TABLE, JsonDocument::class.java, listOf(Field.named("id")))
         assertEquals(5, docs.size, "There should have been 5 documents returned")
         assertEquals(
             "five|four|one|three|two",
@@ -34,7 +35,7 @@ object Find {
 
     fun allDescending(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val docs = db.conn.findAll<JsonDocument>(TEST_TABLE, listOf(Field.named("id DESC")))
+        val docs = db.conn.findAll(TEST_TABLE, JsonDocument::class.java, listOf(Field.named("id DESC")))
         assertEquals(5, docs.size, "There should have been 5 documents returned")
         assertEquals(
             "two|three|one|four|five",
@@ -45,8 +46,9 @@ object Find {
 
     fun allNumOrder(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val docs = db.conn.findAll<JsonDocument>(
+        val docs = db.conn.findAll(
             TEST_TABLE,
+            JsonDocument::class.java,
             listOf(Field.named("sub.foo NULLS LAST"), Field.named("n:numValue"))
         )
         assertEquals(5, docs.size, "There should have been 5 documents returned")
@@ -58,11 +60,15 @@ object Find {
     }
 
     fun allEmpty(db: ThrowawayDatabase) =
-        assertEquals(0, db.conn.findAll<JsonDocument>(TEST_TABLE).size, "There should have been no documents returned")
+        assertEquals(
+            0,
+            db.conn.findAll(TEST_TABLE, JsonDocument::class.java).size,
+            "There should have been no documents returned"
+        )
 
     fun byIdString(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val doc = db.conn.findById<String, JsonDocument>(TEST_TABLE, "two")
+        val doc = db.conn.findById(TEST_TABLE, "two", JsonDocument::class.java)
         assertNotNull(doc, "The document should have been returned")
         assertEquals("two", doc.id, "An incorrect document was returned")
     }
@@ -71,7 +77,7 @@ object Find {
         Configuration.idField = "key"
         try {
             db.conn.insert(TEST_TABLE, NumIdDocument(18, "howdy"))
-            val doc = db.conn.findById<Int, NumIdDocument>(TEST_TABLE, 18)
+            val doc = db.conn.findById(TEST_TABLE, 18, NumIdDocument::class.java)
             assertNotNull(doc, "The document should have been returned")
         } finally {
             Configuration.idField = "id"
@@ -81,16 +87,17 @@ object Find {
     fun byIdNotFound(db: ThrowawayDatabase) {
         JsonDocument.load(db)
         assertNull(
-            db.conn.findById<String, JsonDocument>(TEST_TABLE, "x"),
+            db.conn.findById(TEST_TABLE, "x", JsonDocument::class.java),
             "There should have been no document returned"
         )
     }
 
     fun byFieldsMatch(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val docs = db.conn.findByFields<JsonDocument>(
+        val docs = db.conn.findByFields(
             TEST_TABLE,
             listOf(Field.any("value", listOf("blue", "purple")), Field.exists("sub")),
+            JsonDocument::class.java,
             FieldMatch.ALL
         )
         assertEquals(1, docs.size, "There should have been a document returned")
@@ -99,9 +106,10 @@ object Find {
 
     fun byFieldsMatchOrdered(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val docs = db.conn.findByFields<JsonDocument>(
+        val docs = db.conn.findByFields(
             TEST_TABLE,
             listOf(Field.equal("value", "purple")),
+            JsonDocument::class.java,
             orderBy = listOf(Field.named("id"))
         )
         assertEquals(2, docs.size, "There should have been 2 documents returned")
@@ -110,7 +118,11 @@ object Find {
 
     fun byFieldsMatchNumIn(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val docs = db.conn.findByFields<JsonDocument>(TEST_TABLE, listOf(Field.any("numValue", listOf(2, 4, 6, 8))))
+        val docs = db.conn.findByFields(
+            TEST_TABLE,
+            listOf(Field.any("numValue", listOf(2, 4, 6, 8))),
+            JsonDocument::class.java
+        )
         assertEquals(1, docs.size, "There should have been a document returned")
         assertEquals("three", docs[0].id, "The incorrect document was returned")
     }
@@ -119,7 +131,7 @@ object Find {
         JsonDocument.load(db)
         assertEquals(
             0,
-            db.conn.findByFields<JsonDocument>(TEST_TABLE, listOf(Field.greater("numValue", 100))).size,
+            db.conn.findByFields(TEST_TABLE, listOf(Field.greater("numValue", 100)), JsonDocument::class.java).size,
             "There should have been no documents returned"
         )
     }
@@ -127,7 +139,11 @@ object Find {
     fun byFieldsMatchInArray(db: ThrowawayDatabase) {
         ArrayDocument.testDocuments.forEach { db.conn.insert(TEST_TABLE, it) }
         val docs =
-            db.conn.findByFields<ArrayDocument>(TEST_TABLE, listOf(Field.inArray("values", TEST_TABLE, listOf("c"))))
+            db.conn.findByFields(
+                TEST_TABLE,
+                listOf(Field.inArray("values", TEST_TABLE, listOf("c"))),
+                ArrayDocument::class.java
+            )
         assertEquals(2, docs.size, "There should have been two documents returned")
         assertTrue(listOf("first", "second").contains(docs[0].id), "An incorrect document was returned (${docs[0].id}")
         assertTrue(listOf("first", "second").contains(docs[1].id), "An incorrect document was returned (${docs[1].id}")
@@ -137,9 +153,10 @@ object Find {
         ArrayDocument.testDocuments.forEach { db.conn.insert(TEST_TABLE, it) }
         assertEquals(
             0,
-            db.conn.findByFields<ArrayDocument>(
+            db.conn.findByFields(
                 TEST_TABLE,
-                listOf(Field.inArray("values", TEST_TABLE, listOf("j")))
+                listOf(Field.inArray("values", TEST_TABLE, listOf("j"))),
+                ArrayDocument::class.java
             ).size,
             "There should have been no documents returned"
         )
@@ -147,7 +164,7 @@ object Find {
 
     fun byContainsMatch(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val docs = db.conn.findByContains<JsonDocument, Map<String, String>>(TEST_TABLE, mapOf("value" to "purple"))
+        val docs = db.conn.findByContains(TEST_TABLE, mapOf("value" to "purple"), JsonDocument::class.java)
         assertEquals(2, docs.size, "There should have been 2 documents returned")
         assertTrue(listOf("four", "five").contains(docs[0].id), "An incorrect document was returned (${docs[0].id}")
         assertTrue(listOf("four", "five").contains(docs[1].id), "An incorrect document was returned (${docs[1].id}")
@@ -155,9 +172,10 @@ object Find {
 
     fun byContainsMatchOrdered(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val docs = db.conn.findByContains<JsonDocument, Map<String, Map<String, String>>>(
+        val docs = db.conn.findByContains(
             TEST_TABLE,
             mapOf("sub" to mapOf("foo" to "green")),
+            JsonDocument::class.java,
             listOf(Field.named("value"))
         )
         assertEquals(2, docs.size, "There should have been 2 documents returned")
@@ -168,14 +186,14 @@ object Find {
         JsonDocument.load(db)
         assertEquals(
             0,
-            db.conn.findByContains<JsonDocument, Map<String, String>>(TEST_TABLE, mapOf("value" to "indigo")).size,
+            db.conn.findByContains(TEST_TABLE, mapOf("value" to "indigo"), JsonDocument::class.java).size,
             "There should have been no documents returned"
         )
     }
 
     fun byJsonPathMatch(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val docs = db.conn.findByJsonPath<JsonDocument>(TEST_TABLE, "$.numValue ? (@ > 10)")
+        val docs = db.conn.findByJsonPath(TEST_TABLE, "$.numValue ? (@ > 10)", JsonDocument::class.java)
         assertEquals(2, docs.size, "There should have been 2 documents returned")
         assertTrue(listOf("four", "five").contains(docs[0].id), "An incorrect document was returned (${docs[0].id}")
         assertTrue(listOf("four", "five").contains(docs[1].id), "An incorrect document was returned (${docs[1].id}")
@@ -183,7 +201,12 @@ object Find {
 
     fun byJsonPathMatchOrdered(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val docs = db.conn.findByJsonPath<JsonDocument>(TEST_TABLE, "$.numValue ? (@ > 10)", listOf(Field.named("id")))
+        val docs = db.conn.findByJsonPath(
+            TEST_TABLE,
+            "$.numValue ? (@ > 10)",
+            JsonDocument::class.java,
+            listOf(Field.named("id"))
+        )
         assertEquals(2, docs.size, "There should have been 2 documents returned")
         assertEquals("five|four", docs.joinToString("|") { it.id }, "The documents were not ordered correctly")
     }
@@ -192,30 +215,34 @@ object Find {
         JsonDocument.load(db)
         assertEquals(
             0,
-            db.conn.findByJsonPath<JsonDocument>(TEST_TABLE, "$.numValue ? (@ > 100)").size,
+            db.conn.findByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)", JsonDocument::class.java).size,
             "There should have been no documents returned"
         )
     }
 
     fun firstByFieldsMatchOne(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val doc = db.conn.findFirstByFields<JsonDocument>(TEST_TABLE, listOf(Field.equal("value", "another")))
+        val doc =
+            db.conn.findFirstByFields(TEST_TABLE, listOf(Field.equal("value", "another")), JsonDocument::class.java)
         assertNotNull(doc, "There should have been a document returned")
         assertEquals("two", doc.id, "The incorrect document was returned")
     }
 
     fun firstByFieldsMatchMany(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val doc = db.conn.findFirstByFields<JsonDocument>(TEST_TABLE, listOf(Field.equal("sub.foo", "green")))
+        val doc =
+            db.conn.findFirstByFields(TEST_TABLE, listOf(Field.equal("sub.foo", "green")), JsonDocument::class.java)
         assertNotNull(doc, "There should have been a document returned")
         assertTrue(listOf("two", "four").contains(doc.id), "An incorrect document was returned (${doc.id}")
     }
 
     fun firstByFieldsMatchOrdered(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val doc = db.conn.findFirstByFields<JsonDocument>(
-            TEST_TABLE, listOf(Field.equal("sub.foo", "green")), orderBy = listOf(
-            Field.named("n:numValue DESC")))
+        val doc = db.conn.findFirstByFields(
+            TEST_TABLE, listOf(Field.equal("sub.foo", "green")), JsonDocument::class.java, orderBy = listOf(
+                Field.named("n:numValue DESC")
+            )
+        )
         assertNotNull(doc, "There should have been a document returned")
         assertEquals("four", doc.id, "An incorrect document was returned")
     }
@@ -223,30 +250,31 @@ object Find {
     fun firstByFieldsNoMatch(db: ThrowawayDatabase) {
         JsonDocument.load(db)
         assertNull(
-            db.conn.findFirstByFields(TEST_TABLE, listOf(Field.equal("value", "absent"))),
+            db.conn.findFirstByFields(TEST_TABLE, listOf(Field.equal("value", "absent")), JsonDocument::class.java),
             "There should have been no document returned"
         )
     }
 
     fun firstByContainsMatchOne(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val doc = db.conn.findFirstByContains<JsonDocument, Map<String, String>>(TEST_TABLE, mapOf("value" to "FIRST!"))
+        val doc = db.conn.findFirstByContains(TEST_TABLE, mapOf("value" to "FIRST!"), JsonDocument::class.java)
         assertNotNull(doc, "There should have been a document returned")
         assertEquals("one", doc.id, "An incorrect document was returned")
     }
 
     fun firstByContainsMatchMany(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val doc = db.conn.findFirstByContains<JsonDocument, Map<String, String>>(TEST_TABLE, mapOf("value" to "purple"))
+        val doc = db.conn.findFirstByContains(TEST_TABLE, mapOf("value" to "purple"), JsonDocument::class.java)
         assertNotNull(doc, "There should have been a document returned")
         assertTrue(listOf("four", "five").contains(doc.id), "An incorrect document was returned (${doc.id}")
     }
 
     fun firstByContainsMatchOrdered(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val doc = db.conn.findFirstByContains<JsonDocument, Map<String, String>>(
+        val doc = db.conn.findFirstByContains(
             TEST_TABLE,
             mapOf("value" to "purple"),
+            JsonDocument::class.java,
             listOf(Field.named("sub.bar NULLS FIRST"))
         )
         assertNotNull(doc, "There should have been a document returned")
@@ -256,32 +284,31 @@ object Find {
     fun firstByContainsNoMatch(db: ThrowawayDatabase) {
         JsonDocument.load(db)
         assertNull(
-            db.conn.findFirstByContains<JsonDocument, Map<String, String>>(
-                TEST_TABLE,
-                mapOf("value" to "indigo")
-            ), "There should have been no document returned"
+            db.conn.findFirstByContains(TEST_TABLE, mapOf("value" to "indigo"), JsonDocument::class.java),
+            "There should have been no document returned"
         )
     }
 
     fun firstByJsonPathMatchOne(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val doc = db.conn.findFirstByJsonPath<JsonDocument>(TEST_TABLE, "$.numValue ? (@ == 10)")
+        val doc = db.conn.findFirstByJsonPath(TEST_TABLE, "$.numValue ? (@ == 10)", JsonDocument::class.java)
         assertNotNull(doc, "There should have been a document returned")
         assertEquals("two", doc.id, "An incorrect document was returned")
     }
 
     fun firstByJsonPathMatchMany(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val doc = db.conn.findFirstByJsonPath<JsonDocument>(TEST_TABLE, "$.numValue ? (@ > 10)")
+        val doc = db.conn.findFirstByJsonPath(TEST_TABLE, "$.numValue ? (@ > 10)", JsonDocument::class.java)
         assertNotNull(doc, "There should have been a document returned")
         assertTrue(listOf("four", "five").contains(doc.id), "An incorrect document was returned (${doc.id}")
     }
 
     fun firstByJsonPathMatchOrdered(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        val doc = db.conn.findFirstByJsonPath<JsonDocument>(
+        val doc = db.conn.findFirstByJsonPath(
             TEST_TABLE,
             "$.numValue ? (@ > 10)",
+            JsonDocument::class.java,
             listOf(Field.named("id DESC"))
         )
         assertNotNull(doc, "There should have been a document returned")
@@ -290,6 +317,9 @@ object Find {
 
     fun firstByJsonPathNoMatch(db: ThrowawayDatabase) {
         JsonDocument.load(db)
-        assertNull(db.conn.findFirstByJsonPath<JsonDocument>(TEST_TABLE, "$.numValue ? (@ > 100)"), "There should have been no document returned")
+        assertNull(
+            db.conn.findFirstByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)", JsonDocument::class.java),
+            "There should have been no document returned"
+        )
     }
 }
diff --git a/src/java/src/test/kotlin/integration/common/Patch.kt b/src/java/src/test/kotlin/integration/common/Patch.kt
index 9a4144c..05982ed 100644
--- a/src/java/src/test/kotlin/integration/common/Patch.kt
+++ b/src/java/src/test/kotlin/integration/common/Patch.kt
@@ -1,6 +1,5 @@
 package solutions.bitbadger.documents.java.integration.common
 
-import solutions.bitbadger.documents.*
 import solutions.bitbadger.documents.common.Field
 import solutions.bitbadger.documents.java.*
 import solutions.bitbadger.documents.java.integration.JsonDocument
@@ -19,7 +18,7 @@ object Patch {
     fun byIdMatch(db: ThrowawayDatabase) {
         JsonDocument.load(db)
         db.conn.patchById(TEST_TABLE, "one", mapOf("numValue" to 44))
-        val doc = db.conn.findById<String, JsonDocument>(TEST_TABLE, "one")
+        val doc = db.conn.findById(TEST_TABLE, "one", JsonDocument::class.java)
         assertNotNull(doc, "There should have been a document returned")
         assertEquals("one", doc.id, "An incorrect document was returned")
         assertEquals(44, doc.numValue, "The document was not patched")
@@ -56,7 +55,7 @@ object Patch {
         JsonDocument.load(db)
         val contains = mapOf("value" to "another")
         db.conn.patchByContains(TEST_TABLE, contains, mapOf("numValue" to 12))
-        val doc = db.conn.findFirstByContains<JsonDocument, Map<String, String>>(TEST_TABLE, contains)
+        val doc = db.conn.findFirstByContains(TEST_TABLE, contains, JsonDocument::class.java)
         assertNotNull(doc, "There should have been a document returned")
         assertEquals("two", doc.id, "The incorrect document was returned")
         assertEquals(12, doc.numValue, "The document was not updated")
@@ -73,7 +72,7 @@ object Patch {
         JsonDocument.load(db)
         val path = "$.numValue ? (@ > 10)"
         db.conn.patchByJsonPath(TEST_TABLE, path, mapOf("value" to "blue"))
-        val docs = db.conn.findByJsonPath<JsonDocument>(TEST_TABLE, path)
+        val docs = db.conn.findByJsonPath(TEST_TABLE, path, JsonDocument::class.java)
         assertEquals(2, docs.size, "There should have been two documents returned")
         docs.forEach {
             assertTrue(listOf("four", "five").contains(it.id), "An incorrect document was returned (${it.id})")
diff --git a/src/java/src/test/kotlin/integration/common/RemoveFields.kt b/src/java/src/test/kotlin/integration/common/RemoveFields.kt
index 022c484..d6f2387 100644
--- a/src/java/src/test/kotlin/integration/common/RemoveFields.kt
+++ b/src/java/src/test/kotlin/integration/common/RemoveFields.kt
@@ -1,6 +1,5 @@
 package solutions.bitbadger.documents.java.integration.common
 
-import solutions.bitbadger.documents.*
 import solutions.bitbadger.documents.common.Field
 import solutions.bitbadger.documents.java.*
 import solutions.bitbadger.documents.java.integration.JsonDocument
@@ -17,7 +16,7 @@ object RemoveFields {
     fun byIdMatchFields(db: ThrowawayDatabase) {
         JsonDocument.load(db)
         db.conn.removeFieldsById(TEST_TABLE, "two", listOf("sub", "value"))
-        val doc = db.conn.findById<String, JsonDocument>(TEST_TABLE, "two")
+        val doc = db.conn.findById(TEST_TABLE, "two", JsonDocument::class.java)
         assertNotNull(doc, "There should have been a document returned")
         assertEquals("", doc.value, "The value should have been empty")
         assertNull(doc.sub, "The sub-document should have been removed")
@@ -39,7 +38,7 @@ object RemoveFields {
         JsonDocument.load(db)
         val fields = listOf(Field.equal("numValue", 17))
         db.conn.removeFieldsByFields(TEST_TABLE, fields, listOf("sub"))
-        val doc = db.conn.findFirstByFields<JsonDocument>(TEST_TABLE, fields)
+        val doc = db.conn.findFirstByFields(TEST_TABLE, fields, JsonDocument::class.java)
         assertNotNull(doc, "The document should have been returned")
         assertEquals("four", doc.id, "An incorrect document was returned")
         assertNull(doc.sub, "The sub-document should have been removed")
@@ -62,7 +61,7 @@ object RemoveFields {
         JsonDocument.load(db)
         val criteria = mapOf("sub" to mapOf("foo" to "green"))
         db.conn.removeFieldsByContains(TEST_TABLE, criteria, listOf("value"))
-        val docs = db.conn.findByContains<JsonDocument, Map<String, Map<String, String>>>(TEST_TABLE, criteria)
+        val docs = db.conn.findByContains(TEST_TABLE, criteria, JsonDocument::class.java)
         assertEquals(2, docs.size, "There should have been 2 documents returned")
         docs.forEach {
             assertTrue(listOf("two", "four").contains(it.id), "An incorrect document was returned (${it.id})")
@@ -88,7 +87,7 @@ object RemoveFields {
         JsonDocument.load(db)
         val path = "$.value ? (@ == \"purple\")"
         db.conn.removeFieldsByJsonPath(TEST_TABLE, path, listOf("sub"))
-        val docs = db.conn.findByJsonPath<JsonDocument>(TEST_TABLE, path)
+        val docs = db.conn.findByJsonPath(TEST_TABLE, path, JsonDocument::class.java)
         assertEquals(2, docs.size, "There should have been 2 documents returned")
         docs.forEach {
             assertTrue(listOf("four", "five").contains(it.id), "An incorrect document was returned (${it.id})")
diff --git a/src/java/src/test/kotlin/integration/postgresql/CountIT.kt b/src/java/src/test/kotlin/integration/postgresql/CountIT.kt
index bb6e416..04afcc4 100644
--- a/src/java/src/test/kotlin/integration/postgresql/CountIT.kt
+++ b/src/java/src/test/kotlin/integration/postgresql/CountIT.kt
@@ -7,7 +7,7 @@ import kotlin.test.Test
 /**
  * PostgreSQL integration tests for the `Count` object / `count*` connection extension functions
  */
-@DisplayName("Kotlin | PostgreSQL: Count")
+@DisplayName("Java | Kotlin | PostgreSQL: Count")
 class CountIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/postgresql/CustomIT.kt b/src/java/src/test/kotlin/integration/postgresql/CustomIT.kt
index 803bfb1..673d78d 100644
--- a/src/java/src/test/kotlin/integration/postgresql/CustomIT.kt
+++ b/src/java/src/test/kotlin/integration/postgresql/CustomIT.kt
@@ -8,7 +8,7 @@ import kotlin.test.Test
 /**
  * PostgreSQL integration tests for the `Custom` object / `custom*` connection extension functions
  */
-@DisplayName("PostgreSQL - Custom")
+@DisplayName("Java | Kotlin | PostgreSQL: Custom")
 class CustomIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/postgresql/DefinitionIT.kt b/src/java/src/test/kotlin/integration/postgresql/DefinitionIT.kt
index 9c94abc..ddf47a6 100644
--- a/src/java/src/test/kotlin/integration/postgresql/DefinitionIT.kt
+++ b/src/java/src/test/kotlin/integration/postgresql/DefinitionIT.kt
@@ -7,7 +7,7 @@ import kotlin.test.Test
 /**
  * PostgreSQL integration tests for the `Definition` object / `ensure*` connection extension functions
  */
-@DisplayName("PostgreSQL - Definition")
+@DisplayName("Java | Kotlin | PostgreSQL: Definition")
 class DefinitionIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/postgresql/DeleteIT.kt b/src/java/src/test/kotlin/integration/postgresql/DeleteIT.kt
index 803400c..f5332c7 100644
--- a/src/java/src/test/kotlin/integration/postgresql/DeleteIT.kt
+++ b/src/java/src/test/kotlin/integration/postgresql/DeleteIT.kt
@@ -7,7 +7,7 @@ import kotlin.test.Test
 /**
  * PostgreSQL integration tests for the `Delete` object / `deleteBy*` connection extension functions
  */
-@DisplayName("PostgreSQL - Delete")
+@DisplayName("Java | Kotlin | PostgreSQL: Delete")
 class DeleteIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/postgresql/DocumentIT.kt b/src/java/src/test/kotlin/integration/postgresql/DocumentIT.kt
index 382d64b..bb070d5 100644
--- a/src/java/src/test/kotlin/integration/postgresql/DocumentIT.kt
+++ b/src/java/src/test/kotlin/integration/postgresql/DocumentIT.kt
@@ -7,7 +7,7 @@ import kotlin.test.Test
 /**
  * PostgreSQL integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions
  */
-@DisplayName("PostgreSQL - Document")
+@DisplayName("Java | Kotlin | PostgreSQL: Document")
 class DocumentIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/postgresql/ExistsIT.kt b/src/java/src/test/kotlin/integration/postgresql/ExistsIT.kt
index 757ec75..4c358e3 100644
--- a/src/java/src/test/kotlin/integration/postgresql/ExistsIT.kt
+++ b/src/java/src/test/kotlin/integration/postgresql/ExistsIT.kt
@@ -7,7 +7,7 @@ import kotlin.test.Test
 /**
  * PostgreSQL integration tests for the `Exists` object / `existsBy*` connection extension functions
  */
-@DisplayName("PostgreSQL - Exists")
+@DisplayName("Java | Kotlin | PostgreSQL: Exists")
 class ExistsIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/postgresql/FindIT.kt b/src/java/src/test/kotlin/integration/postgresql/FindIT.kt
index c6a95fe..9907a6c 100644
--- a/src/java/src/test/kotlin/integration/postgresql/FindIT.kt
+++ b/src/java/src/test/kotlin/integration/postgresql/FindIT.kt
@@ -7,7 +7,7 @@ import kotlin.test.Test
 /**
  * PostgreSQL integration tests for the `Find` object / `find*` connection extension functions
  */
-@DisplayName("PostgreSQL - Find")
+@DisplayName("Java | Kotlin | PostgreSQL: Find")
 class FindIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/postgresql/PatchIT.kt b/src/java/src/test/kotlin/integration/postgresql/PatchIT.kt
index d650355..2bf5e63 100644
--- a/src/java/src/test/kotlin/integration/postgresql/PatchIT.kt
+++ b/src/java/src/test/kotlin/integration/postgresql/PatchIT.kt
@@ -7,7 +7,7 @@ import kotlin.test.Test
 /**
  * PostgreSQL integration tests for the `Patch` object / `patchBy*` connection extension functions
  */
-@DisplayName("PostgreSQL - Patch")
+@DisplayName("Java | Kotlin | PostgreSQL: Patch")
 class PatchIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/postgresql/PgDB.kt b/src/java/src/test/kotlin/integration/postgresql/PgDB.kt
index 7fecb54..902acc1 100644
--- a/src/java/src/test/kotlin/integration/postgresql/PgDB.kt
+++ b/src/java/src/test/kotlin/integration/postgresql/PgDB.kt
@@ -1,8 +1,7 @@
 package solutions.bitbadger.documents.java.integration.postgresql
 
-import solutions.bitbadger.documents.*
-import solutions.bitbadger.documents.common.Parameter
-import solutions.bitbadger.documents.common.ParameterType
+import solutions.bitbadger.documents.common.*
+import solutions.bitbadger.documents.java.*
 import solutions.bitbadger.documents.java.integration.TEST_TABLE
 import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
 
@@ -39,7 +38,7 @@ class PgDB : ThrowawayDatabase {
 
     override fun dbObjectExists(name: String) =
         conn.customScalar("SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = :name) AS it",
-            listOf(Parameter(":name", ParameterType.STRING, name)), Results::toExists)
+            listOf(Parameter(":name", ParameterType.STRING, name)), Boolean::class.java, Results::toExists)
 
     companion object {
 
diff --git a/src/java/src/test/kotlin/integration/postgresql/RemoveFieldsIT.kt b/src/java/src/test/kotlin/integration/postgresql/RemoveFieldsIT.kt
index 3a10709..4fa0fac 100644
--- a/src/java/src/test/kotlin/integration/postgresql/RemoveFieldsIT.kt
+++ b/src/java/src/test/kotlin/integration/postgresql/RemoveFieldsIT.kt
@@ -7,7 +7,7 @@ import kotlin.test.Test
 /**
  * PostgreSQL integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions
  */
-@DisplayName("PostgreSQL - RemoveFields")
+@DisplayName("Java | Kotlin | PostgreSQL: RemoveFields")
 class RemoveFieldsIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/sqlite/CountIT.kt b/src/java/src/test/kotlin/integration/sqlite/CountIT.kt
index 62fdb63..4b3581e 100644
--- a/src/java/src/test/kotlin/integration/sqlite/CountIT.kt
+++ b/src/java/src/test/kotlin/integration/sqlite/CountIT.kt
@@ -9,7 +9,7 @@ import kotlin.test.Test
 /**
  * SQLite integration tests for the `Count` object / `count*` connection extension functions
  */
-@DisplayName("SQLite - Count")
+@DisplayName("Java | Kotlin | SQLite: Count")
 class CountIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/sqlite/CustomIT.kt b/src/java/src/test/kotlin/integration/sqlite/CustomIT.kt
index 21adab3..047371f 100644
--- a/src/java/src/test/kotlin/integration/sqlite/CustomIT.kt
+++ b/src/java/src/test/kotlin/integration/sqlite/CustomIT.kt
@@ -7,7 +7,7 @@ import kotlin.test.Test
 /**
  * SQLite integration tests for the `Custom` object / `custom*` connection extension functions
  */
-@DisplayName("SQLite - Custom")
+@DisplayName("Java | Kotlin | SQLite: Custom")
 class CustomIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/sqlite/DefinitionIT.kt b/src/java/src/test/kotlin/integration/sqlite/DefinitionIT.kt
index 3e553ed..ca943f4 100644
--- a/src/java/src/test/kotlin/integration/sqlite/DefinitionIT.kt
+++ b/src/java/src/test/kotlin/integration/sqlite/DefinitionIT.kt
@@ -9,7 +9,7 @@ import kotlin.test.Test
 /**
  * SQLite integration tests for the `Definition` object / `ensure*` connection extension functions
  */
-@DisplayName("SQLite - Definition")
+@DisplayName("Java | Kotlin | SQLite: Definition")
 class DefinitionIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/sqlite/DeleteIT.kt b/src/java/src/test/kotlin/integration/sqlite/DeleteIT.kt
index b3d056a..112711f 100644
--- a/src/java/src/test/kotlin/integration/sqlite/DeleteIT.kt
+++ b/src/java/src/test/kotlin/integration/sqlite/DeleteIT.kt
@@ -9,7 +9,7 @@ import kotlin.test.Test
 /**
  * SQLite integration tests for the `Delete` object / `deleteBy*` connection extension functions
  */
-@DisplayName("SQLite - Delete")
+@DisplayName("Java | Kotlin | SQLite: Delete")
 class DeleteIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/sqlite/DocumentIT.kt b/src/java/src/test/kotlin/integration/sqlite/DocumentIT.kt
index 1745237..c5317e7 100644
--- a/src/java/src/test/kotlin/integration/sqlite/DocumentIT.kt
+++ b/src/java/src/test/kotlin/integration/sqlite/DocumentIT.kt
@@ -7,7 +7,7 @@ import kotlin.test.Test
 /**
  * SQLite integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions
  */
-@DisplayName("SQLite - Document")
+@DisplayName("Java | Kotlin | SQLite: Document")
 class DocumentIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/sqlite/ExistsIT.kt b/src/java/src/test/kotlin/integration/sqlite/ExistsIT.kt
index 55933ac..c1502f8 100644
--- a/src/java/src/test/kotlin/integration/sqlite/ExistsIT.kt
+++ b/src/java/src/test/kotlin/integration/sqlite/ExistsIT.kt
@@ -9,7 +9,7 @@ import kotlin.test.Test
 /**
  * SQLite integration tests for the `Exists` object / `existsBy*` connection extension functions
  */
-@DisplayName("SQLite - Exists")
+@DisplayName("Java | Kotlin | SQLite: Exists")
 class ExistsIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/sqlite/FindIT.kt b/src/java/src/test/kotlin/integration/sqlite/FindIT.kt
index aef0003..dc893b6 100644
--- a/src/java/src/test/kotlin/integration/sqlite/FindIT.kt
+++ b/src/java/src/test/kotlin/integration/sqlite/FindIT.kt
@@ -9,7 +9,7 @@ import kotlin.test.Test
 /**
  * SQLite integration tests for the `Find` object / `find*` connection extension functions
  */
-@DisplayName("SQLite - Find")
+@DisplayName("Java | Kotlin | SQLite: Find")
 class FindIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/sqlite/PatchIT.kt b/src/java/src/test/kotlin/integration/sqlite/PatchIT.kt
index ea1b147..1a51dae 100644
--- a/src/java/src/test/kotlin/integration/sqlite/PatchIT.kt
+++ b/src/java/src/test/kotlin/integration/sqlite/PatchIT.kt
@@ -9,7 +9,7 @@ import kotlin.test.Test
 /**
  * SQLite integration tests for the `Patch` object / `patchBy*` connection extension functions
  */
-@DisplayName("SQLite - Patch")
+@DisplayName("Java | Kotlin | SQLite: Patch")
 class PatchIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/sqlite/RemoveFieldsIT.kt b/src/java/src/test/kotlin/integration/sqlite/RemoveFieldsIT.kt
index 3fbad3c..3cfeab8 100644
--- a/src/java/src/test/kotlin/integration/sqlite/RemoveFieldsIT.kt
+++ b/src/java/src/test/kotlin/integration/sqlite/RemoveFieldsIT.kt
@@ -9,7 +9,7 @@ import kotlin.test.Test
 /**
  * SQLite integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions
  */
-@DisplayName("SQLite - RemoveFields")
+@DisplayName("Java | Kotlin | SQLite: RemoveFields")
 class RemoveFieldsIT {
 
     @Test
diff --git a/src/java/src/test/kotlin/integration/sqlite/SQLiteDB.kt b/src/java/src/test/kotlin/integration/sqlite/SQLiteDB.kt
index 34d977b..0b98253 100644
--- a/src/java/src/test/kotlin/integration/sqlite/SQLiteDB.kt
+++ b/src/java/src/test/kotlin/integration/sqlite/SQLiteDB.kt
@@ -1,8 +1,7 @@
 package solutions.bitbadger.documents.java.integration.sqlite
 
-import solutions.bitbadger.documents.*
-import solutions.bitbadger.documents.common.Parameter
-import solutions.bitbadger.documents.common.ParameterType
+import solutions.bitbadger.documents.common.*
+import solutions.bitbadger.documents.java.*
 import solutions.bitbadger.documents.java.integration.TEST_TABLE
 import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
 import java.io.File
@@ -32,5 +31,5 @@ class SQLiteDB : ThrowawayDatabase {
 
     override fun dbObjectExists(name: String) =
         conn.customScalar("SELECT EXISTS (SELECT 1 FROM sqlite_master WHERE name = :name) AS it",
-            listOf(Parameter(":name", ParameterType.STRING, name)), Results::toExists)
+            listOf(Parameter(":name", ParameterType.STRING, name)), Boolean::class.java, Results::toExists)
 }
diff --git a/src/main/kotlin/Results.kt b/src/main/kotlin/Results.kt
deleted file mode 100644
index e487f0a..0000000
--- a/src/main/kotlin/Results.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-package solutions.bitbadger.documents
-
-import solutions.bitbadger.documents.common.Dialect
-import solutions.bitbadger.documents.common.DocumentException
-import solutions.bitbadger.documents.java.Results
-import java.sql.PreparedStatement
-import java.sql.ResultSet
-import java.sql.SQLException
-
-/**
- * Helper functions for handling results
- */
-object Results {
-
-    /**
-     * Create a domain item from a document, specifying the field in which the document is found
-     *
-     * @param field The field name containing the JSON document
-     * @param rs A `ResultSet` set to the row with the document to be constructed
-     * @return The constructed domain item
-     */
-    inline fun <reified TDoc> fromDocument(field: String): (ResultSet, Class<TDoc>) -> TDoc =
-        { rs, _ -> Results.fromDocument(field, rs, TDoc::class.java) }
-
-    /**
-     * Create a domain item from a document
-     *
-     * @param rs A `ResultSet` set to the row with the document to be constructed<
-     * @param clazz The class of the document to be returned
-     * @return The constructed domain item
-     */
-    inline fun <reified TDoc> fromData(rs: ResultSet, clazz: Class<TDoc> = TDoc::class.java) =
-        Results.fromDocument("data", rs, TDoc::class.java)
-
-    /**
-     * Create a list of items for the results of the given command, using the specified mapping function
-     *
-     * @param stmt The prepared statement to execute
-     * @param mapFunc The mapping function from data reader to domain class instance
-     * @return A list of items from the query's result
-     * @throws DocumentException If there is a problem executing the query
-     */
-    inline fun <reified TDoc : Any> toCustomList(stmt: PreparedStatement, mapFunc: (ResultSet) -> TDoc) =
-        try {
-            stmt.executeQuery().use {
-                val results = mutableListOf<TDoc>()
-                while (it.next()) {
-                    results.add(mapFunc(it))
-                }
-                results.toList()
-            }
-        } catch (ex: SQLException) {
-            throw DocumentException("Error retrieving documents from query: ${ex.message}", ex)
-        }
-
-    /**
-     * Extract a count from the first column
-     *
-     * @param rs A `ResultSet` set to the row with the count to retrieve
-     * @return The count from the row
-     */
-    fun toCount(rs: ResultSet, clazz: Class<Long> = Long::class.java) =
-        when (Configuration.dialect()) {
-            Dialect.POSTGRESQL -> rs.getInt("it").toLong()
-            Dialect.SQLITE -> rs.getLong("it")
-        }
-
-    /**
-     * Extract a true/false value from the first column
-     *
-     * @param rs A `ResultSet` set to the row with the true/false value to retrieve
-     * @return The true/false value from the row
-     */
-    fun toExists(rs: ResultSet, clazz: Class<Boolean> = Boolean::class.java) =
-        when (Configuration.dialect()) {
-            Dialect.POSTGRESQL -> rs.getBoolean("it")
-            Dialect.SQLITE -> toCount(rs) > 0L
-        }
-}