Flutter floor database Future bool action in DAO file? - database

While using floor database can we set an action which will turn as a boolean like below example?
Future<bool> isItAdded(in user_id) async{
var = dbClient = await db;
List<Map> list = await dbClient.rawQuery{SELECT * FROM Users WHERE user_id}, [user_id]}
return list.lenght > 0 ? true : false
}

You may write DAO object:
#dao
abstract class UsersDao {
#Query('SELECT * FROM users WHERE user_id = :userId')
Future<List<User>> findUsers(Int userId);
}
Before that you neeed to create entity:
#Entity(tableName: 'users')
class User{
#PrimaryKey(autoGenerate: true)
final int id;
#ColumnInfo(name: 'user_id')
final int userId;
}
Also you need to create database access object:
part 'database.g.dart'; // the generated code will be there
#Database(version: 1, entities: [User])
abstract class AppDatabase extends FloorDatabase {
UsersDao get usersDao;
}
Then generate additional code by command:
flutter packages pub run build_runner build
And then write check function inside database access object:
Future<bool> isItAdded(in user_id) async {
List<User> list = await usersDao.findUsers(user_id);
return list.lenght > 0;
}
The best solution is not to add user_id column and use only unique id column.

Related

Non-nullable instance field 'id' must be initialized, Non-nullable instance field '_database' must be initialized. in flutter

