Sqflite get locked on bulk insertion even after using transaction object and batch - flutter

I am new to flutter and I am doing bulk insertion in sqflite database. I have tried using both transaction and batch objects but my issue still remains there and database gets locked.
Here is what i am doing.
Future<List<ShopsModel>> fetchShops() async{
int count = 0;
List<ShopsModel> shopsList = [];
int id = 0;
String date = "";
List<SyncDataModel> syncList = await DatabaseHelper.instance.getSyncDataHistory();
syncList.forEach((element) {
id = element.SyncID!;
date = element.ShopSyncDate == null ? "" : element.ShopSyncDate!;
});
//Info.startProgress();
String response = await ApiServices.getMethodApi("${ApiUrls.IMPORT_SHOPS}?date=$date");
if(response.isEmpty || response == null){
return shopsList;
}
var shopsApiResponse = shopsApiResponseFromJson(response);
if(shopsApiResponse.data != null){
shopsApiResponse.data!.forEach((element) async{
await insertShops(element);
count++;
if(count == 1){
syncList.length == 0 ? await DatabaseHelper.instance.insertSyncDataHistory(
SyncDataModel(
ShopSyncDate: DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now()),
LastSyncDate: DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now())
)) :
await DatabaseHelper.instance.updateShopSyncDate(
DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now()), id);
}
});
}
return shopsList;
}
Future insertShops(ShopsModel row) async{
var shopsRow = await DatabaseHelper.instance.getShopByShopId(row.shopID!);
if(shopsRow.IsModify == 0 || shopsRow.IsModify == null) {
var result = await DatabaseHelper.instance.deleteImportedShops(
row.shopID!, DateFormat('yyyy-MM-dd HH:mm:ss').format(
DateTime.parse(row.updatedOn!)));
if (result > 0) {
print('Shop has been deleted');
}
await DatabaseHelper.instance.insertShops(
ShopsModel(
shopID: row.shopID,
shopName: row.shopName,
shopCode: row.shopCode,
contactPerson: row.contactPerson,
contactNo: row.contactNo,
nTNNO: row.nTNNO,
regionID: row.regionID,
areaID: row.areaID,
salePersonID: row.salePersonID,
createdByID: row.createdByID,
updatedByID: row.updatedByID,
systemNotes: row.systemNotes,
remarks: row.remarks,
description: row.description,
entryDate: DateFormat('yyyy-MM-dd HH:mm:ss').format(
DateTime.parse(row.entryDate!)),
branchID: row.branchID,
longitiude: row.longitiude,
latitiude: row.latitiude,
googleAddress: row.googleAddress,
createdOn: DateFormat('yyyy-MM-dd HH:mm:ss').format(
DateTime.parse(row.createdOn!)),
updatedOn: DateFormat('yyyy-MM-dd HH:mm:ss').format(
DateTime.parse(row.updatedOn!)),
tradeChannelID: row.tradeChannelID,
route: row.route,
vPO: row.vPO,
sEO: row.sEO,
imageUrl: row.imageUrl,
IsModify: 0
)
);
}
}
// Below are my Database methods
Future<int> deleteImportedShops(int shopID, String updatedDate) async{
Database db = await instance.database;
return await db.delete("$shopsTable", where: 'ShopID = ? AND UpdatedOn <= ?', whereArgs: [shopID, updatedDate]);
}
Future<void> insertShops(ShopsModel shopsRow) async{
Database db = await instance.database;
await db.transaction((txn) async {
var batch = txn.batch();
batch.insert("$shopsTable", shopsRow.toJson(), conflictAlgorithm: ConflictAlgorithm.replace);
await batch.commit();
});
}
Future<void> insertSyncDataHistory(SyncDataModel row) async{
Database db = await instance.database;
await db.transaction((txn) async {
var batch = txn.batch();
batch.insert("$syncDataTable", row.toJson(), conflictAlgorithm: ConflictAlgorithm.replace);
await batch.commit();
});
}
Future<void> updateShopSyncDate(String? pDate, int id) async{
Database db = await instance.database;
await db.transaction((txn) async {
var batch = txn.batch();
batch.rawUpdate("UPDATE SyncDataHistory SET ShopSyncDate = ?, LastSyncDate = ? WHERE SyncID = ?", [pDate, pDate, id]);
await batch.commit();
});
}
Here are the details what I am getting as an output.
Warning database has been locked for 0:00:10.000000. Make sure you always use the transaction object for database operations during a transaction
Please help me out. Any help would be appreciated.

