Move base dao/db classes, query engine into separate files

This commit is contained in:
Simon Binder 2019-11-15 08:53:11 +01:00
parent 1974307961
commit 22c692c69e
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
8 changed files with 230 additions and 222 deletions

View File

@ -14,7 +14,7 @@ export 'package:moor/src/runtime/executor/executor.dart';
export 'package:moor/src/runtime/executor/transactions.dart'; export 'package:moor/src/runtime/executor/transactions.dart';
export 'package:moor/src/runtime/data_verification.dart'; export 'package:moor/src/runtime/data_verification.dart';
export 'package:moor/src/runtime/data_class.dart'; export 'package:moor/src/runtime/data_class.dart';
export 'package:moor/src/runtime/database.dart'; export 'package:moor/src/runtime/api/runtime_api.dart';
export 'package:moor/src/runtime/types/sql_types.dart' export 'package:moor/src/runtime/types/sql_types.dart'
hide ComparableType, Monoid, FullArithmetic; hide ComparableType, Monoid, FullArithmetic;
export 'package:moor/src/runtime/exceptions.dart'; export 'package:moor/src/runtime/exceptions.dart';

View File

@ -1,4 +1,4 @@
part of 'database.dart'; part of 'runtime_api.dart';
/// Contains operations to run queries in a batched mode. This can be much more /// Contains operations to run queries in a batched mode. This can be much more
/// efficient when running a lot of similar queries at the same time, making /// efficient when running a lot of similar queries at the same time, making

View File

@ -0,0 +1,102 @@
part of 'runtime_api.dart';
/// A database connection managed by moor. Contains three components:
/// - a [SqlTypeSystem], which is responsible to map between Dart types and
/// values understood by the database engine.
/// - a [QueryExecutor], which runs sql commands
/// - a [StreamQueryStore], which dispatches table changes to listening queries,
/// on which the auto-updating queries are based.
class DatabaseConnection {
/// The type system to use with this database. The type system is responsible
/// for mapping Dart objects into sql expressions and vice-versa.
final SqlTypeSystem typeSystem;
/// The executor to use when queries are executed.
final QueryExecutor executor;
/// Manages active streams from select statements.
final StreamQueryStore streamQueries;
/// Constructs a raw database connection from the three components.
DatabaseConnection(this.typeSystem, this.executor, this.streamQueries);
/// Constructs a [DatabaseConnection] from the [QueryExecutor] by using the
/// default type system and a new [StreamQueryStore].
DatabaseConnection.fromExecutor(this.executor)
: typeSystem = SqlTypeSystem.defaultInstance,
streamQueries = StreamQueryStore();
}
/// Manages a [DatabaseConnection] to send queries to the database.
abstract class DatabaseConnectionUser {
/// The database connection used by this [DatabaseConnectionUser].
@protected
final DatabaseConnection connection;
/// The type system to use with this database. The type system is responsible
/// for mapping Dart objects into sql expressions and vice-versa.
SqlTypeSystem get typeSystem => connection.typeSystem;
/// The executor to use when queries are executed.
QueryExecutor get executor => connection.executor;
/// Manages active streams from select statements.
@visibleForTesting
@protected
StreamQueryStore get streamQueries => connection.streamQueries;
/// Constructs a database connection user, which is responsible to store query
/// streams, wrap the underlying executor and perform type mapping.
DatabaseConnectionUser(SqlTypeSystem typeSystem, QueryExecutor executor,
{StreamQueryStore streamQueries})
: connection = DatabaseConnection(
typeSystem, executor, streamQueries ?? StreamQueryStore());
/// Creates another [DatabaseConnectionUser] by referencing the implementation
/// from the [other] user.
DatabaseConnectionUser.delegate(DatabaseConnectionUser other,
{SqlTypeSystem typeSystem,
QueryExecutor executor,
StreamQueryStore streamQueries})
: connection = DatabaseConnection(
typeSystem ?? other.connection.typeSystem,
executor ?? other.connection.executor,
streamQueries ?? other.connection.streamQueries,
);
/// Constructs a [DatabaseConnectionUser] that will use the provided
/// [DatabaseConnection].
DatabaseConnectionUser.fromConnection(this.connection);
/// Marks the tables as updated. This method will be called internally
/// whenever a update, delete or insert statement is issued on the database.
/// We can then inform all active select-streams on those tables that their
/// snapshot might be out-of-date and needs to be fetched again.
void markTablesUpdated(Set<TableInfo> tables) {
streamQueries.handleTableUpdates(tables);
}
/// Creates and auto-updating stream from the given select statement. This
/// method should not be used directly.
Stream<T> createStream<T>(QueryStreamFetcher<T> stmt) =>
streamQueries.registerStream(stmt);
/// Creates a copy of the table with an alias so that it can be used in the
/// same query more than once.
///
/// Example which uses the same table (here: points) more than once to
/// differentiate between the start and end point of a route:
/// ```
/// var source = alias(points, 'source');
/// var destination = alias(points, 'dest');
///
/// select(routes).join([
/// innerJoin(source, routes.startPoint.equalsExp(source.id)),
/// innerJoin(destination, routes.startPoint.equalsExp(destination.id)),
/// ]);
/// ```
T alias<T extends Table, D extends DataClass>(
TableInfo<T, D> table, String alias) {
return table.createAlias(alias).asDslTable;
}
}

