{"id":13989549,"url":"https://github.com/pushtorefresh/storio","last_synced_at":"2025-05-14T18:07:04.273Z","repository":{"id":20230666,"uuid":"23502577","full_name":"pushtorefresh/storio","owner":"pushtorefresh","description":"Reactive API for SQLiteDatabase and ContentResolver.","archived":false,"fork":false,"pushed_at":"2023-09-16T09:28:58.000Z","size":4427,"stargazers_count":2543,"open_issues_count":42,"forks_count":183,"subscribers_count":89,"default_branch":"master","last_synced_at":"2025-04-13T10:58:04.000Z","etag":null,"topics":["android","rxjava","sqlite","storio"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pushtorefresh.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2014-08-30T23:23:41.000Z","updated_at":"2025-01-17T21:02:51.000Z","dependencies_parsed_at":"2024-01-07T06:49:47.676Z","dependency_job_id":"437b16c2-74a0-4df0-8f94-ae74ed181d4d","html_url":"https://github.com/pushtorefresh/storio","commit_stats":null,"previous_names":["pushtorefresh/bamboo-storage"],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pushtorefresh%2Fstorio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pushtorefresh%2Fstorio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pushtorefresh%2Fstorio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pushtorefresh%2Fstorio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pushtorefresh","download_url":"https://codeload.github.com/pushtorefresh/storio/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254198515,"owners_count":22030966,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","rxjava","sqlite","storio"],"created_at":"2024-08-09T13:01:44.532Z","updated_at":"2025-05-14T18:06:59.258Z","avatar_url":"https://github.com/pushtorefresh.png","language":"Java","readme":"#### StorIO — modern API for SQLiteDatabase and ContentResolver\n\n##### Overview:\n* Powerful \u0026 Simple set of Operations: `Put`, `Get`, `Delete`\n* API for Humans: Type Safety, Immutability \u0026 Thread-Safety\n* Convenient builders with compile-time guarantees for required params. Forget about 6-7 `null` in queries\n* Optional Type-Safe Object Mapping, if you don't want to work with `Cursor` and `ContentValues` you don't have to\n* No reflection in Operations and no annotations in the core, also `StorIO` is not ORM\n* **Full control** over queries, transaction and object mapping\n* Every Operation over `StorIO` can be executed as blocking call or as `io.reactivex.Flowable`/`io.reactivex.Single`/`io.reactivex.Completable`/`io.reactivex.Maybe`\n* `RxJava` as first class citizen, but it's not required dependency!\n* **Reactive**: `io.reactivex.Flowable` from `Get` Operation **will observe changes** in `StorIO` (`SQLite` or `ContentProvider`) and receive updates automatically\n* `StorIO` is replacements for `SQLiteDatabase` and `ContentResolver` APIs\n* `StorIO` + `RxJava` is replacement for `Loaders` API\n* We are working on `MockStorIO` (similar to [MockWebServer](https://github.com/square/okhttp/tree/master/mockwebserver)) for easy unit testing\n\n----\n\n##### Why StorIO?\n* Simple concept of just three main Operations: `Put`, `Get`, `Delete` -\u003e less bugs\n* Almost everything is immutable and thread-safe -\u003e less bugs\n* Builders for everything make code much, much more readable and obvious -\u003e less bugs\n* Our builders give compile time guarantees for required parameters -\u003e less bugs\n* `StorIO` annotated with `@NonNull` and `@Nullable` annotations -\u003e less bugs\n* Open Source -\u003e less bugs\n* Documentation, Sample app and Design tests -\u003e less bugs\n* `StorIO` has unit and integration tests [![codecov.io](https://codecov.io/github/pushtorefresh/storio/coverage.svg?branch=master)](https://codecov.io/github/pushtorefresh/storio?branch=master) -\u003e less bugs\n* Less bugs -\u003e less bugs\n\n#### Documentation:\n\n* [`Why we made StorIO`](docs/StorIO.md)\n* [`StorIO SQLite`](docs/StorIOSQLite.md)\n* [`StorIO ContentResolver`](docs/StorIOContentResolver.md)\n\nEasy ways to learn how to use `StorIO` -\u003e check out `Documentation`, `Design Tests` and `Sample App`:\n\n* [Design tests for StorIO SQLite](storio-sqlite/src/test/java/com/pushtorefresh/storio3/sqlite/design)\n* [Design tests for StorIO ContentResolver](storio-content-resolver/src/test/java/com/pushtorefresh/storio3/contentresolver/design)\n* [Sample App](storio-sample-app)\n\n#### Download:\n```groovy\n// If you need StorIO for SQLite\nimplementation 'com.pushtorefresh.storio3:sqlite:3.0.1'\n\n// If you need StorIO for ContentResolver\nimplementation 'com.pushtorefresh.storio3:content-resolver:3.0.1'\n\n// Notice that RxJava is optional dependency for StorIO,\n// So if you need it -\u003e please add it manually.\n```\n\nYou can find all releases on [Maven Central](http://search.maven.org/#search%7Cga%7C1%7Ccom.pushtorefresh.storio3).\n\n\n#### Some examples\n\n##### Get list of objects from SQLiteDatabase\n```java\nList\u003cTweet\u003e tweets = storIOSQLite\n  .get()\n  .listOfObjects(Tweet.class) // Type safety\n  .withQuery(Query.builder() // Query builder\n    .table(\"tweets\")\n    .where(\"author = ?\")\n    .whereArgs(\"artem_zin\") // Varargs Object..., no more new String[] {\"I\", \"am\", \"tired\", \"of\", \"this\", \"shit\"}\n    .build()) // Query is immutable — you can save it and share without worries\n  .prepare() // Operation builder\n  .executeAsBlocking(); // Control flow is readable from top to bottom, just like with RxJava\n\n```\n\n##### Put something to SQLiteDatabase\n```java\nstorIOSQLite\n  .put() // Insert or Update\n  .objects(someTweets) // Type mapping!\n  .prepare()\n  .executeAsBlocking();\n```\n\n##### Delete something from SQLiteDatabase\n```java\nstorIOSQLite\n  .delete()\n  .byQuery(DeleteQuery.builder()\n    .table(\"tweets\")\n    .where(\"timestamp \u003c= ?\")\n    .whereArgs(System.currentTimeMillis() - 86400) // No need to write String.valueOf()\n    .build())\n  .prepare()\n  .executeAsBlocking();\n```\n\n#### Reactive? Single.just(true)!\n\n##### Get something as io.reactivex.Flowable and receive updates!\n```java\nstorIOSQLite\n  .get()\n  .listOfObjects(Tweet.class)\n  .withQuery(Query.builder()\n    .table(\"tweets\")\n    .build())\n  .prepare()\n  .asRxFlowable(BackpressureStrategy.LATEST) // Get Result as io.reactivex.Flowable and subscribe to further updates of tables from Query!\n  .observeOn(mainThread()) // All Rx operations work on Schedulers.io()\n  .subscribe(tweets -\u003e { // Please don't forget to dispose\n  \t  // Will be called with first result and then after each change of tables from Query\n  \t  // Several changes in transaction -\u003e one notification\n  \t  adapter.setData(tweets);\n  \t}\n  );\n```\n\n##### Want to work with plain Cursor, no problems\n```java\nCursor cursor = storIOSQLite\n  .get()\n  .cursor()\n  .withQuery(Query.builder() // Or RawQuery\n    .table(\"tweets\")\n    .where(\"who_cares = ?\")\n    .whereArgs(\"nobody\")\n    .build())\n  .prepare()\n  .executeAsBlocking();\n```\n\n#### How object mapping works?\n##### You can set default type mappings when you build instance of `StorIOSQLite` or `StorIOContentResolver`\n\n```java\nStorIOSQLite storIOSQLite = DefaultStorIOSQLite.builder()\n  .sqliteOpenHelper(someSQLiteOpenHelper)\n  .addTypeMapping(Tweet.class, SQLiteTypeMapping.\u003cTweet\u003ebuilder()\n    .putResolver(new TweetPutResolver()) // object that knows how to perform Put Operation (insert or update)\n    .getResolver(new TweetGetResolver()) // object that knows how to perform Get Operation\n    .deleteResolver(new TweetDeleteResolver())  // object that knows how to perform Delete Operation\n    .build())\n  .addTypeMapping(...)\n  // other options\n  .build(); // This instance of StorIOSQLite will know how to work with Tweet objects\n```\n\nYou can override Operation Resolver per each individual Operation, it can be useful for working with `SQL JOIN`.\n\n---\n\nTo **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\n\n*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*.\n\n`StorIOSQLite`:\n```groovy\ndependencies {\n  implementation 'com.pushtorefresh.storio3:sqlite-annotations:insert-latest-version-here'\n\n  annotationProcessor 'com.pushtorefresh.storio3:sqlite-annotations-processor:insert-latest-version-here'\n}\n```\n\n`StorIOContentResolver`:\n```groovy\ndependencies {\n  implementation 'com.pushtorefresh.storio3:content-resolver-annotations:insert-latest-version-here'\n\n  annotationProcessor 'com.pushtorefresh.storio3:content-resolver-annotations-processor:insert-latest-version-here'\n}\n```\n\n```java\n@StorIOSQLiteType(table = \"tweets\")\npublic class Tweet {\n\n  // Annotated fields should have package-level visibility.\n  @StorIOSQLiteColumn(name = \"author\")\n  String author;\n\n  @StorIOSQLiteColumn(name = \"content\")\n  String content;\n\n  // Please leave default constructor with package-level visibility.\n  Tweet() {}\n}\n```\n\n`Kotlin`:\n\nIn order to make annotation processors work with Kotlin you need to add the following to your `build.gradle`:\n```groovy\napply plugin: 'kotlin-kapt'\n```\nThen use `kapt` configuration instead of `annotationProcessor`.\n ```kotlin\n @StorIOSQLiteType(table = \"tweets\")\n data class Tweet @StorIOSQLiteCreator constructor(\n        StorIOSQLiteColumn(name = \"author\") val author: String,\n        StorIOSQLiteColumn(name = \"content\") val content: String)\n ```\n\n[`AutoValue`](https://github.com/google/auto/blob/master/value/userguide/index.md):\n```java\n@AutoValue\n@StorIOSQLiteType(table = \"tweets\")\npublic abstract class Tweet {\n\n  // Annotated methods should have package-level or public visibility.\n  @StorIOSQLiteColumn(name = \"author\")\n  abstract String author();\n\n  @StorIOSQLiteColumn(name = \"content\")\n  abstract String content();\n\n  // Parameters order depends on declaration order.\n  @StorIOSQLiteCreator\n  static Tweet create(String author, String content) {\n    return new AutoValue_Tweet(author, content);\n  }\n}\n```\n\nAnnotation Processor will generate three classes in same package as annotated class during compilation:\n\n* `TweetStorIOSQLitePutResolver`\n* `TweetStorIOSQLiteGetResolver`\n* `TweetStorIOSQLiteDeleteResolver`\n\nYou just need to apply them:\n\n```java\nStorIOSQLite storIOSQLite = DefaultStorIOSQLite.builder()\n  .sqliteOpenHelper(someSQLiteOpenHelper)\n  .addTypeMapping(Tweet.class, SQLiteTypeMapping.\u003cTweet\u003ebuilder()\n    .putResolver(new TweetStorIOSQLitePutResolver()) // object that knows how to perform Put Operation (insert or update)\n    .getResolver(new TweetStorIOSQLiteGetResolver()) // object that knows how to perform Get Operation\n    .deleteResolver(new TweetStorIOSQLiteDeleteResolver())  // object that knows how to perform Delete Operation\n    .build())\n  .addTypeMapping(...)\n  // other options\n  .build(); // This instance of StorIOSQLite will know how to work with Tweet objects\n```\n\nBTW: [Here is a class](storio-sample-app/src/main/java/com/pushtorefresh/storio3/sample/db/entities/AllSupportedTypes.java) with all types of fields, supported by StorIO SQLite Annotation Processor.\n\nFew tips about Operation Resolvers:\n\n* If your entities are immutable or they have builders or they use AutoValue/AutoParcel -\u003e write your own Operation Resolvers\n* If you want to write your own Operation Resolver -\u003e take a look at Default Operation resolvers, they can fit your needs\n* Via custom Operation Resolvers you can implement any Operation as you want -\u003e store one object in multiple tables, use custom sql things and so on\n\nAPI of `StorIOContentResolver` is same.\n\n----\n\n#### Versioning:\nBecause StorIO works with important things like User data and so on, we use Semantic Versioning 2.0.0 scheme for releases (http://semver.org).\n\nShort example:\n`1.2.3` -\u003e `MAJOR.MINOR.PATCH`\n\n* `MAJOR` version changes when we make incompatible API changes.\n* `MINOR` version changes when we add functionality in a backwards-compatible manner.\n* `PATCH` version changes when we make backwards-compatible bug fixes.\n\nPlease read [`CHANGELOG`](CHANGELOG.md) and check what part of the version has changed, before switching to new version.\n\n#### Architecture:\n`StorIOSQLite` and `StorIOContentResolver` — are abstractions with default implementations: `DefaultStorIOSQLite` and `DefaultStorIOContentResolver`.\n\nIt means, that you can have your own implementation of `StorIOSQLite` and `StorIOContentResolver` with custom behavior, such as memory caching, verbose logging and so on or mock implementation for unit testing (we are working on `MockStorIO`).\n\nOne of the main goals of `StorIO` — clean API for Humans which will be easy to use and understand, that's why `StorIOSQLite` and `StorIOContentResolver` have just several methods, but we understand that sometimes you need to go under the hood and `StorIO` allows you to do it: `StorIOSQLite.LowLevel` and `StorIOContentResolver.LowLevel` encapsulates low-level methods, you can use them if you need, but please try to avoid it.\n\n#### Queries\n\nAll `Query` objects are immutable, you can share them safely.\n\n#### Concept of Prepared Operations\nYou may notice that each Operation (Get, Put, Delete) should be prepared with `prepare()`. `StorIO` has an entity called `PreparedOperation`, and you can use them to perform group execution of several Prepared Operations or provide `PreparedOperation` as a return type of your API (for example in Model layer) and client will decide how to execute it: `executeAsBlocking()` or `asRxFlowable()`. Also, Prepared Operations might be useful for ORMs based on `StorIO`.\n\nYou can customize behavior of every Operation via `Resolvers`: `GetResolver`, `PutResolver`, `DeleteResolver`.\n\n#### Rx Support Design\nEvery Operation can be executed as `io.reactivex.Flowable`, `io.reactivex.Single`, `io.reactivex.Completable` or `io.reactivex.Maybe`. Get Operations will be automatically subscribed to the updates of the data.\nEvery rx operation runs on `Schedulers.io()`. You can change it by `defaultRxScheduler()` or set it to `null` to execute on current thread.\n\n#### 3rd party additions/integrations for StorIO\n\n* [CodeGenUnderStorIO](https://github.com/shivan42/CodeGenUnderStorIO) allows you generate Java classes for db entities from the db schema built in some visual editor.\n\n----\nMaster branch build status: [![Master branch build status](https://travis-ci.org/pushtorefresh/storio.svg?branch=master)](https://travis-ci.org/pushtorefresh/storio)\n\n\n**Made with love** in [Pushtorefresh.com](https://pushtorefresh.com) by [@artem_zin](https://twitter.com/artem_zin), [@nikitin-da](https://github.com/nikitin-da) and [@geralt-encore](https://github.com/geralt-encore)\n","funding_links":[],"categories":["Java","Library","并发编程","Database"],"sub_categories":["一些原理分析的文章"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpushtorefresh%2Fstorio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpushtorefresh%2Fstorio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpushtorefresh%2Fstorio/lists"}