Related

FLUTTER, SQFLITE ,How do I prevent deletion if related data still exists?

Hi guys I'm newbie in Flutter and currently having intern.
So I got 3 tables here, which are Item database is the main , group database, category database.
If the items databases is using the group, how should I prevent user delete group database because the group is in used in item database?
Will be great if someone can guide on me , because I'm totally new to this and because of intern I had to done it quickly..
import 'dart:async';
import 'dart:io' as io;
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'package:ezystock/model/item_model.dart';
import 'package:ezystock/model/group_model.dart';
import 'package:ezystock/model/category_model.dart';
class DBHelper {
static Database _db;
static const String DB_NAME = 'database.db';
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 'item' ('id' INTEGER PRIMARY KEY, 'code' TEXT, 'description' TEXT,'cost' REAL,'price' REAL,'category' TEXT,'group' TEXT)");
await db.execute("CREATE TABLE 'group' ('id' INTEGER PRIMARY KEY, 'code' TEXT, 'description' TEXT)");
await db.execute("CREATE TABLE 'category' ('id' INTEGER PRIMARY KEY, 'code' TEXT, 'description' TEXT)");
}
//Category
Future<Category> saveCategory(Category category) async {
var dbClient = await db;
category.id = await dbClient.insert("category", category.toMap());
return category;
}
Future<List<Category>> getCategory() async {
var dbClient = await db;
List<Map> maps = await dbClient.query("category", columns: ["id", "code","description"]);
//List<Map> maps = await dbClient.rawQuery("SELECT * FROM $TABLE");
List<Category> category = [];
if (maps.length > 0) {
for (int i = 0; i < maps.length; i++) {
category.add(Category.fromMap(maps[i]));
}
}
return category;
}
Future<int> deleteCategory(int id) async {
var dbClient = await db;
return await dbClient.delete("category", where: 'id = ?', whereArgs: [id]);
}
Future<int> updateCategory(Category category) async {
var dbClient = await db;
return await dbClient.update("category", category.toMap(),
where: 'id = ?', whereArgs: [category.id]);
}
//Group
Future<Group> saveGroups(Group group) async {
var dbClient = await db;
group.id = await dbClient.insert("group", group.toMap());
return group;
}
Future<List<Group>> getGroups() async {
var dbClient = await db;
List<Map> maps = await dbClient.query("group", columns: ["id", "code","description"]);
//List<Map> maps = await dbClient.rawQuery("SELECT * FROM $TABLE");
List<Group> groups = [];
if (maps.length > 0) {
for (int i = 0; i < maps.length; i++) {
groups.add(Group.fromMap(maps[i]));
}
}
return groups;
}
Future<int> deleteGroups(int id) async {
var dbClient = await db;
List<Map> items = await dbClient.query("item", where: 'group = ?', whereArgs: [id]);
if (items.length > 0) {
return await dbClient.delete("group", where: 'id = ?', whereArgs: [id]);
} else {
}
}
Future<int> updateGroups(Group group) async {
var dbClient = await db;
return await dbClient.update("group", group.toMap(),
where: 'id = ?', whereArgs: [group.id]);
}
//items
Future<Items> saveItems(Items items) async {
var dbClient = await db;
items.id = await dbClient.insert("item", items.toMap());
return items;
}
Future<List<Items>> getItems() async {
var dbClient = await db;
List<Map> maps = await dbClient.query("item", columns: ["id", "code","description","cost","price","category","group"]);
//List<Map> maps = await dbClient.rawQuery("SELECT * FROM $TABLE");
List<Items> groups = [];
if (maps.length > 0) {
for (int i = 0; i < maps.length; i++) {
groups.add(Items.fromMap(maps[i]));
}
}
return groups;
}
Future<int> deleteItems(int id) async {
var dbClient = await db;
return await dbClient.delete("item", where: 'id = ?', whereArgs: [id]);
}
Future<int> updateItems(Items items) async {
var dbClient = await db;
return await dbClient.update("item", items.toMap(),
where: 'id = ?', whereArgs: [items.id]);
}
Future close() async {
var dbClient = await db;
dbClient.close();
}
}
The answer is: dont provide any method to delete group table.
So remove this code:
Future<int> deleteGroups(int id) async {
var dbClient = await db;
List<Map> items = await dbClient.query("item", where: 'group = ?', whereArgs: [id]);
if (items.length > 0) {
return await dbClient.delete("group", where: 'id = ?', whereArgs: [id]);
} else {
}

Issues sqflite in flutter

It's been a while I started flutter. And now I am working on the database part. I started with sqflite since it was the ideal one for offline apps but now I can't understand a thing please can somebody help me with this.
import 'package:flutter/widgets.dart';
import 'note.dart';
import 'package:sqflite/sqflite.dart';
import 'dart:async';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
class DatabaseHelper {
static DatabaseHelper _databaseHelper = DatabaseHelper();
static Database? _database;
String noteTable = 'note_table';
String colID = 'id';
String colTitle = 'title';
String colDescription = 'description';
String colPriority = 'priority';
String colDate = 'date';
DatabaseHelper._createInstance();
factory DatabaseHelper() {
if (_databaseHelper == null) {
var databaseHelper = DatabaseHelper._createInstance();
_databaseHelper = databaseHelper;
}
return _databaseHelper;
}
//custom getter for the database
Future<Database> get database async {
// ignore: prefer_conditional_assignment
if (_database == null) {
_database = await initializeDatabase();
}
return _database!;
}
Future<Database> initializeDatabase() async {
Directory directory = await getApplicationDocumentsDirectory();
String path = directory.path + 'note.db';
var notesDatabase =
await openDatabase(path, version: 1, onCreate: _createDb);
return notesDatabase;
}
void _createDb(Database db, int newVersion) async {
await db.execute(
'CREATE TABLE $noteTable($colID INTEGER PRIMARY KEY AUTOINCREMENT, $colTitle TEXT, $colDescription TEXT, $colPriority INTEGER, $colDate TEXT)');
}
Future<List<Map<String, dynamic>>> getNoteMapList() async {
Database db = await this.database;
var result = await db.query(noteTable, orderBy: '$colPriority ASC');
return result;
}
Future<int> insertData(Note note) async {
Database db = await this.database;
var result = await db.insert(noteTable, note.toMap());
return result;
}
Future<int> updateNote(Note note) async {
Database db = await this.database;
var result = await db
.update(noteTable, note.toMap(), where: '$colID', whereArgs: [note.id]);
return result;
}
Future<int> deleteNote(int id) async {
Database db = await this.database;
var result =
await db.rawDelete('DELETE FROM $noteTable where $colID = $id');
return result;
}
Future<int> getCount() async {
Database db = await this.database;
List<Map<String, dynamic>> x =
await db.rawQuery('SELECT COUNT (*) FROM $noteTable');
int? result = Sqflite.firstIntValue(x);
return result!;
}
Future<List<Note>> getNoteList() async {
var noteMapList = await getNoteMapList();
int count = noteMapList.length;
}
}
This is the whole code. I have watched many videos but can't understand a thing. The problem starts when I the database part starts. And while answering the questions please try to be a little simple.

Why the Id of object addedItem is always return 1 until I refresh page

I want to get the real Id of object how's I create in real time without close page and return back to it
because I use initState() to get data from Database and if I tried to get Id without do that it will return 1 ;
any one know why this happened and who to fix it ?
this my Function:
item(String name,String desc,int rate) async{
int savedItem = await db.saveMovie(Movie(name, desc,rate.toString()));
Movie addedItem = await db.getMovie(savedItem);
setState(() {
movies.add(addedItem);
});
print("Item id :${addedItem.id} Saved item : ${savedItem}");
}
and this my database helper code :
import 'dart:async';
import 'package:sqflite/sqflite.dart';
import 'dart:io';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:mblists/models/movies.dart';
class DatabaseHelper {
final String moviesTable = "moviesTable";
final String idColumn = "id";
final String nameColumn = "name";
final String descriptionColumn = "description";
final String rateColumn = "rate";
static final DatabaseHelper _instance = DatabaseHelper.internal();
factory DatabaseHelper() => _instance;
static Database _db;
Future<Database> get db async{
if(_db != null){
return _db;
}
_db = await initDb();
return _db;
}
DatabaseHelper.internal();
initDb() async{
Directory fileDirectory = await getApplicationDocumentsDirectory();
String path = join(fileDirectory.path,"maindatabase.db");
var maindb = await openDatabase(path,version: 1,onCreate: _onCreate);
return maindb;
}
void _onCreate(Database db,int newVersion) async{
await db.execute(
"CREATE TABLE $moviesTable($idColumn INTEGER PRIMARY KEY, $nameColumn TEXT, $descriptionColumn TEXT, $rateColumn TEXT)");
}
Future<int> saveMovie(Movie movie) async{
var dbClient = await db;
int res = await dbClient.insert("$moviesTable", movie.toMap());
return res;
}
Future<List> getAllMovies() async{
var dbClient = await db;
var result = await dbClient.rawQuery("SELECT * FROM $moviesTable");
return result;
}
Future<Movie> getMovie(int id) async{
var dbClient = await db;
var result = await dbClient.rawQuery("SELECT * FROM $moviesTable WHERE $id = $id");
if(result.length == 0) {
return null;
}
return Movie.formMap(result.first);
}
Future<int> getCount() async {
var dbCllient = await db;
return Sqflite.firstIntValue(
await dbCllient.rawQuery("SELECT COUNT(*) FROM $moviesTable")
);
}
Future<int> deleteMovie(int id) async {
var dbClient = await db;
return await dbClient.delete(moviesTable,where: "$idColumn = ?",whereArgs: [ id]);
}
Future<int> deleteMovies() async {
var dbClient = await db;
return await dbClient.delete(moviesTable);
}
Future<int> updateMovie(Movie movie) async {
var dbClient = await db;
return await dbClient.update(moviesTable,movie.toMap(),
where: "$idColumn = ?" , whereArgs: [movie.id]
);
}
Future colse() async{
var dbClient = await db;
return await dbClient.close();
}
}
Insert method returns correct new id, but you have a typo in getMovie:
var result = await dbClient.rawQuery("SELECT * FROM $moviesTable WHERE $id = $id");
WHERE condition should contain column name, but your has id = id condition (which is always true) and then it takes the first element (always the same one). Fix it by passing id column's name:
var result = await dbClient.rawQuery("SELECT * FROM $moviesTable WHERE $idColumn = $id");
And it works:
I/flutter ( 5996): Item id :13 Saved item : {id: 13, name: test, description: desc, rate: 1}
I/flutter ( 5996): Item id :14 Saved item : {id: 14, name: test, description: desc, rate: 1}
I/flutter ( 5996): Item id :15 Saved item : {id: 15, name: test, description: desc, rate: 1}

throw ArgumentError("nullColumnHack required when inserting no data");Exception in Flutter

I am working on SqFlite programme but it shows ArgumentError Exception.
My code is not working it showing I can not insert data into database.
Please some one help me with this.SQFlite Operation like CRUD is not performing.
Exception like ArgumentError Exception like occurring some default dart file with Exception Showing.
Exception throw ArgumentError("nullColumnHack required when inserting no data");
import 'dart:async';
import 'dart:io';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqlite_app/models/note.dart';
class DatabaseHelper {
static DatabaseHelper _databaseHelper;
static Database _database;
String noteTable = 'note_table';
String colId = 'id';
String colTitle = 'title';
String colDescription = 'description';
String colPriority = 'priority';
String colDate = 'date';
DatabaseHelper._createInstance();
factory DatabaseHelper(){
if (_databaseHelper == null) {
_databaseHelper = DatabaseHelper._createInstance();
}
return _databaseHelper;
}
Future<Database> get databse async {
if (_database == null) {
_database = await initalizeDatabase();
}
return _database;
}
Future<Database> initalizeDatabase() async {
Directory directory = await getApplicationDocumentsDirectory();
String path = directory.path + 'note.db';
var noteDatabase = await openDatabase(
path, version: 1, onCreate: _createDb);
return noteDatabase;
}
void _createDb(Database db, int newVersion) async {
await db.execute(
'CREATE TABLE $noteTable ($colId INTEGER PRIMARY KEY AUTOINCREMENT,$colTitle TEXT,'
'$colDescription TEXT,$colPriority INTEGER,$colDate TEXT)');
}
Future<List<Map<String, dynamic>>> getNoteMapList() async {
Database db = await this.databse;
//var result=await db.rawQuery('SELECT * FROM $noteTable order by $colPriority ASC');
var result = await db.query(noteTable, orderBy: '$colPriority ASC');
return result;
}
Future<int> insertNote(Note note) async {
Database db = await this.databse;
var result = await db.insert(noteTable, note.toMap());
return result;
}
Future<int> updateNote(Note note) async {
var db = await this.databse;
var result = await db.update(
noteTable, note.toMap(), where: '$colId=?', whereArgs: [note.id]);
return result;
}
Future<int> deleteNote(int id) async {
var db = await this.databse;
int result = await db.rawDelete('DELETE FROM $noteTable WHERE $colId=$id');
return result;
}
Future<int> getCount() async {
Database db = await this.databse;
List<Map<String, dynamic>> x = await db.rawQuery(
"SELECT COUNT (*) from $noteTable");
int result = Sqflite.firstIntValue(x);
return result;
}
Future<List<Note>> getNoteList() async {
var noteMapList = await getNoteMapList();
int count = noteMapList.length;
List<Note> noteList = List<Note>();
for (int i = 0; i < count; i++) {
noteList.add(Note.fromMapObject(noteMapList[i]));
}
return noteList;
}
}
Just pass one more argument in db.insert(i.e. nullColumnHack) with value equal to the Auto Incrementing ID Integer...it will work...Do this for all other operations...
Future<int> insertNote(Note note) async {
Database db = await this.databse;
var result = await db.insert(noteTable, note.toMap(),nullColumnHack: colId);
return result;
}

How to upload a image to sqlite database in flutter using image_picker dependency

Future getImage(ImageSource imageSource) async {
var image = await ImagePicker.pickImage(source: imageSource);
setState(() {
_image = image;
});
}
You can convert the image to byte array and save it sqlite as a blob.
var image = await ImagePicker.pickImage(source: imageSource);
List<int> bytes = await image.readAsBytes();
//save to SQLite as a blob
I got the solution in my question. I'm getting the image from an image_picker and Encode it to BASE64 string value like below
Uint8List _bytesImage;
File _image;
String base64Image;
Future getImage() async {
var image2 = await ImagePicker.pickImage(
source: ImageSource.gallery,
);
List<int> imageBytes = image2.readAsBytesSync();
print(imageBytes);
base64Image = base64Encode(imageBytes);
print('string is');
print(base64Image);
print("You selected gallery image : " + image2.path);
_bytesImage = Base64Decoder().convert(base64Image);
setState(() {
_image=image2;
});
}
after creating an SQLite database dbhelper.dart file to retrieve String values and database model file Image.dart for the get and set the String values.
image.dart
class Image{
int id;
String image;
Employee(this.id, this.image);
Employee.fromMap(Map map) {
id= map[id];
image = map[image];
}
}
save into database
dbhelper.dart
class DBHelper {
static Database _db;
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, "test.db");
var theDb = await openDatabase(path, version: 1, onCreate: _onCreate);
return theDb;
}
void _onCreate(Database db, int version) async {
// When creating the db, create the table
await db.execute(
"CREATE TABLE Imagedata(id INTEGER PRIMARY KEY, image TEXT)");
print("Created tables");
}
void saveImage(Imagedata imagedata) async {
var dbClient = await db;
await dbClient.transaction((txn) async {
return await txn.rawInsert(
'INSERT INTO Imagedata(id, image) VALUES(' +
'\'' +
imagedata.id+
'\'' +
',' +
'\'' +
imagedata.image +
'\'' +
')');
});
}
Future<List<Imagedata>> getMyImage() async {
var dbClient = await db;
List<Map> list = await dbClient.rawQuery('SELECT * FROM Imagedata');
List<Imagedata> images= new List();
for (int i = 0; i < list.length; i++) {
images.add(new Imagedata(list[i]["id"], list[i]["image"]));
}
print(images.length);
return images;
}
Future<int> deleteMyImage(Imagedata imagedata) async {
var dbClient = await db;
int res =
await dbClient.rawDelete('DELETE * FROM Imagedata');
return res;
}
}