mirror of https://github.com/AMT-Cheif/drift.git
Start to work on MySQL example
This commit is contained in:
parent
e46a8d84d5
commit
5f8ab42189
|
@ -2,7 +2,21 @@
|
|||
layout: guide
|
||||
title: Custom databases
|
||||
nav_order: 8
|
||||
since: 2.0
|
||||
permalink: /custom_backend
|
||||
---
|
||||
|
||||
TBD
|
||||
_Note_: This feature is available starting from Moor `2.0`.
|
||||
|
||||
# Custom databases
|
||||
Moor has builtin support for Flutter using the `sqflite` package - it also supports the [web]({{"web" | absolute_url}})
|
||||
and desktop apps. However, you can also use moor with a different database of your choice. In this guide, you'll learn how
|
||||
to use Moor with a `mysql` database running on a server and with an encrypted database on a mobile device!
|
||||
|
||||
## MySQL
|
||||
We'll connect to a MySQL server with the `sqljockey5` library, so let's first add that library to the `pubspec.yaml`:
|
||||
```yaml
|
||||
dependencies:
|
||||
// you'll also need moor, of course
|
||||
sqljocky5: ^2.2.0
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
# Files and directories created by pub
|
||||
.dart_tool/
|
||||
.packages
|
||||
# Remove the following pattern if you wish to check in your lock file
|
||||
pubspec.lock
|
||||
|
||||
# Conventional directory for build outputs
|
||||
build/
|
||||
|
||||
# Directory created by dartdoc
|
||||
doc/api/
|
|
@ -0,0 +1 @@
|
|||
Example that uses moor with an mysql server.
|
|
@ -0,0 +1,21 @@
|
|||
version: "3.7"
|
||||
|
||||
services:
|
||||
database:
|
||||
image: mariadb
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: password
|
||||
MYSQL_DATABASE: example
|
||||
ports:
|
||||
- "3306:3306"
|
||||
phpmyadmin:
|
||||
depends_on:
|
||||
- database
|
||||
image: phpmyadmin/phpmyadmin
|
||||
ports:
|
||||
- "8080:80"
|
||||
environment:
|
||||
PMA_HOST: database
|
||||
PMA_USER: root
|
||||
PMA_PASSWORD: password
|
||||
PMA_PORT: 3306
|
|
@ -0,0 +1,48 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:sqljocky5/connection/settings.dart';
|
||||
|
||||
import 'mysql.dart';
|
||||
|
||||
part 'database.g.dart';
|
||||
|
||||
class Users extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
TextColumn get name => text()();
|
||||
}
|
||||
|
||||
@UseMoor(tables: [Users])
|
||||
class Database extends _$Database {
|
||||
Database._(QueryExecutor e) : super(e);
|
||||
|
||||
factory Database() {
|
||||
final settings = ConnectionSettings(
|
||||
user: 'root',
|
||||
password: 'password',
|
||||
host: 'localhost',
|
||||
port: 3306,
|
||||
db: 'example',
|
||||
);
|
||||
|
||||
return Database._(MySqlBackend(settings));
|
||||
}
|
||||
|
||||
@override
|
||||
int get schemaVersion => 1;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration {
|
||||
return MigrationStrategy(
|
||||
beforeOpen: (engine, details) async {
|
||||
// we don't have migrations in mysql, so let's run them manually here!
|
||||
final migrator = Migrator(this, engine.customStatement);
|
||||
// will emit a "IF NOT EXISTS" statement, so its safe to run this
|
||||
// on every open
|
||||
await migrator.createAllTables();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> insertUser(String name) async {
|
||||
await into(users).insert(UsersCompanion(name: Value(name)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'database.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// MoorGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: unnecessary_brace_in_string_interps
|
||||
class User extends DataClass implements Insertable<User> {
|
||||
final int id;
|
||||
final String name;
|
||||
User({@required this.id, @required this.name});
|
||||
factory User.fromData(Map<String, dynamic> data, GeneratedDatabase db,
|
||||
{String prefix}) {
|
||||
final effectivePrefix = prefix ?? '';
|
||||
final intType = db.typeSystem.forDartType<int>();
|
||||
final stringType = db.typeSystem.forDartType<String>();
|
||||
return User(
|
||||
id: intType.mapFromDatabaseResponse(data['${effectivePrefix}id']),
|
||||
name: stringType.mapFromDatabaseResponse(data['${effectivePrefix}name']),
|
||||
);
|
||||
}
|
||||
factory User.fromJson(Map<String, dynamic> json,
|
||||
{ValueSerializer serializer = const ValueSerializer.defaults()}) {
|
||||
return User(
|
||||
id: serializer.fromJson<int>(json['id']),
|
||||
name: serializer.fromJson<String>(json['name']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson(
|
||||
{ValueSerializer serializer = const ValueSerializer.defaults()}) {
|
||||
return {
|
||||
'id': serializer.toJson<int>(id),
|
||||
'name': serializer.toJson<String>(name),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
T createCompanion<T extends UpdateCompanion<User>>(bool nullToAbsent) {
|
||||
return UsersCompanion(
|
||||
id: id == null && nullToAbsent ? const Value.absent() : Value(id),
|
||||
name: name == null && nullToAbsent ? const Value.absent() : Value(name),
|
||||
) as T;
|
||||
}
|
||||
|
||||
User copyWith({int id, String name}) => User(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
);
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('User(')
|
||||
..write('id: $id, ')
|
||||
..write('name: $name')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => $mrjf($mrjc($mrjc(0, id.hashCode), name.hashCode));
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
identical(this, other) ||
|
||||
(other is User && other.id == id && other.name == name);
|
||||
}
|
||||
|
||||
class UsersCompanion extends UpdateCompanion<User> {
|
||||
final Value<int> id;
|
||||
final Value<String> name;
|
||||
const UsersCompanion({
|
||||
this.id = const Value.absent(),
|
||||
this.name = const Value.absent(),
|
||||
});
|
||||
}
|
||||
|
||||
class $UsersTable extends Users with TableInfo<$UsersTable, User> {
|
||||
final GeneratedDatabase _db;
|
||||
final String _alias;
|
||||
$UsersTable(this._db, [this._alias]);
|
||||
final VerificationMeta _idMeta = const VerificationMeta('id');
|
||||
GeneratedIntColumn _id;
|
||||
@override
|
||||
GeneratedIntColumn get id => _id ??= _constructId();
|
||||
GeneratedIntColumn _constructId() {
|
||||
return GeneratedIntColumn('id', $tableName, false, hasAutoIncrement: true);
|
||||
}
|
||||
|
||||
final VerificationMeta _nameMeta = const VerificationMeta('name');
|
||||
GeneratedTextColumn _name;
|
||||
@override
|
||||
GeneratedTextColumn get name => _name ??= _constructName();
|
||||
GeneratedTextColumn _constructName() {
|
||||
return GeneratedTextColumn(
|
||||
'name',
|
||||
$tableName,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [id, name];
|
||||
@override
|
||||
$UsersTable get asDslTable => this;
|
||||
@override
|
||||
String get $tableName => _alias ?? 'users';
|
||||
@override
|
||||
final String actualTableName = 'users';
|
||||
@override
|
||||
VerificationContext validateIntegrity(UsersCompanion d,
|
||||
{bool isInserting = false}) {
|
||||
final context = VerificationContext();
|
||||
if (d.id.present) {
|
||||
context.handle(_idMeta, id.isAcceptableValue(d.id.value, _idMeta));
|
||||
} else if (id.isRequired && isInserting) {
|
||||
context.missing(_idMeta);
|
||||
}
|
||||
if (d.name.present) {
|
||||
context.handle(
|
||||
_nameMeta, name.isAcceptableValue(d.name.value, _nameMeta));
|
||||
} else if (name.isRequired && isInserting) {
|
||||
context.missing(_nameMeta);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<GeneratedColumn> get $primaryKey => {id};
|
||||
@override
|
||||
User map(Map<String, dynamic> data, {String tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null;
|
||||
return User.fromData(data, _db, prefix: effectivePrefix);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Variable> entityToSql(UsersCompanion d) {
|
||||
final map = <String, Variable>{};
|
||||
if (d.id.present) {
|
||||
map['id'] = Variable<int, IntType>(d.id.value);
|
||||
}
|
||||
if (d.name.present) {
|
||||
map['name'] = Variable<String, StringType>(d.name.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
$UsersTable createAlias(String alias) {
|
||||
return $UsersTable(_db, alias);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _$Database extends GeneratedDatabase {
|
||||
_$Database(QueryExecutor e) : super(const SqlTypeSystem.withDefaults(), e);
|
||||
$UsersTable _users;
|
||||
$UsersTable get users => _users ??= $UsersTable(this);
|
||||
@override
|
||||
List<TableInfo> get allTables => [users];
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
import 'package:moor/backends.dart';
|
||||
import 'package:moor/moor.dart';
|
||||
import 'package:sqljocky5/connection/connection.dart';
|
||||
import 'package:sqljocky5/sqljocky.dart';
|
||||
|
||||
class MySqlBackend extends DelegatedDatabase {
|
||||
MySqlBackend(ConnectionSettings settings)
|
||||
: super(_MySqlDelegate(settings), logStatements: true);
|
||||
}
|
||||
|
||||
mixin _MySqlExecutor on QueryDelegate {
|
||||
Querier get _querier;
|
||||
|
||||
@override
|
||||
Future<void> runBatched(List<BatchedStatement> statements) async {
|
||||
for (var stmt in statements) {
|
||||
await _querier.preparedMulti(stmt.sql, stmt.variables);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> runCustom(String statement, List args) async {
|
||||
await _querier.prepared(statement, args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> runInsert(String statement, List args) async {
|
||||
final result = await _querier.prepared(statement, args);
|
||||
return result.insertId;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<QueryResult> runSelect(String statement, List args) async {
|
||||
final result = await _querier.prepared(statement, args);
|
||||
|
||||
final columns = [for (var field in result.fields) field.name];
|
||||
final rows = result.toList();
|
||||
|
||||
return QueryResult(columns, rows);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> runUpdate(String statement, List args) async {
|
||||
final result = await _querier.prepared(statement, args);
|
||||
|
||||
return result.affectedRows;
|
||||
}
|
||||
}
|
||||
|
||||
class _MySqlDelegate extends DatabaseDelegate with _MySqlExecutor {
|
||||
final ConnectionSettings _settings;
|
||||
|
||||
MySqlConnection _connection;
|
||||
|
||||
_MySqlDelegate(this._settings);
|
||||
|
||||
@override
|
||||
Future<bool> get isOpen async => _connection != null;
|
||||
|
||||
@override
|
||||
Querier get _querier => _connection;
|
||||
|
||||
@override
|
||||
final DbVersionDelegate versionDelegate = const NoVersionDelegate();
|
||||
|
||||
@override
|
||||
TransactionDelegate get transactionDelegate => _TransactionOpener(this);
|
||||
|
||||
@override
|
||||
Future<void> open([GeneratedDatabase db]) async {
|
||||
_connection = await MySqlConnection.connect(_settings);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
class _TransactionOpener extends SupportedTransactionDelegate {
|
||||
final _MySqlDelegate _delegate;
|
||||
|
||||
_TransactionOpener(this._delegate);
|
||||
|
||||
@override
|
||||
void startTransaction(Future Function(QueryDelegate) run) {
|
||||
_delegate._connection.transaction((transaction) async {
|
||||
final executor = _TransactionExecutor(transaction);
|
||||
await run(executor);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _TransactionExecutor extends QueryDelegate with _MySqlExecutor {
|
||||
@override
|
||||
final Querier _querier;
|
||||
|
||||
_TransactionExecutor(this._querier);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import 'lib/database.dart';
|
||||
|
||||
void main() async {
|
||||
final database = Database();
|
||||
await database.insertUser('MySql test');
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
name: mysql
|
||||
description: A sample command-line application.
|
||||
# version: 1.0.0
|
||||
# homepage: https://www.example.com
|
||||
# author: Simon Binder <oss@simonbinder.eu>
|
||||
|
||||
environment:
|
||||
sdk: '>=2.4.0 <3.0.0'
|
||||
|
||||
dependencies:
|
||||
moor: ^1.6.0
|
||||
sqljocky5: ^2.2.0
|
||||
|
||||
dev_dependencies:
|
||||
test: ^1.6.0
|
||||
moor_generator: ^1.6.0
|
||||
build_runner:
|
||||
|
||||
dependency_overrides:
|
||||
moor:
|
||||
path: ../../moor
|
|
@ -42,6 +42,12 @@ abstract class DatabaseDelegate implements QueryDelegate {
|
|||
/// [UseMoor]. It might be useful to read the [GeneratedDatabase.schemaVersion]
|
||||
/// if that information is required while opening the database.
|
||||
Future<void> open([GeneratedDatabase db]);
|
||||
|
||||
/// Closes this database. When the future completes, all resources used
|
||||
/// by this database should have been disposed.
|
||||
Future<void> close() async {
|
||||
// default no-op implementation
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface which can execute sql statements.
|
||||
|
|
Loading…
Reference in New Issue