sqlbrite
A lightweight wrapper around SQLiteOpenHelper which introduces reactive stream semantics to SQL operations.
Top Related Projects
RxJava – Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM.
Event bus for Android and Java that simplifies communication between Activities, Fragments, Threads, Services, etc. Less code, better quality.
Realm is a mobile database: a replacement for SQLite & ORMs
requery - modern SQL based query & persistence for Java / Kotlin / Android
A blazing fast, powerful, and very simple ORM android database library that writes database code for you.
Quick Overview
SQLBrite is a lightweight wrapper around SQLiteDatabase and ContentResolver that makes it easy to work with reactive queries. It provides a simple API for executing SQL queries and observing the results as a stream of data.
Pros
- Reactive Approach: SQLBrite uses RxJava to provide a reactive API for working with SQL data, making it easier to handle asynchronous operations and data changes.
- Lightweight: SQLBrite is a lightweight library that adds minimal overhead to your application, making it a good choice for performance-sensitive projects.
- Abstraction over SQLiteDatabase: SQLBrite provides a higher-level abstraction over the SQLiteDatabase class, simplifying common database operations and reducing boilerplate code.
- ContentResolver Support: SQLBrite can also be used to observe changes in ContentProvider data, making it a versatile tool for working with both local and remote data sources.
Cons
- Dependency on RxJava: SQLBrite requires the use of RxJava, which may not be suitable for all projects or developers who are not familiar with reactive programming.
- Limited Documentation: The project's documentation could be more comprehensive, which may make it more difficult for new users to get started.
- Potential Learning Curve: Developers who are not familiar with reactive programming may need to invest time in learning the concepts and patterns used in SQLBrite.
- Potential Performance Overhead: While SQLBrite is lightweight, the use of RxJava and the additional abstraction layer may introduce some performance overhead in certain scenarios.
Code Examples
Executing a Query
val query = "SELECT * FROM users WHERE name LIKE ?"
val args = arrayOf("%John%")
db.createQuery("users", query, *args)
.map { it.getString(it.getColumnIndex("name")) }
.subscribe { name -> println("User name: $name") }
This code demonstrates how to execute a SQL query using SQLBrite and observe the results as a stream of data.
Observing Changes in a Table
db.createQuery("users", "SELECT * FROM users")
.subscribe { result ->
// Process the result set
while (result.moveToNext()) {
val name = result.getString(result.getColumnIndex("name"))
println("User name: $name")
}
}
This code shows how to observe changes in a database table using SQLBrite. Whenever the "users" table is updated, the subscriber will be notified and can process the new data.
Observing Changes in a ContentProvider
val uri = Uri.parse("content://com.example.provider/users")
contentResolver.createQuery(uri, null, null, null, null)
.subscribe { cursor ->
// Process the cursor
while (cursor.moveToNext()) {
val name = cursor.getString(cursor.getColumnIndex("name"))
println("User name: $name")
}
}
This code demonstrates how to use SQLBrite to observe changes in a ContentProvider. The createQuery
method is used to create an observable that emits the results of a ContentProvider query.
Getting Started
To get started with SQLBrite, you can follow these steps:
- Add the SQLBrite dependency to your project's
build.gradle
file:
dependencies {
implementation 'com.squareup.sqlbrite3:sqlbrite:3.2.0'
}
- Create a
SqlBrite.Builder
instance and configure it with your database orContentResolver
:
val sqlBrite = SqlBrite.Builder()
.logger { message -> Log.d("SQLBrite", message) }
.build()
val db = sqlBrite.wrapDatabaseHelper(MyDatabaseHelper(context), Schedulers.io())
- Use the
createQuery
method to execute SQL queries and observe the results:
db.createQuery("users", "SELECT * FROM users")
.map { it.getString(it.getColumnIndex("name")) }
.subscribe { name -> println("User name: $name") }
- Observe changes in a ContentProvider:
val uri = Uri.parse("content://
Competitor Comparisons
RxJava – Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM.
Pros of RxJava
- More comprehensive and versatile, offering a wide range of operators for complex reactive programming scenarios
- Supports multiple programming paradigms beyond just database operations
- Large community and extensive documentation
Cons of RxJava
- Steeper learning curve due to its extensive API and concepts
- Can be overkill for simple database operations
- Potential performance overhead for basic tasks
Code Comparison
SQLBrite:
briteDatabase.createQuery(TABLE_NAME, QUERY)
.mapToList(cursor -> {
// Map cursor to object
})
.subscribe(items -> {
// Handle items
});
RxJava:
Observable.fromCallable(() -> {
// Perform database query
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
// Handle result
});
Key Differences
- SQLBrite is specifically designed for reactive database operations in Android, while RxJava is a general-purpose reactive programming library
- SQLBrite provides a simpler API for database queries, whereas RxJava offers more flexibility but requires more setup
- RxJava can be used for a wider range of reactive programming scenarios beyond just database operations
Event bus for Android and Java that simplifies communication between Activities, Fragments, Threads, Services, etc. Less code, better quality.
Pros of EventBus
- Simpler API and easier to implement for basic event-driven communication
- Supports sticky events, allowing subscribers to receive the most recent event even if they weren't registered at the time of posting
- Lightweight and has minimal impact on app size
Cons of EventBus
- Can lead to less structured code and make it harder to track event flow
- Lacks built-in RxJava integration, which SQLBrite offers out of the box
- May require more manual memory management to avoid potential memory leaks
Code Comparison
EventBus:
EventBus.getDefault().post(new MessageEvent("Hello!"));
@Subscribe
public void onMessageEvent(MessageEvent event) {
Log.d("EventBus", event.message);
}
SQLBrite:
Observable<Query> users = db.createQuery("users", "SELECT * FROM users");
users.subscribe(query -> {
Cursor cursor = query.run();
// Process cursor
});
While EventBus focuses on event-driven communication between components, SQLBrite is specifically designed for reactive database operations. EventBus offers a simpler API for general-purpose event handling, while SQLBrite provides a more specialized solution for database interactions with RxJava integration.
Realm is a mobile database: a replacement for SQLite & ORMs
Pros of Realm Java
- Object-oriented database with a simple, intuitive API
- Faster performance for complex queries and large datasets
- Built-in support for reactive programming and real-time updates
Cons of Realm Java
- Steeper learning curve for developers familiar with SQLite
- Limited flexibility in database schema changes
- Larger app size due to included Realm core library
Code Comparison
SQLBrite:
SqlBrite sqlBrite = SqlBrite.create();
BriteDatabase db = sqlBrite.wrapDatabaseHelper(dbHelper);
db.createQuery("users", "SELECT * FROM users")
.mapToList(User::new)
.subscribe(users -> {
// Handle user list
});
Realm Java:
Realm realm = Realm.getDefaultInstance();
RealmResults<User> users = realm.where(User.class).findAllAsync();
users.addChangeListener((results, changeSet) -> {
// Handle user list updates
});
Both libraries offer reactive programming support, but Realm Java provides a more straightforward API for working with objects directly. SQLBrite requires more manual mapping between SQL results and objects, while Realm Java allows for direct object manipulation. Realm Java also offers built-in asynchronous queries and change listeners, making it easier to implement real-time updates in your app.
requery - modern SQL based query & persistence for Java / Kotlin / Android
Pros of requery
- Supports multiple database types (SQLite, MySQL, PostgreSQL, etc.)
- Provides an object-oriented query interface
- Offers automatic schema generation and migration
Cons of requery
- Steeper learning curve due to more complex API
- Potentially higher overhead for simple database operations
- Less focus on reactive programming compared to SQLBrite
Code Comparison
SQLBrite:
val query = SqlBrite.Query(
"SELECT * FROM users WHERE age > ?",
arrayOf("18")
)
query.run()
.mapToList { cursor ->
User(cursor.getString(cursor.getColumnIndex("name")))
}
.subscribe { users -> /* Handle result */ }
requery:
val result = data
.select(User::class)
.where(User::age.gt(18))
.get()
.toList()
Summary
SQLBrite is a lightweight SQLite wrapper with reactive extensions, while requery is a more comprehensive ORM solution. SQLBrite offers simplicity and reactive programming support, making it suitable for Android development. requery provides a broader set of features, including support for multiple databases and an object-oriented query interface, but may have a steeper learning curve. The choice between the two depends on project requirements and complexity.
A blazing fast, powerful, and very simple ORM android database library that writes database code for you.
Pros of DBFlow
- More comprehensive ORM solution with advanced features like migrations and model caching
- Supports multiple databases and custom database wrappers
- Provides a fluent query API for complex queries
Cons of DBFlow
- Steeper learning curve due to more complex API and features
- Potentially higher overhead for simple database operations
- Less flexibility for raw SQL queries compared to SQLBrite
Code Comparison
SQLBrite:
val query = SqlBrite.Query(rawQuery, tables)
query.run()
.mapToList { cursor ->
// Map cursor to object
}
.subscribe { results ->
// Handle results
}
DBFlow:
SQLite.select()
.from(MyTable::class.java)
.where(MyTable_Table.column.eq(value))
.async()
.queryListResultCallback { _, results ->
// Handle results
}
.execute()
Both libraries offer reactive database operations, but DBFlow provides a more abstracted, ORM-style API, while SQLBrite focuses on enhancing raw SQL queries with reactive streams. DBFlow's approach may be more suitable for complex data models, while SQLBrite offers more direct control over SQL operations.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
SQL Brite
A lightweight wrapper around SupportSQLiteOpenHelper
and ContentResolver
which introduces reactive
stream semantics to queries.
Deprecated
This library is no longer actively developed and is considered complete.
Its database features (and far, far more) are now offered by SQLDelight and its upgrading guide offers some migration help.
For content provider monitoring please use Copper instead.
Usage
Create a SqlBrite
instance which is an adapter for the library functionality.
SqlBrite sqlBrite = new SqlBrite.Builder().build();
Pass a SupportSQLiteOpenHelper
instance and a Scheduler
to create a BriteDatabase
.
BriteDatabase db = sqlBrite.wrapDatabaseHelper(openHelper, Schedulers.io());
A Scheduler
is required for a few reasons, but the most important is that query notifications can
trigger on the thread of your choice. The query can then be run without blocking the main thread or
the thread which caused the trigger.
The BriteDatabase.createQuery
method is similar to SupportSQLiteDatabase.query
except it takes an
additional parameter of table(s) on which to listen for changes. Subscribe to the returned
Observable<Query>
which will immediately notify with a Query
to run.
Observable<Query> users = db.createQuery("users", "SELECT * FROM users");
users.subscribe(new Consumer<Query>() {
@Override public void accept(Query query) {
Cursor cursor = query.run();
// TODO parse data...
}
});
Unlike a traditional query
, updates to the specified table(s) will trigger additional
notifications for as long as you remain subscribed to the observable. This means that when you
insert, update, or delete data, any subscribed queries will update with the new data instantly.
final AtomicInteger queries = new AtomicInteger();
users.subscribe(new Consumer<Query>() {
@Override public void accept(Query query) {
queries.getAndIncrement();
}
});
System.out.println("Queries: " + queries.get()); // Prints 1
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("jw", "Jake Wharton"));
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("mattp", "Matt Precious"));
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("strong", "Alec Strong"));
System.out.println("Queries: " + queries.get()); // Prints 4
In the previous example we re-used the BriteDatabase
object "db" for inserts. All insert, update,
or delete operations must go through this object in order to correctly notify subscribers.
Unsubscribe from the returned Subscription
to stop getting updates.
final AtomicInteger queries = new AtomicInteger();
Subscription s = users.subscribe(new Consumer<Query>() {
@Override public void accept(Query query) {
queries.getAndIncrement();
}
});
System.out.println("Queries: " + queries.get()); // Prints 1
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("jw", "Jake Wharton"));
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("mattp", "Matt Precious"));
s.unsubscribe();
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("strong", "Alec Strong"));
System.out.println("Queries: " + queries.get()); // Prints 3
Use transactions to prevent large changes to the data from spamming your subscribers.
final AtomicInteger queries = new AtomicInteger();
users.subscribe(new Consumer<Query>() {
@Override public void accept(Query query) {
queries.getAndIncrement();
}
});
System.out.println("Queries: " + queries.get()); // Prints 1
Transaction transaction = db.newTransaction();
try {
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("jw", "Jake Wharton"));
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("mattp", "Matt Precious"));
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("strong", "Alec Strong"));
transaction.markSuccessful();
} finally {
transaction.end();
}
System.out.println("Queries: " + queries.get()); // Prints 2
Note: You can also use try-with-resources with a Transaction
instance.
Since queries are just regular RxJava Observable
objects, operators can also be used to
control the frequency of notifications to subscribers.
users.debounce(500, MILLISECONDS).subscribe(new Consumer<Query>() {
@Override public void accept(Query query) {
// TODO...
}
});
The SqlBrite
object can also wrap a ContentResolver
for observing a query on another app's
content provider.
BriteContentResolver resolver = sqlBrite.wrapContentProvider(contentResolver, Schedulers.io());
Observable<Query> query = resolver.createQuery(/*...*/);
The full power of RxJava's operators are available for combining, filtering, and triggering any number of queries and data changes.
Philosophy
SQL Brite's only responsibility is to be a mechanism for coordinating and composing the notification of updates to tables such that you can update queries as soon as data changes.
This library is not an ORM. It is not a type-safe query mechanism. It won't serialize the same POJOs you use for Gson. It's not going to perform database migrations for you.
Some of these features are offered by SQL Delight which can be used with SQL Brite.
Download
implementation 'com.squareup.sqlbrite3:sqlbrite:3.2.0'
For the 'kotlin' module that adds extension functions to Observable<Query>
:
implementation 'com.squareup.sqlbrite3:sqlbrite-kotlin:3.2.0'
Snapshots of the development version are available in Sonatype's snapshots
repository.
License
Copyright 2015 Square, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Top Related Projects
RxJava – Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM.
Event bus for Android and Java that simplifies communication between Activities, Fragments, Threads, Services, etc. Less code, better quality.
Realm is a mobile database: a replacement for SQLite & ORMs
requery - modern SQL based query & persistence for Java / Kotlin / Android
A blazing fast, powerful, and very simple ORM android database library that writes database code for you.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot