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
|
layout: guide
|
||||||
title: Custom databases
|
title: Custom databases
|
||||||
nav_order: 8
|
nav_order: 8
|
||||||
|
since: 2.0
|
||||||
permalink: /custom_backend
|
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]
|
/// [UseMoor]. It might be useful to read the [GeneratedDatabase.schemaVersion]
|
||||||
/// if that information is required while opening the database.
|
/// if that information is required while opening the database.
|
||||||
Future<void> open([GeneratedDatabase db]);
|
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.
|
/// An interface which can execute sql statements.
|
||||||
|
|
Loading…
Reference in New Issue