mirror of https://github.com/AMT-Cheif/drift.git
Start documenting moor 2.0 features
This commit is contained in:
parent
a38e883282
commit
448ff10823
|
@ -56,6 +56,20 @@ Future<List<TodoEntry>> sortEntriesAlphabetically() {
|
|||
```
|
||||
You can also reverse the order by setting the `mode` property of the `OrderingTerm` to
|
||||
`OrderingMode.desc`.
|
||||
|
||||
### Single values
|
||||
If you now a query is never going to return more than one row, wrapping the result in a `List`
|
||||
can be tedious. Moor lets you work around that with `getSingle` and `watchSingle`:
|
||||
```dart
|
||||
Stream<TodoEntry> entryById(int id) {
|
||||
return (select(todos)..where((t) => t.id.equals(id))).watchSingle();
|
||||
}
|
||||
```
|
||||
If an entry with the provided id exists, it will be sent to the stream. Otherwise,
|
||||
`null` will be added to stream. If a query used with `watchSingle` ever returns
|
||||
more than one entry (which is impossible in this case), an error will be added
|
||||
instead.
|
||||
|
||||
## Updates and deletes
|
||||
You can use the generated classes to update individual fields of any row:
|
||||
```dart
|
||||
|
@ -125,4 +139,7 @@ addTodoEntry(
|
|||
```
|
||||
If a column is nullable or has a default value (this includes auto-increments), the field
|
||||
can be omitted. All other fields must be set and non-null. The `insert` method will throw
|
||||
otherwise.
|
||||
otherwise.
|
||||
|
||||
Multiple inserts can be batched by using `insertAll` - it takes a list of companions instead
|
||||
of a single companion.
|
|
@ -1,11 +1,18 @@
|
|||
---
|
||||
title: "Custom queries"
|
||||
title: "(Legacy) Custom queries"
|
||||
weight: 10
|
||||
description: Let moor generate Dart from your SQL statements
|
||||
aliases:
|
||||
- /queries/custom
|
||||
---
|
||||
|
||||
{{% alert title="Outdated feature" color="warning" %}}
|
||||
With moor 2.0, we moved the new `.moor` files out of preview and added some powerful features to them.
|
||||
They are easier to use than the approaches described here. While these features will continue to
|
||||
be supported, moor files will get better tooling support in the future and we recommend to
|
||||
migrate. See [their api]({{%relref "moor_files.md"%}}) for details.
|
||||
{{% /alert %}}
|
||||
|
||||
Altough moor includes a fluent api that can be used to model most statements, advanced
|
||||
features like `GROUP BY` statements or window functions are not yet supported. You can
|
||||
use these features with custom statements. You don't have to miss out on other benefits
|
||||
|
@ -44,7 +51,7 @@ To use this feature, it's helpful to know how Dart tables are named in sql. For
|
|||
override `tableName`, the name in sql will be the `snake_case` of the class name. So a Dart table
|
||||
called `Categories` will be named `categories`, a table called `UserAddressInformation` would be
|
||||
called `user_address_information`. The same rule applies to column getters without an explicit name.
|
||||
Tables and columns declared in [SQL tables]({{< relref "custom_tables.md" >}}) will always have the
|
||||
Tables and columns declared in [Moor files]({{< relref "moor_files.md" >}}) will always have the
|
||||
name you specified.
|
||||
{{% /alert %}}
|
||||
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
---
|
||||
title: "Tables from SQL"
|
||||
weight: 20
|
||||
description: Generate tables from `CREATE TABLE` statements.
|
||||
---
|
||||
|
||||
{{% alert title="Experimental feature" %}}
|
||||
At the moment, creating table classes from `CREATE TABLE` statements is an experimental feature.
|
||||
If you run into any issues, please create an issue and let us know, thanks!
|
||||
{{% /alert %}}
|
||||
|
||||
With moor, you can specify your table classes in Dart and it will generate matching
|
||||
`CREATE TABLE` statements for you. But if you prefer to write `CREATE TABLE` statements and have
|
||||
moor generating fitting Dart classes, that works too.
|
||||
|
||||
To use this feature, create a (or multiple) `.moor` file somewhere in your project. You can fill
|
||||
them with create table statements:
|
||||
```sql
|
||||
CREATE TABLE states (
|
||||
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE experiments (
|
||||
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
description TEXT NOT NULL,
|
||||
state INT REFERENCES states(id) ON UPDATE CASCADE ON DELETE SET NULL
|
||||
)
|
||||
```
|
||||
|
||||
Then, import these tables to your database with:
|
||||
```dart
|
||||
@UseMoor(include: {'experiments.moor'})
|
||||
class ExperimentsDb extends _$ExperimentsDb {
|
||||
```
|
||||
|
||||
All the tables will then be available inside your database class, just like they
|
||||
would be if you wrote them in Dart. If you want to use this feature on an DAO,
|
||||
you'll also need to `include` the .moor file on that class. Moor supports both
|
||||
relative imports (like above) and absolute imports (like `package:your_app/src/tables/experiments.moor`)
|
||||
Of course, this feature works perfectly together with features like generated
|
||||
custom queries and query-streams.
|
|
@ -0,0 +1,148 @@
|
|||
---
|
||||
title: "Moor files"
|
||||
weight: 1
|
||||
description: Learn everything about the new `.moor` files which can contain tables and queries
|
||||
|
||||
aliases:
|
||||
- /docs/using-sql/custom_tables/ # Redirect from outdated "custom tables" page which has been deleted
|
||||
---
|
||||
|
||||
Moor files are a new feature that lets you write all your database code in SQL - moor will generate typesafe APIs for them.
|
||||
|
||||
## Getting started
|
||||
To use this feature, lets create two files: `database.dart` and `tables.moor`. The Dart file is pretty straightforward:
|
||||
```dart
|
||||
import 'package:moor/moor.dart';
|
||||
|
||||
part 'database.g.dart';
|
||||
|
||||
@UseMoor(
|
||||
include: {'tables.moor'},
|
||||
)
|
||||
class MoorDb extends _$MoorDb {
|
||||
MoorDb() : super(FlutterQueryExecutor.inDatabaseFolder('app.db'));
|
||||
|
||||
@override
|
||||
int get schemaVersion => 1;
|
||||
}
|
||||
```
|
||||
|
||||
We can now declare tables and queries in the moor file:
|
||||
```sql
|
||||
CREATE TABLE todos (
|
||||
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
category INTEGER REFERENCES categories(id)
|
||||
);
|
||||
|
||||
CREATE TABLE categories (
|
||||
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
description TEXT NOT NULL
|
||||
) AS Category; -- the AS xyz after the table defines the data class name
|
||||
|
||||
-- we can put named sql queries in here as well:
|
||||
createEntry: INSERT INTO todos (title, content) VALUES (:title, :content);
|
||||
deleteById: DELETE FROM todos WHERE id = :id;
|
||||
watchAllTodos: SELECT * FROM todos;
|
||||
```
|
||||
|
||||
After running the build runner, moor will write the `database.g.dart`
|
||||
file which contains the `_$MoorDb` superclass. Let's take a look at
|
||||
what we got:
|
||||
|
||||
- Generated data classes (`Todo` and `Category`), and companion versions
|
||||
for inserts (see [Dart Interop](#dart-interop) for info). By default,
|
||||
we strip a trailing "s" from the table name for the class. That's why
|
||||
we used `AS Category` on the second table - it would have been called
|
||||
`Categorie` otherwise.
|
||||
- Methods to run the queries:
|
||||
- a `Future<int> createEntry(String title, String content)` method. It
|
||||
creates a new todo entry with the provided data and returns the id of
|
||||
the entry created.
|
||||
- `Future<int> deleteById(int id)`: Deletes a todo entry by its id, and
|
||||
returns the amount of rows affected.
|
||||
- `Selectable<AllTodosResult> allTodos()`. It can be used to get, or
|
||||
watch, all todo entries. It can be used with `allTodos().get()` and
|
||||
`allTodos().watch()`.
|
||||
- Classes for select statements that don't match a table. In the example
|
||||
above, thats the `AllTodosResult` class, which contains all fields from
|
||||
`todos` and the description of the associated category.
|
||||
|
||||
## Variables
|
||||
We support regular variables (`?`), explictly indexed variables (`?123`)
|
||||
and colon-named variables (`:id`). We don't support variables declared
|
||||
with @ or $. The compiler will attempt to infer the variable's type by
|
||||
looking at its context. This lets moor generate typesafe apis for your
|
||||
queries, the variables will be written as parameters to your method.
|
||||
|
||||
### Arrays
|
||||
If you want to check whether a value is in an array of values, you can
|
||||
use `IN ?`. That's not valid sql, but moor will desugar that at runtime. So, for this query:
|
||||
```sql
|
||||
entriesWithId: SELECT * FROM todos WHERE id IN ?;
|
||||
```
|
||||
Moor will generate a `Selectable<Todo> entriesWithId(List<int> ids)`
|
||||
method (`entriesWithId([1,2])` would run `SELECT * ... id IN (?1, ?2)`
|
||||
and bind the arguments accordingly). To support this, we only have two
|
||||
restrictions:
|
||||
|
||||
1. __No explicit variables__: Running `WHERE id IN ?2` will be rejected
|
||||
at build time. As the variable is expanded, giving it a single index is
|
||||
invalid.
|
||||
2. __No higher explicit index after a variable__: Running
|
||||
`WHERE id IN ? OR title = ?2` will also be rejected. Expanding the
|
||||
variable can clash with the explicit index, which is why moor forbids
|
||||
it. Of course, `id IN ? OR title = ?` will work as expected.
|
||||
|
||||
## Imports
|
||||
{{% alert title="Limited support" %}}
|
||||
> Importing a moor file from another moor file will work as expected.
|
||||
Unfortunately, importing a Dart file from moor does not work in all
|
||||
scenarios. Please upvote [this issue](https://github.com/dart-lang/build/issues/493)
|
||||
on the build package to help solve this.
|
||||
{{% /alert %}}
|
||||
|
||||
You can put import statements at the top of a `moor` file:
|
||||
```sql
|
||||
import 'other.moor'; -- single quotes are required for imports
|
||||
```
|
||||
All tables reachable from the other file will then also be visible in
|
||||
the current file and to the database that `includes` it. Importing
|
||||
Dart files into a moor file will also work - then, all the tables
|
||||
declared via Dart tables can be used inside queries.
|
||||
|
||||
## Dart interop
|
||||
Moor files work perfectly together with moor's existing Dart API:
|
||||
|
||||
- you can write Dart queries for moor files:
|
||||
```dart
|
||||
Future<void> insert(TodosCompanion companion) async {
|
||||
await into(todos).insert(companion);
|
||||
}
|
||||
```
|
||||
- by importing Dart files into a moor file, you can write sql queries for
|
||||
tables declared in Dart.
|
||||
- generated methods for queries can be used in transactions, they work
|
||||
together with auto-updating queries, etc.
|
||||
|
||||
You can make most of both SQL and Dart by "Dart Templates", which is a
|
||||
Dart expression that gets inlined to a query. To use them, declare a
|
||||
$-variable in a query:
|
||||
```sql
|
||||
_filterTodos: SELECT * FROM todos WHERE $predicate;
|
||||
```
|
||||
Moor will generate a `Selectable<Todo> _filterTodos(Expression<bool, BoolType> predicate)` method which can be used to construct dynamic
|
||||
filters at runtime:
|
||||
```dart
|
||||
Stream<List<Todo>> watchInCategory(int category) {
|
||||
return _filterTodos(todos.category.equals(category)).watch();
|
||||
}
|
||||
```
|
||||
This feature works for
|
||||
|
||||
- expressions
|
||||
- single ordering terms: `SELECT * FROM todos ORDER BY $term, id ASC`
|
||||
will generate a method taking an `OrderingTerm`.
|
||||
- whole order-by clauses: `SELECT * FROM todos ORDER BY $order`
|
||||
- limit clauses: `SELECT * FROM todos LIMIT $limit`
|
|
@ -16,7 +16,7 @@ following example, which deals with deleting a category, we move all todo entrie
|
|||
in that category back to the default category:
|
||||
```dart
|
||||
Future deleteCategory(Category category) {
|
||||
return transaction((_) async {
|
||||
return transaction(() async {
|
||||
// first, move the affected todo entries back to the default category
|
||||
await customUpdate(
|
||||
'UPDATE todos SET category = NULL WHERE category = ?',
|
||||
|
@ -30,22 +30,9 @@ Future deleteCategory(Category category) {
|
|||
}
|
||||
```
|
||||
|
||||
{{% alert title="About that _" color="info" %}}
|
||||
You might have noticed that `_` parameter on the `transaction()` callback. That parameter would
|
||||
be a special version of the database that runs all the methods called on it inside the transaction.
|
||||
In previous moor versions, it was important to call everything on that parameter, e.g.
|
||||
```dart
|
||||
transaction((db) async {
|
||||
await db.delete(categories).delete(category);
|
||||
});
|
||||
```
|
||||
Starting from moor 1.6, this is no longer neccessary, we can figure out that you meant to run that
|
||||
in a transaction because it was called from inside a `transaction` callback. We're going to remove
|
||||
that parameter entirely in moor 2.0.
|
||||
{{% /alert %}}
|
||||
|
||||
## ⚠️ Gotchas
|
||||
There are a couple of things that should be kept in mind when working with transactions:
|
||||
|
||||
1. __Await all calls__: All queries inside the transaction must be `await`-ed. The transaction
|
||||
will complete when the inner method completes. Without `await`, some queries might be operating
|
||||
on the transaction after it has been closed! This can cause data loss or runtime crashes.
|
||||
|
|
Loading…
Reference in New Issue