View File

@ -0,0 +1,20 @@
part of 'runtime_api.dart';
/// Class that runs queries to a subset of all available queries in a database.
///
/// This comes in handy to structure large amounts of database code better: The
/// migration logic can live in the main [GeneratedDatabase] class, but code
/// can be extracted into [DatabaseAccessor]s outside of that database.
/// For details on how to write a dao, see [UseDao].
abstract class DatabaseAccessor<T extends GeneratedDatabase>
extends DatabaseConnectionUser with QueryEngine {
@override
final bool topLevel = true;
/// The main database instance for this dao
@protected
final T db;
/// Used internally by moor
DatabaseAccessor(this.db) : super.delegate(db);
}

View File

@ -0,0 +1,93 @@
part of 'runtime_api.dart';
/// A base class for all generated databases.
abstract class GeneratedDatabase extends DatabaseConnectionUser
with QueryEngine {
@override
final bool topLevel = true;
/// Specify the schema version of your database. Whenever you change or add
/// tables, you should bump this field and provide a [migration] strategy.
int get schemaVersion;
/// Defines the migration strategy that will determine how to deal with an
/// increasing [schemaVersion]. The default value only supports creating the
/// database by creating all tables known in this database. When you have
/// changes in your schema, you'll need a custom migration strategy to create
/// the new tables or change the columns.
MigrationStrategy get migration => MigrationStrategy();
MigrationStrategy _cachedMigration;
MigrationStrategy get _resolvedMigration => _cachedMigration ??= migration;
/// A list of tables specified in this database.
List<TableInfo> get allTables;
/// A [Type] can't be sent across isolates. Instances of this class shouldn't
/// be sent over isolates either, so let's keep a reference to a [Type] that
/// definitely prohibits this.
// ignore: unused_field
final Type _$dontSendThisOverIsolates = Null;
/// Used by generated code
GeneratedDatabase(SqlTypeSystem types, QueryExecutor executor,
{StreamQueryStore streamStore})
: super(types, executor, streamQueries: streamStore) {
executor?.databaseInfo = this;
}
/// Used by generated code to connect to a database that is already open.
GeneratedDatabase.connect(DatabaseConnection connection)
: super.fromConnection(connection) {
connection?.executor?.databaseInfo = this;
}
/// Creates a [Migrator] with the provided query executor. Migrators generate
/// sql statements to create or drop tables.
///
/// This api is mainly used internally in moor, for instance in
/// [handleDatabaseCreation] and [handleDatabaseVersionChange]. However, it
/// can also be used if you need to create tables manually and outside of a
/// [MigrationStrategy]. For almost all use cases, overriding [migration]
/// should suffice.
@protected
Migrator createMigrator([SqlExecutor executor]) {
final actualExecutor = executor ?? customStatement;
return Migrator(this, actualExecutor);
}
/// Handles database creation by delegating the work to the [migration]
/// strategy. This method should not be called by users.
Future<void> handleDatabaseCreation({@required SqlExecutor executor}) {
final migrator = createMigrator(executor);
return _resolvedMigration.onCreate(migrator);
}
/// Handles database updates by delegating the work to the [migration]
/// strategy. This method should not be called by users.
Future<void> handleDatabaseVersionChange(
{@required SqlExecutor executor, int from, int to}) {
final migrator = createMigrator(executor);
return _resolvedMigration.onUpgrade(migrator, from, to);
}
/// Handles the before opening callback as set in the [migration]. This method
/// is used internally by database implementations and should not be called by
/// users.
Future<void> beforeOpenCallback(
QueryExecutor executor, OpeningDetails details) {
final migration = _resolvedMigration;
if (migration.beforeOpen != null) {
return _runEngineZoned(
BeforeOpenRunner(this, executor),
() => migration.beforeOpen(details),
);
}
return Future.value();
}
/// Closes this database and releases associated resources.
Future<void> close() async {
await executor.close();
}
}

View File

