Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation on how to use the library #60

Merged
merged 7 commits into from
Feb 23, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
252 changes: 251 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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<AppDatabase> openDatabase() async => _$open();

@Query('SELECT * FROM Person')
Future<List<Person>> findAllPersons();

@Query('SELECT * FROM Person WHERE id = :id')
Future<Person> findPersonById(int id);

@insert
Future<void> 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<Person> findPersonById(int id);

@Query('SELECT * FROM Person WHERE id = :id AND name = :name')
Future<Person> findPersonByIdAndName(int id, String name);

@Query('SELECT * FROM Person')
Future<List<Person>> 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<int>`.
- `void` return nothing
- `int` return primary key of inserted item
- `List<int>` 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<List<int>> insertPersons(List<Person> person);

@update
Future<int> updatePersons(List<Person> person);

@delete
Future<int> deletePersons(List<Person> 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<void>`.

```dart
@transaction
Future<void> replacePersons(List<Person> 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
Expand Down