mirror of https://github.com/AMT-Cheif/drift.git
Update docs on custom SQL
This commit is contained in:
parent
8cc318f258
commit
ef3ee418d4
|
@ -0,0 +1,74 @@
|
|||
import 'package:drift/drift.dart';
|
||||
|
||||
import '../tables/filename.dart';
|
||||
|
||||
part 'custom_queries.g.dart';
|
||||
|
||||
// #docregion manual
|
||||
class CategoryWithCount {
|
||||
final Category category;
|
||||
final int count; // amount of entries in this category
|
||||
|
||||
CategoryWithCount({required this.category, required this.count});
|
||||
}
|
||||
|
||||
// #enddocregion manual
|
||||
|
||||
// #docregion setup
|
||||
@DriftDatabase(
|
||||
tables: [Todos, Categories],
|
||||
queries: {
|
||||
'categoriesWithCount': 'SELECT *, '
|
||||
'(SELECT COUNT(*) FROM todos WHERE category = c.id) AS "amount" '
|
||||
'FROM categories c;'
|
||||
},
|
||||
)
|
||||
class MyDatabase extends _$MyDatabase {
|
||||
// rest of class stays the same
|
||||
// #enddocregion setup
|
||||
@override
|
||||
int get schemaVersion => 1;
|
||||
|
||||
MyDatabase(QueryExecutor e) : super(e);
|
||||
|
||||
// #docregion run
|
||||
Future<void> useGeneratedQuery() async {
|
||||
// The generated query can be run once as a future:
|
||||
await categoriesWithCount().get();
|
||||
|
||||
// Or multiple times as a stream
|
||||
await for (final snapshot in categoriesWithCount().watch()) {
|
||||
print('Found ${snapshot.length} category results');
|
||||
}
|
||||
}
|
||||
|
||||
// #enddocregion run
|
||||
// #docregion manual
|
||||
// then, in the database class:
|
||||
Stream<List<CategoryWithCount>> allCategoriesWithCount() {
|
||||
// select all categories and load how many associated entries there are for
|
||||
// each category
|
||||
return customSelect(
|
||||
'SELECT *, (SELECT COUNT(*) FROM todos WHERE category = c.id) AS "amount"'
|
||||
' FROM categories c;',
|
||||
// used for the stream: the stream will update when either table changes
|
||||
readsFrom: {todos, categories},
|
||||
).watch().map((rows) {
|
||||
// we get list of rows here. We just have to turn the raw data from the
|
||||
// row into a CategoryWithCount instnace. As we defined the Category table
|
||||
// earlier, drift knows how to parse a category. The only thing left to do
|
||||
// manually is extracting the amount.
|
||||
return rows
|
||||
.map((row) => CategoryWithCount(
|
||||
category: Category.fromData(row.data),
|
||||
count: row.read<int>('amount'),
|
||||
))
|
||||
.toList();
|
||||
});
|
||||
}
|
||||
|
||||
// #enddocregion manual
|
||||
|
||||
// #docregion setup
|
||||
}
|
||||
// #enddocregion setup
|
|
@ -2,45 +2,56 @@
|
|||
data:
|
||||
title: "Custom queries"
|
||||
weight: 10
|
||||
description: Let drift generate Dart from your SQL statements
|
||||
description: Write SQL for advanced queries that drift can't express in Dart yet.
|
||||
aliases:
|
||||
- /queries/custom
|
||||
template: layouts/docs/single
|
||||
---
|
||||
|
||||
{% assign snippets = "package:drift_docs/snippets/drift_files/custom_queries.dart.excerpt.json" | readString | json_decode %}
|
||||
|
||||
Although drift includes a fluent api that can be used to model most statements, advanced
|
||||
features like `WITH` clauses or subqueries aren't supported yet. You can
|
||||
use these features with custom statements. You don't have to miss out on other benefits
|
||||
drift brings, though: Drift helps you parse the result rows and custom queries also
|
||||
support auto-updating streams.
|
||||
features like `WITH` clauses or some subqueries aren't supported yet.
|
||||
However, you can use methods like `customSelect` and `customStatement` to run advanced
|
||||
statements on the database by writing the SQL manually.
|
||||
|
||||
For most custom queries, drift can analyze their SQL at compile time, make sure they're valid
|
||||
and generate a type-safe API for them.
|
||||
This approach can be much safer than writing custom SQL at runtime.
|
||||
|
||||
This page describes both approaches: The first section introduces methods generated by drift,
|
||||
the second section gives an example for a custom query defined at runtime.
|
||||
|
||||
## Statements with a generated api
|
||||
|
||||
You can instruct drift to automatically generate a typesafe
|
||||
API for your select, update and delete statements. Of course, you can still write custom
|
||||
sql manually. See the sections below for details.
|
||||
sql manually. See the sections below for details.
|
||||
|
||||
To use this feature, all you need to is define your queries in your `DriftDatabase` annotation:
|
||||
```dart
|
||||
@DriftDatabase(
|
||||
tables: [Todos, Categories],
|
||||
queries: {
|
||||
'categoriesWithCount':
|
||||
'SELECT *, (SELECT COUNT(*) FROM todos WHERE category = c.id) AS "amount" FROM categories c;'
|
||||
},
|
||||
)
|
||||
class MyDatabase extends _$MyDatabase {
|
||||
// rest of class stays the same
|
||||
}
|
||||
```
|
||||
After running the build step again, drift will have written the `CategoriesWithCountResult` class for you -
|
||||
it will hold the result of your query. Also, the `_$MyDatabase` class from which you inherit will have the
|
||||
methods `categoriesWithCount` (which runs the query once) and `watchCategoriesWithCount` (which returns
|
||||
an auto-updating stream).
|
||||
|
||||
Queries can have parameters in them by using the `?` or `:name` syntax. When your queries contains parameters,
|
||||
drift will figure out an appropriate type for them and include them in the generated methods. For instance,
|
||||
{% include "blocks/snippet" snippets = snippets name = "setup" %}
|
||||
|
||||
After running the build step again, drift will have written the `CategoriesWithCountResult` class for you -
|
||||
it will hold the result of your query. Also, the `_$MyDatabase` class from which you inherit will have a
|
||||
`Selectable<CategoriesWithCountResult> categoriesWithCount()` method which can be used to run the query.
|
||||
Like all `Selectable`s in drift, you can use `get()` to run the query once or `watch()` to get an auto-updating
|
||||
stream of results:
|
||||
|
||||
{% block "blocks/alert" title="Better support for custom queries in drift files" %}
|
||||
Defining SQL in the `@DriftDatabase` annotation is a great way to define a few custom queries. For apps that
|
||||
use lots of custom queries, extracting them into separate files may be more manageable.
|
||||
[Drift files]({{ "drift_files.md" | pageUrl }}), which can be included into the database, are a really great fit for this, and may be easier
|
||||
to use.
|
||||
{% endblock %}
|
||||
|
||||
{% include "blocks/snippet" snippets = snippets name = "run" %}
|
||||
|
||||
Queries can have parameters in them by using the `?` or `:name` syntax. For parameters in queries,
|
||||
drift will figure out an appropriate type and include them in the generated methods. For instance,
|
||||
`'categoryById': 'SELECT * FROM categories WHERE id = :id'` will generate the method `categoryById(int id)`.
|
||||
Drift also supports additional convenience features in custom queries, like embededding Dart expressions in
|
||||
SQL. For more details, see the documentation on [drift files]({{ 'drift_files.md' | pageUrl }}).
|
||||
|
||||
{% block "blocks/alert" title="On table names" color="info" %}
|
||||
To use this feature, it's helpful to know how Dart tables are named in sql. For tables that don't
|
||||
|
@ -63,31 +74,9 @@ still send custom queries by calling `customSelect` for a one-time query or
|
|||
the underlying data changes. Using the todo example introduced in the
|
||||
[getting started guide]({{ "../Getting started/index.md" | pageUrl }}), we can
|
||||
write this query which will load the amount of todo entries in each category:
|
||||
```dart
|
||||
class CategoryWithCount {
|
||||
final Category category;
|
||||
final int count; // amount of entries in this category
|
||||
|
||||
CategoryWithCount(this.category, this.count);
|
||||
}
|
||||
{% include "blocks/snippet" snippets = snippets name = "manual" %}
|
||||
|
||||
// then, in the database class:
|
||||
Stream<List<CategoryWithCount>> categoriesWithCount() {
|
||||
// select all categories and load how many associated entries there are for
|
||||
// each category
|
||||
return customSelect(
|
||||
'SELECT *, (SELECT COUNT(*) FROM todos WHERE category = c.id) AS "amount" FROM categories c;',
|
||||
readsFrom: {todos, categories}, // used for the stream: the stream will update when either table changes
|
||||
).watch().map((rows) {
|
||||
// we get list of rows here. We just have to turn the raw data from the row into a
|
||||
// CategoryWithCount. As we defined the Category table earlier, drift knows how to parse
|
||||
// a category. The only thing left to do manually is extracting the amount
|
||||
return rows
|
||||
.map((row) => CategoryWithCount(Category.fromData(row.data, this), row.read<int>('amount')))
|
||||
.toList();
|
||||
});
|
||||
}
|
||||
```
|
||||
For custom selects, you should use the `readsFrom` parameter to specify from which tables the query is
|
||||
reading. When using a `Stream`, drift will be able to know after which updates the stream should emit
|
||||
items.
|
||||
|
|
Loading…
Reference in New Issue