{"id":13472101,"url":"https://github.com/maskarade/Android-Orma","last_synced_at":"2025-03-26T15:31:21.290Z","repository":{"id":152277099,"uuid":"45805791","full_name":"maskarade/Android-Orma","owner":"maskarade","description":"An ORM for Android with type-safety and painless smart migrations","archived":false,"fork":false,"pushed_at":"2020-04-13T06:25:04.000Z","size":3813,"stargazers_count":441,"open_issues_count":26,"forks_count":48,"subscribers_count":30,"default_branch":"master","last_synced_at":"2024-04-13T22:27:06.077Z","etag":null,"topics":["android","database","database-migration","orm","query-builder","sql","sqlite"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maskarade.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2015-11-09T00:26:20.000Z","updated_at":"2024-03-31T14:16:56.000Z","dependencies_parsed_at":null,"dependency_job_id":"cda4584c-e345-4584-a273-436d9be5dd5a","html_url":"https://github.com/maskarade/Android-Orma","commit_stats":{"total_commits":1247,"total_committers":26,"mean_commits":47.96153846153846,"dds":0.1812349639133921,"last_synced_commit":"342ccdb253c42a8b142e50613ac894abfbe98a62"},"previous_names":[],"tags_count":110,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maskarade%2FAndroid-Orma","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maskarade%2FAndroid-Orma/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maskarade%2FAndroid-Orma/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maskarade%2FAndroid-Orma/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maskarade","download_url":"https://codeload.github.com/maskarade/Android-Orma/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221616545,"owners_count":16852419,"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","database","database-migration","orm","query-builder","sql","sqlite"],"created_at":"2024-07-31T16:00:51.911Z","updated_at":"2024-10-30T02:31:25.014Z","avatar_url":"https://github.com/maskarade.png","language":"Java","readme":"# Android Orma [![Circle CI](https://circleci.com/gh/maskarade/Android-Orma/tree/master.svg?style=svg)](https://circleci.com/gh/maskarade/Android-Orma/tree/master) [ ![Download](https://api.bintray.com/packages/orma/maven/orma/images/download.svg) ](https://bintray.com/orma/maven/orma/) [![Gitter](http://badges.gitter.im/Android-Orma.svg)](https://gitter.im/Android-Orma/Lobby) ![backers](https://opencollective.com/android-orma/tiers/backer/badge.svg?label=backer\u0026color=brightgreen)\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"Orma.png\" width=\"256\" height=\"256\"  alt=\"Android Orma\" /\u003e\n\u003c/p\u003e\n\nOrma is a ORM (Object-Relation Mapper) for [Android SQLiteDatabase](http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html).\nBecause it generates helper classes at compile time with **annotation processing**, its query builders are type-safe.\n\nThe interface of Orma is simple and easy to use,\nas the author respects the Larry Wall's wisdom:\n\n\u003e Easy things should be easy, and hard things should be possible\n-- [Larry Wall](http://www.amazon.com/gp/feature.html?ie=UTF8\u0026docId=7137)\n\n## Table of Contents\n\n\u003c!-- TOC depthFrom:2 anchorMode:github.com --\u003e\n\n- [Table of Contents](#table-of-contents)\n- [Motivation](#motivation)\n- [Requirements](#requirements)\n- [Getting Started](#getting-started)\n- [Synopsis](#synopsis)\n- [The Components](#the-components)\n  - [Database Handles](#database-handles)\n  - [Models](#models)\n  - [Schema Helpers](#schema-helpers)\n  - [Relation Helpers](#relation-helpers)\n  - [Selector Helpers](#selector-helpers)\n  - [Updater Helpers](#updater-helpers)\n  - [Deleter Helpers](#deleter-helpers)\n  - [Query Helper Methods](#query-helper-methods)\n    - [List of Query Helper Methods](#list-of-query-helper-methods)\n    - [How to Control Generation of Query Helpers](#how-to-control-generation-of-query-helpers)\n  - [The Inserter Helpers](#the-inserter-helpers)\n- [Details of Database Handles](#details-of-database-handles)\n  - [Configuration of Database Handles](#configuration-of-database-handles)\n  - [Database Handle Builders](#database-handle-builders)\n  - [In-Memory Database](#in-memory-database)\n- [Details of Models](#details-of-models)\n  - [Setters and Getters](#setters-and-getters)\n  - [Immutable Models](#immutable-models)\n  - [Composite Indexes](#composite-indexes)\n  - [Reserved Names](#reserved-names)\n- [RxJava Integration](#rxjava-integration)\n- [Associations](#associations)\n  - [Has-One Associations with `SingleAssociation\u003cT\u003e`](#has-one-associations-with-singleassociationt)\n  - [Direct Associations](#direct-associations)\n  - [Has-Many Associations with `SingleAssociation\u003cT\u003e`](#has-many-associations-with-singleassociationt)\n  - [Has-Many Associations with Direct Associations](#has-many-associations-with-direct-associations)\n  - [Limitations in Associations](#limitations-in-associations)\n- [Type Adapters](#type-adapters)\n  - [How Serialized Types Used](#how-serialized-types-used)\n  - [`@StaticTypeAdapters` for Multiple Serializers at Once](#statictypeadapters-for-multiple-serializers-at-once)\n  - [Built-In Type Adapters](#built-in-type-adapters)\n  - [Generic Type Adapters](#generic-type-adapters)\n- [Pagination](#pagination)\n  - [limit and offset](#limit-and-offset)\n  - [page and per](#page-and-per)\n- [Raw Queries](#raw-queries)\n- [Migration](#migration)\n- [DataSet Changed Events](#dataset-changed-events)\n- [Cooperation with Serialization Libraries](#cooperation-with-serialization-libraries)\n- [Encryption](#encryption)\n- [Example](#example)\n- [Benchmark](#benchmark)\n- [FAQ](#faq)\n  - [How can I enable debug logging on release build?](#how-can-i-enable-debug-logging-on-release-build)\n  - [How can see the generated Java files?](#how-can-see-the-generated-java-files)\n  - [Does Orma work with Kotlin?](#does-orma-work-with-kotlin)\n  - [When the database handle is opened and closed?](#when-the-database-handle-is-opened-and-closed)\n  - [Who uses Orma?](#who-uses-orma)\n- [Support](#support)\n- [Licenses in Runtime Dependencies](#licenses-in-runtime-dependencies)\n- [Contribution](#contribution)\n- [Release Engineering for Maintainers](#release-engineering-for-maintainers)\n  - [Artifact Repository](#artifact-repository)\n  - [What you do](#what-you-do)\n  - [Documentation Tools](#documentation-tools)\n- [See Also](#see-also)\n- [Authors and Contributors](#authors-and-contributors)\n- [License](#license)\n\n\u003c!-- /TOC --\u003e\n\n## Motivation\n\nThere are already [a lot of ORMs for Android](https://github.com/search?q=topic%3Aandroid+topic%3Aorm).\nWhy I have to add another wheel?\n\nThe answer is that I need an ORM that has *all* the following features:\n\n* Fast as hand-written code\n* POJO models\n  * Model classes should have no restriction\n  * Might implement `Parcelable` and/or extend any classes\n  * They should be passed to another thread\n* A database handle must be an object instance\n  * Not a static-method based class\n  * Even though it is designed to be used as a singleton scope\n* Easy migration\n  * Some `ALTER TABLE`, e.g. `add column` and `drop column`, should be detected and processed\n  * There is a wheel in Perl: [SQL::Translator::Diff](https://metacpan.org/pod/SQL::Translator::Diff)\n* Type safe and code completion friendly\n  * `db.selectFromModel()` is better than `new Select(Model.class)`\n  * `todos.idEq(id).toList()` is better than `todos.equalTo(\"id\", id)`\n* Custom raw queries are sometimes inevitable\n  * `GROUP BY ... HAVING ...`\n  * `SELECT max(value), min(value), avg(value), count(value) FROM ...`\n\nAnd now they are exactly what Orma has.\n\n## Requirements\n\n* JDK 8 (1.8.0_66 or later) to build\n* Android API level 15 to use\n\n## Getting Started\n\nDeclare dependencies to use Orma and its annotation processor.\n\n```gradle:build.gradle\ndependencies {\n    annotationProcessor 'com.github.maskarade.android.orma:orma-processor:6.0.2'\n    compile 'com.github.maskarade.android.orma:orma:6.0.2'\n}\n```\n\nNOTE: if you use Android Gradle Plugin before 2.2.0, you must use [android-apt](https://bitbucket.org/hvisser/android-apt) plugin instead of `annotationProcessor` configuration.\n\n## Synopsis\n\nFirst, define model classes annotated with `@Table`, `@Column`, and `@PrimaryKey` and run the **Build APK** command to generate helper classes.\n\n```java\npackage com.github.gfx.android.orma.example;\n\nimport com.github.gfx.android.orma.annotation.Column;\nimport com.github.gfx.android.orma.annotation.PrimaryKey;\nimport com.github.gfx.android.orma.annotation.Table;\n\nimport android.support.annotation.Nullable;\n\n@Table\npublic class Todo {\n\n    @PrimaryKey(autoincrement = true)\n    public long id;\n\n    @Column(indexed = true)\n    public String title;\n\n    @Column\n    @Nullable // allows NULL (default: NOT NULL)\n    public String content;\n\n    @Column\n    public long createdTimeMillis;\n}\n```\n\nSecond, instantiate a database handle `OrmaDatabase`, which is generated by `orma-processor`.\n\nHere is an example to configure `OrmaDatabase`:\n\n```java\n// See OrmaDatabaseBuilderBase for other options.\nOrmaDatabase orma = OrmaDatabase.builder(context)\n    .name(\"main.db\") // default: \"${applicationId}.orma.db\"\n    .build();\n```\n\nThen, you can create, read, update and delete models via `OrmaDatabase`:\n\n```java\nTodo todo = ...;\n\n// create\norma.insertIntoTodo(todo);\n\n// prepared statements with transaction\norma.transactionSync( -\u003e { // or transactionAsync() to execute tasks in background\n    Inserter\u003cTodo\u003e inserter = orma.prepareInsertIntoTodo();\n    inserter.execute(todo);\n});\n\n// read\norma.selectFromTodo()\n  .titleEq(\"foo\") // equivalent to `where(\"title = ?\", \"foo\")`\n  .executeAsObservable() // first-class RxJava interface\n  .subscribe(...);\n\n// update\norma.updateTodo()\n  .titleEq(\"foo\")\n  .content(\"a new content\") // to setup what are updated\n  .execute();\n\n// delete\norma.deleteFromTodo()\n  .titleEq(\"foo\")\n  .execute();\n```\n\n## The Components\n\n### Database Handles\n\nA database handle, named `OrmaDatabase` by default, is generated by `orma-processor`, which is an entry point of all the high-level database operations.\n\nThis is typically used as a singleton instance and you don't need to manage its lifecycle. That is, you don't need to explicitly close it.\n\n### Models\n\nA **model** in Orma is a Java class that is annotated with `@Table`, which\nhas at least one column, a field annotated with `@Column` or `@PrimaryKey`.\n\n`orma-processor` generates helper classes for each model:\n`Schema`, `Relation`, `Selector`, `Updater`, and `Deleter`.\n\nBecause these helper classes are generated at the compile time, you\ncan use Orma as a type-safe ORM.\n\n### Schema Helpers\n\nA Schema helper, e.g. `Todo_Schema`, has metadata for the corresponding model.\n\nThis is an internal helper class and not intended to be employed by users.\n\n### Relation Helpers\n\nA Relation helper, e.g. `Todo_Relation`, is an entry point of table operations.\n\nThis is created by a database handle:\n\n```java\npublic static Todo_Relation relation() {\n  return orma.relationOfTodo();\n}\n```\n\nAnd is able to create `Selector`, `Updater`, `Deleter`, and `Inserter` for the target model.\n\n```java\nTodo_Relation todos = orma.relationOfTodo();\n\ntodos.selector().toList(); // Todo_Selector\ntodos.updater().content(\"foo\").execute(); // Todo_Updater\ntodos.inserter().execute(todo); // Inserter\u003cTodo\u003e\ntodos.deleter().execute(); // Todo_Deleter\n```\n\nThis can be a subset of a table which has `ORDER BY` clauses and `WHERE` clauses with some `List`-like methods:\n\n```java\nTodo_Relation todos = orma.relationOfTodo()\n  .doneEq(false) // can have conditions\n  .orderByCreatedTimeMillis(); // can have orders\n\n// List-like features:\nint count = todos.count();\nTodo todo = todos.get(0);\n\n// Convenience utilities\nint position = todos.indexOf(todo);\ntodos.deleteWithTransactionAsObservable()\n  .subscribeOn(Schedulers.io())\n  .observeOn(AndroidSchedulers.mainThread())\n  .subscribe(position -\u003e {\n    notifyItemRemoved(position); // assumes Adapter#notifyItemRemoved()\n  })\ntodos.truncateWithTransactionAsObservable()\n  .subscribeOn(Schedulers.io())\n  .subscribe();\n\n// Todo_Relation implements Iterable\u003cTodo\u003e\nfor (Todo todo : todos) {\n  // ...\n}\n```\n\nAnd has convenience `#upsert()` to \"save it anyway\", returning a new model:\n\n```java\nTodo_Relation todos = orma.relationOfTodo()\n\nTodo newTodo = todos.upsert(todo); // INSERT if it's not persistent; UPDATE Otherwise\n```\n\nUnlike `INSERT` with `OnConflict.REPLACE`, `#upsert()` doesn't break associations.\n\nNOTE: if you use a model after `#upsert()`, you must use the returned `newModel`.\nThis is because Orma does not change the model's primary key on `INSERT`.\n\n### Selector Helpers\n\nA `Selector` helper, e.g. `Todo_Selector`, is created by a `Relation`:\n\n```java\nTodo_Selector selector = relation().selector();\n// or orma.selectFromTodo();\n```\n\nThis is a query builder for `SELECT ... FROM *` statements.\n\n### Updater Helpers\n\nAn `Updater` helper, e.g. `Todo_Updater`, is created by a `Relation`:\n\n```java\nTodo_Updater updater = relation().updater();\n// or orma.updateTodo();\n```\n\nThis is a query builder for `UPDATE *` statements.\n\n### Deleter Helpers\n\nA `Deleter` helper, e.g. `Todo_Deleter`, is created by a `Relation`:\n\n```java\nTodo_Deleter deleter = relation().deleter();\n// or orma.deleteFromTodo();\n```\n\nThis is a query builder for `DELETE FROM *` statements.\n\n### Query Helper Methods\n\nThere are **Query Helpers** which are generated to query conditions and orders in a type-safe way.\n\nFor example, `titleEq()` shown in the synopsis section, are generated to help make `WHERE` and `ORDER BY` clauses,\nfor `Relation`, `Selector`, `Deleter`, and `Updater`.\n\nMost of them are generated for columns with `indexed = true`, and some are for `@PrimaryKey` columns.\n\n#### List of Query Helper Methods\n\nHere is a list of Query Helpers that are generated for **all** the `indexed` columns, where `*` is a column name pladeholder:\n\n| Method           | SQL                 |\n|:----------------:|:-------------------:|\n| `*Eq(value)`     | `* = value`         |\n| `*NotEq(value)`  | `* \u003c\u003e value`        |\n| `*In(values)`    | `* IN (values)`     |\n| `*NotIn(values)` | `* NOT IN (values)` |\n\nThe following are generated for `@Nullable` columns.\n\n| Method         | SQL             |\n|:--------------:|:---------------:|\n| `*IsNull()`    | `* IS NULL`     |\n| `*IsNotNull()` | `* IS NOT NULL` |\n\nThe following are generated for numeric columns\n (i.e. `byte`, `short`, `int`, `long`, `float`, `double`, and their corresponding box types)\n\n| Method           | SQL                 |\n|:----------------:|:-------------------:|\n| `*Lt(value)`     | `* \u003c value`         |\n| `*Le(values)`    | `* \u003c= value`        |\n| `*Gt(value)`     | `* \u003e value`         |\n| `*Ge(value)`     | `* \u003e= value`        |\n| `*Between(a, b)` | `* BETWEEN a AND b` |\n\nThe following are generated for `TEXT` and not `PRIMARY KEY` columns.\n\n| Method              | SQL                  |\n|:-------------------:|:--------------------:|\n| `*Glob(pattern)`    | `* GLOB pattern`     |\n| `*NotGlob(pattern)` | `* NOT GLOB pattern` |\n| `*Like(pattern)`    | `* LIKE pattern`     |\n| `*NotLike(pattern)` | `* NOT LIKE pattern` |\n\nAnd `ORDER BY` helpers:\n\n| Method           | SQL               |\n|:----------------:|:-----------------:|\n| `orderBy*Asc()`  | `ORDER BY * ASC`  |\n| `orderBy*Desc()` | `ORDER BY * DESC` |\n\n#### How to Control Generation of Query Helpers\n\n**This is an advanced setting for those who know what they do.**\n\nYou can control which Query Helpers are generated for a column by `@Column(helpers = ...)` attribute:\n\n```java\n@Column(\n    helpers = Column.Helpers.AUTO // default to AUTO\n)\n```\n\nHere are the definition of options defined in [Column.java](annotations/src/main/java/com/github/gfx/android/orma/annotation/Column.java):\n\n```java\nlong AUTO = -1; // the default, a smart way\nlong NONE = 0;\n\nlong CONDITION_EQ = 0b01;\nlong CONDITION_NOT_EQ = CONDITION_EQ \u003c\u003c 1;\nlong CONDITION_IS_NULL = CONDITION_NOT_EQ \u003c\u003c 1;\nlong CONDITION_IS_NOT_NULL = CONDITION_IS_NULL \u003c\u003c 1;\nlong CONDITION_IN = CONDITION_IS_NOT_NULL \u003c\u003c 1;\nlong CONDITION_NOT_IN = CONDITION_IN \u003c\u003c 1;\n\nlong CONDITION_LT = CONDITION_NOT_IN \u003c\u003c 1;\nlong CONDITION_LE = CONDITION_LT \u003c\u003c 1;\nlong CONDITION_GT = CONDITION_LE \u003c\u003c 1;\nlong CONDITION_GE = CONDITION_GT \u003c\u003c 1;\nlong CONDITION_BETWEEN = CONDITION_GE \u003c\u003c 1;\n\nlong CONDITIONS = CONDITION_EQ | CONDITION_NOT_EQ | CONDITION_IS_NULL | CONDITION_IS_NOT_NULL\n        | CONDITION_IN | CONDITION_NOT_IN\n        | CONDITION_LT | CONDITION_LE | CONDITION_GT | CONDITION_GE | CONDITION_BETWEEN;\n\nlong ORDER_IN_ASC = CONDITION_BETWEEN \u003c\u003c 1;\nlong ORDER_IN_DESC = ORDER_IN_ASC \u003c\u003c 1;\n\nlong ORDERS = ORDER_IN_ASC | ORDER_IN_DESC;\n\nlong ALL = CONDITIONS | ORDERS;\n```\n\n### The Inserter Helpers\n\nThis is a prepared statement for `INSERT INTO ...` for bulk insertions.\n\n```java\nInserter\u003cTodo\u003e inserter = relation().inserter();\n// or orma.insertIntoTodo()\n\ninserter.execute(todo);\ninserter.executeAll(todos);\n```\n\n## Details of Database Handles\n\nThe section describes the details of database handles.\n\n### Configuration of Database Handles\n\nThe database class is configured by the [`@Database`](https://github.com/gfx/Android-Orma/blob/master/annotations/src/main/java/com/github/gfx/android/orma/annotation/Database.java) annotation:\n\n```java\n@Database(\n    databaseClassName = \"OrmaDatabase\", // default to \"OrmaDatabase\"\n    includes = { /* ... */ } // Give model classes to handle\n    excludes = { /* ... */ } // Give model classes not to handle\n)\npublic class DatabaseConfiguration { }\n```\n\nThe annotated class is not used for now, but the package is used to place the OrmaDatabase class.\n\n### Database Handle Builders\n\n`OrmaDatabase.builder(Context)` returns a builder isntance, which\nhas configure the database handle instance:\n\n| Method                 | Description                 | Default             |\n|:----------------------:|:---------------------------:|:-------------------:|\n| `name(String)`         | The filename of SQLite DB   | `\"${package}.orma.db\"` |\n| `migrationEngine(MigrationEngine)`| Custom migration engine | `OrmaMigration`  |\n| `writeAheadLogging(boolean)`  | SQLite WAL flag      | `true`              |\n| `foreignKeys(boolean)` | SQLite FOREIGN_KEYS flag    | `true`              |\n| `migrationStep(int, ManualStepMigration.Step)` | A migration step | none   |\n| `trace(boolean)`       | Output executed queries to logcat if true | dynamic (*1) |\n| `readOnMainThread(AccessThreadConstraint)`  | Check read operation on main thread | dynamic (*2) |\n| `writeOnMainThread(AccessThreadConstraint)` | Check write operation on main thread | dynaimc (*3) |\n\n* **\\*1** `BuildConfig.DEBUG ? true : false`\n* **\\*2** `BuildConfig.DEBUG ? WARN : NONE`\n* **\\*3** `BuildConfig.DEBUG ? FATAL : NONE`\n\nNote that **Orma aborts if writing occurs on main thread** in debug build.\n\nUse background threads, e.g. via `AsyncTask` for writing, or RxJava interfaces with `Schedulers.io()`.\n\nOtherwise you can disable this behavior:\n\n```java\nOrmaDatabase orma = OrmaDatabase.builder(context)\n    .writeOnMainThread(AccessThreadConstraint.NONE)\n    .build();\n```\n\n### In-Memory Database\n\nYou can create in-memory databases by passing `null` to `OrmaDatabase.Builder#name()`.\n\nThis is useful for testing.\n\n## Details of Models\n\nThe section describes the details of model definition.\n\n### Setters and Getters\n\nOrma can use getters and setters if columns have corresponding methods.\n\nYou can also connect getters and setters with `@Getter` and `@Setter`\nrespectively, which tells `orma-processor` to use accessors.\n\nEach accessor name can have a column name in SQLite databases,\nwhich is inferred from its method name if omitted.\n\n```java\n@Table\npublic class KeyValuePair {\n\n    static final String kKey = \"Key\";\n\n    @Column(kKey) // specifies the name\n    private String key;\n\n    @Column // omits the name\n    private String value;\n\n    @Getter(kKey)\n    public String getKey() {\n        return key;\n    }\n\n    @Setter(kKey)\n    public void setKey(String key) {\n        this.key = key;\n    }\n\n    // used as a getter for the \"value\" column\n    // @Getter is optional in this case\n    public String getValue() {\n        return value;\n    }\n\n    // used as a setter for the \"value\" column\n    // @Setter is optional in this case\n    public void setValue(String value) {\n        this.value = value;\n    }\n}\n```\n\n### Immutable Models\n\nImmutable models, where all the fields are declared with `final`, are supported\nby annotating a constructor with `@Setter`.\n\n```java\n@Table\npublic class KeyValuePair {\n\n    @Column\n    public final String key;\n\n    @Column\n    public final String value;\n\n    @Setter\n    KeyValuePair(String key, String value) {\n        this.key = key;\n        this.value = value;\n    }\n}\n```\n\nIt can be declared with custom names:\n\n```java\n@Table\npublic class KeyValuePair {\n    static final String kKey = \"Key\";\n    static final String kValue = \"Value\";\n\n    @Column(kKey)\n    public final String key;\n\n    @Column(kValue)\n    public final String value;\n\n    KeyValuePair(@Setter(kKey) String key, @Setter(kValue) String value) {\n        this.key = key;\n        this.value = value;\n    }\n}\n```\n\n### Composite Indexes\n\nThere is the `indexes` parameter that `@Table` takes\nin order to create composite indexes (a.k.a. multi-column indexes).\n\n```java\n// for CREATE INDEX:\n@Table(indexes = @Index(value = {\"resourceType\", \"resourceId\"}))\npublic class Entry {\n\n    @PrimaryKey\n    public long id;\n\n    @Column\n    public String resourceType;\n\n    @Column\n    public long resourceId;\n}\n```\n\n\n```java\n// for CREATE UNIQUE INDEX:\n@Table(\n    indexes = @Index(\n                value = {\"resourceType\", \"resourceId\"},\n                unique = true\n        )\n)\npublic class Entry {\n\n    @PrimaryKey\n    public long id;\n\n    @Column\n    public String resourceType;\n\n    @Column\n    public long resourceId;\n}\n```\n\nComposite indexes generate query helper methods only for `==` and `ORDER BY` for helper classes like the following:\n\n* `Selector#resourceTypeAndResourceIdEq(String, long)`\n* `Selector#orderByResourceTypeAndResourceIdAsc()`\n* `Selector#orderByResourceTypeAndResourceIdDesc()`\n\nYou can control generated helpers with the `helpers` parameter.\n\nSee also [Query Helper Methods](#query-helper-methods).\n\n### Reserved Names\n\nColumn names starting `$` are reserved for metadata.\n\nOther names, including SQLite keywords, are allowed.\n\n## RxJava Integration\n\nRxJava integration provides a set of powerful API to transform, filter, and combine DB rows.\n\nFor example, there is a model named `Book` with `@Column(unique = true) String title`\nand you'd like to get a `Map\u003cString, Book\u003e` where the key is `Book#title`:\n\n```java\nMap\u003cString, Book\u003e map = db.selectFromBook()\n    .executeAsObservable()\n        .toMap(new Function\u003cBook, String\u003e() {\n            @Override\n            public String apply(Book book) throws Exception {\n                return book.title;\n            }\n        }).blockingGet();\n```\n\n## Associations\n\nTwo or more Orma models can be associated with **association** mechanism.\n\nThere are two type of associations: **has-one** and **has-many**.\n\nIn addition, there are another two kind of association supports: indirect associations with `SingleAssociation\u003cT\u003e` and direct associations.\n\n### Has-One Associations with `SingleAssociation\u003cT\u003e`\n\nThere is `SingleAssociation\u003cT\u003e` to support has-one associations, which is retrieved on demand; this is useful if the associations are not always used.\n\nFor example, a book has a publisher:\n\n```java\n@Table\nclass Publisher {\n  @PrimaryKey\n  public long id;\n}\n\n@Table\nclass Book {\n\n    @Column\n    public SingleAssociation\u003cPublisher\u003e publisher;\n}\n```\n\nTo save this a book:\n\n```java\nBook book = new Book();\nPublisher publisher = new Publisher();\n\nlong publisherId = db.insertIntoPublisher()\n    .execute(publisher);\n\n// if publisher has a valid primary key, `just(publisher)` is also okay.\nbook.publisher = SingleAssociation.just(publisherId);\n\ndb.insertIntoBook()\n    .execute(book)\n```\n\nTo get a publisher from books:\n\n```java\ndb.selectFromBook()\n    .forEach((book) -\u003e {\n        // blocking:\n        Publisher publisher = book.publisher.get();\n\n        // with RxJava Single\u003cPublisher\u003e:\n        book.publisher.single()\n            .subscribe((publisher) -\u003e {\n                // use publisher\n            })\n    });\n```\n\n### Direct Associations\n\nThere are _direct associations_, where an Orma model has another Orma model directly.\n\nGiven a `has-one` association, `Book has-one Publisher`:\n\n```java\n@Table\nclass Publisher {\n  @PrimaryKey\n  public long id;\n\n  @Column\n  public String name;\n}\n\n@Table\nclass Book {\n\n    @PrimaryKey\n    public long id;\n\n    @column\n    public String title;\n\n    @Column\n    public Publisher publisher;\n}\n```\n\nThe corresponding table definition is something like this:\n\n```sql\nCREATE TABLE `Publisher` (\n  `id` INTEGER PRIMARY KEY,\n  `name` TEXT NOT NULL\n)\nCREATE TABLE `Book` (\n  `id` INTEGER PRIMARY KEY,\n  `title` TEXT NOT NULL,\n  `publisher` INTEGER NOT NULL\n    REFERENCES `Publisher`(`id`) ON UPDATE CASCADE ON DELETE CASCADE\n)\n```\n\nIn SQL, `Book#publisher` refers `Publisher#id`, indicating the two tables\nshould be joined in `SELECT` statements.\n\nIn Java, `Book#publisher` is a `Publisher` instance, which is retrieved in each\n`SELECT` operations. There is no lazy loading in direct associations.\n\n### Has-Many Associations with `SingleAssociation\u003cT\u003e`\n\nHas-many associations are not directly supported but you can define a method to get associated objects:\n\n```java\n@Table\nclass Publisher {\n    @PrimaryKey\n    public long id;\n\n    // NOTE: If OrmaDatabase is a singleton, no parameter is required!\n    public Book_Relation getBooks(OrmaDatabase db) {\n        return db.relationOfBook().publisherEq(this);\n    }\n}\n\n@Table\nclass Book {\n\n    @Column(indexed = true)\n    public SingleAssociation\u003cPublisher\u003e publisher;\n\n}\n```\n\n### Has-Many Associations with Direct Associations\n\nAs `SingleAssociation` is, you can define a helper method to get has-many associations:\n\n```java\n@Table\nclass Publisher {\n    @PrimaryKey\n    public long id;\n\n    // NOTE: If OrmaDatabase is a singleton, no parameter is required!\n    public Book_Relation getBooks(OrmaDatabase db) {\n        return db.relationOfBook().publisherEq(this);\n    }\n}\n\n@Table\nclass Book {\n\n    @Column(indexed = true)\n    public Publisher publisher;\n\n}\n```\n\n### Limitations in Associations\n\n* There are no methods to query associated models\n\nThese issues will be fixed in a future.\n\n## Type Adapters\n\nOrma models are able to have embedded objects with **type adapters**, a.k.a. static type adapters,\nby defining classes with `@StaticTypeAdapter` annotation.\n\nFor example, if you want to embed [LatLng](https://developers.google.com/android/reference/com/google/android/gms/maps/model/LatLng)\nin your Orma model, you can define a type adapter like this:\n\n```java\n@StaticTypeAdapter(\n    targetType = LatLng.class, // required\n    serializedType = String.class // required\n)\npublic class LatLngAdapter {\n\n    // SerializedType serialize(TargetType source)\n    @NonNull\n    public static String serialize(@NonNull LatLng source) {\n        return source.latitude + \",\" + source.longitude\n    }\n\n    // TargetType deserialize(SerializedType serialized)\n    @NonNull\n    public static LatLng deserialize(@NonNull String serialized) {\n        String[] values = serialized.split(\",\");\n        return new LatLng(\n            Double.parseDouble(values[0]),\n            Double.parseDouble(values[1]));\n    }\n}\n```\n\n`@StaticTypeAdapter` requires `targetType` and `serializedType` options and two static methods `SerializedType serialize(TargetType)` and `TargetType deserialize(SerializedType)`.\n\n### How Serialized Types Used\n\nA `@StaticTypeAdapter#serializedType` is bound to an SQLite storage type.\nThus it must be one of the \"Java Type\" listed the table below, where each \"Java Type\" has a corresponding \"SQLite Type\":\n\n| Java Type | SQLite Type |\n|:---------:|:-----------:|\n| int       | INTEGER     |\n| short     | INTEGER     |\n| long      | INTEGER     |\n| boolean   | INTEGER     |\n| float     | REAL        |\n| double    | REAL        |\n| String    | TEXT        |\n| byte[]    | BLOB        |\n\n### `@StaticTypeAdapters` for Multiple Serializers at Once\n\nYou can also define multiple type serializers to single class with `@StaticTypeAdapters` annotation containers:\n\n```java\n@StaticTypeAdapters({\n    @StaticTypeAdapter(\n        targetType = MutableInt.class,\n        serializedType = int.class,\n        serializer = \"serializeMutableInt\",\n        deserializer = \"deserializeMutableInt\"\n    ),\n    @StaticTypeAdapter(\n        targetType = MutableLong.class,\n        serializedType = long.class,\n        serializer = \"serializeMutableLong\",\n        deserializer = \"deserializeMutableLong\"\n    )\n})\npublic class TypeAdapters {\n\n    public static int serializeMutableInt(@NonNull MutableInt target) {\n        return target.value;\n    }\n\n    @NonNull\n    public static MutableInt deserializeMutableInt(int deserialized) {\n        return new MutableInt(deserialized);\n    }\n\n    public static long serializeMutableLong(@NonNull MutableLong target) {\n        return target.value;\n    }\n\n    @NonNull\n    public static MutableLong deserializeMutableLong(long deserialized) {\n        return new MutableLong(deserialized);\n    }\n}\n```\n\n### Built-In Type Adapters\n\nThere are built-in type adapters for typically used value objects and collections:\n\n* `java.math.BigDecimal`\n* `java.math.BigInteger`\n* `java.nio.ByteBuffer`\n* `java.util.Currency`\n* `java.util.Date`\n* `java.sql.Date`\n* `java.sql.Time`\n* `java.sql.Timestamp`\n* `java.util.UUID`\n* `java.util.List\u003cString\u003e`\n* `java.util.ArrayList\u003cString\u003e`\n* `java.util.Set\u003cString\u003e`\n* `java.util.HashSet\u003cString\u003e`\n* `android.net.Uri`\n\n### Generic Type Adapters\n\nIf your `deserialize()` takes a `Class\u003cT\u003e` parameter, the type serializer is _generic_, handling classes with the common base classe.\n\nFor example, if you have some enums that implement `EnumDescription`, e.g. `T extends Enum\u003cT\u003e \u0026 EnumDescription`, you can handle it with a generic type adapter.\n\nGiven an interface `EnumDescription`:\n\n```java\npublic interface EnumDescription {\n\n    long getValue();\n}\n```\n\nAnd here is its type adapter:\n\n\n```java\n@StaticTypeAdapter(\n        targetType = EnumDescription.class,\n        serializedType = long.class\n)\npublic class EnumTypeAdapter {\n\n    public static \u003cT extends Enum\u003cT\u003e \u0026 EnumDescription\u003e long serialize(@NonNull T value) {\n        return value.getValue();\n    }\n\n    @NonNull\n    public static \u003cT extends Enum\u003cT\u003e \u0026 EnumDescription\u003e T deserialize(long serialized, @NonNull Class\u003cT\u003e type) {\n\n        for (T enumValue : type.getEnumConstants()) {\n            if (enumValue.getValue() == serialized) {\n                return enumValue;\n            }\n        }\n\n        throw new RuntimeException(\"Unknown id: \" + serialized + \" for \" + type);\n    }\n}\n```\n\nNow `deserialize()` uses the type information for the conclete target class.\n\n## Pagination\n\nThere are two style pagination. You can use either, but not mixed.\n\n### limit and offset\n\nSQL style pagination:\n\n```java\nfor (Todo todo : orma.selectFromTodo().titleEq(\"buy\").offset(0).limit(10)) {\n    // ...\n}\n```\n\n### page and per\n\n\"paging\" style pagination inspired from Ruby's [kaminari](https://github.com/kaminari/kaminari).\n\n```java\nfor (Todo todo : orma.selectFromTodo().titleEq(\"buy\").page(1).per(10)) {\n    // ...\n}\n```\n\nNote that `page` starts from 1.\n\n## Raw Queries\n\nFor low-level operations, e.g. executing a raw query, you can use\n`OrmaDatabase#getConnection()`, which returns `OrmaConnection`.\n\nFor example of `#rawQuery`:\n\n```java\nCursor cursor = db.getConnection().rawQuery(\"SELECT max(bookId) as max_id, min(bookId) as min_id FROM Book\");\ncursor.moveToFirst();\n// ...get data from cursor...\ncursor.close();\n```\n\nOr, you can use `#execSQL` for mutations:\n\n```java\nOrmaConnection conn = db.getConnection();\nconn.execSQL(\"VACUUM\");\nconn.execSQL(\"ANALYZE\");\n```\n\nNOTE: Don't use `#rawQuery()` for performance because Orma query builders are fast enough.\n\n## Migration\n\nThere is a pluggable migration mechanism via the `MigrationEngine` interface.\n\nThe default migration engine is `SchemaDiffMigration`, which handles\nschema changes by making diff with old and new DDL stored in `sqlite_master`.\nThat is, you don't need migration steps for the following cases:\n\n* Adding tables\n* Adding columns (In this case, you need to add `defaultExpr` or `@Nullable` to new columns for auto-migration to work)\n* Changing column types\n* Changing column constraints (`NOT NULL`, `UNIQUE`, and etc.)\n\nYou can also define migration steps for each schema version, which uses applications version code (i.e. `BuildConfig.VERSION`) as schema versions.\n\nHere is an example to define migration steps:\n\n```java\nint VERSION_2; // a past version of application's VERSION_CODE\n\nOrmaDatabase orma = OrmaDatabase.builder(this)\n        .migrationStep(VERSION_2, new ManualStepMigration.ChangeStep() {\n            @Override\n            public void change(@NonNull ManualStepMigration.Helper helper) {\n              Log.(TAG, helper.upgrade ? \"upgrade\" : \"downgrade\");\n              helper.execSQL(\"DROP TABLE foo\");\n              helper.execSQL(\"DROP TABLE bar\");\n            }\n        })\n        // ... other configurations\n        .build();\n```\n\nSee [migration/README.md](migration/README.md) for details.\n\n## DataSet Changed Events\n\nNOTE: **This is experimental in v4.2.5: its existence, signature or behavior might change without warning from one release to the next.**\n\n`Relation#createQueryObservable()` can create a event stream to observe data-set changed events for the relation.\n\nThis likes [SQLBrite](https://github.com/square/sqlbrite)'s' \"Query Observable\", whereas Orma's does not notify the initial event.\n\n```java\n// NOTE: Keep the observable instance. If it's released, the observable is disposed.\n\n// create a query observable, which is a hot observable\nObservable\u003cAuthor_Selector\u003e observable = db.relationOfAuthor()\n        .createQueryObservable();\n\n// subscribe the events\nobservable.flatMap(new Function\u003cAuthor_Selector, Observable\u003cAuthor\u003e\u003e() {\n    @Override\n    public Observable\u003cAuthor\u003e apply(Author_Selector selector) throws Exception {\n        Log.d(TAG, \"Author has been changed!\");\n        return selector.executeAsObservable();\n    }\n})\n        .map(new Function\u003cAuthor, String\u003e() {\n            @Override\n            public String apply(Author author) throws Exception {\n                return author.name;\n            }\n        })\n        .subscribe(new Consumer\u003cString\u003e() {\n            @Override\n            public void accept(String name) throws Exception {\n                Log.d(TAG, \"name: \" + name);\n            }\n        });\n```\n\nSee `OrmaListAdapter` and `OrmaRecyclerViewAdapter`, which use Query Observables to\ntrigger `#notifyDataSetChanged()`.\n\n* [OrmaListAdapter](https://github.com/gfx/Android-Orma/blob/master/library/src/main/java/com/github/gfx/android/orma/widget/OrmaListAdapter.java)\n* [OrmaRecyclerViewAdapter](https://github.com/gfx/Android-Orma/blob/master/library/src/main/java/com/github/gfx/android/orma/widget/OrmaRecyclerViewAdapter.java)\n\n## Cooperation with Serialization Libraries\n\nBeause Orma reuqires nothing to do to models, serializers, e.g. Android Parcels or GSON, can\nserialize Orma models.\n\n## Encryption\n\nThere's an encryption extension as `orma-encryption` since Orma v5.0.0-rc1:\n\n```build.gradle\ndependencies {\n    compile 'com.github.maskarade.android.orma:orma-encryption:6.0.2'\n}\n```\n\nThat provies `EncryptedDatabase`:\n\n```java\nString password = \"...\";\nOrmaDatabase orma = OrmaDatabase.builder(context)\n    .provider(new EncryptedDatabase.Provider(password))\n    // ...\n    .build();\n```\n\nEncrypted databases are managed by [SQLCipher](https://github.com/sqlcipher/android-database-sqlcipher), so the database files are not compatible with non-encrypted ones.\n\nNote that with this extension the database handle throws `net.sqlcipher.database.SQLException` instead of `android.database.SQLException` as runtime exceptions, so it might not be 100% compatible with the default database.\n\n## Example\n\nThere is [an example app](example/) to demonstrate:\n\n* Migration\n* Orma with `RecyclerView` / `ListView`\n* Benchmark (see below)\n\n## Benchmark\n\nThere is a simple benchmark with [Realm](https://github.com/realm/realm-java) and hand-written SQLiteDatabase code:\n\n[example/BenchmarkFragment](example/src/main/java/com/github/gfx/android/orma/example/fragment/BenchmarkFragment.java)\n\nHere is a result performed on Android 6.0.2 / Xperia Z4\nas of Orma v4.2.5 and Realm 2.3.0, processing 10 items x 100 times:\n\n\u003cimg src=\"benchmark.png\" alt=\"\" width=\"420\"/\u003e\n\nI welcome benchmark in another condition and/or another code.\n\n## FAQ\n\n### How can I enable debug logging on release build?\n\nCall `OrmaDatabase.Builder#trace(boolean)` with `true`:\n\n```java\nOrmaDatabase orma = OrmaDatabase.builder(context)\n    .trace(true)\n    .create();\n```\n\nThis option also enables logging in the default migration engine.\n\nIf you give a custom migration engine to the orma builder, you have to enable\n`trace` flag to its constructor:\n\n```java\nboolean trace = true;\nSchemaDiffMigration migration = new SchemaDiffMigration(context, trace);\n```\n\n### How can see the generated Java files?\n\nAs other annotation processors do, Orma save files to `$modle/build/generated/source/apt/`.\n\nYou can see [generated files for example models](example/build/generated/source/apt/normal/debug/com/github/gfx/android/orma/example/orma).\n\n### Does Orma work with Kotlin?\n\nYes, but it's _experimental_. Here is an example to use Orma with Kotlin:\n\nhttps://github.com/gfx/OrmaWithKotlin\n\nNOTE: Kotlin APT support, a.k.a. _kapt_, is **really unstable**. Don't ask me how to solve kapt problems.\n\n### When the database handle is opened and closed?\n\nOrma opens the database handle in instantiating `OrmaDatabase`, and you don't need to\nclose it.\n\nIn other word, you can define the database handle as a singleton instance in your application scope,\nand forget `close`.\n\n### Who uses Orma?\n\nHere is a list of open-source Androdi apps using Orma which are released to Google Play:\n\n* [gfx/Android-Helium](https://github.com/gfx/Android-Helium)\n* [DroidKaigi/conference-app-2017](https://github.com/DroidKaigi/conference-app-2017)\n\nOr you can search use cases by [GitHub search](https://github.com/search?o=desc\u0026q=%22com.github.gfx.android.orma%22+filename%3Abuild.gradle\u0026ref=searchresults\u0026s=indexed\u0026type=Code\u0026utf8=%E2%9C%93).\n\nAlso, here is a list of apps using Orma which are proprietary:\n\n* [Cookpad (ja)](https://play.google.com/store/apps/details?id=com.cookpad.android.activities)\n* [Abema TV (ja)](https://play.google.com/store/apps/details?id=tv.abema)\n\nTell me if your projects use Orma!\n\n## Support\n\n* Use [GitHub issues](https://github.com/gfx/Android-Orma/issues) for the issue tracker\n* Feel free to ask for questions to the author [@\\_\\_gfx\\_\\_](https://twitter.com/__gfx__)\n* Gitter Rooms:\n    * https://gitter.im/Android-Orma/Lobby (en)\n    * https://gitter.im/Android-Orma/Ja (ja)\n\n## Licenses in Runtime Dependencies\n\n* https://github.com/ReactiveX/RxJava - Apache Software License 2.0\n* [bkiers/sqlite-parser](https://github.com/bkiers/sqlite-parser) - The MIT License\n  * The original code of [SQLite.g4](sqliteparser/src/main/antlr/com/github/gfx/android/orma/sqliteparser/g/SQLite.g4)\n\n## Contribution\n\nPatches are welcome!\n\n## Release Engineering for Maintainers\n\n### Artifact Repository\n\nhttps://bintray.com/orma\n\n### What you do\n\n```shell\n./gradlew bumpMajor # or bumpMinor / bumpPatch\ncode CHANGES.md # edit change logs\ngit add -va\nmake publish # run tests, build artifacts, publish to jcenter, and make a git tag to HEAD\n```\n\nNote that you have to edit `VERSION` file by hand if you'd like to release release candidates.\n\nSee `Makefile` for details.\n\n### Documentation Tools\n\nVisual Studio Code (a.k.a. vscode) is recommended to edit README.md and CHANGELOG.md. Especially the ToC section is managed by [AlanWalk/Markdown-TOC](https://github.com/AlanWalk/Markdown-TOC).\n\n## See Also\n\n* [SQLite](http://sqlite.org/)\n* [SQLiteDatabase](http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html)\n* [Version of SQLite used in Android? - Stack Overflow](http://stackoverflow.com/questions/2421189/version-of-sqlite-used-in-android)\n\n## Authors and Contributors\n\nFUJI Goro ([gfx](https://github.com/gfx)).\n\nAnd contributors are listed here: [Contributors](https://github.com/gfx/Android-Orma/graphs/contributors)\n\nIf you are interested in sponsoring this project, it is really welcome, as I'll spend more time to develop this software: https://opencollective.com/android-orma\n\n## License\n\nCopyright (c) 2015 FUJI Goro (gfx).\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","funding_links":["https://opencollective.com/android-orma"],"categories":["数据库开发","Java"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaskarade%2FAndroid-Orma","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaskarade%2FAndroid-Orma","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaskarade%2FAndroid-Orma/lists"}