could anyone help me with how to manage this problem? I am trying to code exactly likes the course's video, but I got this problem:
I'm getting (Non-nullable instance field 'id' must be initialized.
Non-nullable instance field 'miles' must be initialized.
Non-nullable instance field 'name' must be initialized. Non-nullable instance field '_database' must be initialized.)
. errors in my project.
My car class:
import './dbhelper.dart';
class Car {
int id;
String name;
int miles;
Car(this.id, this.name, this.miles);
**GET ERROR FROM Car.fromMap..**
Car.fromMap(Map<String, dynamic> map) {
** Non-nullable instance field 'id' must be initialized.
Non-nullable instance field 'miles' must be initialized.
Non-nullable instance field 'name' must be initialized.**
id = map['id'];
name = map['name'];
miles = map['miles'];
}
Map<String, dynamic> toMap() {
return {
DatabaseHelper.columnId: id,
DatabaseHelper.columnName: name,
DatabaseHelper.columnMiles: miles,
};
}
}
My dbHelper.dart:
import './car.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DatabaseHelper {
static final _databaseName = "cardb.db";
static final _databaseVersion = 1;
static final table = 'cars_table';
static final columnId = 'id';
static final columnName = 'name';
static final columnMiles = 'miles';
** GET ERROR FROM DatabaseHelper._provateConstructor(); AND _database;
Non-nullable instance field '_database' must be initialized.**
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
** GET ERROR Non-nullable instance field '_database' must be initialized.**
static Database _database;
Future<Database> get database async {
if (_database != null) return _database;
// lazily instantiate the db the first time it is accessed
_database = await _initDatabase();
return _database;
}
// this opens the database (and creates it if it doesn't exist)
_initDatabase() async {
String path = join(await getDatabasesPath(), _databaseName);
return await openDatabase(path,
version: _databaseVersion,
onCreate: _onCreate);
}
// SQL code to create the database table
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE $table (
$columnId INTEGER PRIMARY KEY AUTOINCREMENT,
$columnName TEXT NOT NULL,
$columnMiles INTEGER NOT NULL
)
''');
}
// Helper methods
// Inserts a row in the database where each key in the Map is a column name
// and the value is the column value. The return value is the id of the
// inserted row.
Future<int> insert(Car car) async {
Database db = await instance.database;
return await db.insert(table, {'name': car.name, 'miles': car.miles});
}
// All of the rows are returned as a list of maps, where each map is
// a key-value list of columns.
Future<List<Map<String, dynamic>>> queryAllRows() async {
Database db = await instance.database;
return await db.query(table);
}
// Queries rows based on the argument received
Future<List<Map<String, dynamic>>> queryRows(name) async {
Database db = await instance.database;
return await db.query(table, where: "$columnName LIKE '%$name%'");
}
// All of the methods (insert, query, update, delete) can also be done using
// raw SQL commands. This method uses a raw query to give the row count.
Future<int> queryRowCount() async {
Database db = await instance.database;
return Sqflite.firstIntValue(await db.rawQuery('SELECT COUNT(*) FROM $table'));
}
// We are assuming here that the id column in the map is set. The other
// column values will be used to update the row.
Future<int> update(Car car) async {
Database db = await instance.database;
int id = car.toMap()['id'];
return await db.update(table, car.toMap(), where: '$columnId = ?', whereArgs: [id]);
}
// Deletes the row specified by the id. The number of affected rows is
// returned. This should be 1 as long as the row exists.
Future<int> delete(int id) async {
Database db = await instance.database;
return await db.delete(table, where: '$columnId = ?', whereArgs: [id]);
}
}
You have to put the null aware operator in the variables type for example:
class Car {
int? id; // <---- add a ? to int so it will be int?
String? name; // <-- same here
int? miles; // <--- and here
Car({this.id, this.name, this.miles});
Car.fromMap(Map<String, dynamic> map) {
id = map['id'];
name = map['name'];
miles = map['miles'];
}
Map<String, dynamic> toMap() {
return {
DatabaseHelper.columnId: id,
DatabaseHelper.columnName: name,
DatabaseHelper.columnMiles: miles,
};
}
}
Null safety as it is explain in the dart documentation:
When you opt into null safety, types in your code are non-nullable by
default, meaning that variables can’t contain null unless you say they
can. With null safety, your runtime null-dereference errors turn into
edit-time analysis errors.
More information in: https://dart.dev/null-safety
Maybe the tutorial you are taking was previous the null aware implementation and it does not apply the null aware operators, you can do the same at the beggining of the type declartion where the error is happening in other classes, you just have to add ? to the variable type at the beginning of the variable.

How to save nested list with sqflite in flutter?

Here I want to add bookmark functionality in my flutter news application. I get data from APIs. I display that data below.
This Image shows you how I get data from APIs
I am using this snippet for saving data with SQflite which I display below. I save this file with name bookmark_db_provider.dart.
import 'dart:io';
import 'home_screen_data.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
class DBProvider {
static Database _database;
static final DBProvider db = DBProvider._();
DBProvider._();
Future<Database> get database async {
if (_database != null) _database = await initDB();
return _database;
}
initDB() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
final path = join(documentsDirectory.path, 'ProductData.db');
return await openDatabase(path, version: 1, onOpen: (db) {},
onCreate: (Database db, int version) async {
await db.execute('CREATE TABLE ProductData('
'id INTEGER PRIMARY KEY,' //id
'categoryName Text,' //headline
'publisherName Text,' //description
'isAvailable Text,' //content
'categoryImgUrl Text' //image
')');
});
}
createProductData(ProductData productData) async {
await deleteAllProductData();
final db = await database;
final res = await db.insert('ProductData', productData.toJson());
return res;
}
Future<int> deleteAllProductData() async {
final db = await database;
final res = await db.delete('DELETE FROM ProductData');
return res;
}
Future<List<ProductData>> getProductDataList() async {
final db = await database;
final res = await db.rawQuery("SELECT * FROM ProductData");
List<ProductData> list = res.isNotEmpty ? res.map((x) => ProductData.fromJson(x)).toList() : [];
return list;
}
}
So, I want to how to save data and get data this with SQflite database in flutter. How I accomplish this?
This is an old question and hopes you have gotten an answer to it. However, a lot of flutter dev using sqflite struggles with handling data in a nested array format of the type mentioned e.g
``` {
"id": 1,
"name": "xyz",
"images": [
{
"imageId": 1,
"image": "image1"
},
{
"imageId": 2,
"image": "image2"
}
]
}
```
Since json handling is not part of sqflite at the moment, it is suggested to either;
a., save inner array as a string/text field in the 'image' column of table 'data' like
**
"[{"imageId": 1, "image": 'image1'}, {"imageId": 2, "image":
'image2'},}"
** , no guaranty.
or, b., flatten out the inner array so as to have only **
data[id, name, image1, image2, image3,...].
** this approach may be possible in a simple array as given but in a complex system. flattening out may be really cumbersome.
my suggestion, create two tables, one for data, and another for images. let each row of images table have reference or relationship with corresponding data table row. Your data class and images class will be something like,
```
class Data {
int dataId;
String name;
List<Image> images;
data({this.id, this.images, this.name});
...
```
and
```
class Image {
int imageId;
int dataId;
String image;
Image({this.imageId, this.dataId, this.image});
...
}
```
Your sqflite table data will have only two fields 'dataId' and 'name' and the image table must include 'dataId' as the relationship between the two tables.
to save data, you can use transactions like
```
void saveData(Data data, Map<String, Object> map) async {
await db.execute(""" INSERT INTO "data" (name) values (?) """, [data.name]);
// retrieve dataId of the new row inserted. last_inserted_rowid can also be used if the database does not contain several tables that may have been updated or saved before completing the transaction.
int dataId;
List<Map> x = await db.query('data', columns: ['dataId'], where: 'name = ?', whereArgs: [data.name]);
dataId = x[x.length - 1]['dataId'];
db.transaction((txn) async {
var batch = txn.batch();
data.images
.map((e) => {
batch.rawInsert(
""" INSERT INTO "images" (dataId,image,) values (?,?,?,? ) """, [dataId, e.image])
}).toList();
});
}
```
to retrieve data and images, you can try something like
```
Data _data = new Data();
Future<void> fetchData() async {
if (db != null) {
// do not execute if db is not instantiate
//retrieve all data from data table and store in instantiated, also instantiate images array as empty list
final dataList = await db.query(data);
_data = (dataList as List)
.map(
(data) => Data(
dataId: data['dataId'],
name: data['name'],
images: data['images'] != null ? data['images'] : []
)) .toList();
_data.forEach((data) async {if (data.images != null) {
List<Image> _images = [];
var imageResults = await db.query(images,where: 'dataId =?', whereArgs: [data.dataId]);
_images = (imageResults as List).map((e) => Image(
imageId: e['imageId'],
dataId: e['dataId'],
image: e['image']
)).toList();
_data.images.addAll(_images);
} });
}
}
```
with that approach, you should be able to handle nested array in flutter and sqflite
I might be unclear with your question, but according to what I understood,
You need to call the method of this provider with data that you want:
DBProvider.init() // Use this only one which when the application is instaled
After that, you can call these methods from anywhere to put and get data
DBProvider.createProductData(productData) //insert data
Get data
DBProvider.getProductDataList() //get data

How to create multiple table in Sqlite database at different times in Flutter

So Initially I created a table named "TABLE" in the database. I wanted to add another table. So I added another query to create a table. However I get an error saying "TABLE1 does not exist" when I run the app.
I do feel like there is a flaw in my code. I think the _onCreate() method will only be called the first time I run the app. So any code I add on _onCreate() method afterwards will not run. Any help will be appreciated.
class DBHelper {
static Database _db;
static const String DB_NAME = 'employeeDB';
static const String ID = 'id';
static const String NAME = 'name';
static const String TABLE = 'Employee';
static const String ID1 = 'id1';
static const String NAME1 = 'name1';
static const String TABLE1 = 'Employee1';
Future<Database> get db async {
if (_db != null) {
return _db;
}
_db = await initDb();
return _db;
}
initDb() async {
io.Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, DB_NAME);
var db = await openDatabase(path, version: 1, onCreate: _onCreate);
return db;
}
_onCreate(Database db, int version) async {
await db.execute("CREATE TABLE $TABLE ($ID INTEGER PRIMARY KEY,$NAME TEXT)");
await db.execute("CREATE TABLE $TABLE1 ($ID1 INTEGER PRIMARY KEY,$NAME1 TEXT)");
}
Future<Employee> save(Employee employee) async {
var dbClient = await db;
employee.id = await dbClient.insert(TABLE, employee.toMap());
return employee;
}
Future<Employee1> saveEmp1(Employee1 employee) async {
var dbClient = await db;
employee.id = await dbClient.insert(TABLE1, employee.toMap());
return employee;
}
Future<List<Employee>> getEmployees() async {
var dbClient = await db;
List<Map> maps = await dbClient.query(TABLE, columns: [ID, NAME]);
List<Employee> employees = [];
if (maps.length > 0) {
for (int i = 0; i < maps.length; i++) {
employees.add(Employee.fromMap(maps[i]));
}
}
return employees;
}
Future<List<Employee1>> getEmployees1() async {
var dbClient = await db;
List<Map> maps = await dbClient.query(TABLE1, columns: [ID1,
NAME1]);
List<Employee1> employees = [];
if (maps.length > 0) {
for (int i = 0; i < maps.length; i++) {
employees.add(Employee.fromMap(maps[i]));
}
}
return employees;
}
}
The first time run this app and initDb() in emulator, the db file employeeDB has created.
and it will not be created again
For only test app execution,
you can change
String DB_NAME = 'employeeDB'
to another name
String DB_NAME = 'employeeDB1'
or you can uninstall this app from Emulator first then run it again.
source code snippet of https://github.com/tekartik/sqflite/blob/master/sqflite/lib/sqlite_api.dart
/// Called when the database is created.
OnDatabaseCreateFn onCreate;
For schema migration, you can use OnUpgrade, detail reference https://efthymis.com/migrating-a-mobile-database-in-flutter-sqlite/
code snippet
await openDatabase(path,
version: 1,
onCreate: (Database db, int version) async {
await db.execute(initialSchema));
},
onUpgrade: (Database db, int oldVersion, int newVersion) async {
await db.execute(migrationScript));
});
You have to create migrations and run them against your existing database using the onUpgrade handler. Basically you need to check the existing database version and upgrade if the version is smaller than the migration number.
You can check out the detail steps/code tutorial here.
I needed to create more than just multiple tables; but multiple database. I have since developed a dart package; sqlite_at_runtime that stretches to as far as creating all these entities at run time.
Below is an example code that creates three tables with similar attributes.
await Sqlartime.tableCreate(['sample1','sample2','sample3'],['name TEXT','age INTEGER','temp REAL']);

NHibernate Convert query to async query

I'm looking at async-ifying some of our existing code. Unfortunately my experience with NHibernate is lacking. Most of the NHibernate stuff has been easy, considering NHibernate 5 has a lot of support for async. I am, however, stuck.
Originally, we do something like this using our Dependency Injection:
private readonly IRepository repository;
public MovieRepository(IRepository repository)
{
this.repository = repository;
}
public Movie Get(int id)
{
return (from movie in repository.Query<Movie>()
select new Movie
{
ID = movie.ID,
Title = movie.Title,
Genre = new Genre
{
ID = movie.Genre.ID,
Name = movie.Genre.Name,
},
MaleLead = movie.MaleLead,
FemaleLead = movie.FemaleLead,
}).FirstOrDefault();
}
//Repository Query method in Repository.cs
public IQueryable<TEntity> Query<TEntity>() where TEntity : OurEntity
{
session = session.OpenSession();
return from entity in session.Query<TEntity>() select entity;
}
This works great for our current uses. We write things this way to maintain control over our queries, especially related to more complex objects, ensuring we get back exactly what we need.
I've tried a few things, like making the Query method return a Task< List< TEntity>> and using the ToListAsync() method, however because I am returning it as that kind of list I cannot query on it.
I'm sure I've missed something. If anyone can help me out, I would appreciate it.
You need to use FirstOrDefaultAsync in this case.
public async Task<Movie> Get(int id)
{
return await (from movie in repository.Query<Movie>()
select new Movie
{
ID = movie.ID,
Title = movie.Title,
Genre = new Genre
{
ID = movie.Genre.ID,
Name = movie.Genre.Name,
},
MaleLead = movie.MaleLead,
FemaleLead = movie.FemaleLead,
}).FirstOrDefaultAsync();
}
Add this using statement to your file
using NHibernate.Linq;
Then you can change your method to
public async Task<Movie> Get(int id)
{
return await (from movie in repository.Query<Movie>()
select new Movie
{
ID = movie.ID,
Title = movie.Title,
Genre = new Genre
{
ID = movie.Genre.ID,
Name = movie.Genre.Name,
},
MaleLead = movie.MaleLead,
FemaleLead = movie.FemaleLead,
}).FirstOrDefaultAsync();
}
NB: This is only available from NHibernate 5
Addendum:
The code you have in Repository.cs can be simplified to something like this:
//Repository Query method in Repository.cs
public IQueryable<TEntity> Query<TEntity>() where TEntity : OurEntity
{
//session = session.OpenSession(); //this is obviously wrong, but it's beside the point
var session = sessionFactory.OpenSession();
return session.Query<TEntity>(); //the fix
}

ASP.NET Core encryption and SQL Server

I am creating a default user for my system. However I am creating through SQL Server database.
I use ASP.NET Core and Entity Framework to handle logins, by default Entity Framework creates a table called AspNetUsers; this table has a column called PasswordHash, I believe the encryption used is of the Hash type.
I am entering the password on this user by the database as follows:
DECLARE #HashThis nvarchar(4000);
SET #HashThis = CONVERT(nvarchar(4000),'Administrador');
SELECT HASHBYTES('SHA1', #HashThis)
UPDATE AspNetUsers
SET PasswordHash = HASHBYTES('SHA1', #HashThis),
SecurityStamp = '0b12450e-016d-4cd6-af7b-fa6d2198586f',
ConcurrencyStamp = 'a63a5236-4020-4f69-93b1-9f077ba014cd',
UserName = 'administrador#administrador.com.br'
But the password column is getting strange characters in Japanese, it follows the image:
The biggest issue and when I log in ASP.NET Core, only the password invalidates.
How can I do to bypass this?
Observation: when I create the user through ASP.NET Core, it works normally.
Here is one example how you can seed you user:
In you SecurityDbContext you can create following methods (I added SeedRoles in case you need them):
public static async Task Seed(IServiceProvider serviceProvider)
{
await SeedRoles(serviceProvider);
await SeedUsers(serviceProvider);
}
Seed Roles:
public static async Task SeedRoles(IServiceProvider serviceProvider)
{
RoleManager<ApplicationRole> roleManager = serviceProvider.GetRequiredService<RoleManager<ApplicationRole>>();
string[] roles = ...;
foreach(var role in roles)
{
ApplicationRole appRole = await roleManager.FindByNameAsync(role);
if (appRole == null)
{
await roleManager.CreateAsync(new ApplicationRole(role));
}
}
}
Seed User:
public static async Task SeedUser(IServiceProvider serviceProvider, UserManager<ApplicationUser> userManager, string email, string password, string roleName = "")
{
string userName = roleName;
ApplicationUser user = await userManager.FindByNameAsync(userName);
if (user == null)
{
// Create user account if it doesn't exist
user = new ApplicationUser
{
UserName = userName,
Email = email
};
IdentityResult result = await userManager.CreateAsync(user, password);
// Assign role to the user
if (result.Succeeded)
{
user = await userManager.FindByNameAsync(userName);
}
}
if (user != null && roleName.Length > 0)
{
await userManager.AddToRoleAsync(user, roleName);
}
}
From SeedUsers method, just call SeedUser as many times as you need.
And then just simply call Seed method from Startup.cs Configure method:
SecurityDbContextSeed.Seed(app.ApplicationServices).Wait();

Resources