StorIO

Additional

Language
Java
Version
v3.0.0 (Dec 20, 2017)
Created
Aug 31, 2014
Updated
Oct 15, 2018
Owner
Pushtorefresh (pushtorefresh)
Contributors
Artem Zinnatullin :slowpoke: (artem-zinnatullin)
Shintaro Katafuchi (hotchemi)
Jens Driller (jenzz)
Yasuhiro Shimizu (yshrsmz)
nikitin-da
anton-pogonets
Ilya Zorin (geralt-encore)
karlicoss
mfarid
omihaz
Iiro Krankka (roughike)
skrzyneckik
mlegy
rsinukov
ValeriusGC
mohaxspb
16
Activity
Badge
Generate
Download
Source code
APK file

Advertisement

StorIO — modern API for SQLiteDatabase and ContentResolver

Overview:
  • Powerful & Simple set of Operations: Put, Get, Delete
  • API for Humans: Type Safety, Immutability & Thread-Safety
  • Convenient builders with compile-time guarantees for required params. Forget about 6-7 null in queries
  • Optional Type-Safe Object Mapping, if you don't want to work with Cursor and ContentValues you don't have to
  • No reflection in Operations and no annotations in the core, also StorIO is not ORM
  • Full control over queries, transaction and object mapping
  • Every Operation over StorIO can be executed as blocking call or as io.reactivex.Flowable/io.reactivex.Single/io.reactivex.Completable/io.reactivex.Maybe
  • RxJava as first class citizen, but it's not required dependency!
  • Reactive: io.reactivex.Flowable from Get Operation will observe changes in StorIO (SQLite or ContentProvider) and receive updates automatically
  • StorIO is replacements for SQLiteDatabase and ContentResolver APIs
  • StorIO + RxJava is replacement for Loaders API
  • We are working on MockStorIO (similar to MockWebServer) for easy unit testing

You can find all releases on Maven Central.

Some examples

Get list of objects from SQLiteDatabase
List<Tweet> tweets = storIOSQLite
  .get()
  .listOfObjects(Tweet.class) // Type safety
  .withQuery(Query.builder() // Query builder
    .table("tweets")
    .where("author = ?")
    .whereArgs("artem_zin") // Varargs Object..., no more new String[] {"I", "am", "tired", "of", "this", "shit"}
    .build()) // Query is immutable — you can save it and share without worries
  .prepare() // Operation builder
  .executeAsBlocking(); // Control flow is readable from top to bottom, just like with RxJava
Put something to SQLiteDatabase
storIOSQLite
  .put() // Insert or Update
  .objects(someTweets) // Type mapping!
  .prepare()
  .executeAsBlocking();
Delete something from SQLiteDatabase
storIOSQLite
  .delete()
  .byQuery(DeleteQuery.builder()
    .table("tweets")
    .where("timestamp <= ?")
    .whereArgs(System.currentTimeMillis() - 86400) // No need to write String.valueOf()
    .build())
  .prepare()
  .executeAsBlocking();

Reactive? Single.just(true)!

Get something as io.reactivex.Flowable and receive updates!
storIOSQLite
  .get()
  .listOfObjects(Tweet.class)
  .withQuery(Query.builder()
    .table("tweets")
    .build())
  .prepare()
  .asRxFlowable(BackpressureStrategy.LATEST) // Get Result as io.reactivex.Flowable and subscribe to further updates of tables from Query!
  .observeOn(mainThread()) // All Rx operations work on Schedulers.io()
  .subscribe(tweets -> { // Please don't forget to dispose
     // Will be called with first result and then after each change of tables from Query
     // Several changes in transaction -> one notification
     adapter.setData(tweets);
   }
  );
Want to work with plain Cursor, no problems
Cursor cursor = storIOSQLite
  .get()
  .cursor()
  .withQuery(Query.builder() // Or RawQuery
    .table("tweets")
    .where("who_cares = ?")
    .whereArgs("nobody")
    .build())
  .prepare()
  .executeAsBlocking();

How object mapping works?

You can set default type mappings when you build instance of StorIOSQLite or StorIOContentResolver
StorIOSQLite storIOSQLite = DefaultStorIOSQLite.builder()
  .sqliteOpenHelper(someSQLiteOpenHelper)
  .addTypeMapping(Tweet.class, SQLiteTypeMapping.<Tweet>builder()
    .putResolver(new TweetPutResolver()) // object that knows how to perform Put Operation (insert or update)
    .getResolver(new TweetGetResolver()) // object that knows how to perform Get Operation
    .deleteResolver(new TweetDeleteResolver())  // object that knows how to perform Delete Operation
    .build())
  .addTypeMapping(...)
  // other options
  .build(); // This instance of StorIOSQLite will know how to work with Tweet objects

You can override Operation Resolver per each individual Operation, it can be useful for working with SQL JOIN.

To save you from coding boilerplate classes we created Annotation Processor which will generate PutResolver, GetResolver and DeleteResolver at compile time, you just need to use generated classes

Notice that annotation processors are not part of the library core, you can work with StorIO without them, we just made them to save you from boilerplate.

StorIOSQLite:dependencies { implementation 'com.pushtorefresh.storio3:sqlite-annotations:insert-latest-version-here' annotationProcessor 'com.pushtorefresh.storio3:sqlite-annotations-processor:insert-latest-version-here' }

StorIOContentResolver:dependencies { implementation 'com.pushtorefresh.storio3:content-resolver-annotations:insert-latest-version-here' annotationProcessor 'com.pushtorefresh.storio3:content-resolver-annotations-processor:insert-latest-version-here' }
@StorIOSQLiteType(table="tweets") publicclassTweet { // Annotated fields should have package-level visibility.@StorIOSQLiteColumn(name="author") String author; @StorIOSQLiteColumn(name="content") String content; // Please leave default constructor with package-level visibility.Tweet() {} }

Kotlin:

In order to make annotation processors work with Kotlin you need to add the following to your

build.gradle:apply plugin: 'kotlin-kapt'

Then use

kapt configuration instead of annotationProcessor.@StorIOSQLiteType(table="tweets") dataclassTweet @StorIOSQLiteCreatorconstructor( StorIOSQLiteColumn(name="author") val author:String, StorIOSQLiteColumn(name ="content") valcontent:String)

AutoValue:@AutoValue@StorIOSQLiteType(table="tweets") publicabstractclassTweet { // Annotated methods should have package-level or public visibility.@StorIOSQLiteColumn(name="author") abstractStringauthor(); @StorIOSQLiteColumn(name="content") abstractStringcontent(); // Parameters order depends on declaration order.@StorIOSQLiteCreatorstaticTweetcreate(Stringauthor, Stringcontent) { returnnewAutoValue_Tweet(author, content); } }

Annotation Processor will generate three classes in same package as annotated class during compilation: