From b337055653c0afc5482c547940af24eb1530bb56 Mon Sep 17 00:00:00 2001 From: Vitus Date: Sat, 23 Feb 2019 17:44:00 +0100 Subject: [PATCH] Add documentation on how to use the library (#60) --- README.md | 252 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 251 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b816e79..973e9ace 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,258 @@ # Floor **A supportive SQLite abstraction for your Flutter applications.** -This package is in a very early development stage. Better not use it. +The Floor library provides a lightweight SQLite abstraction with automatic mapping between in-memory objects and database rows while still offering full control of the database with the use of SQL. +This package is still in an early phase and the API will likely change. + +### Table of contents + +1. [How to use this library](#how-to-use-this-library) +1. [Querying](#querying) +1. [Persisting Data Changes](#persisting-data-changes) +1. [Transactions](#transactions) +1. [Entities](#entities) +1. [Foreign Keys](#foreign-keys) +1. [Examples](#examples) +1. [Naming](#naming) +1. [Bugs and Feedback](#bugs-and-feedback) +1. [License](#license) + +## How to use this library +1. Add the runtime dependency `floor` as well as the generator `floor_generator` to your `pubspec.yaml`. + The third dependency is `build_runner` which has to be included as a dev dependency just like the generator. + + - `floor` holds all the code you are going to use in your application. + + - `floor_generator` includes the code for generating the database classes. + + - `build_runner` enables a concrete way of generating source code files. + + ````yaml + dependencies: + flutter: + sdk: flutter + floor: + git: + url: https://github.com/vitusortner/floor.git + path: /floor/ + + dev_dependencies: + flutter_test: + sdk: flutter + floor_generator: + git: + url: https://github.com/vitusortner/floor.git + path: /floor_generator/ + build_runner: ^1.1.3 + ```` + +1. Make sure to import the following libraries. + + ```dart + import 'package:floor/floor.dart'; + import 'package:path/path.dart'; + import 'package:sqflite/sqflite.dart' as sqflite; + ``` + +1. Create an `Entity`. + It will represent a database table as well as the scaffold of your business object. + `@Entity()` marks the class as a persistent class. + It's required to add a primary key to your table. + You can do so by adding the `@PrimarKey()` annotation to an `int` property. + + ````dart + @Entity() + class Person { + @PrimaryKey() + final int id; + + final String name; + + Person(this.id, this.name); + } + ```` + +1. Create the `Database`. + This component is responsible for managing the access to the underlying SQLite database. + It has to be an abstract class which extends `FloorDatabase`. + Furthermore, it's required to add `@Database()` to the signature of the class. + + This class contains the method signatures for querying the database which have to return a `Future`. + It, moreover, holds functionality for opening the database. + `_$open()` is a function that will get implemented by running the code generator. + The warning, of it not being implemented, will go away then. + + - You can define queries by adding the `@Query` annotation to a method. + The SQL statement has to get added in parenthesis. + The method must return a `Future` of the `Entity` you're querying for. + + - `@insert` marks a method as an insertion method. + + ```dart + @Database() + abstract class AppDatabase extends FloorDatabase { + static Future openDatabase() async => _$open(); + + @Query('SELECT * FROM Person') + Future> findAllPersons(); + + @Query('SELECT * FROM Person WHERE id = :id') + Future findPersonById(int id); + + @insert + Future insertPerson(Person person); + } + ``` + +1. Add `part 'database.g.dart';` beneath the imports of this file. + It's important to note, that 'database' has to get exchanged with the name of the file the entity and database is defined in. + In this case the file is named `database.dart`. + +1. Run the generator with `flutter packages pub run build_runner build`. + To automatically run it, whenever a file changes, use `flutter packages pub run build_runner watch`. + +1. Use the generated code. + + ```dart + final database = await AppDatabase.openDatabase(); + + final person = await database.findPersonById(1); + await database.insertPerson(person); + ``` + +For further examples take a look at the [example](https://github.com/vitusortner/floor/tree/develop/example) and [floor_test](https://github.com/vitusortner/floor/tree/develop/floor_test) directories. + +## Querying +Method signatures turn into query methods by adding the `@Query()` annotation with the query in parenthesis to them. +Be patient about the correctness of your SQL statements. +The are only partly validated while generating the code. +These queries have to return an entity. + +````dart +@Query('SELECT * FROM Person WHERE id = :id') +Future findPersonById(int id); + +@Query('SELECT * FROM Person WHERE id = :id AND name = :name') +Future findPersonByIdAndName(int id, String name); + +@Query('SELECT * FROM Person') +Future> findAllPersons(); // select multiple items +```` + +## Persisting Data Changes +Use the `@insert`, `@update` and `@delete` annotations for inserting and changing persistent data. +All these methods accept single or multiple entity instances. + +- **Insert** + + `@insert` marks a method as an insertion method. + These methods can return a `Future` of either `void`, `int` or `List`. + - `void` return nothing + - `int` return primary key of inserted item + - `List` return primary keys of inserted items + +- **Update** + + `@update` marks a method as an update method. + These methods can return a `Future` of either `void` or `int`. + - `void` return nothing + - `int` return number of changed rows + +- **Delete** + + `@delete` marks a method as a deletion method. + These methods can return a `Future` of either `void` or `int`. + - `void` return nothing + - `int` return number of deleted rows + +```dart +// examples of changing multiple items with return + +@insert +Future> insertPersons(List person); + +@update +Future updatePersons(List person); + +@delete +Future deletePersons(List person); +``` + +## Transactions +Whenever you want to perform some operations in a transaction you have to add the `@transaction` annotation to the method. +It's also required to add the `async` modifier. These methods can only return `Future`. + +```dart +@transaction +Future replacePersons(List persons) async { + // execute SQL without automatically generated code + await database.execute('DELETE FROM Person'); + await insertPersons(persons); +} +``` + +## Entities +An entity is a persistent class. +Floor automatically creates the mappings between the in-memory objects and database table rows. +It's possible to supply custom metadata to Floor by adding optional values to the `Entity` annotation. +It has the additional attribute of `tableName` which opens up the possibility to use a custom name for that specific entity instead of using the class name. +Another attribute `foreignKeys` allows to add foreign keys to the entity. +More information on how to use these can be found in the [Foreign Keys](#foreign-keys) section. + +`@PrimaryKey` marks a property of a class as the primary key column. +This property has to be of type int. +The value can be automatically generated by SQLite when `autoGenerate` is enabled. + +`@ColumnInfo` enables custom mapping of single table columns. +With the annotation it's possible to give columns a custom name and define if the column is able to store `null`. + +```dart +@Entity(tableName: 'person') +class Person { + @PrimaryKey(autoGenerate: true) + final int id; + + @ColumnInfo(name: 'custom_name', nullable: false) + final String name; + + Person(this.id, this.name); +} +``` + +## Foreign Keys +Add a list of `ForeignKey`s to the `Entity` annotation of the referencing entity. +`childColumns` define the columns of the current entity, whereas `parentColumns` define the columns of the parent entity. +Foreign key actions can get triggered after defining them for the `onUpdate` and `onDelete` properties. + +```dart +@Entity( + tableName: 'dog', + foreignKeys: [ + ForeignKey( + childColumns: ['owner_id'], + parentColumns: ['id'], + entity: Person, + ) + ], +) +class Dog { + @PrimaryKey() + final int id; + + final String name; + + @ColumnInfo(name: 'owner_id') + final int ownerId; + + Dog(this.id, this.name, this.ownerId); +} +``` + +## Examples +For further examples take a look at the [example](https://github.com/vitusortner/floor/tree/develop/example) and [floor_test](https://github.com/vitusortner/floor/tree/develop/floor_test) directories. + +## Naming *Floor - the bottom layer of a [Room](https://developer.android.com/topic/libraries/architecture/room).* ## Bugs and Feedback