-
Notifications
You must be signed in to change notification settings - Fork 49
/
params.json
6 lines (6 loc) · 34.2 KB
/
params.json
1
2
3
4
5
6
{
"name": "Android-Orma",
"tagline": "A type-safe ORM for Android as a wrapper of SQLiteDatabase",
"body": "# Android Orma [![Circle CI](https://circleci.com/gh/gfx/Android-Orma/tree/master.svg?style=svg)](https://circleci.com/gh/gfx/Android-Orma/tree/master) [ ![Download](https://api.bintray.com/packages/gfx/maven/orma/images/download.svg) ](https://bintray.com/gfx/maven/orma/)\r\n\r\n<p align=\"center\">\r\n<img src=\"Orma.png\" width=\"256\" height=\"256\" alt=\"Android Orma\" />\r\n</p>\r\n\r\nOrma is an ORM (Object-Relation Mapper) for [Android SQLiteDatabase](http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html),\r\ngenerating helper classes at compile time with **annotation processing**, inspired in ActiveAndroid, GreenDAO, and Realm.\r\n\r\nThe interface of Orma is very simple and easy to use,\r\nas the author respects the Larry Wall's wisdom:\r\n\r\n> Easy things should be easy, and hard things should be possible\r\n-- [Larry Wall](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=7137)\r\n\r\n## Table of Contents\r\n\r\n<!-- TOC depthFrom:2 anchorMode:github.com -->\r\n\r\n- [Table of Contents](#table-of-contents)\r\n- [Motivation](#motivation)\r\n- [Requirements](#requirements)\r\n- [Getting Started](#getting-started)\r\n- [Synopsis](#synopsis)\r\n- [The Components](#the-components)\r\n - [Database Handles](#database-handles)\r\n - [Models](#models)\r\n - [Schema Helpers](#schema-helpers)\r\n - [Relation Helpers](#relation-helpers)\r\n - [Selector Helpers](#selector-helpers)\r\n - [Updater Helpers](#updater-helpers)\r\n - [Deleter Helpers](#deleter-helpers)\r\n - [Query Helper Methods](#query-helper-methods)\r\n - [List of Query Helper Methods](#list-of-query-helper-methods)\r\n - [How to Control Generation of Query Helpers](#how-to-control-generation-of-query-helpers)\r\n - [The Inserter Helpers](#the-inserter-helpers)\r\n- [Details of Database Handles](#details-of-database-handles)\r\n - [Configuration of Database Handles](#configuration-of-database-handles)\r\n - [Database Handle Builders](#database-handle-builders)\r\n- [Details of Models](#details-of-models)\r\n - [Setters and Getters](#setters-and-getters)\r\n - [Immutable Models](#immutable-models)\r\n- [Associations](#associations)\r\n - [Has-One Associations with `SingleAssociation<T>`](#has-one-associations-with-singleassociationt)\r\n - [Has-Many Associations with `SingleAssociation<T>`](#has-many-associations-with-singleassociationt)\r\n - [Direct Associations](#direct-associations)\r\n - [Limitations in Associations](#limitations-in-associations)\r\n- [Type Adapters](#type-adapters)\r\n - [How Serialized Types Used](#how-serialized-types-used)\r\n - [`@StaticTypeAdapters` for Multiple Serializers at Once](#statictypeadapters-for-multiple-serializers-at-once)\r\n - [Built-In Type Adapters](#built-in-type-adapters)\r\n- [Raw Queries](#raw-queries)\r\n- [Migration](#migration)\r\n- [DataSet Changed Events](#dataset-changed-events)\r\n- [Cooperation with Serialization Libraries](#cooperation-with-serialization-libraries)\r\n- [Example](#example)\r\n- [Benchmark](#benchmark)\r\n- [Method Count](#method-count)\r\n- [FAQ](#faq)\r\n - [Can't build my project.](#cant-build-my-project)\r\n - [How can I enable debug logging on release build?](#how-can-i-enable-debug-logging-on-release-build)\r\n - [How can see the generated Java files?](#how-can-see-the-generated-java-files)\r\n - [Does Orma work with Kotlin?](#does-orma-work-with-kotlin)\r\n - [Does Orma work with the Jack compiler?](#does-orma-work-with-the-jack-compiler)\r\n - [When the database handle is opened and closed?](#when-the-database-handle-is-opened-and-closed)\r\n - [Who uses Orma?](#who-uses-orma)\r\n- [Support](#support)\r\n- [Licenses in Runtime Dependencies](#licenses-in-runtime-dependencies)\r\n- [Contribution](#contribution)\r\n- [Release Engineering for Maintainers](#release-engineering-for-maintainers)\r\n- [See Also](#see-also)\r\n- [Authors and Contributors](#authors-and-contributors)\r\n- [License](#license)\r\n\r\n<!-- /TOC -->\r\n\r\n## Motivation\r\n\r\nThere are already [a lot of ORMs](https://android-arsenal.com/tag/69). Why I have to add another wheel?\r\n\r\nThe answer is that I need ORM that have *all* the following features:\r\n\r\n* Fast as hand-written code\r\n* POJO models\r\n * Model classes should have no restriction\r\n * Might implement `Parcelable` and/or extend any classes\r\n * They should be passed to another thread\r\n* A database handle must be an object instance\r\n * Not a singleton class\r\n * Not a static-method based class\r\n* Easy migration\r\n * Some `ALTER TABLE`, e.g. `add column` and `drop column`, should be detectd and processed\r\n * There is a wheel in Perl: [SQL::Translator::Diff](https://metacpan.org/pod/SQL::Translator::Diff)\r\n* Code completion friendly\r\n * `db.selectFromModel()` is better than `new Select(Model.class)`\r\n* Custom raw queries are sometimes inevitable\r\n * `GROUP BY ... HAVING ...`\r\n * `SELECT max(value), min(value), avg(value), count(value) FROM ...`\r\n\r\nAnd now they are what Orma has.\r\n\r\n## Requirements\r\n\r\n* JDK 8 (1.8.0_66 or later) to build\r\n* Android API level 15 to use\r\n\r\n## Getting Started\r\n\r\nDeclare dependencies to use Orma and its annotation processor.\r\n\r\n```gradle:build.gradle\r\ndependencies {\r\n annotationProcessor 'com.github.gfx.android.orma:orma-processor:4.0.0'\r\n compile 'com.github.gfx.android.orma:orma:4.0.0'\r\n}\r\n```\r\n\r\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.\r\n\r\n## Synopsis\r\n\r\nFirst, define model classes annotated with `@Table`, `@Column`, and `@PrimaryKey` and run the **Build APK** command to generate helper classes.\r\n\r\n```java\r\npackage com.github.gfx.android.orma.example;\r\n\r\nimport com.github.gfx.android.orma.annotation.Column;\r\nimport com.github.gfx.android.orma.annotation.PrimaryKey;\r\nimport com.github.gfx.android.orma.annotation.Table;\r\n\r\nimport android.support.annotation.Nullable;\r\n\r\n@Table\r\npublic class Todo {\r\n\r\n @PrimaryKey\r\n public long id;\r\n\r\n @Column(indexed = true)\r\n public String title;\r\n\r\n @Column\r\n @Nullable // allows NULL (default: NOT NULL)\r\n public String content;\r\n\r\n @Column\r\n public long createdTimeMillis;\r\n}\r\n```\r\n\r\nSecond, instantiate a database handle `OrmaDatabase`, which is generated by `orma-processor`.\r\n\r\nHere is an example to configure `OrmaDatabase`:\r\n\r\n```java\r\n// See OrmaDatabaseBuilderBase for other options.\r\nOrmaDatabase orma = OrmaDatabase.builder(context)\r\n .build();\r\n```\r\n\r\nThen, you can create, read, update and delete models via `OrmaDatabase`:\r\n\r\n```java\r\nTodo todo = ...;\r\n\r\n// create\r\norma.insertIntoTodo(todo);\r\n\r\n// prepared statements with transaction\r\norma.transactionSync( -> { // or transactionAsync() to execute tasks in background\r\n Inserter<Todo> inserter = orma.prepareInsertIntoTodo();\r\n inserter.execute(todo);\r\n});\r\n\r\n// read\r\norma.selectFromTodo()\r\n .titleEq(\"foo\") // equivalent to `where(\"title = ?\", \"foo\")`\r\n .executeAsObservable() // first-class RxJava interface\r\n .subscribe(...);\r\n\r\n// update\r\norma.updateTodo()\r\n .titleEq(\"foo\")\r\n .content(\"a new content\") // to setup what are updated\r\n .execute();\r\n\r\n// delete\r\norma.deleteFromTodo()\r\n .titleEq(\"foo\")\r\n .execute();\r\n```\r\n\r\nNote that **Orma aborts if writing occurs on main thread** in debug build.\r\n\r\nUse background threads for writing or RxJava interfaces with `Schedulers.io()`.\r\n\r\nOtherwise you can disable this behavior:\r\n\r\n```java\r\nOrmaDatabase orma = OrmaDatabase.builder(context)\r\n .writeOnMainThread(BuildConfig.DEBUG ? AccessThreadConstraint.WARNING : AccessThreadConstraint.NONE)\r\n .build();\r\n```\r\n\r\n## The Components\r\n\r\n### Database Handles\r\n\r\nA database handle, named `OrmaDatabase` by default, is generated by `orma-processor`, which is an entry point of all the high-level database operations.\r\n\r\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.\r\n\r\n### Models\r\n\r\nA **model** in Orma is a Java class that is annotated with `@Table`, which\r\nhas at least one column, a field annotated with `@Column` or `@PrimaryKey`.\r\n\r\n`orma-processor` generates helper classes for each model:\r\n`Schema`, `Relation`, `Selector`, `Updater`, and `Deleter`.\r\n\r\nBecause these helper classes are generated at the compile time, you\r\ncan use Orma as a type-safe ORM.\r\n\r\n### Schema Helpers\r\n\r\nA Schema helper, e.g. `Todo_Schema`, has metadata for the corresponding model.\r\n\r\nThis is an internal helper class and not intended to be employed by users.\r\n\r\n### Relation Helpers\r\n\r\nA Relation helper, e.g. `Todo_Relation`, is an entry point of table operations, which has conditions and orderings.\r\n\r\nThis is created by a database handle:\r\n\r\n```java\r\npublic static Todo_Relation relation() {\r\n return orma.relationOfTodo();\r\n}\r\n```\r\n\r\nAnd is able to create `Selector`, `Updater`, `Deleter`, and `Inserter` for the model.\r\n\r\n```java\r\nTodo_Relation todos = orma.relationOfTodo();\r\n\r\ntodos.selector().toList(); // Todo_Selector\r\ntodos.updater().content(\"foo\").execute(); // Todo_Updater\r\ntodos.inserter().execute(todo); // Inserter<Todo>\r\ntodos.deleter().execute(); // Todo_Deleter\r\n```\r\n\r\nThis can be a subset of a table which has `ORDER BY` clauses and `WHERE` clauses with some `List`-like methods:\r\n\r\n```java\r\nTodo_Relation todos = orma.relationOfTodo()\r\n .doneEq(false) // can have conditions\r\n .orderByCreatedTimeMillis(); // can have orders\r\n\r\n// List-like features:\r\nint count = todos.count();\r\nTodo todo = todos.get(0);\r\n\r\n// Convenience utilities\r\nint position = todos.indexOf(todo);\r\ntodos.deleteWithTransactionAsObservable()\r\n .subscribeOn(Schedulers.io())\r\n .observeOn(AndroidSchedulers.mainThread())\r\n .subscribe(position -> {\r\n notifyItemRemoved(position); // assumes Adapter#notifyItemRemoved()\r\n })\r\ntodos.truncateWithTransactionAsObservable()\r\n .subscribeOn(Schedulers.io())\r\n .subscribe();\r\n\r\n// Todo_Relation implements Iterable<Todo>\r\nfor (Todo todo : todos) {\r\n // ...\r\n}\r\n```\r\n\r\n### Selector Helpers\r\n\r\nA `Selector` helper, e.g. `Todo_Selector`, is created by a `Relation`:\r\n\r\n```java\r\nTodo_Selector selector = relation().selector();\r\n// or orma.selectFromTodo();\r\n```\r\n\r\nThis is a query builder for `SELECT ... FROM *` statements.\r\n\r\n### Updater Helpers\r\n\r\nAn `Updater` helper, e.g. `Todo_Updater`, is created by a `Relation`:\r\n\r\n```java\r\nTodo_Updater updater = relation().updater();\r\n// or orma.updateTodo();\r\n```\r\n\r\nThis is a query builder for `UPDATE *` statements.\r\n\r\n### Deleter Helpers\r\n\r\nA `Deleter` helper, e.g. `Todo_Deleter`, is created by a `Relation`:\r\n\r\n```java\r\nTodo_Deleter deleter = relation().deleter();\r\n// or orma.deleteFromTodo();\r\n```\r\n\r\nThis is a query builder for `DELETE FROM *` statements.\r\n\r\n### Query Helper Methods\r\n\r\nThere are **Query Helpers** which are generated to query conditions and orders in a type-safe way.\r\n\r\nFor example, `titleEq()` shown in the synopsis section, are generated to help make `WHERE` and `ORDER BY` clauses,\r\nfor `Relation`, `Selecotr`, `Deleter`, and `Updater`.\r\n\r\nMost of them are generated for columns with `indexed = true`, and some are for `@PrimaryKey` columns.\r\n\r\n#### List of Query Helper Methods\r\n\r\nHere is a list of Query Helpers that are generated for **all** the `indexed` columns, where `*` is a column name pladeholder:\r\n\r\n| Method | SQL |\r\n|:----------------:|:-------------------:|\r\n| `*Eq(value)` | `* = value` |\r\n| `*NotEq(value)` | `* <> value` |\r\n| `*In(values)` | `* IN (values)` |\r\n| `*NotIn(values)` | `* NOT IN (values)` |\r\n\r\nThe following are generated for `@Nullable` columns.\r\n\r\n| Method | SQL |\r\n|:--------------:|:---------------:|\r\n| `*IsNull()` | `* IS NULL` |\r\n| `*IsNotNull()` | `* IS NOT NULL` |\r\n\r\nThe following are generated for numeric columns\r\n (i.e. `byte`, `short`, `int`, `long`, `float`, `double`, and their corresponding box types)\r\n\r\n| Method | SQL |\r\n|:----------------:|:-------------------:|\r\n| `*Lt(value)` | `* < value` |\r\n| `*Le(values)` | `* <= value` |\r\n| `*Gt(value)` | `* > value` |\r\n| `*Ge(value)` | `* >= value` |\r\n| `*Between(a, b)` | `* BETWEEN a AND b` |\r\n\r\nAnd `ORDER BY` helpers:\r\n\r\n| Method | SQL |\r\n|:----------------:|:-----------------:|\r\n| `orderBy*Asc()` | `ORDER BY * ASC` |\r\n| `orderBy*Desc()` | `ORDER BY * DESC` |\r\n\r\n#### How to Control Generation of Query Helpers\r\n\r\n**This is an advanced setting for those who know what they do.**\r\n\r\nYou can control which Query Helpers are generater for a column by `@Column(helpers = ...)` attribute:\r\n\r\n```java\r\n@Column(\r\n helpers = Column.Helpers.AUTO // default to AUTO\r\n)\r\n```\r\n\r\nHere are the definition of options defined in [Column.java](annotations/src/main/java/com/github/gfx/android/orma/annotation/Column.java):\r\n\r\n```java\r\nlong AUTO = -1; // the default, a smart way\r\nlong NONE = 0;\r\n\r\nlong CONDITION_EQ = 0b01;\r\nlong CONDITION_NOT_EQ = CONDITION_EQ << 1;\r\nlong CONDITION_IS_NULL = CONDITION_NOT_EQ << 1;\r\nlong CONDITION_IS_NOT_NULL = CONDITION_IS_NULL << 1;\r\nlong CONDITION_IN = CONDITION_IS_NOT_NULL << 1;\r\nlong CONDITION_NOT_IN = CONDITION_IN << 1;\r\n\r\nlong CONDITION_LT = CONDITION_NOT_IN << 1;\r\nlong CONDITION_LE = CONDITION_LT << 1;\r\nlong CONDITION_GT = CONDITION_LE << 1;\r\nlong CONDITION_GE = CONDITION_GT << 1;\r\nlong CONDITION_BETWEEN = CONDITION_GE << 1;\r\n\r\nlong CONDITIONS = CONDITION_EQ | CONDITION_NOT_EQ | CONDITION_IS_NULL | CONDITION_IS_NOT_NULL\r\n | CONDITION_IN | CONDITION_NOT_IN\r\n | CONDITION_LT | CONDITION_LE | CONDITION_GT | CONDITION_GE | CONDITION_BETWEEN;\r\n\r\nlong ORDER_IN_ASC = CONDITION_BETWEEN << 1;\r\nlong ORDER_IN_DESC = ORDER_IN_ASC << 1;\r\n\r\nlong ORDERS = ORDER_IN_ASC | ORDER_IN_DESC;\r\n\r\nlong ALL = CONDITIONS | ORDERS;\r\n```\r\n\r\n### The Inserter Helpers\r\n\r\nThis is a prepared statement for `INSERT INTO ...` for bulk insertions.\r\n\r\n```java\r\nInserter<Todo> inserter = relation().inserter();\r\n// or orma.insertIntoTodo()\r\n\r\ninserter.execute(todo);\r\ninserter.executeAll(todos);\r\n```\r\n\r\n## Details of Database Handles\r\n\r\nThe section describes the details of database handles.\r\n\r\n### Configuration of Database Handles\r\n\r\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:\r\n\r\n```java\r\n@Database(\r\n databaseClassName = \"OrmaDatabase\", // default to \"OrmaDatabase\"\r\n includes = { /* ... */ } // Give model classes to handle\r\n excludes = { /* ... */ } // Give model classes not to handle\r\n)\r\npublic class DatabaseConfiguration { }\r\n```\r\n\r\nThe annotated class is not used for now, but the package is used to place the OrmaDatabase class.\r\n\r\n### Database Handle Builders\r\n\r\n`OrmaDatabase.builder(Context)` returns a builder isntance, which\r\nhas configure the database handle instance:\r\n\r\n| Method | Description | Default |\r\n|:----------------------:|:---------------------------:|:-------------------:|\r\n| `name(String)` | The filename of SQLite DB | `\"${package}.orma.db\"` |\r\n| `migrationEngine(MigrationEngine)`| Custom migration engine | `OrmaMigration` |\r\n| `writeAheadLogging(boolean)` | SQLite WAL flag | `true` |\r\n| `foreignKeys(boolean)` | SQLite FOREIGN_KEYS flag | `true` |\r\n| `migrationStep(int, ManualStepMigration.Step)` | A migration step | none |\r\n| `trace(boolean)` | Output executed queries to logcat if true | dynamic (*1) |\r\n| `readOnMainThread(AccessThreadConstraint)` | Check read operation on main thread | dynamic (*2) |\r\n| `writeOnMainThread(AccessThreadConstraint)` | Check write operation on main thread | dynaimc (*3) |\r\n\r\n* **\\*1** `BuildConfig.DEBUG ? true : false`\r\n* **\\*2** `BuildConfig.DEBUG ? WARN : NONE`\r\n* **\\*3** `BuildConfig.DEBUG ? FATAL : NONE`\r\n\r\n## Details of Models\r\n\r\nThe section describes the details of model definition.\r\n\r\n### Setters and Getters\r\n\r\nOrma can use getters and setters if columns have corresponding methods.\r\n\r\nYou can also connect getters and setters with `@Getter` and `@Setter`\r\nrespectively, which tells `orma-processor` to use accessors.\r\n\r\nEach accessor name can have a column name in SQLite databases,\r\nwhich is inferred from its method name if omitted.\r\n\r\n```java\r\n@Table\r\npublic class KeyValuePair {\r\n\r\n static final String kKey = \"Key\";\r\n\r\n @Column(kKey) // specifies the name\r\n private String key;\r\n\r\n @Column // omits the name\r\n private String value;\r\n\r\n @Getter(kKey)\r\n public String getKey() {\r\n return key;\r\n }\r\n\r\n @Setter(kKey)\r\n public void setKey(String key) {\r\n this.key = key;\r\n }\r\n\r\n // used as a getter for the \"value\" column\r\n // @Getter is optional in this case\r\n public String getValue() {\r\n return value;\r\n }\r\n\r\n // used as a setter for the \"value\" column\r\n // @Setter is optional in this case\r\n public void setValue(String value) {\r\n this.value = value;\r\n }\r\n}\r\n```\r\n\r\n### Immutable Models\r\n\r\nImmutable models, where all the fields are declared with `final`, are supported\r\nby annotating a constructor with `@Setter`.\r\n\r\n```java\r\n@Table\r\npublic class KeyValuePair {\r\n\r\n @Column\r\n public final String key;\r\n\r\n @Column\r\n public final String value;\r\n\r\n @Setter\r\n KeyValuePair(String key, String value) {\r\n this.key = key;\r\n this.value = value;\r\n }\r\n}\r\n```\r\n\r\nIt can be declared with custom names:\r\n\r\n```java\r\n@Table\r\npublic class KeyValuePair {\r\n static final String kKey = \"Key\";\r\n static final String kValue = \"Value\";\r\n\r\n @Column(kKey)\r\n public final String key;\r\n\r\n @Column(kValue)\r\n public final String value;\r\n\r\n KeyValuePair(@Setter(kKey) String key, @Setter(kValue) String value) {\r\n this.key = key;\r\n this.value = value;\r\n }\r\n}\r\n```\r\n\r\n## Associations\r\n\r\nTwo or more Orma models can be associated with **association** mechanism.\r\n\r\nThere are two type of associations: **has-one** and **has-many**.\r\n\r\nIn addition, there are another two kind of association supports: indirect associations with `SingleAssociation<T>` and direct associations.\r\n\r\n### Has-One Associations with `SingleAssociation<T>`\r\n\r\nThere is `SingleAssociation<T>` to support has-one associations, which is\r\nretrieved on demand, or loaded lazily.\r\n\r\nFor example, a book has a publisher:\r\n\r\n```java\r\n@Table\r\nclass Publisher {\r\n @PrimaryKey\r\n public long id;\r\n}\r\n\r\n@Table\r\nclass Book {\r\n\r\n @Column\r\n public SingleAssociation<Publisher> publisher;\r\n\r\n}\r\n```\r\n\r\nThe entity of `Book#publisher` is `Publisher#id`.\r\n\r\n### Has-Many Associations with `SingleAssociation<T>`\r\n\r\nHas-many associations are not directly supported but you can define a method to get associated objects:\r\n\r\n```java\r\n@Table\r\nclass Publisher {\r\n @PrimaryKey\r\n public long id;\r\n\r\n public Book_Relation getBooks(OrmaDatabase orma) {\r\n return orma.relationOfBook().publisherEq(this);\r\n }\r\n}\r\n\r\n@Table\r\nclass Book {\r\n\r\n @Column(indexed = true)\r\n public SingleAssociation<Publisher> publisher;\r\n\r\n}\r\n```\r\n\r\n### Direct Associations\r\n\r\nThere are _direct associations_, where an Orma model has another Orma model directly.\r\n\r\nGiven a `has-one` association, `Book has-one Publisher`:\r\n\r\n```java\r\n@Table\r\nclass Publisher {\r\n @PrimaryKey\r\n public long id;\r\n\r\n @Column\r\n public String name;\r\n}\r\n\r\n@Table\r\nclass Book {\r\n\r\n @PrimaryKey\r\n public long id;\r\n\r\n @column\r\n public String title;\r\n\r\n @Column\r\n public Publisher publisher;\r\n}\r\n```\r\n\r\nThe corresponding table definition is something like this:\r\n\r\n```sql\r\nCREATE TABLE `Publisher` (\r\n `id` INTEGER PRIMARY KEY,\r\n `name` TEXT NOT NULL\r\n)\r\nCREATE TABLE `Book` (\r\n `id` INTEGER PRIMARY KEY,\r\n `title` TEXT NOT NULL,\r\n `publisher` INTEGER NOT NULL\r\n REFERENCES `Publisher`(`id`) ON UPDATE CASCADE ON DELETE CASCADE\r\n)\r\n```\r\n\r\nIn SQL, `Book#publisher` refers `Publisher#id`, indicating the two tables\r\nshould be joined in `SELECT` statements.\r\n\r\nIn Java, `Book#publisher` is a `Publisher` instance, which is retrieved in each\r\n`SELECT` operations. There is no lazy loading in direct associations.\r\n\r\n### Limitations in Associations\r\n\r\n* There are no methods to query associated models\r\n\r\nThese issues will be fixed in a future.\r\n\r\n## Type Adapters\r\n\r\nOrma models are able to have embedded objects with **type adapters**, a.k.a. static type adapters,\r\nby defining classes with `@StaticTypeAdapter` annotation.\r\n\r\nFor example, if you want to embed [LatLng](https://developers.google.com/android/reference/com/google/android/gms/maps/model/LatLng)\r\nin your Orma model, you can define a type adapter like this:\r\n\r\n```java\r\n@StaticTypeAdapter(\r\n targetType = LatLng.class, // required\r\n serializedType = String.class // required\r\n)\r\npublic class LatLngAdapter {\r\n\r\n // SerializedType serialize(TargetType source)\r\n @NonNull\r\n public static String serialize(@NonNull LatLng source) {\r\n return source.latitude + \",\" + source.longitude\r\n }\r\n\r\n // TargetType deserialize(SerializedType serialized)\r\n @NonNull\r\n public static LatLng deserialize(@NonNull String serialized) {\r\n String[] values = serialized.split(\",\");\r\n return new LatLng(\r\n Double.parseDouble(values[0]),\r\n Double.parseDouble(values[1]));\r\n }\r\n}\r\n```\r\n\r\n`@StaticTypeAdapter` requires `targetType` and `serializedType` options and two static methods `SerializedType serialize(TargetType)` and `TargetType deserialize(SerializedType)`.\r\n\r\n### How Serialized Types Used\r\n\r\nA `@StaticTypeAdapter#serializedType` is bound to an SQLite storage type.\r\nThus it must be one of the \"Java Type\" listed the table below, where each \"Java Type\" has a corresponding \"SQLite Type\":\r\n\r\n| Java Type | SQLite Type |\r\n|:---------:|:-----------:|\r\n| int | INTEGER |\r\n| short | INTEGER |\r\n| long | INTEGER |\r\n| boolean | INTEGER |\r\n| float | REAL |\r\n| double | REAL |\r\n| String | TEXT |\r\n| byte[] | BLOB |\r\n\r\n### `@StaticTypeAdapters` for Multiple Serializers at Once\r\n\r\nYou can also define multiple type serializers to single class with `@StaticTypeAdapters` annotation containers:\r\n\r\n```java\r\n@StaticTypeAdapters({\r\n @StaticTypeAdapter(\r\n targetType = MutableInt.class,\r\n serializedType = int.class,\r\n serializer = \"serializeMutableInt\",\r\n deserializer = \"deserializeMutableInt\"\r\n ),\r\n @StaticTypeAdapter(\r\n targetType = MutableLong.class,\r\n serializedType = long.class,\r\n serializer = \"serializeMutableLong\",\r\n deserializer = \"deserializeMutableLong\"\r\n )\r\n})\r\npublic class TypeAdapters {\r\n\r\n public static int serializeMutableInt(@NonNull MutableInt target) {\r\n return target.value;\r\n }\r\n\r\n @NonNull\r\n public static MutableInt deserializeMutableInt(int deserialized) {\r\n return new MutableInt(deserialized);\r\n }\r\n\r\n public static long serializeMutableLong(@NonNull MutableLong target) {\r\n return target.value;\r\n }\r\n\r\n @NonNull\r\n public static MutableLong deserializeMutableLong(long deserialized) {\r\n return new MutableLong(deserialized);\r\n }\r\n}\r\n```\r\n\r\n### Built-In Type Adapters\r\n\r\nThere are built-in type adapters for typically used value objects and collections:\r\n\r\n* `java.math.BigDecimal`\r\n* `java.math.BigInteger`\r\n* `java.nio.ByteBuffer`\r\n* `java.util.Currency`\r\n* `java.util.Date`\r\n* `java.sql.Date`\r\n* `java.sql.Time`\r\n* `java.sql.Timestamp`\r\n* `java.util.UUID`\r\n* `java.util.List<String>`\r\n* `java.util.ArrayList<String>`\r\n* `java.util.Set<String>`\r\n* `java.util.HashSet<String>`\r\n* `android.net.Uri`\r\n\r\n## Raw Queries\r\n\r\nFor low-level operations, e.g. executing a raw query, you can use\r\n`OrmaDatabase#getConnection()`, which returns `OrmaConnection`.\r\n\r\nFor example:\r\n\r\n```java\r\nCursor cursor = db.getConnection().rawQuery(\"SELECT max(bookId) as max_id, min(bookId) as min_id FROM Book\");\r\ncursor.moveToFirst();\r\n// get data from cursor\r\ncursor.close();\r\n```\r\n\r\nNOTE: Don't use `rawQuery()` for performance because Orma query builders are fast enough.\r\n\r\n## Migration\r\n\r\nThere is a pluggable migration mechanism via the `MigrationEngine` interface.\r\n\r\nThe default migration engine is `SchemaDiffMigration`, which handles\r\nschema changes by making diff with old and new DDL stored in `sqlite_master`.\r\nThat is, you don't need migration steps for the following cases:\r\n\r\n* Adding tables\r\n* Adding columns\r\n* Changing column types\r\n* Changing column constraints (`NOT NULL`, `UNIQUE`, and etc.)\r\n\r\nOf course, you can define migration steps for each schema version (or `BuildConfig.VERSION`).\r\n\r\nHere is an example to define migration steps:\r\n\r\n```java\r\nint VERSION_2; // a past version of VERSION_CODE\r\n\r\nOrmaDatabase orma = OrmaDatabase.builder(this)\r\n .migrationStep(VERSION_2, new ManualStepMigration.ChangeStep() {\r\n @Override\r\n public void change(@NonNull ManualStepMigration.Helper helper) {\r\n Log.(TAG, helper.upgrade ? \"upgrade\" : \"downgrade\");\r\n helper.execSQL(\"DROP TABLE foo\");\r\n helper.execSQL(\"DROP TABLE bar\");\r\n }\r\n })\r\n // ... other configurations\r\n .build();\r\n```\r\n\r\nSee [migration/README.md](migration/README.md) for details.\r\n\r\n## DataSet Changed Events\r\n\r\nNOTE: **This is experimental in v4.0.0: its existence, signature or behavior might change without warning from one release to the next.**\r\n\r\n`Relation#createQueryObservable()` can create a event stream to observe data-set changed events for the relation.\r\n\r\nThis likes [SQLBrite](https://github.com/square/sqlbrite)'s' \"Query Observable\", whereas Orma's does not notify the initial event.\r\n\r\n```java\r\n// NOTE: Keep the observable instance. If it's released, the observable is disposed.\r\n\r\n// create a query observable, which is a hot observable\r\nObservable<Author_Selector> observable = db.relationOfAuthor()\r\n .createQueryObservable();\r\n\r\n// subscribe the events\r\nobservable.flatMap(new Function<Author_Selector, Observable<Author>>() {\r\n @Override\r\n public Observable<Author> apply(Author_Selector selector) throws Exception {\r\n Log.d(TAG, \"Author has been changed!\");\r\n return selector.executeAsObservable();\r\n }\r\n})\r\n .map(new Function<Author, String>() {\r\n @Override\r\n public String apply(Author author) throws Exception {\r\n return author.name;\r\n }\r\n })\r\n .subscribe(new Consumer<String>() {\r\n @Override\r\n public void accept(String name) throws Exception {\r\n Log.d(TAG, \"name: \" + name);\r\n }\r\n });\r\n```\r\n\r\nSee `OrmaListAdapter` and `OrmaRecyclerViewAdapter`, which use Query Observables to\r\ntrigger `#notifyDataSetChanged()`.\r\n\r\n* [OrmaListAdapter](https://github.com/gfx/Android-Orma/blob/master/library/src/main/java/com/github/gfx/android/orma/widget/OrmaListAdapter.java)\r\n* [OrmaRecyclerViewAdapter](https://github.com/gfx/Android-Orma/blob/master/library/src/main/java/com/github/gfx/android/orma/widget/OrmaRecyclerViewAdapter.java)\r\n\r\n## Cooperation with Serialization Libraries\r\n\r\nBeause Orma reuqires nothing to do to models, serializers, e.g. Android Parcels or GSON, can\r\nserialize Orma models.\r\n\r\n## Example\r\n\r\nThere is [an example app](example/) to demonstrate:\r\n\r\n* Migration\r\n* Orma with `RecyclerView` / `ListView`\r\n* Benchmark (see below)\r\n\r\n## Benchmark\r\n\r\nThere is a simple benchmark with [Realm](https://github.com/realm/realm-java) and hand-written SQLiteDatabase code:\r\n\r\n[example/BenchmarkFragment](example/src/main/java/com/github/gfx/android/orma/example/fragment/BenchmarkFragment.java)\r\n\r\nHere is a result performed on Android 6.0.0 / Xperia Z4\r\nas of Orma v3.0.0 and Realm 2.0.2, processing 10 items x 100 times:\r\n\r\n<img src=\"benchmark-v3.0.png\" alt=\"\" width=\"420\"/>\r\n\r\nI welcome benchmark in another condition and/or another code.\r\n\r\n## Method Count\r\n\r\nOrma runtime is very lightweight: [Method Count for v4.0.0](http://www.methodscount.com/?lib=com.github.gfx.android.orma:orma:4.0.0)\r\n\r\n## FAQ\r\n\r\n### Can't build my project.\r\n\r\nCheck your toolchain. FYI here are my toolchain versions:\r\n\r\n* JDK 1.8.0_66\r\n* Android SDK Tools 25 or later\r\n* Android SDK Platform Tools 24 or later\r\n* Android SDK Build Tools 24 or later\r\n* Android Gradle Plugin 2.2.0 or later\r\n\r\n### How can I enable debug logging on release build?\r\n\r\nCall `OrmaDatabase.Builder#trace(boolean)` with `true`:\r\n\r\n```java\r\nOrmaDatabase orma = OrmaDatabase.builder(context)\r\n .trace(true)\r\n .create();\r\n```\r\n\r\nThis option also enables logging in the default migration engine.\r\n\r\nIf you give a custom migration engine to the orma builder, you have to enable\r\n`trace` flag to its constructor:\r\n\r\n```java\r\nboolean trace = true;\r\nSchemaDiffMigration migration = new SchemaDiffMigration(context, trace);\r\n```\r\n\r\n### How can see the generated Java files?\r\n\r\nAs other annotation processors do, Orma save files to `$modle/build/generated/source/apt/`.\r\n\r\nYou can see [generated files for example models](example/build/generated/source/apt/debug/com/github/gfx/android/orma/example/orma).\r\n\r\n### Does Orma work with Kotlin?\r\n\r\nYes, but it's _experimental_. Here is an example to use Orma with Kotlin:\r\n\r\nhttps://github.com/gfx/OrmaWithKotlin\r\n\r\nNOTE: Kotlin APT support, a.k.a. _kapt_, is **really unstable**. Don't ask me how to solve kapt problems.\r\n\r\n### Does Orma work with the Jack compiler?\r\n\r\nYes. As of Android Gradle Plugin 2.2.2, Orma should work with Jack.\r\n\r\n```gradle:build.gradle\r\ndependencies {\r\n annotationProcessor 'com.github.gfx.android.orma:orma-processor:4.0.0'\r\n compile 'com.github.gfx.android.orma:orma:4.0.0'\r\n}\r\n```\r\n\r\nSee https://github.com/gfx/OrmaWithJack for a working example.\r\n\r\n### When the database handle is opened and closed?\r\n\r\nOrma opens the database handle in instantiating `OrmaDatabase`, and you don't need to\r\nclose it.\r\n\r\nIn other word, you can define the database handle as a singleton instance in your application scope,\r\nand forget `close`.\r\n\r\n### Who uses Orma?\r\n\r\nHere is a list of open-source Androdi apps using Orma which are released to Google Play:\r\n\r\n* [gfx/Android-Helium](https://github.com/gfx/Android-Helium)\r\n* [konifar/droidkaigi2016](https://github.com/konifar/droidkaigi2016)\r\n\r\nHere is a list of apps using Orma which are proprietary and released to Google Play:\r\n\r\n* [Cookpad (ja)](https://play.google.com/store/apps/details?id=com.cookpad.android.activities)\r\n* [Abema TV (ja)](https://play.google.com/store/apps/details?id=tv.abema)\r\n\r\nTell me if your projects use Orma!\r\n\r\n## Support\r\n\r\n* Use [GitHub issues](https://github.com/gfx/Android-Orma/issues) for the issue tracker\r\n* Feel free to ask for questions to the author [@\\_\\_gfx\\_\\_](https://twitter.com/__gfx__)\r\n\r\n## Licenses in Runtime Dependencies\r\n\r\n* https://github.com/ReactiveX/RxJava - Apache Software License 2.0\r\n* [bkiers/sqlite-parser](https://github.com/bkiers/sqlite-parser) - The MIT License\r\n * The original code of [SQLite.g4](sqliteparser/src/main/antlr/com/github/gfx/android/orma/sqliteparser/g/SQLite.g4)\r\n\r\n## Contribution\r\n\r\nPatches are welcome!\r\n\r\n## Release Engineering for Maintainers\r\n\r\n```shell\r\n./gradlew bumpMajor # or bumpMinor / bumpPatch\r\ngit add -va\r\nmake publish # run tests, build artifacts, publish to jcenter, and make a tag\r\n```\r\n\r\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).\r\n\r\n## See Also\r\n\r\n* [SQLite](http://sqlite.org/)\r\n* [SQLiteDatabase](http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html)\r\n* [Version of SQLite used in Android? - Stack Overflow](http://stackoverflow.com/questions/2421189/version-of-sqlite-used-in-android)\r\n\r\n## Authors and Contributors\r\n\r\nFUJI Goro ([gfx](https://github.com/gfx)).\r\n\r\nAnd contributors are listed here: [Contributors](https://github.com/gfx/Android-Orma/graphs/contributors)\r\n\r\n## License\r\n\r\nCopyright (c) 2015 FUJI Goro (gfx).\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\nhttp://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n",
"note": "Don't delete this file! It's used internally to help with page regeneration."
}