A simple library that helps with serializing Android SQLite queries to and from Java objects:
public Collection<Library> getLibraries(Context context) {
try (RepositoryAccessHelper repositoryAccessHelper = new RepositoryAccessHelper(context)) {
return repositoryAccessHelper
.mapSql("SELECT * FROM library")
.fetch(Library.class);
}
}What problems does it solve?
The standard Android library has for ages had numerous ways to pull back data from its built-in SQLite database. However, none of these ways either a) are easy to use, or b) give developers the power they need to develop performant, flexible applications.
In the C# world, developers have long had the excellent Dapper library, which maps C# types nicely to and from C# objects without getting in the way of how you want to write your queries. QueryDroid started off with Dapper as an inspiration and made querying your data in the built-in SQLite library much more natural and cleaner!
Currently, QueryDroid is available on Maven Central.
dependencies {
implementation 'com.namehillsoftware:querydroid:0.4.0'
}QueryDroid will handle serializing any objects with either public fields or with getter's and setters. For example, the class used in the above example could look like this:
QueryDroid is very fetching, and will always be when fetching you data. To fetch, pass the type you wish to fetch into the fetch method, as above:
public Collection<Library> getLibraries(Context context) {
try (RepositoryAccessHelper repositoryAccessHelper = new RepositoryAccessHelper(context)) {
return repositoryAccessHelper
.mapSql("SELECT * FROM library")
.fetch(Library.class);
}
}fetch will return a collection of Library objects. If you know you'll only need one Library, then you can use fetchFirst. Parameters can also be added to your query. These can be any of the primitive types listed above. For example:
public Library getLibrary(Context context) {
try (RepositoryAccessHelper repositoryAccessHelper = new RepositoryAccessHelper(context)) {
return repositoryAccessHelper
.mapSql("SELECT * FROM library WHERE id = @myKnownId")\
.addParameter("myKnownId", 14)
.fetchFirst(Library.class);
}
}The in filter clause is not yet supported, and neither are fetching primitive types.
public class Library {
private int id;
private String libraryName;
private String accessCode;
private String authKey;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLibraryName() {
return libraryName;
}
public void setLibraryName(String libraryName) {
this.libraryName = libraryName;
}
public String getAccessCode() {
return accessCode;
}
public void setAccessCode(String accessCode) {
this.accessCode = accessCode;
}
public String getAuthKey() {
return authKey;
}
public void setAuthKey(String authKey) {
this.authKey = authKey;
}
}And QueryDroid will gladly serialize this class, following some simple serialization rules. The rules are:
-
Fields that you wish to have serialized/deserialized must be
public, and they must match the name of the column you wish to deserialize -
Getter/Setter methods that you wish to use instead for serialization/deserialization must be
public, and they must follow these rules:- Getters must be named either
getFieldName(), orisFieldTrue()forbooleanfields - Setters must be named
setFieldName(Fieldtype value), includingbooleanfields, e.g.setIsFieldTrue(boolean value)
- Getters must be named either
-
Either fields or the getters/setters can basically be the primitive Java types:
int/Integerlong/Longboolean/Booleanshort/Shortdouble/DoubleStringEnumEnumfields, as always, deserve special mention:Enumfields are serialized to/fromStringfields.
Inserts/Updates will also work using the above example, the main difference being you call the execute() method:
public long getLibrary(Context context) {
try (RepositoryAccessHelper repositoryAccessHelper = new RepositoryAccessHelper(context)) {
return repositoryAccessHelper
.mapSql("INSERT INTO library (libraryName, accessCode, authKey) " +
"VALUES (@libraryName, @accessCode, @authKey)")
.addParameter("libraryName", "New Library!")
.addParameter("accessCode", "secret")
.addParameter("authKey", "123@!")
.execute();
}
}execute() will return a long reflecting the number of affected rows.
There are also some helpers that make inserting and updating a breeze. For example, take this Kotlin class (it can be a Java POJO as well):
data class JavaDataTypes(
var id: Int = 0,
var integerColumn: Int = 0,
var longColumn: Long = 0L,
var floatColumn: Float = 0f,
var doubleColumn: Double = 0.0,
var booleanColumn: Boolean = false,
var stringColumn: String? = null,
)Inserting a new value is as simple as making a call like this:
SqLiteAssistants.insertValue(
it,
tableName,
JavaDataTypes(
booleanColumn = true,
integerColumn = 673,
longColumn = 76772878174L,
floatColumn = 222.18f,
doubleColumn = 733.72,
stringColumn = "winter"
)
)And updating is similar:
val dataUpdate = insertedData.copy(
floatColumn = 370.03f,
stringColumn = null,
integerColumn = 275,
booleanColumn = false,
)
SqLiteAssistants.updateValue(it, tableName, dataUpdate)See a full example in the tests.