GithubHelp home page GithubHelp logo

pinchbv / floor Goto Github PK

View Code? Open in Web Editor NEW
924.0 14.0 187.0 1.92 MB

The typesafe, reactive, and lightweight SQLite abstraction for your Flutter applications

Home Page: https://pinchbv.github.io/floor/

License: Apache License 2.0

Java 0.02% Ruby 0.25% Objective-C 0.09% Dart 61.42% Shell 0.21% HTML 0.21% JavaScript 37.80%
flutter dart sqlite

floor's Introduction

Floor

See the project's website for the full documentation.

Floor provides a neat SQLite abstraction for your Flutter applications inspired by the Room persistence library. It comes with automatic mapping between in-memory objects and database rows while still offering full control of the database with the use of SQL. As a consequence, it's necessary to have an understanding of SQL and SQLite in order to harvest Floor's full potential.

  • null-safe
  • typesafe
  • reactive
  • lightweight
  • SQL centric
  • no hidden magic
  • no hidden costs
  • iOS, Android, Linux, macOS, Windows

โš ๏ธ The library is open to contributions! Refer to GitHub Discussions for questions, ideas, and discussions.

pub package build status codecov

Getting Started

1. Setup Dependencies

Add the runtime dependency floor as well as the generator floor_generator to your pubspec.yaml. The third dependency is build_runner which has to be included as a dev dependency just like the generator.

  • floor holds all the code you are going to use in your application.
  • floor_generator includes the code for generating the database classes.
  • build_runner enables a concrete way of generating source code files.
dependencies:
  flutter:
    sdk: flutter
  floor: ^1.4.2

dev_dependencies:
  floor_generator: ^1.4.2
  build_runner: ^2.1.2

2. Create an Entity

It will represent a database table as well as the scaffold of your business object. @entity marks the class as a persistent class. It's required to add a primary key to your table. You can do so by adding the @primaryKey annotation to an int property. There is no restriction on where you put the file containing the entity.

// entity/person.dart

import 'package:floor/floor.dart';

@entity
class Person {
  @primaryKey
  final int id;

  final String name;

  Person(this.id, this.name);
}

3. Create a DAO (Data Access Object)

This component is responsible for managing access to the underlying SQLite database. The abstract class contains the method signatures for querying the database which have to return a Future or Stream.

  • You can define queries by adding the @Query annotation to a method. The SQL statement has to get added in parenthesis. The method must return a Future or Stream of the Entity you're querying for.
  • @insert marks a method as an insertion method.
// dao/person_dao.dart

import 'package:floor/floor.dart';

@dao
abstract class PersonDao {
  @Query('SELECT * FROM Person')
  Future<List<Person>> findAllPeople();

  @Query('SELECT name FROM Person')
  Stream<List<String>> findAllPeopleName();

  @Query('SELECT * FROM Person WHERE id = :id')
  Stream<Person?> findPersonById(int id);

  @insert
  Future<void> insertPerson(Person person);
}

4. Create the Database

It has to be an abstract class which extends FloorDatabase. Furthermore, it's required to add @Database() to the signature of the class. Make sure to add the created entity to the entities attribute of the @Database annotation. In order to make the generated code work, it's required to also add the listed imports.

Make sure to add part 'database.g.dart'; beneath the imports of this file. It's important to note that 'database' has to get exchanged with the filename of the database definition. In this case, the file is named database.dart.

// database.dart

// required package imports
import 'dart:async';
import 'package:floor/floor.dart';
import 'package:sqflite/sqflite.dart' as sqflite;

import 'dao/person_dao.dart';
import 'entity/person.dart';

part 'database.g.dart'; // the generated code will be there

@Database(version: 1, entities: [Person])
abstract class AppDatabase extends FloorDatabase {
  PersonDao get personDao;
}

5. Run the Code Generator

Run the generator with flutter packages pub run build_runner build. To automatically run it, whenever a file changes, use flutter packages pub run build_runner watch.

6. Use the Generated Code

For obtaining an instance of the database, use the generated $FloorAppDatabase class, which allows access to a database builder. The name is being composed by $Floor and the database class name. The string passed to databaseBuilder() will be the database file name. For initializing the database, call build() and make sure to await the result.

In order to retrieve the PersonDao instance, invoking the persoDao getter on the database instance is enough. Its functions can be used as shown in the following snippet.

final database = await $FloorAppDatabase.databaseBuilder('app_database.db').build();

final personDao = database.personDao;
final person = Person(1, 'Frank');

await personDao.insertPerson(person);
final result = await personDao.findPersonById(1);

For further examples take a look at the example and test directories.

Naming

The library's name derives from the following. Floor as the bottom layer of a Room which points to the analogy of the database layer being the bottom and foundation layer of most applications. Where fl also gives a pointer that the library is used in the Flutter context.

Bugs, Ideas, and Feedback

For bugs please use GitHub Issues. For questions, ideas, and discussions use GitHub Discussions.

License

Copyright 2023 The Floor Project Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

floor's People

Contributors

0biwankenobi avatar afzaal-ahmad-zeeshan avatar cassioseffrin avatar dkaera avatar finger-software avatar hendrikvdkaaden avatar jiechic avatar john-he-928 avatar manoylok avatar mirland avatar mqus avatar noordawod avatar paricleu avatar rivasdiaz avatar stephanmantel avatar thekewlstore avatar thomasmiddel avatar vitusortner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

floor's Issues

Generate code for database access functions

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.

  • query
  • insert
  • update
  • delete

Dependent #4

Mapping between entity objects and table rows

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 - INTEGER
  • double - REAL
  • String - TEXT
  • bool - INTEGER (false = 0, true = 1)

Require Entity classes to be added to Database annotation

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 ClassElements.

Return last inserted ID/s

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.

  • Insert
  • Update
  • Delete

(0.4.0) queryList - the parameter 'mapper' is required

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

Undefined StreamController, undefined PersonMapper

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);
}

Parse queries into objects

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.

Support embedded objects

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.

(0.4.0) Syntax error in generated code for SQL IN clause

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 []}

Provide more query validation

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.

Add support for DateTime type

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

Create initial README

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.

Allow query to return objects not entities

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. :)

InsertionAdapter not found in generated database file.

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);
  }
}

Enable custom mapping of a column

This should include a custom column name and probably the ability for nullable and non-nullable columns.

This required some additional logic in QueryMethodWriter.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.