@ -1,136 +1,10 @@
import 'dart:async'; part of 'runtime_api.dart';
import 'package:meta/meta.dart';
import 'package:moor/moor.dart';
import 'package:moor/src/runtime/executor/stream_queries.dart';
part 'batch.dart';
const _zoneRootUserKey = #DatabaseConnectionUser; const _zoneRootUserKey = #DatabaseConnectionUser;
typedef _CustomWriter<T> = Future<T> Function( typedef _CustomWriter<T> = Future<T> Function(
QueryExecutor e, String sql, List<dynamic> vars); QueryExecutor e, String sql, List<dynamic> vars);
/// Class that runs queries to a subset of all available queries in a database.
///
/// This comes in handy to structure large amounts of database code better: The
/// migration logic can live in the main [GeneratedDatabase] class, but code
/// can be extracted into [DatabaseAccessor]s outside of that database.
/// For details on how to write a dao, see [UseDao].
abstract class DatabaseAccessor<T extends GeneratedDatabase>
extends DatabaseConnectionUser with QueryEngine {
@override
final bool topLevel = true;
/// The main database instance for this dao
@protected
final T db;
/// Used internally by moor
DatabaseAccessor(this.db) : super.delegate(db);
}
/// A database connection managed by moor. Contains three components:
/// - a [SqlTypeSystem], which is responsible to map between Dart types and
/// values understood by the database engine.
/// - a [QueryExecutor], which runs sql commands
/// - a [StreamQueryStore], which dispatches table changes to listening queries,
/// on which the auto-updating queries are based.
class DatabaseConnection {
/// The type system to use with this database. The type system is responsible
/// for mapping Dart objects into sql expressions and vice-versa.
final SqlTypeSystem typeSystem;
/// The executor to use when queries are executed.
final QueryExecutor executor;
/// Manages active streams from select statements.
final StreamQueryStore streamQueries;
/// Constructs a raw database connection from the three components.
DatabaseConnection(this.typeSystem, this.executor, this.streamQueries);
/// Constructs a [DatabaseConnection] from the [QueryExecutor] by using the
/// default type system and a new [StreamQueryStore].
DatabaseConnection.fromExecutor(this.executor)
: typeSystem = SqlTypeSystem.defaultInstance,
streamQueries = StreamQueryStore();
}
/// Manages a [DatabaseConnection] to send queries to the database.
abstract class DatabaseConnectionUser {
/// The database connection used by this [DatabaseConnectionUser].
@protected
final DatabaseConnection connection;
/// The type system to use with this database. The type system is responsible
/// for mapping Dart objects into sql expressions and vice-versa.
SqlTypeSystem get typeSystem => connection.typeSystem;
/// The executor to use when queries are executed.
QueryExecutor get executor => connection.executor;
/// Manages active streams from select statements.
@visibleForTesting
@protected
StreamQueryStore get streamQueries => connection.streamQueries;
/// Constructs a database connection user, which is responsible to store query
/// streams, wrap the underlying executor and perform type mapping.
DatabaseConnectionUser(SqlTypeSystem typeSystem, QueryExecutor executor,
{StreamQueryStore streamQueries})
: connection = DatabaseConnection(
typeSystem, executor, streamQueries ?? StreamQueryStore());
/// Creates another [DatabaseConnectionUser] by referencing the implementation
/// from the [other] user.
DatabaseConnectionUser.delegate(DatabaseConnectionUser other,
{SqlTypeSystem typeSystem,
QueryExecutor executor,
StreamQueryStore streamQueries})
: connection = DatabaseConnection(
typeSystem ?? other.connection.typeSystem,
executor ?? other.connection.executor,
streamQueries ?? other.connection.streamQueries,
);
/// Constructs a [DatabaseConnectionUser] that will use the provided
/// [DatabaseConnection].
DatabaseConnectionUser.fromConnection(this.connection);
/// Marks the tables as updated. This method will be called internally
/// whenever a update, delete or insert statement is issued on the database.
/// We can then inform all active select-streams on those tables that their
/// snapshot might be out-of-date and needs to be fetched again.
void markTablesUpdated(Set<TableInfo> tables) {
streamQueries.handleTableUpdates(tables);
}
/// Creates and auto-updating stream from the given select statement. This
/// method should not be used directly.
Stream<T> createStream<T>(QueryStreamFetcher<T> stmt) =>
streamQueries.registerStream(stmt);
/// Creates a copy of the table with an alias so that it can be used in the
/// same query more than once.
///
/// Example which uses the same table (here: points) more than once to
/// differentiate between the start and end point of a route:
/// ```
/// var source = alias(points, 'source');
/// var destination = alias(points, 'dest');
///
/// select(routes).join([
/// innerJoin(source, routes.startPoint.equalsExp(source.id)),
/// innerJoin(destination, routes.startPoint.equalsExp(destination.id)),
/// ]);
/// ```
T alias<T extends Table, D extends DataClass>(
TableInfo<T, D> table, String alias) {
return table.createAlias(alias).asDslTable;
}
}
/// Mixin for a [DatabaseConnectionUser]. Provides an API to execute both /// Mixin for a [DatabaseConnectionUser]. Provides an API to execute both
/// high-level and custom queries and fetch their results. /// high-level and custom queries and fetch their results.
mixin QueryEngine on DatabaseConnectionUser { mixin QueryEngine on DatabaseConnectionUser {
@ -442,95 +316,3 @@ mixin QueryEngine on DatabaseConnectionUser {
return context; return context;
} }
} }
/// A base class for all generated databases.
abstract class GeneratedDatabase extends DatabaseConnectionUser
with QueryEngine {
@override
final bool topLevel = true;
/// Specify the schema version of your database. Whenever you change or add
/// tables, you should bump this field and provide a [migration] strategy.
int get schemaVersion;
/// Defines the migration strategy that will determine how to deal with an
/// increasing [schemaVersion]. The default value only supports creating the
/// database by creating all tables known in this database. When you have
/// changes in your schema, you'll need a custom migration strategy to create
/// the new tables or change the columns.
MigrationStrategy get migration => MigrationStrategy();
MigrationStrategy _cachedMigration;
MigrationStrategy get _resolvedMigration => _cachedMigration ??= migration;
/// A list of tables specified in this database.
List<TableInfo> get allTables;
/// A [Type] can't be sent across isolates. Instances of this class shouldn't
/// be sent over isolates either, so let's keep a reference to a [Type] that
/// definitely prohibits this.
// ignore: unused_field
final Type _$dontSendThisOverIsolates = Null;
/// Used by generated code
GeneratedDatabase(SqlTypeSystem types, QueryExecutor executor,
{StreamQueryStore streamStore})
: super(types, executor, streamQueries: streamStore) {
executor?.databaseInfo = this;
}
/// Used by generated code to connect to a database that is already open.
GeneratedDatabase.connect(DatabaseConnection connection)
: super.fromConnection(connection) {
connection?.executor?.databaseInfo = this;
}
/// Creates a [Migrator] with the provided query executor. Migrators generate
/// sql statements to create or drop tables.
///
/// This api is mainly used internally in moor, for instance in
/// [handleDatabaseCreation] and [handleDatabaseVersionChange]. However, it
/// can also be used if you need to create tables manually and outside of a
/// [MigrationStrategy]. For almost all use cases, overriding [migration]
/// should suffice.
@protected
Migrator createMigrator([SqlExecutor executor]) {
final actualExecutor = executor ?? customStatement;
return Migrator(this, actualExecutor);
}
/// Handles database creation by delegating the work to the [migration]
/// strategy. This method should not be called by users.
Future<void> handleDatabaseCreation({@required SqlExecutor executor}) {
final migrator = createMigrator(executor);
return _resolvedMigration.onCreate(migrator);
}
/// Handles database updates by delegating the work to the [migration]
/// strategy. This method should not be called by users.
Future<void> handleDatabaseVersionChange(
{@required SqlExecutor executor, int from, int to}) {
final migrator = createMigrator(executor);
return _resolvedMigration.onUpgrade(migrator, from, to);
}
/// Handles the before opening callback as set in the [migration]. This method
/// is used internally by database implementations and should not be called by
/// users.
Future<void> beforeOpenCallback(
QueryExecutor executor, OpeningDetails details) {
final migration = _resolvedMigration;
if (migration.beforeOpen != null) {
return _runEngineZoned(
BeforeOpenRunner(this, executor),
() => migration.beforeOpen(details),
);
}
return Future.value();
}
/// Closes this database and releases associated resources.
Future<void> close() async {
await executor.close();
}
}

View File

@ -0,0 +1,11 @@
import 'dart:async';
import 'package:meta/meta.dart';
import 'package:moor/moor.dart';
import 'package:moor/src/runtime/executor/stream_queries.dart';
part 'batch.dart';
part 'connection.dart';
part 'db_base.dart';
part 'dao_base.dart';
part 'query_engine.dart';

View File

@ -2,7 +2,7 @@ import 'dart:async';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:moor/backends.dart'; import 'package:moor/backends.dart';
import 'package:moor/src/runtime/database.dart'; import 'package:moor/moor.dart' show GeneratedDatabase;
import 'package:moor/src/utils/hash.dart'; import 'package:moor/src/utils/hash.dart';
/// A query executor is responsible for executing statements on a database and /// A query executor is responsible for executing statements on a database and