Updated ORM

This commit is contained in:
thomashii@dukefirehawk.com 2021-05-04 12:03:08 +08:00
parent a4ff96a831
commit e11f8f77e1
7 changed files with 92 additions and 90 deletions

View file

@ -141,9 +141,13 @@ class EnumSqlExpressionBuilder<T> extends SqlExpressionBuilder<T> {
UnsupportedError('Enums do not support this operation.'); UnsupportedError('Enums do not support this operation.');
@override @override
String? compile() { String compile() {
if (_raw != null) return _raw; if (_raw != null) {
if (_value == null) return null; return _raw!;
}
if (_value == null) {
return '';
}
return '$_op $_value'; return '$_op $_value';
} }
@ -243,7 +247,7 @@ class StringSqlExpressionBuilder extends SqlExpressionBuilder<String> {
void isBetween(String lower, String upper) { void isBetween(String lower, String upper) {
query.substitutionValues[lowerName] = lower; query.substitutionValues[lowerName] = lower;
query.substitutionValues[upperName] = upper; query.substitutionValues[upperName] = upper;
_raw = "BETWEEN @$lowerName AND @$upperName"; _raw = 'BETWEEN @$lowerName AND @$upperName';
_hasValue = true; _hasValue = true;
} }
@ -506,10 +510,10 @@ abstract class JsonSqlExpressionBuilder<T, K> extends SqlExpressionBuilder<T> {
} }
@override @override
String? compile() { String compile() {
var s = _compile(); var s = _compile();
if (!_properties.any((p) => p.hasValue)) return s; if (!_properties.any((p) => p.hasValue)) return s;
s ??= ''; //s ??= '';
for (var p in _properties) { for (var p in _properties) {
if (p.hasValue) { if (p.hasValue) {
@ -517,7 +521,7 @@ abstract class JsonSqlExpressionBuilder<T, K> extends SqlExpressionBuilder<T> {
if (c != null) { if (c != null) {
_hasValue = true; _hasValue = true;
s ??= ''; //s ??= '';
if (p.typed is! DateTimeSqlExpressionBuilder) { if (p.typed is! DateTimeSqlExpressionBuilder) {
s += '${p.typed!.columnName} '; s += '${p.typed!.columnName} ';
@ -531,10 +535,14 @@ abstract class JsonSqlExpressionBuilder<T, K> extends SqlExpressionBuilder<T> {
return s; return s;
} }
String? _compile() { String _compile() {
if (_raw != null) return _raw; if (_raw != null) {
if (_value == null) return null; return _raw!;
return "::jsonb $_op @$substitution::jsonb"; }
if (_value == null) {
return '';
}
return '::jsonb $_op @$substitution::jsonb';
} }
void contains(T value) { void contains(T value) {

View file

@ -9,7 +9,7 @@ import 'query_where.dart';
import 'package:optional/optional.dart'; import 'package:optional/optional.dart';
/// A SQL `SELECT` query builder. /// A SQL `SELECT` query builder.
abstract class Query<T, Where extends QueryWhere?> extends QueryBase<T> { abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
final List<JoinBuilder> _joins = []; final List<JoinBuilder> _joins = [];
final Map<String, int> _names = {}; final Map<String, int> _names = {};
final List<OrderBy> _orderBy = []; final List<OrderBy> _orderBy = [];
@ -80,27 +80,27 @@ abstract class Query<T, Where extends QueryWhere?> extends QueryBase<T> {
/// Determines whether this query can be compiled. /// Determines whether this query can be compiled.
/// ///
/// Used to prevent ambiguities in joins. /// Used to prevent ambiguities in joins.
bool canCompile(Set<String>? trampoline) => true; bool canCompile(Set<String> trampoline) => true;
/// Shorthand for calling [where].or with a [Where] clause. /// Shorthand for calling [where].or with a [Where] clause.
void andWhere(void Function(Where) f) { void andWhere(void Function(Where) f) {
var w = newWhereClause(); var w = newWhereClause();
f(w); f(w);
where?.and(w); where.and(w);
} }
/// Shorthand for calling [where].or with a [Where] clause. /// Shorthand for calling [where].or with a [Where] clause.
void notWhere(void Function(Where) f) { void notWhere(void Function(Where) f) {
var w = newWhereClause(); var w = newWhereClause();
f(w); f(w);
where?.not(w); where.not(w);
} }
/// Shorthand for calling [where].or with a [Where] clause. /// Shorthand for calling [where].or with a [Where] clause.
void orWhere(void Function(Where) f) { void orWhere(void Function(Where) f) {
var w = newWhereClause(); var w = newWhereClause();
f(w); f(w);
where?.or(w); where.or(w);
} }
/// Limit the number of rows to return. /// Limit the number of rows to return.
@ -128,12 +128,12 @@ abstract class Query<T, Where extends QueryWhere?> extends QueryBase<T> {
_crossJoin = tableName; _crossJoin = tableName;
} }
String _joinAlias(Set<String>? trampoline) { String _joinAlias(Set<String> trampoline) {
var i = _joins.length; var i = _joins.length;
while (true) { while (true) {
var a = 'a$i'; var a = 'a$i';
if (trampoline!.add(a)) { if (trampoline.add(a)) {
return a; return a;
} else { } else {
i++; i++;
@ -310,8 +310,8 @@ abstract class Query<T, Where extends QueryWhere?> extends QueryBase<T> {
} }
var whereClause = var whereClause =
where?.compile(tableName: includeTableName ? tableName : null); where.compile(tableName: includeTableName ? tableName : null);
if (whereClause?.isNotEmpty == true) { if (whereClause.isNotEmpty == true) {
b.write(' WHERE $whereClause'); b.write(' WHERE $whereClause');
} }
if (_groupBy != null) b.write(' GROUP BY $_groupBy'); if (_groupBy != null) b.write(' GROUP BY $_groupBy');
@ -357,7 +357,7 @@ abstract class Query<T, Where extends QueryWhere?> extends QueryBase<T> {
Future<Optional<T>> insert(QueryExecutor executor) { Future<Optional<T>> insert(QueryExecutor executor) {
var insertion = values.compileInsert(this, tableName); var insertion = values.compileInsert(this, tableName);
if (insertion == null) { if (insertion == '') {
throw StateError('No values have been specified for update.'); throw StateError('No values have been specified for update.');
} else { } else {
// TODO: How to do this in a non-Postgres DB? // TODO: How to do this in a non-Postgres DB?
@ -375,12 +375,12 @@ abstract class Query<T, Where extends QueryWhere?> extends QueryBase<T> {
var updateSql = StringBuffer('UPDATE $tableName '); var updateSql = StringBuffer('UPDATE $tableName ');
var valuesClause = values.compileForUpdate(this); var valuesClause = values.compileForUpdate(this);
if (valuesClause == null) { if (valuesClause == '') {
throw StateError('No values have been specified for update.'); throw StateError('No values have been specified for update.');
} else { } else {
updateSql.write(' $valuesClause'); updateSql.write(' $valuesClause');
var whereClause = where?.compile(); var whereClause = where.compile();
if (whereClause?.isNotEmpty == true) { if (whereClause.isNotEmpty == true) {
updateSql.write(' WHERE $whereClause'); updateSql.write(' WHERE $whereClause');
} }
if (_limit != null) updateSql.write(' LIMIT $_limit'); if (_limit != null) updateSql.write(' LIMIT $_limit');

View file

@ -14,12 +14,13 @@ abstract class QueryValues {
} }
} }
String? compileInsert(Query query, String tableName) { String compileInsert(Query query, String tableName) {
var data = Map<String, dynamic>.from(toMap()); var data = Map<String, dynamic>.from(toMap());
var keys = data.keys.toList(); var keys = data.keys.toList();
keys.where((k) => !query.fields.contains(k)).forEach(data.remove); keys.where((k) => !query.fields.contains(k)).forEach(data.remove);
if (data.isEmpty) return null; if (data.isEmpty) {
return '';
}
var fieldSet = data.keys.join(', '); var fieldSet = data.keys.join(', ');
var b = StringBuffer('INSERT INTO $tableName ($fieldSet) VALUES ('); var b = StringBuffer('INSERT INTO $tableName ($fieldSet) VALUES (');
var i = 0; var i = 0;
@ -37,9 +38,11 @@ abstract class QueryValues {
return b.toString(); return b.toString();
} }
String? compileForUpdate(Query query) { String compileForUpdate(Query query) {
var data = toMap(); var data = toMap();
if (data.isEmpty) return null; if (data.isEmpty) {
return '';
}
var b = StringBuffer('SET'); var b = StringBuffer('SET');
var i = 0; var i = 0;

View file

@ -2,27 +2,27 @@ import 'builder.dart';
/// Builds a SQL `WHERE` clause. /// Builds a SQL `WHERE` clause.
abstract class QueryWhere { abstract class QueryWhere {
final Set<QueryWhere?> _and = Set(); final Set<QueryWhere> _and = {};
final Set<QueryWhere?> _not = Set(); final Set<QueryWhere> _not = {};
final Set<QueryWhere?> _or = Set(); final Set<QueryWhere> _or = {};
Iterable<SqlExpressionBuilder> get expressionBuilders; Iterable<SqlExpressionBuilder> get expressionBuilders;
void and(QueryWhere? other) { void and(QueryWhere other) {
_and.add(other); _and.add(other);
} }
void not(QueryWhere? other) { void not(QueryWhere other) {
_not.add(other); _not.add(other);
} }
void or(QueryWhere? other) { void or(QueryWhere other) {
_or.add(other); _or.add(other);
} }
String compile({String? tableName}) { String compile({String? tableName}) {
var b = StringBuffer(); var b = StringBuffer();
int i = 0; var i = 0;
for (var builder in expressionBuilders) { for (var builder in expressionBuilders) {
var key = builder.columnName; var key = builder.columnName;
@ -40,17 +40,17 @@ abstract class QueryWhere {
} }
for (var other in _and) { for (var other in _and) {
var sql = other!.compile(); var sql = other.compile();
if (sql.isNotEmpty) b.write(' AND ($sql)'); if (sql.isNotEmpty) b.write(' AND ($sql)');
} }
for (var other in _not) { for (var other in _not) {
var sql = other!.compile(); var sql = other.compile();
if (sql.isNotEmpty) b.write(' NOT ($sql)'); if (sql.isNotEmpty) b.write(' NOT ($sql)');
} }
for (var other in _or) { for (var other in _or) {
var sql = other!.compile(); var sql = other.compile();
if (sql.isNotEmpty) b.write(' OR ($sql)'); if (sql.isNotEmpty) b.write(' OR ($sql)');
} }

View file

@ -7,7 +7,7 @@ import 'package:angel_orm_test/src/models/car.dart';
class CarController extends Controller { class CarController extends Controller {
@Expose('/luxury') @Expose('/luxury')
Future<List<Car?>> getLuxuryCars(QueryExecutor connection) { Future<List<Car?>> getLuxuryCars(QueryExecutor connection) {
var query = new CarQuery(); var query = CarQuery();
query.where query.where
?..familyFriendly.equals(false) ?..familyFriendly.equals(false)
..createdAt.year.greaterThanOrEqualTo(2014) ..createdAt.year.greaterThanOrEqualTo(2014)

View file

@ -12,7 +12,7 @@ abstract class _Role {
String? get role; String? get role;
@ManyToMany(_RoleUser) @ManyToMany(_RoleUser)
List<_User?>? get users; List<_User> get users;
} }
@serializable @serializable
@ -35,5 +35,5 @@ abstract class _User {
String? get password; String? get password;
@ManyToMany(_RoleUser) @ManyToMany(_RoleUser)
List<_Role?>? get roles; List<_Role> get roles;
} }

View file

@ -59,7 +59,7 @@ class UserMigration extends Migration {
// OrmGenerator // OrmGenerator
// ************************************************************************** // **************************************************************************
class RoleQuery extends Query<Role?, RoleQueryWhere?> { class RoleQuery extends Query<Role, RoleQueryWhere> {
RoleQuery({Query? parent, Set<String>? trampoline}) : super(parent: parent) { RoleQuery({Query? parent, Set<String>? trampoline}) : super(parent: parent) {
trampoline ??= Set(); trampoline ??= Set();
trampoline.add(tableName); trampoline.add(tableName);
@ -137,8 +137,7 @@ class RoleQuery extends Query<Role?, RoleQueryWhere?> {
var l = out[idx]; var l = out[idx];
return out return out
..[idx] = l.copyWith( ..[idx] = l.copyWith(
users: List<_User?>.from(l.users ?? []) users: List<_User>.from(l.users)..addAll(model!.users));
..addAll(model!.users ?? []));
} }
}); });
}); });
@ -156,8 +155,7 @@ class RoleQuery extends Query<Role?, RoleQueryWhere?> {
var l = out[idx]!; var l = out[idx]!;
return out return out
..[idx] = l.copyWith( ..[idx] = l.copyWith(
users: List<_User?>.from(l.users ?? []) users: List<_User>.from(l.users)..addAll(model!.users));
..addAll(model!.users ?? []));
} }
}); });
}); });
@ -175,8 +173,7 @@ class RoleQuery extends Query<Role?, RoleQueryWhere?> {
var l = out[idx]; var l = out[idx];
return out return out
..[idx] = l.copyWith( ..[idx] = l.copyWith(
users: List<_User?>.from(l.users ?? []) users: List<_User>.from(l.users)..addAll(model!.users));
..addAll(model!.users ?? []));
} }
}); });
}); });
@ -329,7 +326,7 @@ class RoleUserQueryValues extends MapQueryValues {
} }
} }
class UserQuery extends Query<User?, UserQueryWhere?> { class UserQuery extends Query<User, UserQueryWhere> {
UserQuery({Query? parent, Set<String>? trampoline}) : super(parent: parent) { UserQuery({Query? parent, Set<String>? trampoline}) : super(parent: parent) {
trampoline ??= Set(); trampoline ??= Set();
trampoline.add(tableName); trampoline.add(tableName);
@ -345,7 +342,7 @@ class UserQuery extends Query<User?, UserQueryWhere?> {
@override @override
final UserQueryValues values = UserQueryValues(); final UserQueryValues values = UserQueryValues();
UserQueryWhere? _where; UserQueryWhere _where;
@override @override
get casts { get casts {
@ -363,7 +360,7 @@ class UserQuery extends Query<User?, UserQueryWhere?> {
} }
@override @override
UserQueryWhere? get where { UserQueryWhere get where {
return _where; return _where;
} }
@ -401,17 +398,16 @@ class UserQuery extends Query<User?, UserQueryWhere?> {
@override @override
get(QueryExecutor executor) { get(QueryExecutor executor) {
return super.get(executor).then((result) { return super.get(executor).then((result) {
return result.fold<List<User?>>([], (out, model) { return result.fold<List<User>>([], (out, model) {
var idx = out.indexWhere((m) => m!.email == model!.email); var idx = out.indexWhere((m) => m.email == model.email);
if (idx == -1) { if (idx == -1) {
return out..add(model); return out..add(model);
} else { } else {
var l = out[idx]!; var l = out[idx];
return out return out
..[idx] = l.copyWith( ..[idx] = l.copyWith(
roles: List<_Role?>.from(l.roles ?? []) roles: List<_Role>.from(l.roles)..addAll(model.roles));
..addAll(model!.roles ?? []));
} }
}); });
}); });
@ -421,16 +417,15 @@ class UserQuery extends Query<User?, UserQueryWhere?> {
update(QueryExecutor executor) { update(QueryExecutor executor) {
return super.update(executor).then((result) { return super.update(executor).then((result) {
return result.fold<List<User>>([], (out, model) { return result.fold<List<User>>([], (out, model) {
var idx = out.indexWhere((m) => m.email == model!.email); var idx = out.indexWhere((m) => m.email == model.email);
if (idx == -1) { if (idx == -1) {
return out..add(model!); return out..add(model);
} else { } else {
var l = out[idx]; var l = out[idx];
return out return out
..[idx] = l.copyWith( ..[idx] = l.copyWith(
roles: List<_Role?>.from(l.roles ?? []) roles: List<_Role>.from(l.roles)..addAll(model.roles));
..addAll(model!.roles ?? []));
} }
}); });
}); });
@ -440,16 +435,15 @@ class UserQuery extends Query<User?, UserQueryWhere?> {
delete(QueryExecutor executor) { delete(QueryExecutor executor) {
return super.delete(executor).then((result) { return super.delete(executor).then((result) {
return result.fold<List<User>>([], (out, model) { return result.fold<List<User>>([], (out, model) {
var idx = out.indexWhere((m) => m.email == model!.email); var idx = out.indexWhere((m) => m.email == model.email);
if (idx == -1) { if (idx == -1) {
return out..add(model!); return out..add(model);
} else { } else {
var l = out[idx]; var l = out[idx];
return out return out
..[idx] = l.copyWith( ..[idx] = l.copyWith(
roles: List<_Role?>.from(l.roles ?? []) roles: List<_Role>.from(l.roles)..addAll(model.roles));
..addAll(model!.roles ?? []));
} }
}); });
}); });
@ -508,16 +502,16 @@ class UserQueryValues extends MapQueryValues {
@generatedSerializable @generatedSerializable
class Role implements _Role { class Role implements _Role {
const Role({this.role, this.users}); const Role({this.role, this.users = const []});
@override @override
final String? role; final String? role;
@override @override
final List<_User?>? users; final List<_User> users;
Role copyWith({String? role, List<_User?>? users}) { Role copyWith({String? role, List<_User> users = const []}) {
return Role(role: role ?? this.role, users: users ?? this.users); return Role(role: role ?? this.role, users: users);
} }
bool operator ==(other) { bool operator ==(other) {
@ -537,7 +531,7 @@ class Role implements _Role {
return "Role(role=$role, users=$users)"; return "Role(role=$role, users=$users)";
} }
Map<String, dynamic>? toJson() { Map<String, dynamic> toJson() {
return RoleSerializer.toMap(this); return RoleSerializer.toMap(this);
} }
} }
@ -577,7 +571,7 @@ class RoleUser implements _RoleUser {
@generatedSerializable @generatedSerializable
class User implements _User { class User implements _User {
const User({this.email, this.name, this.password, this.roles}); const User({this.email, this.name, this.password, this.roles = const []});
@override @override
final String? email; final String? email;
@ -589,15 +583,18 @@ class User implements _User {
final String? password; final String? password;
@override @override
final List<_Role?>? roles; final List<_Role> roles;
User copyWith( User copyWith(
{String? email, String? name, String? password, List<_Role?>? roles}) { {String? email,
String? name,
String? password,
List<_Role> roles = const []}) {
return User( return User(
email: email ?? this.email, email: email ?? this.email,
name: name ?? this.name, name: name ?? this.name,
password: password ?? this.password, password: password ?? this.password,
roles: roles ?? this.roles); roles: roles);
} }
bool operator ==(other) { bool operator ==(other) {
@ -605,7 +602,7 @@ class User implements _User {
other.email == email && other.email == email &&
other.name == name && other.name == name &&
other.password == password && other.password == password &&
ListEquality<_Role?>(DefaultEquality<_Role>()) ListEquality<_Role>(DefaultEquality<_Role>())
.equals(other.roles, roles); .equals(other.roles, roles);
} }
@ -619,7 +616,7 @@ class User implements _User {
return "User(email=$email, name=$name, password=$password, roles=$roles)"; return "User(email=$email, name=$name, password=$password, roles=$roles)";
} }
Map<String, dynamic>? toJson() { Map<String, dynamic> toJson() {
return UserSerializer.toMap(this); return UserSerializer.toMap(this);
} }
} }
@ -657,16 +654,13 @@ class RoleSerializer extends Codec<Role, Map?> {
users: map['users'] is Iterable users: map['users'] is Iterable
? List.unmodifiable(((map['users'] as Iterable).whereType<Map>()) ? List.unmodifiable(((map['users'] as Iterable).whereType<Map>())
.map(UserSerializer.fromMap)) .map(UserSerializer.fromMap))
: null); : []);
} }
static Map<String, dynamic>? toMap(_Role? model) { static Map<String, dynamic> toMap(_Role model) {
if (model == null) {
return null;
}
return { return {
'role': model.role, 'role': model.role,
'users': model.users?.map((m) => UserSerializer.toMap(m)).toList() 'users': model.users.map((m) => UserSerializer.toMap(m)).toList()
}; };
} }
} }
@ -714,8 +708,8 @@ class RoleUserSerializer extends Codec<RoleUser, Map> {
static Map<String, dynamic> toMap(_RoleUser model) { static Map<String, dynamic> toMap(_RoleUser model) {
return { return {
'role': RoleSerializer.toMap(model.role), 'role': (model.role != null) ? RoleSerializer.toMap(model.role!) : '',
'user': UserSerializer.toMap(model.user) 'user': (model.user != null) ? UserSerializer.toMap(model.user!) : ''
}; };
} }
} }
@ -759,18 +753,15 @@ class UserSerializer extends Codec<User, Map?> {
roles: map['roles'] is Iterable roles: map['roles'] is Iterable
? List.unmodifiable(((map['roles'] as Iterable).whereType<Map>()) ? List.unmodifiable(((map['roles'] as Iterable).whereType<Map>())
.map(RoleSerializer.fromMap)) .map(RoleSerializer.fromMap))
: null); : []);
} }
static Map<String, dynamic>? toMap(_User? model) { static Map<String, dynamic> toMap(_User model) {
if (model == null) {
return null;
}
return { return {
'email': model.email, 'email': model.email,
'name': model.name, 'name': model.name,
'password': model.password, 'password': model.password,
'roles': model.roles?.map((m) => RoleSerializer.toMap(m)).toList() 'roles': model.roles.map((m) => RoleSerializer.toMap(m)).toList()
}; };
} }
} }