pinchbv / floor Goto Github PK
View Code? Open in Web Editor NEWThe typesafe, reactive, and lightweight SQLite abstraction for your Flutter applications
Home Page: https://pinchbv.github.io/floor/
License: Apache License 2.0
The typesafe, reactive, and lightweight SQLite abstraction for your Flutter applications
Home Page: https://pinchbv.github.io/floor/
License: Apache License 2.0
Right now there is some validation for queries. But all types of queries (SELECT, INSERT, UPDATE, DELETE) could be fully validated when generating the methods that implement them.
Generate a SQL CREATE TABLE
statement from an object (entity).
Hi,
I've stumbled upon your library, and being an Android developer I've loved the Room inspiration of it.
I've started introducing it in one of my project, and found out the DateTime type is not supported.
I think it would be great to add this datatype to the ones recognized by the library.
Thanks in advance
Now generated SQL is 'select * from user where id = $id'
It's best to generate like this
query('select * from user where id = ?', [id])
This enables to swap out the SQLite database access library (sqflite) with only touching a single class.
This opens up the possibility to generate the required code for all entity classes even when they are located in a different file or directory. But this would probably also require a custom Builder
to collect all entity ClassElement
s.
Hi,
I'm using your amazing package for my first app on Flutter and I'm having an issue.
I want to query the data and return the result of these queries into some objects that are not table.
For instance if I want to have the result of a group by query like this one:
Select R.roomName, count(*) as numberOfStudents
from tblRooms R
Inner Join tblStudents S on (S.roomId=R.roomId)
Group By R.roomName
I need the possibility to setup a class like this:
@View()
class RoomStudentsCountView {
String roomName;
int numberOfStudents;
RoomStudentsCountView (this.roomName, this.numberOfStudents);
}
I replaced the @entity with @view as alternative to describe this type of entity.
I also find sometime that I need the possibility to define field that are not column of the table but just calculated field.
Here's an example:
@Entity(tableName: "tblRooms")
class RoomEntity {
@PrimaryKey(autoGenerate: true)
int roomId;
String roomName;
@CalculatedField()
int numberOfStudents;
RoomEntity (this.roomId, this.roomName, this.numberOfStudents);
}
I can try to help by looking at the code and try to adapt it but I'm not sure I have enough experience to make it right.
I hope you can help on this. :)
Hi,
Not sure if this is related to #113. But I have a simple DAO with the following query
@Query("select * from sports")
Future<List<Sport>> getAll() ;
However, I get the following compilation error
lib/db/db.g.dart:70:35: Error: Too many positional arguments: 1 allowed, but 2 found.
Try removing the extra positional arguments.
return _queryAdapter.queryList('select * from sports', _sportsMapper);
in the generated .part file
@override
Future<List<Sport>> getAll() async {
return _queryAdapter.queryList('select * from sports', _sportsMapper);
}
This started happening when I upgraded from 0.3.0 to 0.4.0. And I was upgrading because the code for inserting List into the DB did not seem to be working in 0.3.0
Apply them by using @transaction
annotation.
Whenever inserting objects open up the possibility to return the inserted ID or IDs in case a list is getting inserted.
Investigate what Room returns on update and delete and decide if support these as well.
This also includes adding a version field to the database annotation.
Create SqlParser
that returns a query object, which holds information about the query (SELECT, DELETE, etc.).
This is only required for queries annotated with @Query
.
This is the first step in making returning the result of inserting, updating or deleting records possible.
Let's just stick with SQLite natively supported types, except booleans, for now. Embedded objects and relations will get added later on.
Type mapping:
int
- INTEGERdouble
- REALString
- TEXTbool
- INTEGER (false = 0, true = 1)This should get enabled by using the tableName
field of the Entity
annotation.
An entity should be able to hold nested objects. There are different strategies for the implementation of this. One is, to embed all fields of the nested object in the surrounding object table.
This is required to allow queries like DELETE FROM Person
.
It is also required to return objects that are not entities when using @Query()
.
E.g.
@Query('SELECT * FROM person WHERE id = :id AND name = :name')
Future<Person> findPersonByIdAndName(int id);
Do some benchmarks to find out the overhead of using transactions.
This should include a custom column name and probably the ability for nullable and non-nullable columns.
This required some additional logic in QueryMethodWriter
.
@Ignore
annotation has to get introduced for this.
After running command flutter packages pub run build_runner build
, i am getting syntax error in database.g.dart file which is undefined StreamController and undefined PersonMapper.
pubspec.yaml
environment:
sdk: ">=2.1.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
floor: ^0.2.0
dev_dependencies:
flutter_test:
sdk: flutter
floor_generator: ^0.2.0
build_runner: ^1.2.8
database.dart
import 'package:floor/floor.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart' as sqflite;
import './person.dart';
import './person-dao.dart';
part 'database.g.dart'; // the generated code will be there
@Database(version: 1, entities: [Person])
abstract class AppDatabase extends FloorDatabase {
static Future<AppDatabase> openDatabase() async => _$open();
PersonDao get personDao;
}
person.dart
import 'package:floor/floor.dart';
@entity
class Person {
@primaryKey
final int id;
final String name;
Person(this.id, this.name);
}
person-dao.dart
import 'package:floor/floor.dart';
import './person.dart';
@dao
abstract class PersonDao {
@Query('SELECT * FROM Person')
Future<List<Person>> findAllPersons();
@Query('SELECT * FROM Person WHERE id = :id')
Future<Person> findPersonById(int id);
@insert
Future<void> insertPerson(Person person);
}
Make sure to run these on a CI (Travis most likely).
It could be done similar to this:
Future<MyDatabase> _$open() async {
final database = _$MyDatabase();
database.database = await database.open();
return database;
}
The @Entity
annotation should have a new field primaryKey
, which holds information about the key.
I created entity with name QuestionData
and set its table name as questions
.
When i ran command flutter packages pub run build_runner build
, it generated database file with function _questionsInsertionAdapter
being called in insert method, but it declared _questionDataInsertionAdapter
in class _$QuestionsDao
, instead of declaring _questionsInsertionAdapter. It caused syntax error of undefined _questionDataInsertionAdapter
in generated file.
Below is are my files' code to better explain this bug.
questions-dao.dart
import 'package:floor/floor.dart';
import '../modals/response.dart';
@dao
abstract class QuestionsDao {
@Query('SELECT * from ${QuestionData.TABLE_NAME}')
Future<List<QuestionData>> findAllQuestions();
@Query('SELECT * from ${QuestionData.TABLE_NAME} where isAnswered=false')
Future<List<QuestionData>> findAllUnansweredQuestions();
@Insert()
Future<void> insertQuestions(List<QuestionData> questions);
}
response.dart
import 'package:floor/floor.dart';
@Entity(tableName: QuestionData.TABLE_NAME)
class QuestionData {
static const TABLE_NAME = 'questions';
@primaryKey
int id;
String questionOne;
String questionTwo;
int questionOneVoteCount;
int questionTwoVoteCount;
bool isAnswered = false;
QuestionData();
QuestionData.fromMap(Map<String, dynamic> map) {
id = map['id'];
questionOne = map['questionOne'];
questionTwo = map['questionTwo'];
questionOneVoteCount = map['questionOneVoteCount'];
questionTwoVoteCount = map['questionTwoVoteCount'];
}
String getQuestionOneVotePercentage() {
final percentage =
(this.questionOneVoteCount + 1) / (this.getTotalVotesGiven() + 1) * 100;
return '${percentage.toInt()}%';
}
String getQuestionTwoVotePercentage() {
final percentage =
(this.questionTwoVoteCount + 1) / (this.getTotalVotesGiven() + 1) * 100;
return '${percentage.toInt()}%';
}
int getTotalVotesGiven() {
return this.questionOneVoteCount + this.questionTwoVoteCount;
}
}
akkh-database.dart
import 'dart:async';
import 'package:floor/floor.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart' as sqflite;
import '../modals/response.dart';
import 'questions-dao.dart';
part 'akkh-database.g.dart';
@Database(version: 1, entities: [QuestionData])
abstract class AkkhDatabase extends FloorDatabase {
static Future<AkkhDatabase> openDatabase() async => _$open();
QuestionsDao get questionsDao;
}
akkh-database.g.dart
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'akkh-database.dart';
// **************************************************************************
// FloorGenerator
// **************************************************************************
Future<AkkhDatabase> _$open([List<Migration> migrations = const []]) async {
final database = _$AkkhDatabase();
database.database = await database.open(migrations);
return database;
}
class _$AkkhDatabase extends AkkhDatabase {
QuestionsDao _questionsDaoInstance;
@override
Future<sqflite.Database> open(List<Migration> migrations) async {
final path = join(await sqflite.getDatabasesPath(), 'akkhdatabase.db');
return sqflite.openDatabase(
path,
version: 1,
onConfigure: (database) async {
await database.execute('PRAGMA foreign_keys = ON');
},
onUpgrade: (database, startVersion, endVersion) async {
MigrationAdapter.runMigrations(
database, startVersion, endVersion, migrations);
},
onCreate: (database, version) async {
await database.execute(
'CREATE TABLE IF NOT EXISTS `questions` (`TABLE_NAME` TEXT, `id` INTEGER PRIMARY KEY NOT NULL, `questionOne` TEXT, `questionTwo` TEXT, `questionOneVoteCount` INTEGER, `questionTwoVoteCount` INTEGER, `isAnswered` INTEGER)');
},
);
}
@override
QuestionsDao get questionsDao {
return _questionsDaoInstance ??= _$QuestionsDao(database, changeListener);
}
}
class _$QuestionsDao extends QuestionsDao {
_$QuestionsDao(this.database, this.changeListener)
: _queryAdapter = QueryAdapter(database),
_questionDataInsertionAdapter = InsertionAdapter(
database, 'questions', (QuestionData item) => <String, dynamic>{});
final sqflite.DatabaseExecutor database;
final StreamController<String> changeListener;
final QueryAdapter _queryAdapter;
final _questionsMapper = (Map<String, dynamic> row) => QuestionData();
final InsertionAdapter<QuestionData> _questionDataInsertionAdapter;
@override
Future<List<QuestionData>> findAllQuestions() async {
return _queryAdapter.queryList('SELECT * from questions', _questionsMapper);
}
@override
Future<List<QuestionData>> findAllUnansweredQuestions() async {
return _queryAdapter.queryList(
'SELECT * from questions where isAnswered=false', _questionsMapper);
}
@override
Future<void> insertQuestions(List<QuestionData> questions) async {
await _questionsInsertionAdapter.insertList(
questions, sqflite.ConflictAlgorithm.abort);
}
}
It should contain something like:
The library is heavily influenced by the Room abstraction layer. As I wanted to stay in the housing terms and the database usually is the lowest layer of an application, the name Floor came to my mind.
Hi Vitus,
The following query
@Query("update sports set rated = 1 where id in (:ids)")
Future<void> setRated(List<int> ids) ;
generates the following in the .part
. Note the missing closing parenthesis in the query - (? instead of (?)
@override
Future<void> setRated(List<int> ids) async {
await _queryAdapter
.queryNoReturn('update sports set rated = 1 where id in (?');
}
This causes the following run-time exception
DatabaseException(near "?": syntax error (code 1): , while compiling: update sports set rated = 1 where id in (?) sql 'update sports set rated = 1 where id in (?' args []}
Functions inside the database class (annotated with @Database
), which are annotated with @Query
,@insert
,@update
and @delete
should get generated.
This task is dependent on #4, because the mapping between the in-memory objects and the table rows should happen in these functions.
Dependent #4
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.